[STM32F4]

STM32F407调试笔记 HAL+UCOSIII+Fatfs+i2s音频+LWIP以太网移植过程...

[复制链接]
7059|46
手机看帖
扫描二维码
随时随地手机跟帖
liuxiang5119|  楼主 | 2019-1-18 09:40 | 显示全部楼层 |阅读模式
本帖最后由 liuxiang5119 于 2019-2-25 09:46 编辑

微信截图_20190118092130.png
自己做的板子
stm32f407+spiflash+sd卡+usb+eth网口+i2s音频+音频放大(15*2W)+两路继电器12V输出+按键(5+2)+USART3(调试口)+USART2(485)+SWD调试口    功能算是比较全了吧   板子做下来一直没时间搞,焊接了两块,然后开始搞代码!


以前一直用std库搞,同事说现在用那个cube很简便,好吧真心没听说过,孤陋寡闻了,3年多一直做嵌入式硬件了,代码都很少写了,那就搞搞这个cube吧,就当学习了。开始正式一抹黑啊,网上各种教程,各种学习,看怎么用cube图形导出工程代码,cube自带freeROTS 这个真心没用过,以前用stm32f207搞过ucos-iii ,想了下那就用cube导出的工程移植ucos吧,然后整个学习过程就是这个样子了:
第一步:cube工程配置(简单的led灯)+ucos-iii移植(多任务调用)
第二步:串口中断接收,使用ucos,阻塞接收容易出错,所以接收发送都用中断处理,从这里开始就进入了挖坑模式
第三步:定时器配置以及中断处理。实际是第二步的延续(串口中断接收使用定时器去做了接收完成处理)
第四步:sd卡+fatfs文件系统配置(主要是分频哪里有点问题,速率高了不稳定,不清楚是硬件有问题还是怎么,发了个帖子o(╯□╰)o)
第五步:I2S音频输出,芯片是WM8978,以前没折腾过音频,估计又是各种挖空,等sdio折腾清楚了搞这个
第六步:eth网口调试  LwIP协议栈+UCOS-III移植  
第七步:spi读写
第八步:整体功能调试 到这里板子基本就调通了  该有的功能也基本全了!  
整个完成不知道多久,有时间就调试下!
评论
BestHD 2019-1-21 20:35 回复TA
优秀 
liuxiang5119|  楼主 | 2019-1-18 09:57 | 显示全部楼层
本帖最后由 liuxiang5119 于 2019-1-25 11:23 编辑

第二步:串口中断接收(自学摸索 有问题欢迎指教)从cube导出的hal库,设置时候给开了中断,在stm32f4xx_hal_msp.c中,所有的底层硬件初始化都这这里,所以cube更新硬件设置后,这个文件以及它的.h文件都得替换下;初始化的函数是在main里边,因为使用ucos,所以把初始化代码放在了bsp_ser里边
1、串口中断在ucos里边得使用ucos自己的中断向量表以及中断函数配置,不能使用hal库自带的中断初始化,否则进不去ucos中断,所以在cube配置的时候,我把串口中断给关了,然后初始化中断用以下函数替代:
 BSP_IntVectSet(BSP_INT_ID_USART2, BSP_Ser2_ISR_Handler);//BSP_Ser2_ISR_Handler为中断处理函数
        BSP_IntPrioSet(BSP_INT_ID_USART2,4);
        BSP_IntEn(BSP_INT_ID_USART2);

        BSP_IntVectSet(BSP_INT_ID_USART3, BSP_Ser3_ISR_Handler);//BSP_Ser3_ISR_Handler为中断处理函数
        BSP_IntPrioSet(BSP_INT_ID_USART3,4);
        BSP_IntEn(BSP_INT_ID_USART3);

        //该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
        HAL_UART_Receive_IT(&Uart3Handle, (uint8_t *)aRxBuffer3, USART3_REC_SIZE);
        
        //该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
        HAL_UART_Receive_IT(&Uart2Handle, (uint8_t *)aRxBuffer2, USART2_REC_SIZE);


     2、中断数据的接收,网上有人说在回调处理效率比较低,所以直接在中断里边去处理,完了再调用HAL_UART_IRQHandler(&Uart3Handle);个人认为实际上中断处理时间是一样的,区别是先读数据还是先读状态寄存器处理错误标志,当然主频低的话可能会造成数据读取错误。主频率168M实际测不出来到底有多大的影响,后期实际应用再测试吧。
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
        if (huart->Instance==USART2)//串口2数据处理
        {
                while (HAL_UART_Receive_IT(&Uart2Handle, (uint8_t *)aRxBuffer2, USART2_REC_SIZE) != HAL_OK);
        }
        else if (huart->Instance==USART3)//串口3数据处理
        {
                BSP_LED_Toggle(2);
                if (USART3_RX_STA < (USART3_REC_LEN - 1))      //还可以接收数据
                {
                        __HAL_TIM_SET_COUNTER(&htim3,0);//计数器清空
                        if (USART3_RX_STA == 0)
                        {
                                BSP_TIM_ENABLE(&htim3);        //使能定时器3的中断
                        }
                        USART3_RX_BUF[USART3_RX_STA++] = aRxBuffer3[0];        //记录接到的值USART3_RX_BUF[USART3_RX_STA++] = aRxBuffer3[0];        //记录接到的值
                }else
                {
                        //USART3_RX_BUF[(USART3_REC_LEN-1)] = aRxBuffer3[0];        //记录最后一个数
                        BSP_UART_RXC(&Uart3Handle);
                }
                if (HAL_UART_Receive_IT(&Uart3Handle, (uint8_t *)aRxBuffer3, USART3_REC_SIZE) != HAL_OK)
                {
                        __HAL_UNLOCK(&Uart3Handle);
                        BSP_printf(("BSP USART3 RECEIVE IT ERR in bsp_ser.c line 219\r"));        
                }
        }
}
void BSP_Ser3_ISR_Handler(void)
{
        HAL_UART_IRQHandler(&Uart3Handle);        //调用HAL库中断处理公用函数
}

