一、绝对值编码器
绝对值编码器可以认为是与增量式编码器相对的一种编码器,与使用增量式编码器读取脉冲值不同,绝对值编码器可以直接读取编码器所处的角度值。根据量程,绝对值编码器也可以分为单圈编码器和多圈编码器。实际上根据每个厂家设计的编码器的通讯方式也可以再细分,这里我不再赘述。实验室使用绝对值编码器的目的是能够利用正交两个绝对值编码器,设计一个准确度高的里程计,借此机会学习一下绝对值编码器。
本文所使用的绝对值编码器是BRITER的1024分度CAN总线绝对值编码器,其官方文档如下:
CAN说明书通信协议+单圈V2.2.pdf
https://www.briter.net/web/image ... 95%E5%9C%88V2.2.pdf
根据官方文档提供的方法可以轻松写出一个编码器读取的示例,我这里为了便于移植,直接写成库的形式,仅供参考。
二、软件编写
1.STM32CubeMX配置
这里我们需要使用CAN通信,需打开CAN的RX中断,波特率配置参考官方文档,默认波特率为500KHz(可以更改)
我这里打开USART1便于与PC通信,同样需打开中断,用于printf的重定向
2.使用BTR_Encoder库
我参考了已有的CAN库和CSDN上少有的资料,写了一套库程序,供各位食用
BTR_Encoder.h:编码器外设库头文件
#ifndef BTR_ENCODER__H
#define BTR_ENCODER__H
#include "stm32f4xx_hal.h"
#include "stm32f4xx_hal_can.h"
extern CAN_HandleTypeDef hcan1;
#define Encoder_CAN hcan1 //定义用于编码器的CAN句柄
#define Encoder_Baud_500K ((uint8_t)0x00)
#define Encoder_Baud_1M ((uint8_t)0x01)
#define Encoder_Baud_250K ((uint8_t)0x02)
#define Encoder_Baud_125K ((uint8_t)0x03)
#define Encoder_Baud_100K ((uint8_t)0x04)
//定义编码器和储存参数的结构体,在存在多个编码器的时候十分有用
typedef struct
{
uint8_t Encoder_id;
int16_t Encoder_value;
int16_t Encoder_value_last;
int16_t Encoder_value_diff;
int64_t Encoder_value_sum;
float Encoder_speed;
int8_t first_flag;
}BTR_EncoderTypeDef;
uint8_t Filter_Init(); //设置Filter过滤器是一个可选项,如果你已经在主文件中写了一套Filter初始化,可以忽略这一步,当然我也是提供了很好的Filter初始化模版
BTR_EncoderTypeDef Encoder_Init(uint8_t id); //初始化一个编码器结构体,id用于区分不同的编码器,确保CAN总线上的编码器有不同id,以及处于同一波特率
uint8_t Encoder_Send(uint32_t id, uint8_t *msg, uint8_t len); //内部函数,无需使用
uint8_t Encoder_GetValue(BTR_EncoderTypeDef* btr_encoder); //在主函数中调用该函数可以让编码器返回当前的值,获取值的过程需要用户自己在回调函数中写
void Encoder_ValueProcess(BTR_EncoderTypeDef* btr_encoder,uint8_t *msg); //在回调函数用于将编码器返回的值处理
uint8_t Enocder_Enable_AutoCallBack(BTR_EncoderTypeDef* btr_encoder); //打开编码器自动回传
uint8_t Encoder_Disable_AutoCallBack(BTR_EncoderTypeDef* btr_encoder); //关闭编码器自动回传
uint8_t Encoder_Set_Zero(BTR_EncoderTypeDef* MyEncoder); //编码器置零
uint8_t Encoder_Set_AutoCallBack(BTR_EncoderTypeDef* btr_encoder,uint16_t utime); //编码器自动回传设置,时间单位为微秒
uint8_t Encoder_Set_Id(BTR_EncoderTypeDef* btr_encoder,uint8_t id); //设置编码器id,设置成功的话,结构体内的id也会更新
uint8_t Enocder_Set_Baud(BTR_EncoderTypeDef* btr_encoder,uint8_t baud); //设置编码器波特率,这个函数注定是一次性的
#endif
BTR_Encoder.c:编码器外设库源文件
#include "BTR_Encoder.h"
uint8_t Filter_Init(){
CAN_FilterTypeDef sFilterConfig;
sFilterConfig.FilterActivation = ENABLE;//打开过滤器
sFilterConfig.FilterBank = 0;//过滤器0 这里可设0-13
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;//采用掩码模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;//采用32位掩码模式
sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;//采用FIFO0
sFilterConfig.FilterIdHigh = 0x0000; //设置过滤器ID高16位
sFilterConfig.FilterIdLow = 0x0000;//设置过滤器ID低16位
sFilterConfig.FilterMaskIdHigh = 0x0000;//设置过滤器掩码高16位
sFilterConfig.FilterMaskIdLow = 0x0000;//设置过滤器掩码低16位
if(HAL_CAN_ConfigFilter(&Encoder_CAN,&sFilterConfig) != HAL_OK)//初始化过滤器
{
return 0;
}
if(HAL_CAN_Start(&Encoder_CAN) != HAL_OK)//打开can
{
return 0;
}
if(HAL_CAN_ActivateNotification(&Encoder_CAN,CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)//开启接受邮箱0挂起中断
{
return 0;
}
return 1;
}
BTR_EncoderTypeDef Encoder_Init(uint8_t id){
BTR_EncoderTypeDef btr_encoder;
btr_encoder.Encoder_id = id;
btr_encoder.Encoder_value=0;
btr_encoder.Encoder_value_last=0;
btr_encoder.Encoder_value_diff=0;
btr_encoder.Encoder_value_sum=0;
btr_encoder.Encoder_speed=0.0;
btr_encoder.first_flag=0;
return btr_encoder;
}
uint8_t Encoder_Send(uint32_t id, uint8_t *msg, uint8_t len){
uint16_t t = 0;
uint32_t TxMailbox = CAN_TX_MAILBOX0;
CAN_TxHeaderTypeDef TXHeader;
TXHeader.StdId = id;
TXHeader.ExtId = id;
TXHeader.IDE = CAN_ID_STD;
TXHeader.RTR = CAN_RTR_DATA;
TXHeader.DLC = len;
if (HAL_CAN_AddTxMessage(&Encoder_CAN, &TXHeader, msg, &TxMailbox) != HAL_OK)
{
return 1;
}
while (HAL_CAN_GetTxMailboxesFreeLevel(&Encoder_CAN) != 3)
{
t++;
if (t > 0xFFF)
{
HAL_CAN_AbortTxRequest(&Encoder_CAN, TxMailbox);
return 1;
}
}
return 0;
}
uint8_t Encoder_GetValue(BTR_EncoderTypeDef* btr_encoder){
uint8_t cmd[4] = {0x04,btr_encoder->Encoder_id,0x01,0x00};
if(Encoder_Send(btr_encoder->Encoder_id,cmd,4)){
return 1;
}
return 0;
}
void Encoder_ValueProcess(BTR_EncoderTypeDef* btr_encoder,uint8_t *msg){
btr_encoder->Encoder_value=msg[3]|msg[4]<<8|msg[5]<<16|msg[6]<<24;
btr_encoder->Encoder_value_diff=(float)(btr_encoder->Encoder_value-btr_encoder->Encoder_value_last);
btr_encoder->Encoder_speed=btr_encoder->Encoder_value_diff/0.001f;
if(btr_encoder->first_flag!=0)
{
if(btr_encoder->Encoder_value_diff<-12000)
{
btr_encoder->Encoder_value_sum+=btr_encoder->Encoder_value_diff+24576;
}else if(btr_encoder->Encoder_value_diff>12000)
{
btr_encoder->Encoder_value_sum+=btr_encoder->Encoder_value_diff-24576;
}else
{
btr_encoder->Encoder_value_sum+=btr_encoder->Encoder_value_diff;
}
}
btr_encoder->Encoder_value_last = btr_encoder->Encoder_value;
btr_encoder->first_flag=1;
}
uint8_t Encoder_Enable_AutoCallBack(BTR_EncoderTypeDef* btr_encoder){
uint8_t cmd[4] = {0x04,btr_encoder->Encoder_id,0x04,0xAA};
if(Encoder_Send(btr_encoder->Encoder_id,cmd,4)){
return 1;
}
return 0;
}
uint8_t Encoder_Disable_AutoCallBack(BTR_EncoderTypeDef* btr_encoder){
uint8_t cmd[4] = {0x04,btr_encoder->Encoder_id,0x04,0x00};
if(Encoder_Send(btr_encoder->Encoder_id,cmd,4)){
return 1;
}
return 0;
}
uint8_t Encoder_Set_AutoCallBack(BTR_EncoderTypeDef* btr_encoder,uint16_t utime){
Encoder_Enable_AutoCallBack(btr_encoder);
uint8_t cmd[5] = {0x05,btr_encoder->Encoder_id,0x05,utime&0x0F,utime>>8};
if(Encoder_Send(btr_encoder->Encoder_id,cmd,5)){
return 1;
}
return 0;
}
uint8_t Encoder_Set_Zero(BTR_EncoderTypeDef* btr_encoder){
uint8_t cmd[4] = {0x04,btr_encoder->Encoder_id,0x06,0x00};
if(Encoder_Send(btr_encoder->Encoder_id,cmd,4)){
return 1;
}
return 0;
}
uint8_t Encoder_Set_Id(BTR_EncoderTypeDef* btr_encoder,uint8_t id){
uint8_t cmd[4] = {0x04,btr_encoder->Encoder_id,0x02,id};
if(Encoder_Send(btr_encoder->Encoder_id,cmd,4)){
return 1;
}
btr_encoder->Encoder_id = id;
return 0;
}
uint8_t Encoder_Set_Baud(BTR_EncoderTypeDef* btr_encoder,uint8_t baud){
uint8_t cmd[4] = {0x04,btr_encoder->Encoder_id,0x03,baud};
if(Encoder_Send(btr_encoder->Encoder_id,cmd,4)){
return 1;
}
return 0;
}
main.c:这当然不用说是stm32的入口文件,这里我只给定代码沙箱
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "BTR_Encoder.h"
/* USER CODE END Includes */
/* USER CODE BEGIN 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;
}
//以上是我的编译器(CubeIDE)的printf串口重定向,CubeMX要使用下面的重定向
//uint8_t Encoder_Set_Baud(BTR_EncoderTypeDef* btr_encoder,uint8_t baud){
// uint8_t cmd[4] = {0x04,btr_encoder->Encoder_id,0x03,baud};
// if(Encoder_Send(btr_encoder->Encoder_id,cmd,4)){
// return 1;
// }
// return 0;
//}
BTR_EncoderTypeDef MyEncoder;
void CAN_Encoder(BTR_EncoderTypeDef* Encoder,uint8_t *mes){
printf("\nEncoder ID: 0x%x\n",Encoder->Encoder_id);
if(mes[2] == 0x01){
Encoder_ValueProcess(Encoder,mes);
printf("Value:%d \n",Encoder->Encoder_value);
printf("Diff:%d \n",Encoder->Encoder_value_diff);
printf("Speed:%.0f \n",Encoder->Encoder_speed);
printf("Sum: %lld \n\n",Encoder->Encoder_value_sum);
}
else if(mes[2] == 0x02)
printf("Encoder Set ID success!\n\n");
else if(mes[2] == 0x04)
printf("Encoder Set AutoCallBack success!\n\n");
else if(mes[2] == 0x06)
printf("Encoder Set Zero success!\n\n");
}
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan){
if(hcan->Instance==CAN1){
CAN_RxHeaderTypeDef RXHeader;
uint8_t RXmessage[8];
HAL_CAN_GetRxMessage(hcan,CAN_FILTER_FIFO0,&RXHeader,RXmessage);
if(RXHeader.StdId == MyEncoder.Encoder_id){
CAN_Encoder(&MyEncoder,RXmessage);
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
MyEncoder = Encoder_Init(0x01);
if(!Filter_Init()){
printf("CAN_Filter_Init Failed!\n");
}
else{
printf("CAN_Filter_Init success!\n");
}
//Encocer_Set_AutoCallBack(&MyEncoder,0xFFFF);
//Encoder_Disable_AutoCallBack(&MyEncoder);
//Encoder_Set_Zero(&MyEncoder);
//Encoder_Set_Id(&MyEncoder,0x02);
//Encoder_Set_Baud(&MyEncoder,Encoder_Baud_1M)
/* USER CODE END 2 */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
Encoder_GetValue(&MyEncoder);
HAL_Delay(1000);
}
/* USER CODE END 3 */
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/Hrilug/article/details/137757697
|
|