打印
[STM32F4]

STM32F4 LVGL(8.3)+FreeRTOS+软件分层设计

[复制链接]
657|14
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
zero949079783|  楼主 | 2024-2-17 11:38 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 zero949079783 于 2024-2-18 22:26 编辑

STM32F429 LVGL(8.3)+FreeRTOS(FreeRTOS V10.4.6)+软件分层设计
GD32F427 LVGL(8.3)+FreeRTOS(FreeRTOS V10.4.6)+软件分层设计

应用层:主要编写应用方面的,如功能实现等,实现的串口收发(演示),驱动层:各种外设软件层,应用层与驱动分离方式使用指针函数和回调函数分开
标准库层:仅STM32标准库或GD32库
中间层:LVGL,FreeRTOS,同理也软件与驱动分层。 使用systick定时器作为时钟节拍。
每一层都使函数指针

// 函数指针变量,保存任务调度的函数地址
void (*usartTakeFunCb)(uint8_t *data,uint16_t datalen);

/**
***********************************************************
* [url=home.php?mod=space&uid=247401]@brief[/url] 注册任务调度回调函数
* @param pFunc, 传入回调函数地址
* [url=home.php?mod=space&uid=266161]@return[/url]
***********************************************************
*/

void usart1TakeCb(void(*pFunc)(uint8_t *data,uint16_t datalen))
{
        usartTakeFunCb = pFunc;
}

应用层:实现的串口收发(演示)
发送接收缓冲区大小:2048
单次接收最大量:256
串口接收:DMA+空闲中断+双指针+双环形缓冲区
串口发送:DMA+发送中断+双指针+双环形缓冲区

#define UARTx_RX_SIZE   2048                 //接收缓冲区长度
#define UARTx_TX_SIZE   2048                //发送缓冲区长度
#define UARTx_RX_MAX    256                        //单次接收最大量
#define UARTx_TX_MAX    256                        //单次接收最大量
#define SE_PTR_NUM      10                        //se指针对结构体数组长度


typedef struct{
    uint8_t *start;                                         //start用于标记起始位置                                                                        
    uint8_t *end;                                        //end用于标记结束位置
}LCB;                        //se 指针对结构体

typedef struct{
        
        
        uint16_t RxCounter;        //累计接收数据量
        uint16_t TxCounter;
        uint16_t TxState;
        LCB RxLocation[SE_PTR_NUM];        //se指针对结构体数组
        LCB *RxInPtr;                        //IN指针用于标记接收数据
        LCB *RxOutPtr;                        //OUT指针用于提取接收的数据
        LCB *RxEndPtr;                        ////IN和OUT指针的结尾标志
        
        LCB TxLocation[SE_PTR_NUM];        //se指针对结构体数组
        LCB *TxInPtr;                        //IN指针用于标记发送数据
        LCB *TxOutPtr;                        //OUT指针用于提取发送的数据
        LCB *TxEndPtr;                        ////IN和OUT指针的结尾标志

        USART_InitTypeDef usart;
        DMA_InitTypeDef  dmatx;        
        DMA_InitTypeDef  dmarx;
        
}Usartx_Control_Block;

extern uint8_t Usart1_RxBuffer[UARTx_RX_SIZE];      //串口1接收缓冲区
extern uint8_t Usart1_TxBuffer[UARTx_TX_SIZE];      //串口1发送缓冲区
extern Usartx_Control_Block  usart1;                                                               //串口控制结构体

串口驱动层接收:
void USART1_IRQHandler(void)
{
        //发送中断
        if(USART_GetITStatus(USART1,USART_IT_TC) != RESET)
        {
                USART_ClearITPendingBit(USART1,USART_IT_TC);

        }                                
        //空闲中断
        if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET)
        {               
                USART1->SR;                       //清中断
                USART1->DR;                       //清中断               
                usart1.RxCounter +=((UARTx_RX_MAX+1)- DMA_GetCurrDataCounter(DMA2_Stream2));
                usart1.RxInPtr->end = &Usart1_RxBuffer[usart1.RxCounter - 1];
               
                usart1.RxInPtr++;
                if(usart1.RxInPtr == usart1.RxEndPtr)
                {
                        usart1.RxInPtr = &usart1.RxLocation[0];
                }
                if(UARTx_RX_SIZE - usart1.RxCounter < UARTx_RX_MAX){
                        usart1.RxInPtr->start = Usart1_RxBuffer;
                        usart1.RxCounter = 0;                                                               
                }else{
                        usart1.RxInPtr->start = &Usart1_RxBuffer[usart1.RxCounter];         //标记接位置
                }               
               
                DMA_Cmd(DMA2_Stream2, DISABLE);     //关闭DMA
                DMA_SetCurrDataCounter(DMA2_Stream2, UARTx_RX_MAX+1);
                DMA2_Stream2->M0AR = (uint32_t)usart1.RxInPtr->start;
                DMA_Cmd(DMA2_Stream2, ENABLE);     //打开DMA                        
        }        
}



