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

目录

前言

[一、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,减少编译依赖。
相关推荐
笨笨饿32 分钟前
#121_图传中的H.364编码与MP4的联系
linux·运维·前端·单片机·嵌入式硬件·面试·职场和发展
送外卖的工程师1 小时前
STM32智能鞋柜(机智云)
stm32·单片机·嵌入式硬件
Romantic_love_3 小时前
USART串口初始化源码解析
stm32·单片机·嵌入式硬件·学习
oshan20123 小时前
零基础STM32入门教程--11-USART/UART 串行通迅
stm32·单片机·嵌入式硬件
BW.SU4 小时前
RUI Studio--嵌入式 UI 开发新范式
单片机·ui·嵌入式开发
集芯微电科技有限公司4 小时前
700V/17A高频开关低导通阻GaN HEMT驱动器适用于快充,LLC,PFC功率校正
人工智能·嵌入式硬件·深度学习·神经网络·机器学习·生成对抗网络·计算机外设
恒锐丰小瑞4 小时前
率能SS6285M 33V/5.5A/单通道直流电机驱动IC,低待机功耗与多重保护,用于电子锁/玩具/机器人
stm32·单片机·嵌入式硬件·机器人
天空'之城5 小时前
单片机基础核心知识点汇总(二十一)
单片机·嵌入式硬件
恒锐丰科技林技术员5 小时前
SS6286L:大电流双向有刷电机驱动芯片,赋能电机控制多元场景
经验分享·嵌入式硬件·硬件工程