这里只做了串口3的接收处理,实际测试200字节,间隔50ms循环发送没出现问题
744985c4131f0095d6.png
另外记录下发送这块,被自己给坑了,如果在中断模式,发送数据前需要判断设备状态,否则连续两条发送数据会丢失第二条(开始真心不知道)HAL_UART_Transmit_IT(&Uart3Handle, (uint8_t *)&USART3_RX_BUF, USART3_RX_STA&0x7FFF);
HAL_UART_Transmit_IT(&Uart3Handle, (uint8_t *)&USART3_RX_BUF, USART3_RX_STA&0x7FFF);
如上所示,执行的时候第一条会返回HAL_OK,然后进入串口发送中断程序,第二条直接返回HAL_BUSY,所以第二条数据是不会发送的!
改成下边这条:
while(HAL_UART_Transmit_IT(&Uart3Handle, (uint8_t *)&USART3_RX_BUF, USART3_RX_STA&0x7FFF) != HAL_OK);
while(HAL_UART_Transmit_IT(&Uart3Handle, (uint8_t *)&USART3_RX_BUF, USART3_RX_STA&0x7FFF) != HAL_OK);
第二条程序会等待设备空闲,然后再执行,这里如果后期要用,需要做个等待时间处理,防止一直等待造成程序假死!
3、串口发送重定向
串口重定向,网上搜的程序一般都是
CPU_INT32S fputc(CPU_INT32S ch, FILE *f)
{
        HAL_UART_Transmit_IT(&Uart3Handle, (uint8_t *)&ch, 1);这个或者下面那条命令
        HAL_UART_Transmit(&Uart3Handle, (uint8_t *)&ch, 1, 0xFFFF);   
        return ch;
}
这里给改成下面那条命令,HAL_UART_Transmit这个是非阻塞发送,和接收中断容易引起互锁,改成HAL_UART_Transmit_IT,这条是中断发送,但是有个问题,就是上边说的需要查询设备状态,否则会丢数据! 所以加个while判断状态,或者不怕效率低直接在后边加个小延时程序也可以!

CPU_INT32S fputc(CPU_INT32S ch, FILE *f)
{
        while(HAL_UART_Transmit_IT(&Uart3Handle, (uint8_t *)&ch, 1) != HAL_OK);     
        return ch;
}