串口驱动层发送:
void Usart1_Txdata(uint8_t *tdata,uint16_t datalen)
{
        
if((UARTx_TX_SIZE - usart1.TxCounter) >= datalen) //发送空间大于等于发数据
        {
                usart1.TxInPtr ->start = &Usart1_TxBuffer[usart1.TxCounter];
        }else
        {
                usart1.TxCounter = 0;        //发送空间小于发送数据,清0
                usart1.TxInPtr->start = Usart1_TxBuffer;
        }
        
        memcpy(usart1.TxInPtr ->start,tdata,datalen);        //数据拷贝
        while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE);        //确保DMA可以被设置
        DMA_SetCurrDataCounter(DMA2_Stream7,datalen);                        //设置数据传输长度
        
        usart1.TxCounter += datalen;        //统计每次发送量
        usart1.TxInPtr ->end = &Usart1_TxBuffer[usart1.TxCounter-1];//标记end位,为下次作准备
        DMA2_Stream7->M0AR  = (uint32_t)usart1.TxInPtr->start;
        DMA_SetCurrDataCounter(DMA2_Stream7, UARTx_TX_MAX);        
        usart1.TxInPtr++;
        if(usart1.TxInPtr == usart1.TxEndPtr){
                usart1.TxInPtr = &usart1.TxLocation[0];               
        }
}


void Usart1_Event(uint8_t *data,uint16_t datalen)//任务事件入口
{
        Usart1_Txdata(data,datalen);
}

void Usart1_Taskapp(void)
{
        usart1TakeCb(Usart1_Event);//任务事件回函数入口地址
}


串口任务函数:
void usart1_take(void)
{
        if(usart1.RxOutPtr != usart1.RxInPtr)
        {
                usartTakeFunCb(usart1.RxOutPtr->start,(usart1.RxOutPtr->end - usart1.RxOutPtr->start+1));
               
                usart1.RxOutPtr++;
                if(usart1.RxOutPtr == usart1.RxEndPtr)
                {
                        usart1.RxOutPtr = &usart1.RxLocation[0];
                }
        }

        if((usart1.TxOutPtr != usart1.TxInPtr) &&( usart1.TxState == 0))
        {
                usart1.TxState = 1;

                DMA_Cmd(DMA2_Stream7,ENABLE);        

                usart1.TxOutPtr++;
                if(usart1.TxOutPtr == usart1.TxEndPtr)
                {
                        usart1.TxOutPtr = &usart1.TxLocation[0];
                }
               
        }        
}
延时函数:
/**
***********************************************************
* [url=home.php?mod=space&uid=247401]@brief[/url] DWT初始化配置
* @param
* @return
*  使用JLINK和STLINK烧写程序,不能对DWT复位,要手动复位,用DAP烧写可以正常
***********************************************************
**/
void Delay_Init(void)
{
        /* 关闭 TRC */
        CoreDebug->DEMCR &= ~CoreDebug_DEMCR_TRCENA_Msk;
        /* 打开 TRC */
        CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;        
        /* 关闭计数功能 */
        DWT->CTRL &= ~DWT_CTRL_CYCCNTENA_Msk;
        /* 计数清零 */
        DWT->CYCCNT = 0;        
        /* 打开计数功能 */
        DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;        
}
/**
***********************************************************
* [url=home.php?mod=space&uid=247401]@brief[/url] 微秒级延时函数
* @param nUs,最大延时时间( 2^32 / 内核主频 ) * 10^6 us
* [url=home.php?mod=space&uid=266161]@return[/url]
***********************************************************/
void Delay_us(uint32_t nus)
{               
        uint32_t tickStart = DWT->CYCCNT;
        
        /* 转换为nUs对应的时钟跳动次数*/
        nus *= (SystemCoreClock / 1000000);
        
        /* 延时等待 */
        while((DWT->CYCCNT - tickStart) <nus);
        
}

