| 本帖最后由 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,等玩了更高级的功能之后再分享给大家,谢谢! |