找回密码
 立即注册
首页 业界区 业界 stm32cubemx+DMA+freertos实现usart不定长数据接收和发 ...

stm32cubemx+DMA+freertos实现usart不定长数据接收和发送

梭净挟 3 天前
本博客讲解如何在stm32cubemx+freertos+dma的情况下实现usart不定长接收和发送数据
cubemx配置

1.png

2.png

波特率随意
freertos没啥特别的配置,打开即可,我用的是CMSIS_V1,如下:
3.png

代码

先初始化,如下:
  1. void GimbalInitStart(void)
  2. {
  3.     // 启动DMA接收
  4.     HAL_UART_Receive_DMA(&huart2, rx2_buffer, RX2_BUFFER_SIZE);
  5.     // 启用空闲中断
  6.     __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
  7.     // 使能DMA传输完成中断
  8.     __HAL_DMA_ENABLE_IT(&hdma_usart2_tx, DMA_IT_TC);
  9. }
复制代码
后重写下面几个函数,完成接收:
  1. // 接收中断
  2. void Gimbal_USART2_IRQHandler(void)
  3. {
  4.     if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE) != RESET) {
  5.         __HAL_UART_CLEAR_IDLEFLAG(&huart2);
  6.         
  7.         // 停止 DMA 接收
  8.         HAL_UART_DMAStop(&huart2);
  9.         
  10.         // 计算接收长度
  11.         uint16_t temp = __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);
  12.         rx2_length = RX2_BUFFER_SIZE - temp;
  13.         
  14.         // 发送信号量,原来在freertos下提醒另一个任务可以处理数据了
  15.         BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  16.         xSemaphoreGiveFromISR(uart_rx_sem, &xHigherPriorityTaskWoken);
  17.         portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  18.     }
  19. }
  20. // 自己写的处理具体数据的任务,可在freertos的某个任务下循环调用
  21. void Gimbal_Process_Rx_Data(void)
  22. {
  23.     // 对应上面的发送信号量,检测到信号量后就执行数据处理任务
  24.     if(xSemaphoreTake(uart_rx_sem, 0) == pdTRUE) {
  25.         
  26.         // 重新启动DMA接收
  27.         HAL_UART_Receive_DMA(&huart2, rx2_buffer, RX2_BUFFER_SIZE);
  28.     }
  29. }
复制代码
完整代码如下,可参考下:
  1. // gimbal.h
  2. #ifndef GIMBAL_H
  3. #define GIMBAL_H
  4. #include "stm32f1xx_hal.h"
  5. #include "FreeRTOS.h"
  6. #include "semphr.h"
  7. #include "queue.h"
  8. #include "cmsis_os.h"
  9. #include "string.h"
  10. #include "crc16.h"
  11. // 底层能调用的函数和接口
  12. #define RX2_BUFFER_SIZE 256
  13. #define TX2_BUFFER_SIZE 128
  14. #define ORDER_LIST_SIZE 64
  15. // 角度限制
  16. #define MAX_PITCH_ANGLE 25 * 10  // 最大25° 即向上
  17. #define MIN_PITCH_ANGLE -90 * 10  // 最小-90° 即向下
  18. void GimbalInitStart(void);   
  19. void Gimbal_USART2_IRQHandler(void);  // 自定义的回调处理函数,计算接收的数据长度,在“stm32f1xx_it.c”中被调用
  20. void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart); // 错误处理
  21. void Gimbal_Process_Rx_Data(void);  // 处理接收到的数据
  22. void Gimbal_DMA_Send_record(const uint8_t *data, uint16_t size);  // 会自动在环形数组中记录当前发送的是啥东西
  23. void UART2_DMA_Send(const uint8_t *data, uint16_t size);  // 底层发送函数
  24. void circle_write(uint8_t data);  // 环形数组写入
  25. uint8_t circle_read(void);  // 环形数组读取
  26. void Gimbal_Init_Func(void);  // 云台初始化发送指令
  27. void Add_crc16_code(uint8_t *data, uint16_t size);  // 按照给定数组和长度,在最后面加上两字节的校验位
  28. uint8_t check_crc16(uint8_t *data, uint16_t size);  // 检查收到的数据是否正确
  29. void Gimbal_total_Init(void);  // 其他初始化
  30. void Set_Gimbal_Pitch(int16_t pitch_angle);
  31. #endif // GIMBAL_H
