打印
[应用相关]

stm32-绝对值编码器BRITER

[复制链接]
654|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2024-4-19 13:05 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
一、绝对值编码器
        绝对值编码器可以认为是与增量式编码器相对的一种编码器,与使用增量式编码器读取脉冲值不同,绝对值编码器可以直接读取编码器所处的角度值。根据量程,绝对值编码器也可以分为单圈编码器和多圈编码器。实际上根据每个厂家设计的编码器的通讯方式也可以再细分,这里我不再赘述。实验室使用绝对值编码器的目的是能够利用正交两个绝对值编码器,设计一个准确度高的里程计,借此机会学习一下绝对值编码器。
         本文所使用的绝对值编码器是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

使用特权

评论回复
沙发
yutingwei| | 2024-8-31 22:31 | 只看该作者
绝对值编码器是一种可以直接提供其绝对位置的传感器

使用特权

评论回复
板凳
yutingwei| | 2024-8-31 22:31 | 只看该作者
相比增量式编码器,绝对值编码器的主要优势在于其可以直接读取当前位置的角度值,即使在断电后恢复时,也不会丢失位置信息。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1935

主题

15659

帖子

12

粉丝