开启辅助访问 切换到窄版

打印 上一主题 下一主题

STM32F407 自己做的板子,记录下HAL库+ucos 挖坑模式

[复制链接]
楼主
跳转到指定楼层
| 只看该作者 回帖奖励 |倒序浏览 |阅读模式
(本文摘自21ic论坛,作者是21ic网友 liuxiang5119)
自己做的板子

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循环发送没出现问题
另外记录下发送这块,被自己给坑了,如果在中断模式,发送数据前需要判断设备状态,否则连续两条发送数据会丢失第二条(开始真心不知道)
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的延时这个时间足够,而且人眼反应速度也没这么快

代码如下:
staticvoidAppTaskSerEcho (void*p_arg)

{

    OS_ERR  err;



    (void)p_arg;



    CPU_SR_ALLOC();

   

    while (DEF_TRUE)     {

        #ifdefUSART3_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后没有新的数据则认为接收完成)




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


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

2、IAR cstack设置

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


更多操作


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


现在还不确定到底是本身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中国电子网

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表