/**
***********************************************************
* @brief 毫秒级延时函数
* @param nMs,延时时间n毫秒
* [url=home.php?mod=space&uid=266161]@return[/url]
***********************************************************
**/
void Delay_ms(uint32_t nms)
{        uint32_t i;
        for (i = 0; i < nms; i++)
        {
                Delay_us(1000);
        }
}

//static __IO uint32_t TimingDelay;

///**
//  * @brief  启动系统滴答定时器 SysTick
//  * @param  无
//  * @retval 无
//  */
//void Delay_Init(void)
//{
//        /* SystemFrequency / 1000    1ms中断一次
//         * SystemFrequency / 100000         10us中断一次
//         * SystemFrequency / 1000000 1us中断一次
//         */
//        if (SysTick_Config(SystemCoreClock / 1000000))
//        {
//                /* Capture error */
//                while (1);
//        }
//}

///**
//  * @brief   us延时程序,10us为一个单位
//  * @param  
//  *                [url=home.php?mod=space&uid=2817080]@ARG[/url] nTime: Delay_us( 1 ) 则实现的延时为 1 * 10us = 10us
//  * @retval  无
//  */
//void Delay_us(__IO uint32_t nTime)
//{
//        TimingDelay = nTime;        

//        while(TimingDelay != 0);
//}

///**
//  * @brief  获取节拍程序
//  * @param  无
//  * @retval 无
//  * [url=home.php?mod=space&uid=93590]@Attention[/url]  在 SysTick 中断函数 SysTick_Handler()调用
//  */
//void TimingDelay_Decrement(void)
//{
//        if (TimingDelay != 0x00)
//        {
//                TimingDelay--;
//        }




此部分内容已被设置为付费内容,您可以在支付 1 元 人民币后浏览本楼层全部付费内容点击购买









   
本楼层付费信息已有6人购买

使用特权

评论回复
沙发
tpgf| | 2024-4-2 15:06 | 只看该作者
用户需要对中间层的驱动进行处理吗

使用特权

评论回复
板凳
xiaoqizi| | 2024-4-2 15:45 | 只看该作者
发送缓冲区的大小可以动态进行调整吗

使用特权

评论回复
地板
zero949079783|  楼主 | 2024-4-2 19:19 | 只看该作者
xiaoqizi 发表于 2024-4-2 15:45
发送缓冲区的大小可以动态进行调整吗

固定好了,一包数据最大是256,缓冲区2048,觉得上,可自调整

使用特权

评论回复
5
zero949079783|  楼主 | 2024-4-2 19:21 | 只看该作者
tpgf 发表于 2024-4-2 15:06
用户需要对中间层的驱动进行处理吗

中间层主要是做一些逻辑处理的,把接口做好了,就放在应用层使用的。这个只是框架

使用特权

评论回复
6
caigang13| | 2024-4-2 20:36 | 只看该作者
我晕,还要付费才能看。

使用特权

评论回复
7
wowu| | 2024-4-2 21:42 | 只看该作者
无论是什么应用都是这样分层的吗

使用特权

评论回复
8
wakayi| | 2024-4-2 22:14 | 只看该作者
这两种单片机的设计理念应该都是相同的

使用特权

评论回复
9
zero949079783|  楼主 | 2024-4-2 22:22 | 只看该作者
wakayi 发表于 2024-4-2 22:14
这两种单片机的设计理念应该都是相同的

相同的,只是不同MCU

使用特权

评论回复
10
renzheshengui| | 2024-4-2 22:46 | 只看该作者
感觉这种分层方式有点像是以太网的分层

使用特权

评论回复
11
paotangsan| | 2024-4-2 23:18 | 只看该作者
软件的分层是依据什么来划分的呢

使用特权

评论回复
12
chenjun89| | 2024-4-3 08:08 | 只看该作者
纳尼,还得付费才能看。

使用特权

评论回复
13
earlmax| | 2024-4-7 10:21 | 只看该作者
LVGL 是一个开源的嵌入式GUI库,适用于微控制器和处理器。
它轻量级、开源、易于使用,支持多平台,包括 STM32。

使用特权

评论回复
14
wangdezhi| | 2024-4-7 22:42 | 只看该作者
FreeRTOS 是一个开源的实时操作系统(RTOS),它为嵌入式系统提供了任务调度、时间管理、内存管理等功能。

使用特权

评论回复
15
EmmaTT| | 2024-4-8 15:26 | 只看该作者
还有付费可见的

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

31

主题

81

帖子

1

粉丝