这两天继续折腾了下串口,只因为想用串口调试做个回显,比如说CRT上调试 可以直接敲字符直接显示,类似于linux系统的调试本来想法很简单,谁知道实现过程有点坑
1、使用串口中断接收以及中断发送,那么实时回显就很难做到,串口接收中断里边不可能直接开个发送中断的;
2、既然这样,那就再中断里直接做个阻塞发送,反正一个字符占用很少时间;
3、做完之后测试,键盘一个个敲,基本没有问题,想着我直接复制200多个字符过去,然后问题就出现了,串口中断假死了,应该是互锁了,发送的无法获取串口空闲状态,HAL库好麻烦,开始一直去处理假定返回USART_BUSY,然后怎么去处理,是直接解锁串口,还是去初始化;初始化倒是能好使,但是容易出错直接进入硬件错误
4、后来想了下,既然中断里边没法做大数据回显 那就不再中断里做了  ucos直接另外开了一个任务函数  只做数据回显用  那么就涉及到能不能回显的及时,以及大数据回显时候会不会丢数据
5、通过调试基本实现,想法是这样,在任务里做了个5ms延时,5ms内如果没有新数据接收到,那么任务该组数据接收完成,不管是一个字符还是一大串数据都没问题,接收完再回显,5ms的延时  这个时间足够,而且人眼反应速度也没这么快
微信截图_20190125112208.png
代码如下:
static  void  AppTaskSerEcho (void  *p_arg)
{
        OS_ERR      err;

        (void)p_arg;

        CPU_SR_ALLOC();
       
        while (DEF_TRUE)         {
                #ifdef  USART3_ECHO
                if ((USART3_TX_STA != 0)&&(USART3_TX_STA == USART3_TX_REC))//接收到数据 并且5ms内没接收到数据 则回显
                {
                        CPU_CRITICAL_ENTER();
                        HAL_UART_Transmit(&Uart3Handle, (uint8_t *)&USART3_TX_BUF, USART3_TX_STA, 0xFFFF);
                        USART3_TX_STA = 0;
                        USART3_TX_REC = 0;
                        CPU_CRITICAL_EXIT();
                }
                USART3_TX_REC = USART3_TX_STA;//记录上一次数据
                #endif
                OSTimeDlyHMSM(0, 0, 0, 5,
                              OS_OPT_TIME_HMSM_STRICT,
                              &err);
        }
}



使用特权

评论回复
评论
liuxiang5119 2019-1-25 11:12 回复TA
@caijie001 :暂时没用dma 处理串口 dma后边fatfs 音频都有用到 
caijie001 2019-1-19 14:12 回复TA
可以用空闲中断+dma传数据,可以使用缓冲区,速度太快可以慢慢处理 
liuxiang5119|  楼主 | 2019-1-18 10:14 | 显示全部楼层
第三步 定时器部分
串口数据接收用定时器去判断3.5个数据时钟,结果定时器一打开,立即跳到定时器中断程序了,导致数据接收不完全,并且下次不会进入串口中断,因为没有重新使能串口中断,所以这个是个大坑!
使能定时器按照以下顺序处理
                __HAL_TIM_CLEAR_IT(&htim4, TIM_IT_UPDATE);
                __HAL_TIM_SET_COUNTER(&htim4,0);
                BSP_IntEn(BSP_INT_ID_TIM4);
                HAL_TIM_Base_Start_IT(&htim4);



定时器配置使用cube直接导出的即可,另外定时器中断得使用ucos自带中断配置
开了三个定时器,定时器1实现呼吸灯,定时器2实现1s定时(备用),定时器4实现10s定时(备用),定时器3实现 6ms定时(辅助串口接收,6ms后没有新的数据则认为接收完成)
899635c413681eff04.png
默认不开启定时器
635095c41365c28c8d.png

使用特权

评论回复
liuxiang5119|  楼主 | 2019-1-18 10:56 | 显示全部楼层
第四步   sd卡+fatfs    帖子不是教程   需要教程的去网上找   这里只记录移植过程中出现的问题以及处理
1、fatfs配置  中文简体支持以及长文件名支持 微信截图_20190118102200.png
2、IAR cstack设置
微信截图_20190118102234.png
3、sdio以及dma  中断配置  使用ucos自己的中断配置方式
微信图片_20190118093611.png


使用特权

评论回复
mmuuss586| | 2019-1-18 12:48 | 显示全部楼层
不错,感谢分享

使用特权

评论回复
稳稳の幸福| | 2019-1-18 14:21 | 显示全部楼层
这说明掌握了一整套流程。

使用特权

评论回复
liuxiang5119|  楼主 | 2019-1-18 15:36 | 显示全部楼层
本帖最后由 liuxiang5119 于 2019-1-18 15:57 编辑

第四步   sd卡+fatfs 续    连续测试了两天fatfs的稳定性  
static void MX_SDIO_SD_Init(void)
{

        /*需要注意的是初始化时bus width只能设置成1bit,
        然后通过HAL_SD_WideBusOperation_Config(&hsd, SDIO_BUS_WIDE_4B)来切换成4位模式,
        在初始化时直接设置成4位是无效的。*/
        hsd.Instance = SDIO;
        hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
        hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
        hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
        hsd.Init.BusWide = SDIO_BUS_WIDE_1B;
        hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
        hsd.Init.ClockDiv = 12;//  这里分频数   168M/(分频数+2)   
}


