【SWM320】FreeRTOS搭建工程——1、框架学习

一、 软件框架设计

1. 最底层:驱动层 (drv/drvp)

复制代码
// drv (drivers) - 硬件驱动
// 例如:GPIO驱动、UART驱动、I2C驱动等
drv_gpio.c
drv_uart.c
drv_spi.c

// drvp (drivers port) - 驱动移植层
// 针对具体芯片的适配代码
drvp_stm32f4.c    // 针对STM32F4的移植
drvp_air7xx.c     // 针对air7xx的移植

作用:直接操作硬件寄存器,提供底层硬件访问接口

1.1 软件包层 (softpack)

FreeRTOS - 实时操作系统内核

lwIP - 轻量级TCP/IP协议栈

FatFS - 文件系统

mbedtls - 加密库

...

作用:第三方开源软件,提供基础服务

1.2 BSP框架层 (bsp_framework)

// 板级支持包框架

bsp_init() // 板级初始化

bsp_clock_config() // 时钟配置

bsp_interrupt() // 中断管理

bsp_timer() // 定时器服务

作用:封装底层驱动,为上层提供统一的板级接口

2. 机器层 (machine)

作用:将整个设备抽象成一个"机器",提供统一的操作接口

2.1 进程管理部分

// 各个独立的任务

task_start() // 启动任务 - 系统初始化

task_listen() // 监听任务 - 等待命令/连接

task_ctrl() // 控制任务 - 执行具体控制逻辑

task_host() // 主机任务 - 与上位机通信

task_manager() // 管理任务 - 任务调度管理

2.2 网络部分

// 网络模块抽象

net_init() // 网络初始化

// 具体实现

air7xx.c - 4G模块驱动

esp8266.c - WiFi模块驱动

作用:提供统一的网络接口,屏蔽不同网络模块的差异

2.3 工作逻辑部分

// 具体板载外设功能

led_control() // LED控制

cycle_work() // 周期性工作(如LED闪烁)

event_report() // 事件上报

3. 整体框图

二、学习使用RTOS

打开官方例程

这是一个ADC采集数据的例子

cpp 复制代码
#include "SWM320.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

QueueHandle_t queueADC;

void TaskADC(void *arg);
void TaskPWM(void *arg);
void SerialInit(void);

int main(void)
{ 	
 	SystemInit();
	
	SerialInit();
	
	GPIO_Init(GPIOA, PIN5, 1, 0, 0);		//调试指示信号
	
	xTaskCreate(TaskADC, (const char *)"ADC", 128, NULL, 2, NULL);
	xTaskCreate(TaskPWM, (const char *)"PWM", 128, NULL, 3, NULL);
	
	queueADC = xQueueCreate(16, 2);
	
	vTaskStartScheduler();
}

/****************************************************************************************************************************************** 
* 函数名称:	TaskADC()
* 功能说明: 启动ADC采集任务
* 输    入: void *arg
* 输    出: 无
* 注意事项: 无
******************************************************************************************************************************************/
void TaskADC(void *arg)
{
	ADC_InitStructure ADC_initStruct;
	
	PORT_Init(PORTA, PIN12, PORTA_PIN12_ADC0_IN4, 0);	//PA.12 => ADC.CH4
	PORT_Init(PORTA, PIN11, PORTA_PIN11_ADC0_IN5, 0);	//PA.11 => ADC.CH5
	PORT_Init(PORTA, PIN10, PORTA_PIN10_ADC0_IN6, 0);	//PA.10 => ADC.CH6
	PORT_Init(PORTA, PIN9,  PORTA_PIN9_ADC0_IN7,  0);	//PA.9  => ADC.CH7
	
	ADC_initStruct.clk_src = ADC_CLKSRC_VCO_DIV64;
	ADC_initStruct.clk_div = 25;
	ADC_initStruct.pga_ref = PGA_REF_INTERNAL;
	ADC_initStruct.channels = ADC_CH4;
	ADC_initStruct.samplAvg = ADC_AVG_SAMPLE1;
	ADC_initStruct.trig_src = ADC_TRIGSRC_SW;
	ADC_initStruct.Continue = 0;						//非连续模式,即单次模式
	ADC_initStruct.EOC_IEn = ADC_CH4;	
	ADC_initStruct.OVF_IEn = 0;
	ADC_initStruct.HFULL_IEn = 0;
	ADC_initStruct.FULL_IEn = 0;
	ADC_Init(ADC0, &ADC_initStruct);					//配置ADC
	
	NVIC_SetPriority(ADC0_IRQn, 5);
	
	ADC_Open(ADC0);										//使能ADC
	
	while(1)
	{
		ADC_Start(ADC0);
		
		vTaskDelay(200);
	}
}

