STM32 HAL库 总线舵机驱动库的编写
文章目录
- STM32 HAL库 总线舵机驱动库的编写
- 1 理论基础
- 1.1 硬件
- 1.2 电路图
- 1.3 原理
- 1.4 通信协议
- 2 程序编写
- 2.1 cube mx设置
- (1)USART1设置
- (2)USART3设置
- 2.2 程序编写
- (1)bsp_key.c
- (2)bsp_key.h
- (3)bsp_uart.c
- (4)bsp_uart.h
- (5)bsp_bool.h
- (6)bsp_serial_servo.c
- (7)bsp_serial_servo.h
- (8)bsp.c
- (9)bsp.h
1 理论基础
1.1 硬件
版本一:幻尔串行总线舵机、亚博智能 ROS机器人控制器STM32主控
版本二:串行总线舵机、任意型号的STM32开发板、SN74AHC1G04DBV(单路反相器)、74HC126D(缓冲器)、MF-MSMF300自恢复保险丝等
1.2 电路图
1.3 原理
本次所用的舵机为串行总线舵机,采用异步串行总线通讯方式,理论多至 253 个机器人舵机可以通过总线组 成链型,一条总线上允许挂载多个舵机,且每个舵机均有一个唯一ID(0~253),通过 UART 异步串行接口统一控制。
每个舵机可以设定不同的节点地址,多个舵机可以统一运动也可以单个独立控制。通过异步串行接口与用户的上位机(控制器或PC机)通讯,可对其进行参数设置、功能控制。通过异步串行接口发送指令,可以设置为电机控制模式或位置控制模式。
在电机控制模式下,可以作为直流减速电机使用,速度可调;在位置控制模式下,拥有 0-240° 的转动范围,外加±30°的偏差可调范围,在此范围内具备精确位置控制性能,速度可调。只要符合协议的半双工UART异步串行接口都可以和舵机进行通讯,对舵机进行各种控制。
原理图如上图所示,舵机用程序代码对UART(本次示例中使用USART3)异步串口进行时序控制,实现半双工异步串行总线通讯,即半双工的主从问答式通信,通信波特率为115200bps。
原理可以理解为:舵机控制器作为主机发送读写指令给舵机,舵机作为从机根据具体情况执行相应动作或应答。
1.4 通信协议
(1)指令包格式
帧头 | ID号 | 数据长度 | 指令 | 参数 | 校验和 |
---|---|---|---|---|---|
0x55 0x55 | ID | Length | Cmd | Prm 1…Prm N | Checksum |
- 帧头:连续收到两个 0x55 ,表示有数据包到达。
- ID: 每个舵机都有一个 ID 号。ID 号范围 0~253,转换为十六进制 0x00~0xFD。 广播 ID: ID 号254(0xFE) 为广播 ID,若控制器发出的ID号为 254(0xFE),所有的舵机均接收指令,但都不返回应答信息,(读取舵机 ID 号除外,具体说明参见下面指令介绍)以防总线冲突。
- 数据长度:等于待发送的数据(包含本身一个字节)长度,即数据长度Length加3等于这一包指令的长度,从帧头到校验和。
- 指令:控制舵机的各种指令,如位置、速度控制等。
- 参数:除指令外需要补充的控制信息。
- 校验和:校验和 Checksum,计算方法如下:Checksum = ~ (ID + Length + Cmd+ Prm1 + … Prm N)若括号内的计算和超出 255, 则取最低的一个字节,“~”表示取反。
(2)指令类型
指令有两种,写指令和读指令。
- 写指令:后面一般带有参数,将相应功能的参数写进舵机,来完成某种动作。
- 读指令:后面一般不带参数,舵机接收到读指令后会立即返回相应数据,返回的指令值和发送给舵机的“读指令”值相同,并且带有参数。所以上位机发送读指令后要立马准备将自己变为读取状态。
2 程序编写
2.1 cube mx设置
(1)USART1设置
-
串口1模式配置为同步通讯,波特率改为115200bps,数据宽度8位,检验None,停止位1位
-
增加DMA发送通道
-
打开串口1中断设置
(2)USART3设置
-
串口3模式配置为同步通讯,波特率改为115200bps,数据宽度8位,检验None,停止位1位
-
由于默认串口3引脚是PB10和PB11,而扩展版原理图钟串口3连接的是PC10和PC11,所以串口需要重映射。
先点击PC11引脚,然后选择USART3_RX,这样操作后,串口3的引脚就会被重映射为PC10和PC11了。
-
打开串口3中断设置
2.2 程序编写
(1)bsp_key.c
#include "bsp_key.h"
#include "bsp.h"
// 判断按键是否被按下,按下返回KEY_PRESS,松开返回KEY_RELEASE
// Determine if the key is pressed, press to return KEY_PRESS, release to return KEY_RELEASE
static uint8_t Key1_is_Press(void)
{
if (!HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin))
{
return KEY_PRESS; // 如果按键被按下,则返回KEY_PRESS
}
return KEY_RELEASE; // 如果按键是松开状态,则返回KEY_RELEASE
}
// 读取按键K1的状态,按下返回KEY_PRESS,松开返回KEY_RELEASE.
// mode:设置模式,0:按下一直返回KEY_PRESS;1:按下只返回一次KEY_PRESS
// Read the state of key K1, press down to return KEY_PRESS, release to return key_release.
// mode: setting mode, 0: press down to return KEY_PRESS; 1: KEY_PRESS is returned only once
uint8_t Key1_State(uint8_t mode)
{
static uint16_t key1_state = 0;
if (Key1_is_Press() == KEY_PRESS)
{
if (key1_state < (mode + 1) * 2)
{
key1_state++;
}
}
else
{
key1_state = 0;
}
if (key1_state == 2)
{
return KEY_PRESS;
}
return KEY_RELEASE;
}
/*********************************************END OF FILE**********************/
(2)bsp_key.h
#ifndef __BSP_KEY_H__
#define __BSP_KEY_H__
#include "gpio.h"
#define KEY_PRESS 1
#define KEY_RELEASE 0
#define KEY_MODE_ONE_TIME 1
#define KEY_MODE_ALWAYS 0
uint8_t Key1_State(uint8_t mode);
#endif /* __BSP_KEY_H__ */
(3)bsp_uart.c
/*
* bsp_uart.c
*
* Created on: Mar 4, 2022
* Author: Administrator
*/
#include "bsp_uart.h"
#include "bsp.h"
#define ENABLE_UART_DMA 1
uint8_t RxTemp = 0;
uint8_t RxTemp_3 = 0;
uint8_t UART_RX_BUF[16];
bool isUartRxCompleted= false;
// Initialize USART1 初始化串口1
void USART1_Init(void)
{
HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxTemp, 1);
}
// The serial port sends one byte 串口发送一个字节
void USART1_Send_U8(uint8_t ch)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
}
// The serial port sends a string of data 串口发送一串数据
void USART1_Send_ArrayU8(uint8_t *BufferPtr, uint16_t Length)
{
#if ENABLE_UART_DMA
HAL_UART_Transmit_DMA(&huart1, BufferPtr, Length);
#else
while (Length--)
{
USART1_Send_U8(*BufferPtr);
BufferPtr++;
}
#endif
}
// Initialize USART3 初始化串口3
void USART3_Init(void)
{
HAL_UART_Receive_IT(&huart3, (uint8_t *)&RxTemp, 1);
}
// The serial port sends one byte 串口发送一个字节
void USART3_Send_U8(uint8_t ch)
{
HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 0xFFFF);
}
// The serial port sends a string of data 串口发送一串数据
void USART3_Send_ArrayU8(uint8_t *BufferPtr, uint16_t Length)
{
while (Length--)
{
USART3_Send_U8(*BufferPtr);
BufferPtr++;
}
}
/**
* @brief 通过UART发送一个数据缓冲区中的数据。使用一个循环逐个发送数据字节。
* @param
*
* uint8_t *buf: 是指向数据缓冲区的指针,其中包含要发送的数据。
* uint8_t len: 是要发送的数据长度
* while (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_TXE) == RESET): 这是一个等待循环,
* 这是一个等待循环,它会检查UART外设的发送缓冲区是否为空。
* 循环将一直执行,直到发送缓冲区为空,
* 表示前一个字节已经发送完毕,可以发送下一个字节。
* HAL_UART_Transmit(&huart3, buf, 1, HAL_MAX_DELAY):
* 这是使用HAL库的UART发送函数。
* 它将一个字节的数据从buf发送到UART外设。
* 第三个参数1表示发送一个字节,
* HAL_MAX_DELAY 表示在发送完成之前不会超时。
* buf++:这将指针buf向后移动一个字节,以指向下一个要发送的字节。
*
* @retval None
*/
void uartWriteBuf(uint8_t *buf, uint8_t len)
{
while (len--)
{
while (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_TXE) == RESET);
HAL_UART_Transmit(&huart3, buf, 1, HAL_MAX_DELAY);
buf++;
}
}
// The serial port receiving is interrupted. Procedure 串口接收完成中断
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart==&huart1)
{
// 测试发送数据,实际应用中不应该在中断中发送数据
// Test sending data. In practice, data should not be sent during interrupts
USART1_Send_U8(RxTemp);
// Continue receiving data 继续接收数据
HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxTemp, 1);
}
if (huart==&huart3)
{
static bool isGotFrameHeader = false; //静态变量是不是帧头
static uint8_t frameHeaderCount = 0; //帧头计数
static uint8_t dataLength = 2; //数据长度2
static uint8_t dataCount = 0; //数据计数
uint8_t Res; // 假设 UART_RX_BUF 是接收缓冲区
//读取USART3外设的接收数据寄存器(DR)中的数据。
Res = (uint8_t)(huart3.Instance->DR & (uint8_t)0xFF);
if (!isGotFrameHeader)
{
if (Res == 0x55)
{
frameHeaderCount++;
if (frameHeaderCount == 2)
{
frameHeaderCount = 0;
isGotFrameHeader = true;
dataCount = 1;
}
}
else
{
isGotFrameHeader = false;
dataCount = 0;
frameHeaderCount = 0;
}
}
if (isGotFrameHeader)
{
UART_RX_BUF[dataCount] = Res; //将接收到的数据存储到接收缓冲区UART_RX_BUF的相应位置
if (dataCount == 3)
{
dataLength = UART_RX_BUF[dataCount];
/*如果数据长度小于 3 或大于 7,表示数据长度异常,
将数据长度恢复为默认值 3,并将标志变量 isGotFrameHeader 设置为 false,
表示需要重新接收帧头。
*/
if (dataLength < 3 || dataLength > 7)
{
dataLength = 3;
isGotFrameHeader = false;
}
}
dataCount++;
/*
* 如果数据计数器 dataCount 的值等于
* 数据长度加上帧头和数据长度字段的长度(即数据计数达到了预期的总长度),
* 则表示接收完成。
*/
if (dataCount == dataLength + 3)
{
/*
* 如果 isUartRxCompleted 的值为false,表示之前的接收未完成,
* 将其设置为true,表示接收完成。
*/
if (isUartRxCompleted == false)
{
isUartRxCompleted = true;
//使用 memcpy 函数将接收到的数据复制到目标缓冲区LobotRxBuf,复制的长度为数据计数加上2(帧头和数据长度字段的长度)
memcpy(LobotRxBuf, UART_RX_BUF, dataCount + 2);
}
//将标志变量 isGotFrameHeader 设置为false,表示需要重新接收帧头。
isGotFrameHeader = false;
}
}
/*
* 启动下一次接收,使用 HAL_UART_Receive_IT 函数以中断方式接收一个字节的数据,
* 将其存储到Res变量中。这样可以实现连续接收数据的功能。
*/
HAL_UART_Receive_IT(&huart3, &Res, 1);
}
}
bool isRxCompleted(void)
{
if(isUartRxCompleted == true){
isUartRxCompleted = false;
return true;
}else{
return false;
}
}
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
PUTCHAR_PROTOTYPE
{
/* Place your implementation of fputc here */
/* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
(4)bsp_uart.h
/*
* bsp_uart.h
*
* Created on: Mar 4, 2022
* Author: Administrator
*/
#ifndef BSP_UART_H_
#define BSP_UART_H_
#include "stdint.h"
#include "string.h"
#include "bsp_bool.h"
void USART1_Init(void);
void USART1_Send_U8(uint8_t ch);
void USART1_Send_ArrayU8(uint8_t *BufferPtr, uint16_t Length);
void USART3_Init(void);
void USART3_Send_U8(uint8_t ch);
void USART3_Send_ArrayU8(uint8_t *BufferPtr, uint16_t Length);
void uartWriteBuf(uint8_t *buf,uint8_t len);
bool isRxCompleted(void);
#endif /* BSP_UART_H_ */
(5)bsp_bool.h
/*
* bsp_bool.h
*
* Created on: May 11, 2023
* Author: 77454
*/
#ifndef BSP_BOOL_H_
#define BSP_BOOL_H_
typedef enum{
false =0,true=!false
}bool;
#endif /* BSP_BOOL_H_ */
(6)bsp_serial_servo.c
/*
* bsp_serial_servo.c
*
* Created on: May 9, 2023
* Author: 77454
*/
#include "bsp_serial_servo.h"
#include "bsp.h"
#define LobotSerialWrite uartWriteBuf
#define GET_LOW_BYTE(A) ((uint8_t)(A))
//宏函数 获得A的低八位
#define GET_HIGH_BYTE(A) ((uint8_t)((A) >> 8))
//宏函数 获得A的高八位
#define BYTE_TO_HW(A, B) ((((uint16_t)(A)) << 8) | (uint8_t)(B))
//宏函数 将高低八位合成为十六位
uint8_t LobotRxBuf[16];
/**
* @brief 计算校验和函数。checksum=~(ID+Length+Cmd+Prm1+...+PrmN)
* @param None
* @retval None
*/
uint8_t LobotCheckSum(uint8_t buf[])
{
uint8_t i;
uint16_t temp = 0;
for (i = 2; i < buf[3] + 2; i++) {
temp += buf[i];
}
temp = ~temp;
i = (uint8_t)temp;
return i;
}
/**
* @brief 设置舵机ID,并且掉电保存
* @param
* buf[0]、buf[1] :将数组的第一个和第二个元素都设置为0x55,表示有数据包到达;
* buf[2] :将数组的第三个元素设置为oldID,即当前舵机的ID;
* buf[3] :将数组的第四个元素设置为4。表示数据帧的长度,指示了要发送的指令的字节数。
* buf[4] :调用设置舵机ID的指令,指令名为LOBOT_SERVO_ID_WRITE。指令值为13
* buf[5] :将数组的第六个元素设置为newID,即要设置的新舵机ID
* buf[6] :将数组的第七个元素设置为通过调用LobotCheckSum函数计算得到的校验和。
* @retval None
*/
void LobotSerialServoSetID(uint8_t oldID, uint8_t newID)
{
uint8_t buf[7];//定义了一个长度为7的uint8_t类型数组buf,用于存储发送给舵机的指令数据。
buf[0] = buf[1] = LOBOT_SERVO_FRAME_HEADER;
buf[2] = oldID;//
buf[3] = 4;
buf[4] = LOBOT_SERVO_ID_WRITE;
buf[5] = newID;
buf[6] = LobotCheckSum(buf);
//USART3_Send_ArrayU8(buf,sizeof(buf));
LobotSerialWrite(buf, 7);
}
/**
* @brief 舵机转动指令
* @param
* 检查舵机位置 position 的取值范围,如果小于0则将其设为0,如果大于1000则将其设为1000,确保位置值在合法范围内。
* buf[0]、buf[1] :将数组的第一个和第二个元素都设置为0x55,表示有数据包到达;
* buf[2] :控制舵机的ID;
* buf[3] :将数组的第四个元素设置为7。表示数据帧的长度,指示了要发送的指令的字节数。
* buf[4] :调用设置舵机ID的指令,指令名为LOBOT_SERVO_MOVE_TIME_WRITE。指令值为1
* buf[5] :角度低八位
* buf[6] :角度高八位0-1000,对应舵机角度为0-240°
* buf[7] :时间低八位
* buf[8] :时间高八位0-30000ms
* buf[9] :将数组的第十个元素设置为通过调用LobotCheckSum函数计算得到的校验和。
* @retval None
*/
void LobotSerialServoMove(uint8_t id, int16_t position, uint16_t time)
{
uint8_t buf[10];
if(position < 0)
position = 0;
if(position > 1000)
position = 1000;
buf[0] = buf[1] = LOBOT_SERVO_FRAME_HEADER;
buf[2] = id;
buf[3] = 7;
buf[4] = LOBOT_SERVO_MOVE_TIME_WRITE;
buf[5] = GET_LOW_BYTE(position);
buf[6] = GET_HIGH_BYTE(position);
buf[7] = GET_LOW_BYTE(time);
buf[8] = GET_HIGH_BYTE(time);
buf[9] = LobotCheckSum(buf);
//USART3_Send_ArrayU8(buf,sizeof(buf));
LobotSerialWrite(buf, 10);
}
/**
* @brief 装载电机,有力矩输出,参数为1
* @param
* buf[0]、buf[1] :将数组的第一个和第二个元素都设置为0x55,表示有数据包到达;
* buf[2] :将数组的第三个元素设置为ID,即当前舵机的ID;
* buf[3] :将数组的第四个元素设置为4。表示数据帧的长度,指示了要发送的指令的字节数。
* buf[4] :调用加载或卸载写的指令,指令名为LOBOT_SERVO_LOAD_OR_UNLOAD_WRITE。指令值为31
* buf[5] :参数为1,则装载电机,有力矩输出
* buf[6] :将数组的第七个元素设置为通过调用LobotCheckSum函数计算得到的校验和。
* @retval None
*/
void LobotSerialServoLoad(uint8_t id)
{
uint8_t buf[7];
buf[0] = buf[1] = LOBOT_SERVO_FRAME_HEADER;
buf[2] = id;
buf[3] = 4;
buf[4] = LOBOT_SERVO_LOAD_OR_UNLOAD_WRITE;
buf[5] = 1;
buf[6] = LobotCheckSum(buf);
// USART3_Send_ArrayU8(buf,sizeof(buf));
LobotSerialWrite(buf,7);
}
/**
* @brief 卸载电机,无力矩输出,参数为1
* @param
* buf[0]、buf[1] :将数组的第一个和第二个元素都设置为0x55,表示有数据包到达;
* buf[2] :将数组的第三个元素设置为ID,即当前舵机的ID;
* buf[3] :将数组的第四个元素设置为4。表示数据帧的长度,指示了要发送的指令的字节数。
* buf[4] :调用加载或卸载写的指令,指令名为LOBOT_SERVO_LOAD_OR_UNLOAD_WRITE。指令值为31
* buf[5] :参数为0,则卸载电机,无力矩输出
* buf[6] :将数组的第七个元素设置为通过调用LobotCheckSum函数计算得到的校验和。
* @retval None
*/
void LobotSerialServoUnload(uint8_t id)
{
uint8_t buf[7];
buf[0] = buf[1] = LOBOT_SERVO_FRAME_HEADER;
buf[2] = id;
buf[3] = 4;
buf[4] = LOBOT_SERVO_LOAD_OR_UNLOAD_WRITE;
buf[5] = 0;
buf[6] = LobotCheckSum(buf);
LobotSerialWrite(buf, 7);
}
/**
* @brief 读取舵机的温度
* @param
* buf[0]、buf[1] :将数组的第一个和第二个元素都设置为0x55,表示有数据包到达;
* buf[2] :将数组的第三个元素设置为ID,即当前舵机的ID;
* buf[3] :将数组的第四个元素设置为3。表示数据帧的长度,指示了要发送的指令的字节数。
* buf[4] :调用加载或卸载写的指令,指令名为 LOBOT_SERVO_POS_READ。指令值为28
* buf[5] :将数组的第六个元素设置为通过调用LobotCheckSum函数计算得到的校验和。
* @retval 定义一个名为temp的变量,用于存储函数的返回值,即读取到的温度。
*/
int LobotSerialServoReadTemperature(uint8_t id)
{
int temp;
uint8_t buf[6];
buf[0] = buf[1] = LOBOT_SERVO_FRAME_HEADER;
buf[2] = id;
buf[3] = 3;
buf[4] = LOBOT_SERVO_TEMP_READ;
buf[5] = LobotCheckSum(buf);
LobotSerialWrite(buf, 6);
temp = LobotSerialMsgHandle();
return temp;
}
/**
* @brief 读取舵机的角度位置
* @param
* buf[0]、buf[1] :将数组的第一个和第二个元素都设置为0x55,表示有数据包到达;
* buf[2] :将数组的第三个元素设置为ID,即当前舵机的ID;
* buf[3] :将数组的第四个元素设置为3。表示数据帧的长度,指示了要发送的指令的字节数。
* buf[4] :调用加载或卸载写的指令,指令名为 LOBOT_SERVO_POS_READ。指令值为28
* buf[5] :将数组的第六个元素设置为通过调用LobotCheckSum函数计算得到的校验和。
* @retval 定义一个名为ret的变量,用于存储函数的返回值,即读取到的位置信息。
*/
int LobotSerialServoReadPosition(uint8_t id)
{
int ret;
uint8_t buf[6];
buf[0] = buf[1] = LOBOT_SERVO_FRAME_HEADER;
buf[2] = id;
buf[3] = 3;
buf[4] = LOBOT_SERVO_POS_READ;
buf[5] = LobotCheckSum(buf);
LobotSerialWrite(buf, 6);
//调用 LobotSerialMsgHandle函数,处理接收到的舵机返回数据,并将处理结果赋值给ret变量
ret = LobotSerialMsgHandle();
return ret;
}
/**
* @brief 处理串口消息的函数
* @param
*
* @retval 定义一个名为ret的变量,用于存储函数的返回值,即读取到的位置信息。
*/
int LobotSerialMsgHandle(void)
{
int count = 50000; //该变量用于循环计数,限制处理时间以避免无限循环。
uint8_t cmd; //用于存储接收到的指令类型
int ret; //用于存储函数的返回值。
int temp;
/*
使用循环判断串口接收是否完成,如果接收未完成,则继续循环等待。
如果count变量小于0,表示超过了预定的等待时间,返回一个特定的错误码-2048。
*/
while(!isRxCompleted())
{
count--;
if(count < 0)
return -2048;
}
/*
校验接收到的数据帧的校验和是否正确
通过调用 LobotCheckSum函数计算接收缓冲区LobotRxBuf中数据的校验和,
然后将其与数据帧中的校验和进行比较。如果校验和不匹配,返回一个特定的错误码-2049。
*/
if(LobotCheckSum(LobotRxBuf) != LobotRxBuf[LobotRxBuf[3]+2])
{
return -2049;
}
cmd = LobotRxBuf[4];//将接收到的数据帧中的指令类型存储在cmd变量中。
/*
将接收到的舵机位置数据转换为一个int类型的值,并存储在 ret 变量中。
这里使用了一个BYTE_TO_HW宏来将两个字节合并成一个 16 位的值。
*/
switch(cmd)
{
case LOBOT_SERVO_POS_READ:
ret = (int)BYTE_TO_HW(LobotRxBuf[6], LobotRxBuf[5]);
return ret; //将变量 ret 作为函数的返回值返回,表示读取到的舵机位置。
case LOBOT_SERVO_TEMP_READ:
temp = LobotRxBuf[5];
return temp;
default:
break;
}
return 0; //如果指令类型不匹配或者没有特定的处理操作,返回一个默认的值 0。
}
(7)bsp_serial_servo.h
/*
* bsp_serial_servo.h
*
* Created on: May 9, 2023
* Author: 77454
*/
#ifndef BSP_SERIAL_SERVO_H_
#define BSP_SERIAL_SERVO_H_
#include "stdint.h"
#define LOBOT_SERVO_FRAME_HEADER 0x55 //帧头
#define LOBOT_SERVO_MOVE_TIME_WRITE 1 //移动时间写入
#define LOBOT_SERVO_MOVE_TIME_READ 2 //移动时间读取
#define LOBOT_SERVO_MOVE_TIME_WAIT_WRITE 7 //移动时间等待写
#define LOBOT_SERVO_MOVE_TIME_WAIT_READ 8 //移动时间等待读
#define LOBOT_SERVO_MOVE_START 11 //移动开始
#define LOBOT_SERVO_MOVE_STOP 12 //移动停止
#define LOBOT_SERVO_ID_WRITE 13 //舵机ID写
#define LOBOT_SERVO_ID_READ 14 //舵机ID读
#define LOBOT_SERVO_ANGLE_OFFSET_ADJUST 17 //角度偏移调整
#define LOBOT_SERVO_ANGLE_OFFSET_WRITE 18 //角度偏移写
#define LOBOT_SERVO_ANGLE_OFFSET_READ 19 //角度偏移读
#define LOBOT_SERVO_ANGLE_LIMIT_WRITE 20 //角度限制写
#define LOBOT_SERVO_ANGLE_LIMIT_READ 21 //角度限制读
#define LOBOT_SERVO_VIN_LIMIT_WRITE 22 //VIN限制写
#define LOBOT_SERVO_VIN_LIMIT_READ 23 //VIN限制读
#define LOBOT_SERVO_TEMP_MAX_LIMIT_WRITE 24 //温度最大限度写
#define LOBOT_SERVO_TEMP_MAX_LIMIT_READ 25 //温度最大限度读
#define LOBOT_SERVO_TEMP_READ 26 //温度读
#define LOBOT_SERVO_VIN_READ 27 //VIN电压读
#define LOBOT_SERVO_POS_READ 28 //POS位置读
#define LOBOT_SERVO_OR_MOTOR_MODE_WRITE 29 //模式写
#define LOBOT_SERVO_OR_MOTOR_MODE_READ 30 //模式读
#define LOBOT_SERVO_LOAD_OR_UNLOAD_WRITE 31 //加载或卸载写
#define LOBOT_SERVO_LOAD_OR_UNLOAD_READ 32 //加载或卸载读
#define LOBOT_SERVO_LED_CTRL_WRITE 33 //LED控制写
#define LOBOT_SERVO_LED_CTRL_READ 34 //LED控制读
#define LOBOT_SERVO_LED_ERROR_WRITE 35 //LED错误写
#define LOBOT_SERVO_LED_ERROR_READ 36 //LED错误读
#define LOBOT_DEBUG 1
extern uint8_t LobotRxBuf[16];
uint8_t LobotCheckSum(uint8_t buf[]);
void LobotSerialServoSetID(uint8_t oldID, uint8_t newID);
void LobotSerialServoMove(uint8_t id, int16_t position, uint16_t time);
void LobotSerialServoLoad(uint8_t id);
void LobotSerialServoUnload(uint8_t id);
int LobotSerialServoReadTemperature(uint8_t id);
int LobotSerialServoReadPosition(uint8_t id);
int LobotSerialMsgHandle(void);
#endif /* BSP_SERIAL_SERVO_H_ */
(8)bsp.c
#include "bsp.h"
// The peripheral device is initialized 外设设备初始化
void Bsp_Init(void)
{
USART1_Init();
USART3_Init();
}
// main.c中循环调用此函数,避免多次修改main.c文件。
// This function is called in a loop in main.c to avoid multiple modifications to the main.c file
void Bsp_Loop(void)
{
//Detect button down events 检测按键按下事件
if (Key1_State(KEY_MODE_ONE_TIME))
{
Beep_On_Time(50);
static int press = 0;
int temp;
press++;
printf("press:%d\n", press);
// UartServo_Get_Angle(servo_id);
HAL_Delay(12);
if (press%2)
{
LobotSerialServoMove(1, 500, 500);
LobotSerialServoMove(2, 500, 500);
temp=LobotSerialServoReadTemperature(1);
printf("temp :%d\n", temp);
}
else
{
LobotSerialServoMove(1, 150, 500);
LobotSerialServoMove(2, 150, 500);
temp=LobotSerialServoReadTemperature(1);
printf("temp :%d\n", temp);
}
}
HAL_Delay(10);
}
(9)bsp.h
#ifndef __BSP_H__
#define __BSP_H__
/* Import HAL related library 导入HAL相关库 */
#include "main.h"
#include "gpio.h"
#include "usart.h"
#include "stm32f1xx_hal.h"
#include "stm32f103xe.h"
/* Import device driver library 导入设备驱动库 */
#include "bsp_key.h"
#include "bsp_uart.h"
#include "bsp_serial_servo.h"
#include "stdio.h"
/* functions */
void Bsp_Init(void);
void Bsp_Loop(void);
#endif /* __BSP_H__ */