【FreeRTOS】STM32Cube + FreeRTOS 快速上手指南(更新ing)


简介

FreeRTOS 是一个 RTOS 类的嵌入式实时操作系统,掌握 FreeRTOS 是从单片机裸机编程过渡到更复杂的嵌入式Linux开发的重要一步。本文总结了笔者在学习FreeRTOS过程中的经验和见解,若有任何错误或建议,欢迎随时指正。


1. 操作系统简介

单片机程序设计分为两种,主要可以分为两大类:裸机编程和基于实时操作系统的编程。我们参考一些百问网的内容来进行讲解。

1.1. 裸机程序

裸机程序的设计模式可以分为:轮询、前后台、定时器驱动、基于状态机

1.1.1. 轮询

int main()
{
    while (1)
    {
        task1();
        task2();
        task3();
    }
}

使用轮询模式编写程序看起来很简单,但是要求while 循环里调用到的函数要执行得非常快,在复杂场景里反而增加了编程难度。

1.1.2. 前后台

int main()
{
    // 后台程序
    while (1)
    {
        Backend();
    }
}

// 前台程序
void EXTIx_IRQHandler(void)
{
    Reception();
}

前后台是一种常见的架构模式,例如在平衡小车中,采用MPU6050每5ms产生的INT中断信号来触发前台任务处理,确保即时响应传感器数据的变化。与此同时,后台任务通过while(1)运行,负责诸如屏幕更新等不需要严格实时性的操作。

1.1.3. 定时器驱动

定时器驱动模式,是前后台模式的一种,可以按照不用的频率执行各种函数。

int main(void)
{
    // 后台程序
    while (1)
    {
        Backend(); // 主循环可以在这里添加其他任务
    }
}

// 前台程序
void TIMx_IRQHandler(void)
{
    static int cnt = 0;
    if (TIM_GetITStatus(TIMx, TIM_IT_Update) != RESET)
    {
        if (cnt % 2 == 0)
            Reception1();
        else if (cnt % 5 == 0)
            Reception2();
        cnt++;
        TIM_ClearITPendingBit(TIMx, TIM_IT_Update); // 清除中断标志位
    }
}

1.1.4. 基于状态机

无论使用前面的哪种设计模式,都会退化到轮询模式的缺点:函数相互之间有影响。可以使用状态机来解决这个缺点。

使用状态机模式,可以解决裸机程序的难题:假设有A、B 两个都很耗时的函数,怎样降低它们相互之间的影响。但是很多场景里,函数A、B 并不容易拆分为多个状态,并且这些状态执行的时间并不好控制。

所以这并不是最优的解决方法,需要使用多任务系统。

1.2. 多任务系统

多任务系统会依次给这些任务分配时间:你执行一会,我执行一会,如此循环。只要切换的间隔足够短,用户会“感觉这些任务在同时运行”。

2. 创建工程模板

接下来以STM32F103RCT6为例,创建FreeRTOS工程模板:

2.1. 配置STM32CubeMX

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
生成代码

2.2. 添加延时、外设代码

  1. .\Drivers新建文件夹,取名BSP (或自拟);
  2. 新建以下文件夹和对应的.c.h文件;

在这里插入图片描述

  1. delay.h
#include "main.h"

#ifndef __DELAY_H
#define __DELAY_H

uint64_t system_get_ns(void); // 获得系统时间
void delay_us(uint32_t nus);  /* 延时nus */
void delay_ms(uint16_t nms);  /* 延时nms */

#endif

  1. delay.c
#include "delay.h"

/**
 * @brief   获得系统时间(单位ns)
 * @param   无
 * @retval  系统时间(单位ns)
 */
uint64_t system_get_ns(void)
{

    extern TIM_HandleTypeDef htim6;
    TIM_HandleTypeDef *hHalTim = &htim6;

    uint64_t ns = HAL_GetTick(); // 获取系统启动以来的毫秒计数值
    uint64_t cnt;
    uint64_t reload;

    cnt = __HAL_TIM_GET_COUNTER(hHalTim);
    reload = __HAL_TIM_GET_AUTORELOAD(hHalTim);

    ns *= 1000000;
    ns += cnt * 1000000 / reload;
    return ns;
}

/**
 * @brief   延时nus
 * @param   nus: 要延时的us数.
 * @retval  无
 */
void delay_us(uint32_t nus)
{
    extern TIM_HandleTypeDef htim6;
    TIM_HandleTypeDef *hHalTim = &htim6;

    uint32_t ticks;
    uint32_t told, tnow, tcnt = 0;
    uint32_t reload = __HAL_TIM_GET_AUTORELOAD(hHalTim);

    ticks = nus * reload / (1000); /* 假设reload对应1ms */
    told = __HAL_TIM_GET_COUNTER(hHalTim);
    while (1)
    {
        tnow = __HAL_TIM_GET_COUNTER(hHalTim);
        if (tnow != told)
        {
            if (tnow > told)
            {
                tcnt += tnow - told;
            }
            else
            {
                tcnt += reload - told + tnow;
            }
            told = tnow;
            if (tcnt >= ticks)
            {
                break;
            }
        }
    }
}

// 延时nms
// nms:要延时的ms数
void delay_ms(uint16_t nms)
{
    uint32_t i;
    for (i = 0; i < nms; i++)
        delay_us(1000);
}

LED部分代码可忽略,在STM32CubeMX中配置相关引脚输出即可。

2.3. 添加路径,编译验证

在这里插入图片描述
在这里插入图片描述
打开freertos.c添加头文件

#include "delay.h"
#include "led.h"

在这里插入图片描述

找到函数StartDefaultTask(void *argument),添加以下内容:

HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, 0);
delay_ms(500);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, 1);
delay_ms(500);

在这里插入图片描述
编译下载验证
在这里插入图片描述

在这里插入图片描述
至此,我们的工程模板创建完毕。

3.

4.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值