自己做的板子
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的延时 这个时间足够,而且人眼反应速度也没这么快
代码如下:
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后没有新的数据则认为接收完成)
第四步
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、完了就是测试程序 这个一大堆 自己找下就好
下周开始折腾音频的 。。。