单片机基础核心知识点汇总(二十)

目录

前言

[一、Demo 坏代码示例(](#一、Demo 坏代码示例()

[二、四层分层模型(裸机、RTOS 通用)](#二、四层分层模型(裸机、RTOS 通用))

[1. 驱动层 driver](#1. 驱动层 driver)

[2. 组件层 component](#2. 组件层 component)

[3. 业务层 app](#3. 业务层 app)

[4. 调度层 scheduler](#4. 调度层 scheduler)

三、重构实操核心规则

[四、RTOS 下简单改造要点](#四、RTOS 下简单改造要点)

五、头文件书写规范(工程必守)


前言

本篇聚焦工程重构实战,区分 demo 代码和产品代码,讲解分层原则、模块解耦、裸机与 RTOS 工程改造,附带极简示例代码,为后面进阶专题做铺垫。

一、Demo 坏代码示例(

特征:业务、驱动混在一起,大量全局变量,直接操作寄存器,到处魔数

复制代码
#include "stm32f10x.h"

uint16_t adc_val;
uint8_t flag;

int main(void)
{
    RCC->APB2ENR |= (1<<2)|(1<<9);
    GPIOA->CRL &= ~0x0000000F;
    ADC1->CR2 |= 1;

    while(1)
    {
        ADC1->SWSTART |= 1;
        while(!(ADC1->SR & (1<<1)));
        adc_val = ADC1->DR;

        if(adc_val > 3000)
        {
            GPIOA->BSRR = 1<<5;
            flag = 1;
        }
        else
        {
            GPIOA->BRR = 1<<5;
            flag = 0;
        }
    }
}

问题:

  1. 寄存器直接散落在 main,换芯片全部重写
  2. ADC 采集、判断逻辑、IO 控制全部耦合
  3. 魔数3000、引脚 5 硬编码,修改要多处改动
  4. 没有封装,无法复用,不能单元测试

二、四层分层模型(裸机、RTOS 通用)

1. 驱动层 driver

只做硬件操作,不含业务逻辑。对外只提供函数接口。

driver_adc.h

复制代码
#ifndef __DRIVER_ADC_H
#define __DRIVER_ADC_H

#include <stdint.h>

void drv_adc_init(void);
uint16_t drv_adc_get_sample(void);

#endif

driver_adc.c

复制代码
#include "driver_adc.h"
#include "stm32f10x.h"

void drv_adc_init(void)
{
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_ADC1EN;
    GPIOA->CRL &= ~GPIO_CRL_CNF0;
    ADC1->CR2 |= ADC_CR2_ADON;
}

uint16_t drv_adc_get_sample(void)
{
    ADC1->SWSTART |= ADC_CR2_SWSTART;
    while(!(ADC1->SR & ADC_SR_EOC));
    return ADC1->DR;
}

driver_led.h

复制代码
#ifndef __DRIVER_LED_H
#define __DRIVER_LED_H

void drv_led_init(void);
void drv_led_set(uint8_t on);

#endif

driver_led.c

复制代码
#include "driver_led.h"
#include "stm32f10x.h"

#define LED_PIN     GPIO_Pin_5
#define LED_PORT    GPIOA

void drv_led_init(void)
{
    GPIO_InitTypeDef gpio_cfg;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    gpio_cfg.GPIO_Pin = LED_PIN;
    gpio_cfg.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio_cfg.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(LED_PORT, &gpio_cfg);
}

void drv_led_set(uint8_t on)
{
    if(on)
        GPIO_SetBits(LED_PORT, LED_PIN);
    else
        GPIO_ResetBits(LED_PORT, LED_PIN);
}

2. 组件层 component

数据处理、工具组件,不操作硬件。 component_filter.h 简单滑动滤波示例

复制代码
#ifndef __COMPONENT_FILTER_H
#define __COMPONENT_FILTER_H

#include <stdint.h>

#define FILTER_BUF_LEN  8

typedef struct
{
    uint16_t buf[FILTER_BUF_LEN];
    uint8_t index;
}filter_slide_t;

void filter_slide_init(filter_slide_t *f);
uint16_t filter_slide_calc(filter_slide_t *f, uint16_t new_val);

#endif

component_filter.c

复制代码
#include "component_filter.h"

void filter_slide_init(filter_slide_t *f)
{
    for(int i=0;i<FILTER_BUF_LEN;i++)
        f->buf[i] = 0;
    f->index = 0;
}

uint16_t filter_slide_calc(filter_slide_t *f, uint16_t new_val)
{
    f->buf[f->index++] = new_val;
    if(f->index >= FILTER_BUF_LEN) f->index = 0;

    uint32_t sum = 0;
    for(int i=0;i<FILTER_BUF_LEN;i++)
        sum += f->buf[i];
    return sum / FILTER_BUF_LEN;
}

3. 业务层 app

只处理业务逻辑,调用驱动与组件,不碰寄存器。 app_detect.h

复制代码
#ifndef __APP_DETECT_H
#define __APP_DETECT_H

void app_detect_init(void);
void app_detect_task(void);

#endif

app_detect.c

复制代码
#include "app_detect.h"
#include "driver_adc.h"
#include "driver_led.h"
#include "component_filter.h"

#define ADC_THRESHOLD   3000

static filter_slide_t adc_filter;

void app_detect_init(void)
{
    drv_adc_init();
    drv_led_init();
    filter_slide_init(&adc_filter);
}

void app_detect_task(void)
{
    uint16_t raw = drv_adc_get_sample();
    uint16_t val = filter_slide_calc(&adc_filter, raw);

    if(val > ADC_THRESHOLD)
    {
        drv_led_set(1);
    }
    else
    {
        drv_led_set(0);
    }
}

4. 调度层 scheduler

裸机时间戳轮询调度,分离各个业务的执行节拍,消除阻塞 delay。 scheduler.h

复制代码
#ifndef __SCHEDULER_H
#define __SCHEDULER_H

void scheduler_init(void);
void scheduler_run(void);

#endif

scheduler.c

复制代码
#include "scheduler.h"
#include "app_detect.h"
#include "stm32f10x.h"

static uint32_t tick_ms = 0;

void SysTick_Handler(void)
{
    tick_ms++;
}

void scheduler_init(void)
{
    SysTick_Config(SystemCoreClock / 1000U);
    app_detect_init();
}

void scheduler_run(void)
{
    static uint32_t t1 = 0;
    if(tick_ms - t1 >= 20)
    {
        t1 = tick_ms;
        app_detect_task();
    }
}

main.c 最终入口,极度简洁

复制代码
#include "scheduler.h"

int main(void)
{
    scheduler_init();
    while(1)
    {
        scheduler_run();
    }
}

核心原则:上层可以调用下层,下层绝对不调用上层。禁止跨层直接访问变量。

三、重构实操核心规则

  1. 单一职责:一个函数只完成一件事,拒绝几百行大函数。
  2. 模块拆分 :一类功能放到同一组.c/.h,内部变量尽量用static私有化,头文件只对外暴露必要接口。
  3. 禁止跨层访问:app 不能直接调用寄存器;driver 层不能包含业务判断。
  4. 魔数全部宏定义,阈值、引脚、周期集中管理。
  5. 大数组禁止放局部栈内,使用 static 或者全局。
  6. 中断只做标记,业务处理交给调度层任务。

四、RTOS 下简单改造要点

以 FreeRTOS 举例,业务层几乎不用改,调度层换成任务:

复制代码
void app_detect_task_thread(void *arg)
{
    (void)arg;
    app_detect_init();
    for(;;)
    {
        app_detect_task();
        vTaskDelay(pdMS_TO_TICKS(20));
    }
}

//main中创建任务
xTaskCreate(app_detect_task_thread, "detect", 512, NULL, 2, NULL);

注意:多任务访问共享数据,需要加互斥锁保护。

五、头文件书写规范(工程必守)

  1. .h只放:函数声明、宏、typedef,不能放变量定义 ;变量定义放在.c,h 用extern引出。
  2. 头文件保护宏#ifndef / #define / #endif必须写。
  3. 尽量少 include 其他头文件在.h,减少编译依赖。
相关推荐
单片机仿真设计2 分钟前
【proteus仿真】基于 STM32F103 的汽车温度监测系统(仿真图+程序)
stm32·单片机·嵌入式硬件·proteus·课设
anew___8 分钟前
舵机——让 Arduino 动起来
c++·stm32·单片机·嵌入式硬件·c
周洲083036 分钟前
STM32 SPI 通信实战|高速通信、片选、数据收发
stm32·嵌入式硬件
POMAGTOR41 分钟前
【5G设备POGOPIN连接器 高频弹簧针通信连接器件】
嵌入式硬件·5g·机器人
意法半导体STM321 小时前
【官方原创】LAT1709STM32C5硬件CRC值的计算话题
stm32·单片机·嵌入式硬件·mcu·crc·微控制器·stm32c5
新思维软件1 小时前
基于 STM32 的快递柜自动存取系统设计与实现(项目编号 X604041)
stm32·嵌入式硬件·物联网·物联网开发
xx~t1 小时前
嵌入式——ARM——汇编2
linux·汇编·arm开发·嵌入式硬件·启动代码
M78佐菲2 小时前
ARM学习笔记(三)
linux·arm开发·笔记·嵌入式硬件·学习
威嵌神州2 小时前
天脉 3 开发环境与 BSP 适配:IDE license、驱动开发、飞腾平台
ide·驱动开发·嵌入式硬件·fpga开发
辰域电子2 小时前
STM32项目开源:智能插座控制系统(代码 + 原理图 + 仿真)
stm32·单片机·嵌入式硬件·开源·51单片机·proteus