本帖最后由 karaxiaoyu 于 2015-12-24 16:10 编辑
记得在活动开始的第一天我就提交了申请,看了STM32F469官方的介绍视频我就很想拥有一块板子,这里感谢21IC。拿到板子后,感觉板子的造型去掉下巴就像几年前的一部手机了,板子特殊的部分在于同时集成了适用于Flash存储器的ART Accelerator™和适用于结合LCD-TFT和MIPI-DSI显示接口进行图形处理的Chrom-ART Accelerator™,能够为高要求的实时处理提供足够资源和高级用户界面。 硬件方面,原理图由STM32F469NIH6U、AUDIO、LCD、数字麦克风、SD卡、USB_OTG、128M外部SDRAM、Arduino接口、外部引脚、ST_LINK、Power供电11个块组成。PCB不是AD格式的,为什么用AD画原理图,Cadence Allegro画PCB呢,AD画6层板没问题呀,好吧,硬件就简单说到这里。 下面会送上Cadence Allegro的百度网盘下载链接和原理图PCB文件
下面实现用CubeMX实现串口DMA接收不定长数据 先说下串口实现的3种方式,可以用轮询、中断以及DMA实现,轮询模式为堵塞模式,必须在设定的时间内接收到设定的数据长度才会正确接收;中断模式为非堵塞模式,也是平时用的比较多的,但在HAL库中必须在每次接完之后就要重新开启接收中断,这样在接收不确定长度数据就不好处理;最后发现有一个比较好的方法那就是用串口的空闲中断+DMA实现串口数据的接收,在接收一帧数据只需要中断一次,这样就可以接收不定长数据了,下面介绍具体过程:
首先查阅官方原理图知道Arduino接口的串口是使用USART6 在STM32CubeMX Pinout中选择USART6为 Asynchronous, 然后设置定时器3 接下来设置时钟,时钟配置真是方便,主频肯定是180MHz啦,定时器3是挂载在APB1上的, 波特率115200bit/s,8位数据位,无奇偶校验,1位停止位,其它默认 这里配置USART的DMA,选择USART6_RX为DMA Request,DMA2通道1,其它默认 使能USART6中断 接下来配置定时器,89分频,定时10ms中断一次 使能定时器3中断 把这个勾上代码层次比较好
打开生成的代码后,首先定义BUFFER_SIZE,接收到数据的长度,接收完标志,接收数组 #define BUFFER_SIZE 128 uint8_t rx_len=0; uint8_t recv_end_flag=0; uint8_t rx_buffer[BUFFER_SIZE]; 然后再开启串口空闲中断和定时器中断 __HAL_UART_ENABLE_IT(&huart6,UART_IT_IDLE); HAL_TIM_Base_Start_IT(&htim3); 在串口6的中断函数中,,写入如下代码 uint32_t tmp_flag = 0; uint32_t temp; tmp_flag = __HAL_UART_GET_FLAG(&huart6,UART_FLAG_IDLE); if((tmp_flag != RESET)) { __HAL_UART_CLEAR_IDLEFLAG(&huart6); temp = huart6.Instance->SR; temp = huart6.Instance->DR; HAL_UART_DMAStop(&huart6); temp = hdma_usart6_rx.Instance->NDTR; rx_len = BUFFER_SIZE - temp; recv_end_flag = 1; } 首先判断是不是空闲中断,然后清除标志位,暂停DMA传输后得到剩余的空间大小,通过总大小减去剩余的就是此次接收到的数据长度。在接收完成标志位置一之后,就可以在主函数中处理了
把接收到的数据发出去,重新开启DMA接收 if(recv_end_flag==1) { HAL_UART_Transmit(&huart6,rx_buffer,rx_len,10); rx_len=0; recv_end_flag=0; HAL_UART_Receive_DMA(&huart6,rx_buffer,BUFFER_SIZE); } 这样就可以通过串口DMA接收不定长串口数据了。
在定时器中断回调函数中加入如下代码 voidHAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance== TIM3) { static uint16_t tim3_cnt = 0; if(tim3_cnt== 100) { tim3_cnt= 0; HAL_GPIO_TogglePin(GPIOG,GPIO_PIN_6); } else { tim3_cnt++; } } } 实现定时器中断翻转IO口状态。 下面加入任务轮询系统: 1:初始化定时器,这里设定定时中断为10ms,中断太频繁效率低 2:设计一个结构体 //任务结构 typedef struct _TASK_COMPONENTS { unsigned char Run; //任务运行标记 0-不运行 1-运行 unsigned int Timer; //计时器 unsigned int ItvTime; //任务运行时间间隔 void (*TaskHook)(void); //要运行的任务函数 }TASK_COMPONENTS; //任务定义 3:任务运行标志 void TaskRemarks(void) { unsignedchar i; for(i=0;i<TASK_MAX; i++) //逐个时间处理 { if(TaskComps.Timer) //时间不为0 { TaskComps.Timer--; //减去一个节拍 if(TaskComps.Timer == 0) { TaskComps.Timer =TaskComps.ItvTime; //恢复计时器值 TaskComps.Run = 1; //任务可以运行 } } } } 4:任务处理 voidTaskProcess(void) { unsigned char i; for(i=0; i<TASK_MAX; i++) //逐个任务时间处理 { if(TaskComps.Run) { TaskComps.TaskHook(); //运行任务 TaskComps.Run = 0; //标志清零 } __delay_cycles(100); } } 这个架构只需要在定时器中调用TaskRemarks(),定义自己的任务数之后,在主程序中调用TaskProcess()就行。
这也是一个小小的个人认为不错的框架,暂时就先写这么多,之后准备上FreeRTOS,等玩了更高级的功能之后再分享给大家,谢谢! |