目录
刚从标准库转到HAL学习,最近需要做一个机械臂控制,打算用USART1串口中断的方式控制四个舵机运行,现在记录一下过程,主控使用STM32F103ZET6。(本文只讲中断的接收,舵机控制暂时不涉及)
一、CUBEmx配置
1.设置系统时钟,配置SYS,配置时钟树
2.配置串口USART1
3.配置NVIC,开启串口中断
这里我将中断的优先级设置为12(单独学习串口中断并不需要修改优先级配置,按照0级即可)
4.点击GENERATE CODE输出文件即可
二、代码部分
我使用CLION进行编写,使用keil的方式也是一样的。
0.串口重定向——printf
在用户自定义区加上如下代码即可使用:
#ifdef __GNUC__ #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) #else #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) #endif PUTCHAR_PROTOTYPE { HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF); return ch; }
1.关于舵机
舵机使用了两个180°舵机和两个270°舵机,由于uint8_t最大到255,所以打算使用两个八位去存储舵机旋转的角度。
数据包采用如下形式,通过定义一个数组去存放数据包
uint8_t Rx_data[5];//数据包加上包头一共5个byte uint8_t Rx_flag=0;//设置一个中断完成的标志位
2.开启串口中断函数
HAL_UART_Receive_IT(&huart1,(uint8_t*)Rx_data,5);//开启串口接收中断函数
函数的三个参数,第一个指定是USART1的串口中断,第二个参数指定接受数据的位置,第三个指定数据接收的长度,当串口接收到5个字节的数据时,才会触发中断回调函数。
3.编写串口回调函数
回调函数仅仅负责将接受完成的标志位置1,处理工作再主函数while循环中进行。
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance==USART1)//首先判断是否是USART1触发的中断 { Rx_flag=1; } }
4.主函数部分
首先判断标志位,即是否接受完5个字节的数据,判断完成后,先将标志位即Rx_flag清零,否则会一直卡在主循环中,接着开启下一次的串口中断。
if(Rx_flag==1) { Rx_flag=0; HAL_UART_Receive_IT(&huart1,(uint8_t*)Rx_data,5); }
接着就是对数据包中的数据进行解析了:
1.首先判断包头包尾是否满足协议规范。然后在处理里面的数据:这里我通过switch语句对舵机的ID进行判断:0x01-0x04代表着四个舵机。
2.关于度数的转换:angle=Rx_data[2] * 256 + Rx_data[3],即高位为Rx_data[2] ,低位为Rx_data[3]。
switch (Rx_data[1]) { case 0x01: { printf("Servo_1 turn\r\n"); printf("angle=%d\r\n", Rx_data[2] * 256 + Rx_data[3]); } break; case 0x02: { printf("Servo_2 turn\r\n"); printf("angle=%d\r\n", Rx_data[2] * 256 + Rx_data[3]); } break; case 0x03: { printf("Servo_3 turn\r\n"); printf("angle=%d\r\n", Rx_data[2] * 256 + Rx_data[3]); } break; case 0x04: { printf("Servo_4 turn\r\n"); printf("angle=%d\r\n", Rx_data[2] * 256 + Rx_data[3]); } break; default: printf("data_error!"); }
3.处理完数据后需要把Rx_data[]中的值全部清零以便下一次的接收:
for (int i = 0; i < 5; ++i) { Rx_data[i]=0; }
4.主循环全部函数
while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ if(Rx_flag==1) { Rx_flag=0; HAL_UART_Receive_IT(&huart1,(uint8_t*)Rx_data,5); if(Rx_data[0]==0xaa&&Rx_data[4]==0xff) { switch (Rx_data[1]) { case 0x01: { printf("Servo_1 turn\r\n"); printf("angle=%d\r\n", Rx_data[2] * 256 + Rx_data[3]); } break; case 0x02: { printf("Servo_2 turn\r\n"); printf("angle=%d\r\n", Rx_data[2] * 256 + Rx_data[3]); } break; case 0x03: { printf("Servo_3 turn\r\n"); printf("angle=%d\r\n", Rx_data[2] * 256 + Rx_data[3]); } break; case 0x04: { printf("Servo_4 turn\r\n"); printf("angle=%d\r\n", Rx_data[2] * 256 + Rx_data[3]); } break; default: printf("data_error!"); } } else { printf("Data——error!\r\n"); } for (int i = 0; i < 5; ++i) { Rx_data[i]=0; } } }
三、实验现象:
多数厂家的ZET6带有ch340芯片,不需要单独使用usb转串口的工具,直接使用USB连接电脑即可。
四、总结
这仅仅是串口中断接收的部分,关于舵机控制会在下一篇文章给出,第一次写文章,能力不足,有许多改进的地方,希望大家批评指正!