一开始总是返回硬件错误,经确定应该是速率太高导致,sdio支持从400k到24M时钟,开始不知道,168M主频直接给设置了0分频,实际工作频率是 168/2 84m 所以工作一段时间就会出现底层硬件错误
今天上午设置了5分频,下来是24M工作频率,连续工作还是会出现错误
后来更改为12   也就是工作频率12M   经测试比较稳定了
48035c4182154e6ec.png
现在还不确定到底是本身sd速率限制,还是说板子做的有问题,24M时容易出错,不过24M也不算太高频率吧o(╯□╰)o
实际整个移植需要改的不多,sd卡的初始化部分只需要实现
1、sdio结构体部分初始化,dma中断、sdio中断(这两个因为用ucos 所以自己写下,要是不用操作系统,直接在cube里边配置好中断就可以了)

2、sd_diskio.c里边的sd卡插入检测  实际这个在cube里也可以配置  
3、dma以及sdio中断的回调函数声明  bsp_driver_sd.h  在这里声明下  否则会出错误警告
4、dma回调函数更改  
5、完了就是测试程序  这个一大堆  自己找下就好  


下周开始折腾音频的 。。。

使用特权

评论回复
GULANGGOGO| | 2019-1-19 22:17 | 显示全部楼层
ding qi.

使用特权

评论回复
七颗咖啡豆| | 2019-1-21 11:07 | 显示全部楼层
freertos用起来简单,移植起来也简单,重点是免费

使用特权

评论回复
蒋博1026| | 2019-1-21 11:09 | 显示全部楼层
楼主厉害,顶一个,支持一下

使用特权

评论回复
Hstarr| | 2019-1-22 00:08 | 显示全部楼层
还是习惯用freertos了。

使用特权

评论回复
yediezeus| | 2019-1-22 09:43 | 显示全部楼层

使用特权

评论回复
yediezeus| | 2019-1-22 09:55 | 显示全部楼层

使用特权

评论回复
liuxiang5119|  楼主 | 2019-1-22 11:36 | 显示全部楼层
七颗咖啡豆 发表于 2019-1-21 11:07
freertos用起来简单,移植起来也简单,重点是免费

习惯问题  以前用过ucos  毕竟熟悉点 所以就直接移植ucos了

使用特权

评论回复
jstiger| | 2019-1-22 17:26 | 显示全部楼层
留脚印,学习一下。

楼主,共享一下PCB文件呗。

使用特权

评论回复
cxcdcfcv| | 2019-1-22 19:29 | 显示全部楼层
感谢分享,跟着学习一下!

使用特权

评论回复
liuxiang5119|  楼主 | 2019-1-25 10:59 | 显示全部楼层
jstiger 发表于 2019-1-22 17:26
留脚印,学习一下。

楼主,共享一下PCB文件呗。

额  板子还没调完   两大块还没折腾 音频  网口   挖完坑可以考虑分享  allegro16.6画的 微信截图_20190125105119.png
微信截图_20190125105826.png

使用特权

评论回复
lihui567| | 2019-1-29 13:47 | 显示全部楼层
楼主刚开始用HAL库的话,感觉入门怎么样,是不是哟好多坑啊

使用特权

评论回复
liuxiang5119|  楼主 | 2019-1-29 17:00 | 显示全部楼层
lihui567 发表于 2019-1-29 13:47
楼主刚开始用HAL库的话,感觉入门怎么样,是不是哟好多坑啊

也不知道是不是坑  关键是一抹黑 找不到啊   好些函数虽然说可以直接用  但是总想进去看看这货是怎么运行的 。。。

使用特权

评论回复
评论
lihui567 2019-1-29 17:02 回复TA
这个刚开始用着可不习惯了 
liuxiang5119|  楼主 | 2019-1-29 17:20 | 显示全部楼层
抱歉我直接跳过了第五步  先搞了下第六步网口这块  (这应该是年前最后一更 过两天回家了)     因为偶然间看到一个文档 (以太网进阶培训Part4_培训实验1) (文件放下边了需要的自己下);
      以太网进阶培训Part1_STM32以太网外设.pdf (1.98 MB)

使用特权

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

本版积分规则

13

主题

124

帖子

4

粉丝