最好根据手册自己写
#include <avr/io.h>
#include <util/delay.h>
void InitUart0(void)
{
UCSR0A = 0x02// 设置为倍速模式
UBRR0H = 0
UBRR0L = 1
UCSR0B = (1<<RXEN)|(1<<TXEN)// 接收器与发送器使能
UCSR0C = (3<<UCSZ0)
DDRE &= ~_BV(PE0)// 初始化RX 端口默认方向为输入
PORTE &= ~_BV(PE0)// 初始化RX 端口默认状态为高阻
DDRE |= _BV(PE1)// 初始化TX 端口默认方向为输出
PORTE |= _BV(PE1)// 初始化TX 端口默认状态为高电平
DDRA |= _BV(PA0)// 初始化使能端口状态方向为输出
PORTA &= ~_BV(PA0)// 初始化使能端口状态为RX 状态
DDRA |= _BV(PA1)// 初始化使能端口状态方向为输出
PORTA |= _BV(PA1)// 初始化使能端口状态方为TX 状态
}
void SendUart0Byte(unsigned char data)
{
while ( !( UCSR0A &(1<<UDRE)) )// 等待发送缓冲器为空
UDR0 = data/* 将数据放入缓冲器,发送数据*/
}
void SetServoLimit(unsigned char id, unsigned short int cw_limit, unsigned short int ccw_limit)
{
unsigned short int temp_ccw = 0// 临时速度,用于进行方向判别
unsigned short int temp_cw = 0
unsigned char temp_ccw_h = 0// 待发送数据h 位
unsigned char temp_ccw_l = 0// 待发送数据l 位
unsigned char temp_cw_h = 0
unsigned char temp_cw_l = 0
unsigned char temp_sum = 0// 校验和寄存变量
if (ccw_limit >1023)
{
temp_ccw = 1023// 限制速度值在可用范围内
}
else
{
temp_ccw = ccw_limit
}
if (cw_limit >1023)
{
temp_cw = 1023
}
else
{
temp_cw = cw_limit
}
temp_ccw_h = (unsigned char)(temp_ccw >>8)
temp_ccw_l = (unsigned char)temp_ccw// 将16bit 数据拆为2个8bit 数据
temp_cw_h = (unsigned char)(temp_cw >>8)
temp_cw_l = (unsigned char)temp_cw// 将16bit 数据拆为2个8bit 数据
PORTA &= ~_BV(PA1)
PORTA |= _BV(PA0)// 使总线处于主机发送状态
UCSR0A |= (1<<TXC0)// 清除UART0写完成标志
SendUart0Byte(0xFF)// 发送启动符号0xFF
SendUart0Byte(0xFF)// 发送启动符号0xFF
SendUart0Byte(id)// 发送id
SendUart0Byte(7)// 发送数据长度为参数长度+2,参数长度为3
SendUart0Byte(0x03)// 命令数据为“WRITE DATA”
SendUart0Byte(0x06)// 舵机控制寄存器首地址
SendUart0Byte(temp_cw_l)// 发送顺时针位置限制低位
SendUart0Byte(temp_cw_h)// 发送顺时针位置限制高位
SendUart0Byte(temp_ccw_l)// 发送逆时针位置限制低位
SendUart0Byte(temp_ccw_h)// 发送逆时针位置限制高位
temp_sum = id + 7 + 0x03 + 0x06 + temp_cw_l + temp_cw_h + temp_ccw_l + temp_ccw_h
temp_sum = ~temp_sum// 计算校验和
SendUart0Byte(temp_sum)// 发送校验和
while ( !( UCSR0A &(1<<TXC0)) ) // 等待发送完成
{ // (Waiting for finishing sending)
}
PORTA |= _BV(PA1)
PORTA &= ~_BV(PA0)// 使总线处于主机接收状态
_delay_ms(2)//送完成后,总线会被从机占用,反馈应答数据,所以进行延时
}
void SetServoPosition(unsigned char id, unsigned short int position, unsigned short int
velocity)
{
unsigned short int temp_velocity = 0// 临时速度,用于进行方向判别
unsigned short int temp_position = 0
unsigned char temp_velocity_h = 0// 待发送数据h 位
unsigned char temp_velocity_l = 0// 待发送数据l 位
unsigned char temp_position_h = 0
unsigned char temp_position_l = 0
unsigned char temp_sum = 0// 校验和寄存变量
if (velocity >1023)
{
temp_velocity = 1023// 限制速度值在可用范围内
}
else
{
temp_velocity = velocity
}
if (position >1023)
{
temp_position = 1023
}
else
{
temp_position = position
}
temp_velocity_h = (unsigned char)(temp_velocity >>8)
// 将16bit 数据拆为2个8bit 数据
temp_velocity_l = (unsigned char)temp_velocity
temp_position_h = (unsigned char)(temp_position >>8)
// 将16bit 数据拆为2个8bit 数据
temp_position_l = (unsigned char)temp_position
PORTA &= ~_BV(PA1)
PORTA |= _BV(PA0)// 使总线处于主机发送状态
UCSR0A |= (1<<TXC0)// 清除UART0写完成标志
SendUart0Byte(0xFF)// 发送启动符号0xFF
SendUart0Byte(0xFF)
SendUart0Byte(id)// 发送id
SendUart0Byte(7)// 发送数据长度为参数长度+2,参数长度为3
SendUart0Byte(0x03)// 命令数据为“WRITE DATA”
SendUart0Byte(0x1E)// 舵机控制寄存器首地址
SendUart0Byte(temp_position_l)// 发送速度数据低位
SendUart0Byte(temp_position_h)// 发送速度数据高位
SendUart0Byte(temp_velocity_l)//发送位置低字节
SendUart0Byte(temp_velocity_h)// 发送位置高字节
temp_sum = id + 7 + 0x03 + 0x1E + temp_position_l + temp_position_h + temp_velocity_l +
temp_velocity_h
temp_sum = ~temp_sum// 计算校验和
SendUart0Byte(temp_sum)// 发送校验和 (Send the checksum)
while ( !( UCSR0A &(1<<TXC0)) ) // 等待发送完成
{ // (Waiting for finishing sending)
}
PORTA |= _BV(PA1)
PORTA &= ~_BV(PA0)// 使总线处于主机接收状态
_delay_ms(2)// 发送完成后,总线会被从机占用,反馈应答数据,所以进行延时
}
void SetServoVelocity(unsigned char id, signed short int velocity)
{
unsigned char temp_sign = 0// 临时符号,用于进行方向判别
unsigned short int temp_velocity = 0// 临时速度,用于进行方向判别
unsigned char temp_value_h = 0// 待发送数据h 位
unsigned char temp_value_l = 0// 待发送数据l 位
unsigned char temp_sum = 0// 校验和寄存变量
if (velocity <0)
{
temp_velocity = -velocity// 如果为负数,则取绝对值
temp_sign = 1// 设置负数符号标志
}
else
{
temp_velocity = velocity
temp_sign = 0// 设置正数符号标志
}
if (temp_velocity >1023)
{
temp_velocity = 1023// 限制速度值在可用范围内
}
temp_velocity |= (temp_sign <<10)
temp_value_h = (unsigned char)(temp_velocity >>8)
// 将16bit 数据拆为2个8bit 数据
temp_value_l = (unsigned char)temp_velocity
PORTA &= ~_BV(PA1)
PORTA |= _BV(PA0)// 使总线处于主机发送状态
UCSR0A |= (1<<TXC0)// 清除UART0写完成标志
SendUart0Byte(0xFF)// 发送启动符号0xFF
SendUart0Byte(0xFF)// 发送启动符号0xFF
SendUart0Byte(id)// 发送id
SendUart0Byte(5)// 发送数据长度为参数长度+2,参数长度为3
SendUart0Byte(0x03)// 命令数据为“WRITE DATA”
SendUart0Byte(0x20)// 舵机控制寄存器首地址
SendUart0Byte(temp_value_l)// 发送速度数据低位
SendUart0Byte(temp_value_h)// 发送速度数据高位
temp_sum = id + 5 + 0x03 + 0x20 + temp_value_l + temp_value_h
temp_sum = ~temp_sum// 计算校验和
SendUart0Byte(temp_sum)// 发送校验和
while ( !( UCSR0A &(1<<TXC0)) ) // 等待发送完成
{
}
PORTA |= _BV(PA1)
PORTA &= ~_BV(PA0)// 使总线处于主机接收状态
_delay_ms(2)// 发送完成后,总线会被从机占用,反馈应答数据,所以进行延时
}
int main(void)
{
InitUart0()
SetServoLimit(2,0,1023)
while(1)
{
_delay_ms(1000)//延时1s
SetServoPosition(2, 1000, 500)//控制舵机以500的速度运动到1000的位置
_delay_ms(1000)//延时1s
SetServoPosition(2, 200, 100)//控制舵机以100的速度运动到200的位置
}
}
清单STM32F103C8T6 1个,Jlink下载线 1根
MG90S舵机 1个
12V电源 1个,12V转5V降压模块(给舵机供电) 1个
杜邦线数根
MG90S舵机
如图所示,舵机有三根线,红色接5V正极,黑色接地;黄色为信号线,接收PWM信号
实物图
输出PWM控制舵机
STM32控制舵机,总的来说是向舵机信号线发送周期为20ms的PWM(Pulse Width Modulation 脉冲宽度调制)信号来控制舵机转角,PWM脉冲的占空比决定了舵机的转角位置,对应关系如下图;
STM32靠定时器TIM实现PWM信号的输出,STM32有多个定时器,分为通用定时器和高级定时器,每个通用定时器都可以输出4路PWM,本文采用TM2_CH4,及定时器2的第4路PWM;对应STM32F103C8T6上的PB11引脚;
创建工程输出PWM
本文使用STM32CubeMX创建工程,更加简单高效,即便对单片机不熟悉也可 *** 作成功;
一、打开STM32CubeMX,新建一个工程,搜索并选择STM32F103C8,然后start project;
二、进行基本设置,配置RCC、SYS、HCLK
RCC ->设置高速外部时钟HSE 选择外部时钟源
SYS ->选择Debug方式 serial wire
HCLK ->设置为72MHZ
三、设置定时器
选择TIM2
选择内部时钟源
设置Channel4为PWM模式
右侧对应管脚自动设置为复用模式
设置定时器参数
控制舵机我们需要设置PWM输出一个周期时间为20ms的信号,定时器时钟为72Mhz
预分频Prescaler设置为720-1,则单周期为72M/(Prescaler + 1) = 100Khz(0.01ms)
以20ms为周期,则Counter Period为20ms/0.01ms - 1 = 1999
此处可设定默认脉冲宽度为0.5ms,及0.5/20*2000 = 50
四、接下来设置输出格式
添加工程名PWM
修改IDE为MDK-ARM V5
在Code Genetator中设置包含库与工程格式
至此STM32CubeMX已设置完成,接下来GENERATE CODE,并打开文件;
在main函数中启动定时器,并输出一定的脉冲的PWM信号即可驱动舵机;
五、修改代码
在main.c文件相应位置处添加如下代码
启动PWM输出
输出PWM信号
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_4)//启动定时器
int compareValue = 150//将compareValue设置为50、150、250分别对应0°、90°、180°
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_4, compareValue)//输出PWM信号
HAL_Delay(10)
}
/* USER CODE END 3 */
}
登录后复制
添加完以上代码之后,编译,然后通过JLink将程序LOAD到STM32F103C8T6,将舵机信号线连接PB11(TIM2_CHANNEL4)口,并接上电源,舵机即可转动到指定角度;由以上定时器设定可知,compareValue值50、150、250 对应 0.5ms、1.5ms、2.5ms 对应 0°、90°、180°;若想让舵机连续转动,在50~250内循环改变compareValue值即可;
六、接线
开始时用单片机的5V输出给舵机供电,舵机哒哒哒响,但是不转,怀疑是供电不给力,单独供电后驱动成功;
JIink连接电脑和单片机,下载程序到单片机并给单片机供电
舵机黄色信号线接STM32F103C8T6引脚PB11,接收PWM信号
舵机红色电源线接稳压板5V输出正极,舵机黑色地线需与单片机地相连
调试
实 *** 中可能会出现各种各样的问题,以下有几种调试方法
一、舵机测试
调试模块一端接电,一段接舵机,手动模式下,舵机会跟着旋钮同步旋转,可以用来测试舵机是否健康;
二、串口调试助手
可下载串口调试助手,用ttl转USB模块连接电脑,在特定位置进行打印输出,检查程序是否按预设运行
stm32
分析:
首先已经存在于STM32中的动作组文件。数据已到达flash或内存,那么无需了解格式了。
外部信号触发,触发作为发送开始条件,可使用标志触发,或中断触发。
串口通信给舵机控制板,那么你要分为是否有协议,当前有已使用协议把数据合理的分包填进去发送即可,使用成品通信协议同上。自己编写简单通信协议就需要先编写两端协议然后在将数据处理发送。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)