stm32f407程序_STM32F407 自己做的板子,记录下HAL库+ucos 挖坑模式

(本文摘自21ic论坛,作者是21ic网友 liuxiang5119)

628d0f790dbb1ef0dd6521392840db6f.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读写
第八步:整体功能调试 到这里板子基本就调通了  该有的功能也基本全了!  


整个完成不知道多久,有时间就调试下!

第二步:串口中断接收(自学摸索 有问题欢迎指教)从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循环发送没出现问题d3222c010336f20c4cb340e6c75f2fc5.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的延时  这个时间足够,而且人眼反应速度也没这么快

f5ae2425baae73b81fa1f9017e6d28f9.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);

        }

}

第三步 定时器部分
串口数据接收用定时器去判断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后没有新的数据则认为接收完成)9bb6b62b479dbf3c5c5913a28974249c.png

a8d75210b914ab1713ae8f5e1f856e2e.png

第四步

sd卡+fatfs    帖子不是教程   需要教程的去网上找   这里只记录移植过程中出现的问题以及处理


1、fatfs配置  中文简体支持以及长文件名支持  

bef8d58fc53c73a34054c22b268a903f.png

2、IAR cstack设置 

c1e6960e9b3da03d98a87a4d6a0a1b0e.png

3、sdio以及dma  中断配置  使用ucos自己的中断配置方式 

52f9fff9de488dec0f864182f69a194d.png

更多操作

第四步   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   经测试比较稳定了 

bb3e665414efee32c500b8999276aacc.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、完了就是测试程序  这个一大堆  自己找下就好  
下周开始折腾音频的 。。。

推荐阅读

  • 高手讲解电源原理图,附:每个元件的功能详解

  • 离职后的第81天,附:找工作的种种~~~

  • 嵌入式开发中i2c协议是怎么玩的?

  • ARM嵌入式常用开发工具及开发流程(插画版)

  • 好文推荐!维修电路板的8个狠招

扫一扫关注21ic中国电子网 

29585af6c57557afe23e0de236a3cd65.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值