【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

// 条件成立!退出循环

相关推荐
blueSatchel1 天前
UVC驱动源码研究
linux·嵌入式
haliu1 天前
【FHE 同态加密】我们如何实现同态加密推理(十五):实测账本——一跳 1.8 小时,钱花在哪(同态加密推理性能剖析)
人工智能·嵌入式·c·fhe·推理引擎·c11·边缘推理·同态加密推理
一直C2 天前
【ARM嵌入式开发】LCD液晶屏显示原理、色彩模式与显存地址映射详解
lcd·嵌入式·arm·寄存器·cortex-a7
Hespethorn2 天前
洪泛、路由、网络栈:LoRa 自组网三条路线的设计取舍与量化对比
物联网·嵌入式
用户0510122572962 天前
RGA(二)——RK3588 的核拓扑与能力差异
linux·嵌入式
用户0510122572962 天前
RGA(一)——基础知识
linux·嵌入式
用户0510122572962 天前
RGA(三)——实宽高、虚宽高与对齐约束
linux·嵌入式
燕卫博2 天前
SSD201 BSP 开发教程
嵌入式·bsp·ssd201
一直C2 天前
【嵌入式 ARM 驱动开发】PWM 脉宽调制原理与 GT9147 电容触控屏驱动开发详解
嵌入式硬件·lcd·嵌入式·arm汇编·imx6ull·cortex-a7
haliu2 天前
【FHE 同态加密】我们如何实现同态加密推理(十四):为什么 `RESULT=PASS` 不是判据(纯 C11 · 零依赖)
人工智能·嵌入式·c·fhe·推理引擎·c11·边缘推理·同态加密推理