void ADC0_Handler(void)
{
	uint16_t val;
	
	ADC0->IF = (1 << ADC_IF_CH4EOC_Pos);
	
	val = ADC_Read(ADC0, ADC_CH4);
	
	xQueueSendFromISR(queueADC, &val, 0);
	
	GPIO_InvBit(GPIOA, PIN5);
}

/****************************************************************************************************************************************** 
* 函数名称:	TaskPWM()
* 功能说明: 等待ADC转换结果,根据ADC转换结果设置PWM占空比
* 输    入: void *arg
* 输    出: 无
* 注意事项: 无
******************************************************************************************************************************************/
void TaskPWM(void *arg)
{
	PWM_InitStructure  PWM_initStruct;
	
	PWM_initStruct.clk_div = PWM_CLKDIV_8;		//F_PWM = 20M/8 = 2.5M
	
	PWM_initStruct.mode = PWM_MODE_INDEP;		//A路和B路独立输出					
	PWM_initStruct.cycleA = 10000;				//2.5M/10000 = 250Hz			
	PWM_initStruct.hdutyA =  2500;				//2500/10000 = 25%
	PWM_initStruct.initLevelA = 1;
	PWM_initStruct.cycleB = 10000;
	PWM_initStruct.hdutyB =  5000;				//5000/10000 = 50%
	PWM_initStruct.initLevelB = 1;
	PWM_initStruct.HEndAIEn = 0;
	PWM_initStruct.NCycleAIEn = 0;
	PWM_initStruct.HEndBIEn = 0;
	PWM_initStruct.NCycleBIEn = 0;
	
	PWM_Init(PWM0, &PWM_initStruct);
	
	PORT_Init(PORTA, PIN4, FUNMUX0_PWM0A_OUT, 0);
	PORT_Init(PORTA, PIN6, FUNMUX0_PWM0B_OUT, 0);
	
	PWM_Start(PWM0, 1, 1);
	
	while(1)
	{
		uint16_t duty;
		
		if(xQueueReceive(queueADC, &duty, 10) == pdTRUE)
		{
			printf("%d,", duty);
			if(duty <  100) duty =  100;
			if(duty > 4000) duty = 4000;
			
			PWM_SetHDuty(PWM0, PWM_CH_A, 10000 * duty / 4096);
			PWM_SetHDuty(PWM0, PWM_CH_B, 10000 - PWM_GetHDuty(PWM0, PWM_CH_A));
		}
	}
}


void SerialInit(void)
{
	UART_InitStructure UART_initStruct;
	
	PORT_Init(PORTA, PIN2, FUNMUX0_UART0_RXD, 1);	//GPIOA.2配置为UART0输入引脚
	PORT_Init(PORTA, PIN3, FUNMUX1_UART0_TXD, 0);	//GPIOA.3配置为UART0输出引脚
 	
 	UART_initStruct.Baudrate = 57600;
	UART_initStruct.RXThresholdIEn = 0;
	UART_initStruct.TXThresholdIEn = 0;
	UART_initStruct.TimeoutIEn = 0;
 	UART_Init(UART0, &UART_initStruct);
	UART_Open(UART0);
}

/****************************************************************************************************************************************** 
* 函数名称: fputc()
* 功能说明: printf()使用此函数完成实际的串口打印动作
* 输    入: int ch		要打印的字符
*			FILE *f		文件句柄
* 输    出: 无
* 注意事项: 无
******************************************************************************************************************************************/
int fputc(int ch, FILE *f)
{
 	while(UART_IsTXFIFOFull(UART0));
	
	UART_WriteByte(UART0, ch);
 	
	return ch;
}

从main.c中我们可以学习到

  • xTaskCreate(TaskADC, (const char *)"ADC", 128, NULL, 2, NULL);
  • queueADC = xQueueCreate(16, 2);
  • vTaskStartScheduler();
  • vTaskDelay(200);
  • xQueueSendFromISR(queueADC, &val, 0);
  • xQueueReceive(queueADC, &duty, 10);