复制代码
  1. #include "gimbal.h"
  2. static uint8_t rx2_buffer[RX2_BUFFER_SIZE];
  3. static uint8_t tx_buffer[TX2_BUFFER_SIZE];
  4. static volatile uint16_t rx2_length = 0;
  5. SemaphoreHandle_t uart_rx_sem;
  6. SemaphoreHandle_t gimbal_init_flag;
  7. extern UART_HandleTypeDef huart2;
  8. extern DMA_HandleTypeDef hdma_usart2_tx;
  9. extern DMA_HandleTypeDef hdma_usart2_rx;
  10. extern volatile uint8_t beer_ring_mode;
  11. // 环形数组,按顺序记录和相机通信的指令,若发送失败则可快速找到上一条指令重新发送
  12. static uint8_t order_list[ORDER_LIST_SIZE];
  13. static volatile uint8_t write_pointer;  // 写指针(新数据位置)
  14. static volatile uint8_t read_pointer;   // 读指针(若要读数据一般从该位置的下一个开始读)
  15. // 各种特定的指令代号
  16. static uint8_t set_video_argument = 0x21;  // 设置云台图像的各种参数
  17. // 一些固定的指令,直接封装成一条了
  18. static uint8_t get_gimbal_config[] = {0x55, 0x66, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0F, 0x75};  // 获取云台配置
  19. static uint8_t set_follow_mode[] = {0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0C, 0x04, 0xB0, 0x8E};  // 设置云台跟随模式,即只有pitch轴能控制
  20. static uint8_t set_CVBS_output[] = {0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x0C, 0x07, 0xD3, 0xBE}; // 设置视频CVBS输出
  21. static uint8_t get_gimbal_firmware_version[] = {0x55, 0x66, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x64, 0xC4};  // 获取云台固件版本号
  22. static uint8_t set_frame_super_definition[] = {0x55, 0x66, 0x01, 0x09, 0x00, 0x00, 0x00, 0x21, 0x01, 0x02, 0x80, 0x07, 0x38, 0x04, 0xD0, 0x07, 0x00, 0x5A, 0x68};  // 设置画面超清
  23. static uint8_t set_frame_high_definition[] = {0x55, 0x66, 0x01, 0x09, 0x00, 0x00, 0x00, 0x21, 0x01, 0x02, 0x00, 0x05, 0xD0, 0x02, 0xDC, 0x05, 0x00, 0x58, 0x45};  // 设置画面高清
  24. static uint8_t set_frame_mode3[] = {0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x11, 0x03, 0x78, 0x8B};  // 设置模式3,主码流为变焦相机
  25. static uint8_t set_frame_mode7[] = {0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x11, 0x07, 0xFC, 0xCB};  // 设置模式7,主码流为热成像相机
  26. static uint8_t one_click_back[] = {0x55, 0x66, 0x01, 0x01, 0x00, 0x00, 0x00, 0x08, 0x01, 0xD1, 0x12};  // 云台一键回中
  27. static uint8_t set_gimbal_pitch[] = {0x55, 0x66, 0x01, 0x04, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};  // 设置云台pitch角度
  28. // 其他相关的一些云台的参数
  29. static uint8_t camera_firmware_version[3];  // 相机固件号,可以用来判断相机初始化结束了没有,大端模式
  30. extern volatile uint8_t camera_init = 0;  // 相机是否初始化
  31. static volatile uint8_t video_output_mode = 0;  // 相机输出模式 0x00表示还未读取到信息 0x01 表示读取到了但并不是预期的CVBS模式 0x02表示当前模式为预期的模式CVBS
  32. static volatile uint8_t video_super_or_high_definition = 0;  // 0表示超清,1表示高清
  33. static volatile uint8_t current_frame_mode = 0;  // 0表示模式3, 1表示模式7
  34. void GimbalInitStart(void)
  35. {
  36.     // 启动DMA接收
  37.     HAL_UART_Receive_DMA(&huart2, rx2_buffer, RX2_BUFFER_SIZE);
  38.     // 启用空闲中断
  39.     __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
  40.     // 创建二值信号量
  41.     uart_rx_sem = xSemaphoreCreateBinary();
  42.     // 使能DMA传输完成中断
  43.     __HAL_DMA_ENABLE_IT(&hdma_usart2_tx, DMA_IT_TC);
  44. }
  45. void Gimbal_Process_Rx_Data(void)
  46. {
  47.     if(xSemaphoreTake(uart_rx_sem, 0) == pdTRUE) {
  48.         // 处理接收到的数据
  49.         // 校验crc16
  50.         if(check_crc16(rx2_buffer, rx2_length) == 0x01)
  51.         {
  52.             switch(rx2_buffer[7]){
  53.                 case 0x01:  // 获取云台固件
  54.                 {
  55.                     // 判断固件号有没有,有就是初始化成功了
  56.                     if(!((rx2_buffer[8] == 0x00) && (rx2_buffer[9] == 0x00) && (rx2_buffer[10] == 0x00)))
  57.                     {
  58.                         camera_init = 1;
  59.                         memcpy(camera_firmware_version, rx2_buffer + 8, 3);
  60.                     }
  61.                     xSemaphoreGive(gimbal_init_flag);
  62.                     break;
  63.                 }
  64.                 case 0x0A:  // 云台配置信息
  65.                 {
  66.                     if(rx2_buffer[14] == 0x00) video_output_mode = 0x01;
  67.                     else if(rx2_buffer[14] == 0x01) video_output_mode = 0x02;
  68.                     
  69.                     break;
  70.                 }
  71.                 case 0x21:  // 设置画面的清晰度
  72.                 {
  73.                     uint8_t now_code = circle_read();
  74.                     if(rx2_buffer[9] != 0x01)
  75.                     {
  76.                         if(video_super_or_high_definition == 0x00)
  77.                         {
  78.                             Gimbal_DMA_Send_record(set_frame_super_definition, sizeof(set_frame_super_definition));
  79.                         }else
  80.                         {
  81.                             Gimbal_DMA_Send_record(set_frame_high_definition, sizeof(set_frame_high_definition));
  82.                         }
  83.                     }
  84.                     if(now_code == 0x21)
  85.                     {
  86.                     
  87.                     }
  88.                     else
  89.                     {
  90.                         // 若不是当前的恢复,好像也没啥办法哈哈
  91.                     }
  92.                     
  93.                     break;
  94.                 }
  95.                 case 0x11:
  96.                 {
  97.                     uint8_t now_code = circle_read();
  98.                     if(rx2_buffer[8] == 0x03)  // 表示当前画面为模式3
  99.                     {
  100.                         if(current_frame_mode == 0x01)
  101.                         {
  102.                             Gimbal_DMA_Send_record(set_frame_mode7, sizeof(set_frame_mode7));
  103.                         }
  104.                     }else if(rx2_buffer[8] == 0x07) // 表示模式7
  105.                     {
  106.                         if(current_frame_mode == 0x00)
  107.                         {
  108.                             Gimbal_DMA_Send_record(set_frame_mode3, sizeof(set_frame_mode3));
  109.                         }
  110.                     }else  // 其他的一些不想要的模式
  111.                     {
  112.                         // 就默认设置为模式3
  113.                         Gimbal_DMA_Send_record(set_frame_mode3, sizeof(set_frame_mode3));
  114.                     }
  115.                     if(now_code == 0x21)
  116.                     {
  117.                     
  118.                     }
  119.                     else
  120.                     {
  121.                         // 若不是当前的恢复,好像也没啥办法哈哈
  122.                     }
  123.                     
  124.                     break;
  125.                 }
  126.                 case 0x08:
  127.                 {
  128.                     uint8_t now_code = circle_read();
  129.                     if(rx2_buffer[8] == 0x00)
  130.                     {
  131.                         Gimbal_DMA_Send_record(one_click_back, sizeof(one_click_back));
  132.                     }
  133.                     
  134.                     break;
  135.                 }
  136.                 default:
  137.                 {
  138.                     // 若收到一些预期之外的信息,则直接掠过
  139.                     break;
  140.                 }
  141.             }
  142.         }
  143.         
  144.         // 重新启动DMA接收
  145.         HAL_UART_Receive_DMA(&huart2, rx2_buffer, RX2_BUFFER_SIZE);
  146.     }
  147. }
  148. void Gimbal_USART2_IRQHandler(void)
  149. {
  150.     if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE) != RESET) {
  151.         __HAL_UART_CLEAR_IDLEFLAG(&huart2);
  152.         
  153.         // 停止 DMA 接收
  154.         HAL_UART_DMAStop(&huart2);
  155.         
  156.         // 计算接收长度
  157.         uint16_t temp = __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);
  158.         rx2_length = RX2_BUFFER_SIZE - temp;
  159.         
  160.         // 发送信号量
  161.         BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  162.         xSemaphoreGiveFromISR(uart_rx_sem, &xHigherPriorityTaskWoken);
  163.         portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  164.     }
  165. }
  166. void Gimbal_DMA_Send_record(const uint8_t *data, uint16_t size)
  167. {
  168.     // 记录当前发送的指令代号
  169.     circle_write(data[7]);
  170.     // 底层发送
  171.     UART2_DMA_Send(data, size);
  172. }
  173. void UART2_DMA_Send(const uint8_t *data, uint16_t size)
  174. {
  175.    
  176.     HAL_UART_Transmit_DMA(&huart2, data, size);
  177. }
  178. void circle_write(uint8_t data)
  179. {
  180.     __disable_irq();  // 关中断(ARM Cortex-M)
  181.     uint8_t next_addr = (write_pointer + 1) & (ORDER_LIST_SIZE - 1);
  182.     if(write_pointer == read_pointer) read_pointer = next_addr;
  183.     order_list[next_addr] = data;
  184.     write_pointer = next_addr;
  185.     __enable_irq();   // 开中断
  186. }
  187. uint8_t circle_read(void)
  188. {
  189.     __disable_irq();  // 关中断(ARM Cortex-M)
  190.     if(write_pointer == read_pointer) return 0xFF;  // 代表没有数据能读
  191.     read_pointer = (read_pointer + 1) & (ORDER_LIST_SIZE - 1);
  192.     return order_list[read_pointer];
  193.     __enable_irq();   // 开中断
  194. }
  195. void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {
  196.     if(huart->Instance == USART2) {
  197.         if(huart->ErrorCode & HAL_UART_ERROR_DMA) {
  198.             // 重新初始化DMA
  199.             HAL_UART_DMAStop(huart);
  200.             // 接收重启
  201.             HAL_UART_Receive_DMA(huart, rx2_buffer, RX2_BUFFER_SIZE);
  202.         }
  203.         
  204.     }
  205. }
  206. void Gimbal_Init_Func(void)
  207. {
  208.     UART2_DMA_Send(get_gimbal_firmware_version, sizeof(get_gimbal_firmware_version));
  209. }
  210. // 此处size为要发送的数据。不包括两字节的校验码
  211. void Add_crc16_code(uint8_t *data, uint16_t size)
  212. {
  213.     uint16_t crc16_code = do_crc16_table(data, size);
  214.     // 小端模式赋到数组的末尾
  215.     data[size] = crc16_code & 0xFF;
  216.     data[size + 1] = (crc16_code >> 8) & 0xFF;
  217. }
  218. // 此处size为整个接收到的数据的长度,包括两位的校验码
  219. uint8_t check_crc16(uint8_t *data, uint16_t size)
  220. {
  221.     uint16_t calculate_crc16_code = do_crc16_table(data, size - 2);
  222.     if((data[size - 1] == ((calculate_crc16_code >> 8) & 0xFF)) && (data[size - 2] == (calculate_crc16_code & 0xFF)))
  223.     {
  224.         return 0x01;  // 校验成功
  225.     }else{
  226.         return 0x00;  // 校验失败
  227.     }
  228. }
  229. void Gimbal_total_Init(void)
  230. {
  231.     while(video_output_mode != 0x02)
  232.     {
  233.         if(video_output_mode == 0x00)
  234.         {
  235.             beer_ring_mode = 4;
  236.             UART2_DMA_Send(get_gimbal_config, sizeof(get_gimbal_config));
  237.             osDelay(100);
  238.         }else if(video_output_mode == 0x01)
  239.         {
  240.             UART2_DMA_Send(set_CVBS_output, sizeof(set_CVBS_output));
  241.             beer_ring_mode = 3;
  242.             while(1);  // 会卡死在这,代表需要重新断电重启才行
  243.         }
  244.     }
  245.     // 设置画面超清
  246.     video_super_or_high_definition = 0;
  247.     Gimbal_DMA_Send_record(set_frame_super_definition, sizeof(set_frame_super_definition));
  248.     osDelay(100);  // 延迟一段时间,一个一个初始化
  249.     // 设置推流方式3
  250.     current_frame_mode = 0;
  251.     Gimbal_DMA_Send_record(set_frame_mode3, sizeof(set_frame_mode3));
  252.     osDelay(100);  // 延迟一段时间,一个一个初始化
  253.     // 设置跟随模式
  254.     UART2_DMA_Send(set_follow_mode, sizeof(set_follow_mode));  // 无ack,不加入环形数组
  255.     osDelay(100);  // 延迟一段时间,一个一个初始化
  256. }
  257. void Set_Gimbal_Pitch(int16_t pitch_angle)
  258. {
  259.     if(pitch_angle > MAX_PITCH_ANGLE) pitch_angle = MAX_PITCH_ANGLE;
  260.     if(pitch_angle < MIN_PITCH_ANGLE) pitch_angle = MIN_PITCH_ANGLE;
  261.     set_gimbal_pitch[10] = (pitch_angle >> 8) & 0xFF;  // 大端模式
  262.     set_gimbal_pitch[11] = pitch_angle & 0xFF;
  263.     Add_crc16_code(set_gimbal_pitch, sizeof(set_gimbal_pitch) - 2);
  264.     Gimbal_DMA_Send_record(set_gimbal_pitch, sizeof(set_gimbal_pitch));
  265. }
复制代码
4.png

5.png


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册