工训竞赛物流车技术日志2021.3.3
HWT101数据处理
过程
记录一个今天挖到的坑,使用stm32CubeMx生成代码的时候不要有中文路径,今天我按照昨天设置好的Cube文档生成到了一个中文路径里,在我改好程序后找了很久发现没有Hex文件,幸亏我反映的快,应该是中文路径的问题,换了一个中文路径就成功了,要不又要浪费很多时间了。
还有一个点是,昨天一个朋友告诉我,使用CubeMx生成代码,只要把用户自定义的代码写在规定好的位置里,在生成代码的时候就不会覆盖掉。
好了,HWT101的数据处理方法上次已经记录过了,收到的数据主要有两部分,角速度和角度:
可以看出角速度前的标志位是0x55和0x52而角度的标志位是0x55和0x53
观察通讯协议之后,我更改了一下代码变成了这样:
首先是自定义变量:
uint8_t RxByte;//串口每接收一个数据,就存入RxByte中
uint8_t RxBuff[256];
uint16_t Rx_Count;
float Angle;
bool start1=false;
bool start2=false;
回调函数的内容如下:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
UNUSED(huart);
if(RxByte==0x55)
{
start1=true;
return;
}
if(start1==true && RxByte==0x53) start2=true;
else start1=false;
if(start2==true)
{
if(Rx_Count<=9) RxBuff[Rx_Count++]=RxByte;
else
{
Rx_Count=0;
start1=false;
start2=false;
Angle = (((short)RxBuff[6]<<8)|RxBuff[5])/32768.0*180.0;
}
}
if(Rx_Count>=254)
{
Rx_Count=0;
}
while(HAL_UART_Receive_IT(&huart2,&RxByte,1)==HAL_OK);
}
这段代码的意思是,当检测到这次收到的数据是0x55时,跳出回调函数,直接记录下一个数据,如果下一个数据不是0x53那么上次记录无效,需要重新记录,如果是0x53,那么证明有两个连续的0x55和0x53,说明目前是在记录角度,再记录9个数据到数组里,取第5号和第6号作为z轴角度数据,再把6号左移8位转为有符号的short类型,与5号并在一起,转变成float后就是角度数据了。
但实际上上面的程序没有任何输出结果,应该是条件太苛刻了,或者出现了判断交叉的环节,我又换了另一种方式:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
UNUSED(huart);
if(RxByte==0x55)
{
start1=true;
}
if(start1==true)
{
if(Rx_Count<=7) RxBuff[Rx_Count++]=RxByte;
else
{
Rx_Count=0;
start1=false;
Angle = (((short)RxBuff[6]<<8)|RxBuff[5])/32768.0*180.0;
}
if(Rx_Count>2)
{
if(RxBuff[1]!=0x53)
{
Rx_Count=0;
start1=false;
}
}
}
if(Rx_Count>=254)
{
Rx_Count=0;
}
while(HAL_UART_Receive_IT(&huart2,&RxByte,1)==HAL_OK);
}
其实我完全可以只要检测到0x53之后就向后去5号和6号位置的数据,但主要是担心5号和6号位置本身就含有0x53或者是最后的sum位含有0x53或者其他位置也有,这样的话就乱了,而0x55和0x53并在一起连着的情况在其他位置出现的概率几乎没有,所以就稍微麻烦一点,以免出现错误处理的数据。
理论上来讲,这样处理完数据之后应该没有问题了,但是我看不到结果是否正确,我试图调用HAL_UART_Transmit这个HAL库自带的串口发送函数,但是这个函数只能发送u8类型的数据,我需要一个float转u8的函数,于是写了下面的函数:
void FloatToUint8(uint8_t * char_array,float data)
{
uint8_t i;
for(i=0;i<4;i++)
{
char_array[i] = ((uint8_t*)(&data))[i];
}
}
这个函数可以把float转成4位的u8类型数据,我使用了这个函数后,报错也没有了,但是串口监视器上的数据实在是可读性太差了,满屏幕的十六进制
而且也无法直接关闭16进制显示,这个我目前也没有明白为什么有的数据可以直接关闭16进制显示后,就可以显示字符串了,反而这个数据不可以。
关闭16进制显示后出现一堆憨憨 -.-
这个时候还是printf好用,要想用printf必须要做printf的重定向,将printf的输出目标定向到uart1上。
首先在main.c中添加头文件:
#include "stdio.h"
之后在main.c中重写fputc和fgetc,主要是fputc:
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
return ch;
}
这样子的话可以直接调用printf函数了,同时也可以在串口监视器上观察到结果:
printf("Angle = %f\n",Angle);
不清楚为什么没有换行,但关键还在于结果有跳变,在静止的时候也有跳变
没有找到原因在哪里,又更改了一下代码,这次屈服了,为了赶工作进度,只检测了0x53,这次成功了,但是在剧烈变化的时候会冒出0这个数据,但是似乎只出现这个数据,我就单独把这个数据滤掉了
代码如下(简化了许多):
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
UNUSED(huart);
if(RxByte==0x53) start_u2=true;
if(start_u2==true)
{
RxBuff[Rx_Count++]=RxByte;
if(Rx_Count>=7)
{
Rx_Count=0;
start_u2=false;
if(((((short)RxBuff[6]<<8)|RxBuff[5])/32768.0*180.0)!=0) Angle = (((short)RxBuff[6]<<8)|RxBuff[5])/32768.0*180.0;
printf("Angle = %f\n",Angle);
}
}
if(Rx_Count>=254)
{
Rx_Count=0;
}
while(HAL_UART_Receive_IT(&huart2,&RxByte,1)==HAL_OK);
}
最终代码
#include "stdbool.h"
#include "string.h"
#include "stdio.h"
uint8_t RxByte;//串口每接收一个数据,就存入RxByte中
uint8_t RxBuff[256];
uint16_t Rx_Count;
float Angle;
bool start_u2=false;
HAL_UART_Receive_IT(&huart2,&RxByte,1);//串口2每接收到一个数据存入RxByte中,并调用一次回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
UNUSED(huart);
if(RxByte==0x53) start_u2=true;
if(start_u2==true)
{
RxBuff[Rx_Count++]=RxByte;
if(Rx_Count>=7)
{
Rx_Count=0;
start_u2=false;
if(((((short)RxBuff[6]<<8)|RxBuff[5])/32768.0*180.0)!=0) Angle = (((short)RxBuff[6]<<8)|RxBuff[5])/32768.0*180.0;
printf("Angle = %f\n",Angle);
}
}
if(Rx_Count>=254)
{
Rx_Count=0;
}
while(HAL_UART_Receive_IT(&huart2,&RxByte,1)==HAL_OK);
}
//printf重定向到串口1
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
return ch;
}
遗留问题
有一个比较重要的遗留问题,在我之前使用arduino连接HWT101的时候,结果是有正负之分的,但是现在的结果被映射到了0~360°,在0°和360°之间会有一个突变,这在后续的数据处理中是一个问题,有待被解决。
PWM波驱动直流电机
过程
目前还在测试阶段,真正应用应该是回学校或者等机械臂设计好之后才会正式安装,现在各种连接方式都是为了方便测试连接的。并且蓝丁胶和粗导线没有到货,我的3D打印机也没有回来,零件安装板也不方便安装,所以就没有办法组装机器。
而对于电机的测试,编码器和驱动板都需要5V电压,我打算直接从stm32F4的板子上的5V引出来,这样的话总输入的12V就无须接变压器转5V了,12V直接接到驱动板上,先测试一个电机。
首先是记录几个坑
第一个坑是商家程序的pwm波频率用了10kHz,所以我也改成了10kHz
但是以上改法是完全错误的,这样改的结果是Counter Period这个值是0,而更改占空比,也就是更改CCR寄存器的值,是无效的
比较合适的改法如下:
这样改过之后频率还是10kHz,占空比的变动范围是0~99
第二个坑是我以前用的是TIM9~TIM14来输出PWM,但我惊奇的发现CubeMx里这几个定时器的接口在stm32上根本找不到,所以很尴尬我改成了TIM3和TIM4,但是这样的话编码器接口就不够用了。
注:我已经把第一篇的电机部分连接更改了
但是昨天和同学交流的时候他之前为了节省定时器,编码器用的是输入中断的方式连接,所以现在的情况是我也只能用输入中断的方式来连接了。
接下来是程序部分,一次成功,没有更改过程。
最终代码
使能8个电机的PWM通道:
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);//使能8个电机pwm通道
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_4);
HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_3);
HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_4);
引入商家给的motor.c和motor.h并做一些更改:
#include "moto.h"
/**************************************************************************
函数功能:电机的正反转
入口参数:
moto:电机 1=电机A,0=电机B
pwm1:IN1的PWM的CRR寄存器赋值占空比 就是pwm1/7200
pwm2:IN2的PWM的CRR寄存器赋值占空比 就是pwm2/7200
返回 值: 无
a=pwm1-pwm2
|a| 的大小决定转速度
a 的符号决定转速
**************************************************************************/
void control(int moto, int pwm1, int pwm2)
{
if(moto==1)
{
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_1, pwm1); //修改比较值,修改占空比pwm1/7200
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_2, pwm2); //修改比较值,修改占空比pwm2/7200
}
if(moto==2)
{
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_3, pwm1); //修改比较值,修改占空比pwm1/7200
__HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_4, pwm2); //修改比较值,修改占空比pwm2/7200
}
if(moto==3)
{
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_1, pwm1); //修改比较值,修改占空比pwm1/7200
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_2, pwm2); //修改比较值,修改占空比pwm2/7200
}
if(moto==4)
{
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, pwm1); //修改比较值,修改占空比pwm1/7200
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, pwm2); //修改比较值,修改占空比pwm2/7200
}
}
#ifndef __MOTO_H
#define __MOTO_H
#include "tim.h"
void control(int moto, int pwm1, int pwm2);
#endif
用control这个函数控制电机正反转以及调速:
control(1,0,50);
control(2,0,50);
control(3,0,50);
control(4,0,50);
胡乱接线
输出成功pwm波
电机转动
总结
目前做的工作还都是非常非常简单和基础的,并没有把各个模块结合起来,其实理论上来讲没有什么难度,但是还是用了不少时间,还是因为我自己的stm32基础差,不太行啊
安排下一步任务
- 完成激光测距
以上都完成后就可以写底盘总程序了