①xTaskCreate(TaskADC, (const char *)"ADC", 128, NULL, 2, NULL);

BaseType_t xTaskCreate(

TaskFunction_t pxTaskCode, // 任务函数指针

const char * const pcName, // 任务名称

const uint16_t usStackDepth, // 任务堆栈大小

void * const pvParameters, // 任务参数

UBaseType_t uxPriority, // 任务优先级

TaskHandle_t * const pxCreatedTask // 任务句柄

);

②xQueueCreate(16, 2);

QueueHandle_t xQueueCreate(

UBaseType_t uxQueueLength, // 队列长度(能存储多少个项目)

UBaseType_t uxItemSize // 每个项目的大小(字节)

);

③ vTaskStartScheduler()

vTaskStartScheduler() 是 FreeRTOS 中启动任务调度器的函数。

这是整个 RTOS 运行的起点,调用这个函数后,控制权就交给了 FreeRTOS 内核。

它启动调度器后,就由 FreeRTOS 接管 CPU 控制权,开始按照优先级调度各个任务运行。

④vTaskDelay(200);

vTaskDelay(200) 中的 200 不是毫秒! 它是 系统时钟节拍数 (Ticks)。

⑤ xQueueSendFromISR(queueADC, &val, 0);

在中断服务程序(ISR)里向队列发送数据的函数

BaseType_t xQueueSendFromISR(

QueueHandle_t xQueue, // 队列句柄

const void *pvItemToQueue, // 要发送的数据指针

BaseType_t *pxHigherPriorityTaskWoken // 是否唤醒高优先级任务

);

⑥ xQueueReceive(queueADC, &duty, 10)

从 queueADC 队列接收数据,接收到的数据存入 duty 变量,最多等待 10 个系统节拍

BaseType_t xQueueReceive(

QueueHandle_t xQueue, // 队列句柄

void *pvBuffer, // 接收数据的缓冲区指针

TickType_t xTicksToWait // 等待时间(单位:系统节拍)

);

接下来,看一下RTOS配置

CPU工作频率: SWM320RET7经过倍频后,工作在110592000 Hz

系统滴答:1秒钟产生多少次系统滴答 。没有别的需求,设置1000即可配置堆大小,

SWM320RET7拥有128KB大小的SRAM,80KB拿出来给freertos管理。

创建freertos任务

cpp 复制代码
//静态分配一个任务句柄
static TaskHandle_t TaskHandle_start;

//任务入口函数,相当于main函数
void task_start_entry(void *arg)
{
    printf("%s\r\n",__FUNCTION__);
    while(1)
    {

    }
}

//创建任务函数
void create_task_of_start( void )
{    
    //创建任务
    xTaskCreate( task_start_entry, (const char *)"start", 128, NULL, 2, &TaskHandle_start );
}

FUNCTION 是一个预定义宏,它会被编译器自动替换为当前函数的名称。

使用互斥锁

把它设为1

cpp 复制代码
#include "semphr.h"        

static SemaphoreHandle_t cslock;    //保存锁对象

static int flag_init = 0;            //描述锁是否已经初始化了

//初始化锁
void cslock_init(void)
{
    if( flag_init == 1 ) return;
    cslock = xSemaphoreCreateMutex();
    if( cslock == NULL )
    {//说明不成功
        printf( "err:%s\r\n",__func__ );
        while(1);
    }
    flag_init = 1;
}

//获取锁
void cslock_get(void)
{
    if( flag_init == 0 ) return;

    //获取cslock信号量,最多等待0xFFFFFFFF个tick(几乎无限等待)
    int ret = xSemaphoreTake( cslock,0xFFFFFFFF );
    if( ret == pdTRUE )
    {//说明,成功获取了

    }
    else
    {//失败

    }
}

//释放锁
void cslock_free(void)
{
    if( flag_init == 0 ) return;
    xSemaphoreGive( cslock );
}

//实现自己的打印函数
void myprintf( char* format, ... )
{
    if( flag_init == 0 ) return;

    char buf[256];        //最大可以打印的字符串长度

    va_list v_args;
    va_start( v_args,format );
    (void)vsnprintf( buf,sizeof(buf),format,v_args );

    va_end( v_args );

    //获取锁
    cslock_get();
    //使用互斥资源,因为printf最终调用到uart,指向同一个硬件资源,好几个任务同时使用,是不行的。
    printf("%s",buf);
    //释放锁
    cslock_free();
}

链表

根节点数据结构

typedef struct xLIST

{

/* 1. 链表完整性检查(调试用) */

listFIRST_LIST_INTEGRITY_CHECK_VALUE

/* 2. 链表中项目的数量 */

configLIST_VOLATILE UBaseType_t uxNumberOfItems;

/* 3. 当前索引指针(用于遍历链表) */

ListItem_t * configLIST_VOLATILE pxIndex;

/* 4. 链表结束标记(最小值/最大值) */

MiniListItem_t xListEnd;

/* 5. 第二个完整性检查 */

listSECOND_LIST_INTEGRITY_CHECK_VALUE

} List_t;

节点数据结构
创建一个链表的根节点
初始化一个节点
在链表尾部插入新元素
在链表排序插入新元素
在链表中移除一个元素
演示FreeRTOS链表操作的示例代码
cpp 复制代码
List_t mylist;        //链表根节点

void task_start_entry(void *arg)
{
    uint32_t tick = 0;
    printf("%s\r\n",__FUNCTION__);

    //初始化 myprintf
    cslock_init();
    //创建其他任务
    create_task_of_host();
    create_task_of_listen();
    create_task_of_ctrl();

    //在栈中(函数内)分配链表节点
    ListItem_t node0;
    ListItem_t node1;
    ListItem_t node2;

    //①初始化链表的根节点
    vListInitialise( &mylist ) ;

    //②初始化 所有节点
    vListInitialiseItem( &node0 );
    vListInitialiseItem( &node1 );
    vListInitialiseItem( &node2 );

    node0.xItemValue = 3;
    node1.xItemValue = 5;
    node2.xItemValue = 2;

    //③把这些节点插入到链表里面去
#if ( 0 )
    //排序插入
    vListInsert( &mylist,&node0 );
    vListInsert( &mylist,&node1 );
    vListInsert( &mylist,&node2 );


#else
    //插入到尾部
    vListInsertEnd( &mylist,&node0 );
    vListInsertEnd( &mylist,&node1 );
    vListInsertEnd( &mylist,&node2 );

#endif
    //④遍历链表,把所有元素都打印出来
    ListItem_t* node = listGET_HEAD_ENTRY( &mylist );
    while( 1 )
    {
        //如果当前的节点,就是链表的根节点 pxIndex ,说明就是表尾,表尾即退出循环
        if( node == mylist.pxIndex ) break;

        myprintf(" val = %08x\r\n",node->xItemValue );

        node = node->pxNext;
    }

    while(1)
    {
        vTaskDelay( pdMS_TO_TICKS( 1000 ) );
    }
}

ListItem_t* node = listGET_HEAD_ENTRY(&mylist);

// node 指向第一个数据节点 node0

if(node == mylist.pxIndex) break; // node == xListEnd, pxIndex == xListEnd

// 条件成立!退出循环

相关推荐
小杨同学492 小时前
STM32 进阶封神之路(二十二):DMA 实战全攻略 ——ADC 采集 + 串口收发 + 内存复制(库函数 + 代码落地)
后端·单片机·嵌入式
隔壁大炮3 小时前
速度环实现行进&角度环实现转弯
嵌入式·pid·江协科技·平衡小车
aspirestro三水哥4 小时前
9.4贡献自己的第一个patch
rtos·xenomai
busideyang5 小时前
STM32中__weak(弱定义)函数核心总结
c语言·stm32·单片机·嵌入式硬件·嵌入式
大志出奇迹5 小时前
FreeRTOS中创建任务的顺序是否会影响任务运行的顺序?【面试重点】
c语言·rtos
421!5 小时前
ESP32学习笔记之UART
笔记·学习·嵌入式·esp32·通信
隔壁大炮6 小时前
PID控制结构&角度环实现直立
stm32·嵌入式·硬件·pid·平衡车·江协科技
FreakStudio18 小时前
不用费劲编译ulab了!纯Mpy矩阵micronumpy库,单片机直接跑
python·嵌入式·边缘计算·电子diy
aspirestro三水哥19 小时前
9.2向社区寻求帮助
rtos·xenomai