标题:
stm32f103c8t6单片机和PCF9685控制16路舵机通过串口调试程序
[打印本页]
作者:
2013261071
时间:
2024-12-12 18:13
标题:
stm32f103c8t6单片机和PCF9685控制16路舵机通过串口调试程序
#include "stm32f10x.h" // Device header
#include "MyI2C.h"
#include "PCA9685_Reg.h"
#include "math.h"
#include "Delay.h"
#include "Serial.h"
#include "OLED.h"
#include "string.h"
#include "stdlib.h"
uint8_t Channel_Flag = 0;
/**
* 函 数:PCA9685写寄存器
* 参 数:RegAddress 寄存器地址,范围:参考PCA9685手册的寄存器描述
* 参 数:Data 要写入寄存器的数据,范围:0x00~0xFF
* 返 回 值:无
*/
void PCA9685_WriteReg(uint8_t RegAddress, uint8_t Data)
{
MyI2C_Start(); //I2C起始
MyI2C_SendByte(ADDRESS); //发送从机地址,读写位为0,表示即将写入
MyI2C_ReceiveAck(); //接收应答
MyI2C_SendByte(RegAddress); //发送寄存器地址
MyI2C_ReceiveAck(); //接收应答
MyI2C_SendByte(Data); //发送要写入寄存器的数据
MyI2C_ReceiveAck(); //接收应答
MyI2C_Stop(); //I2C终止
}
//void PCA9685_Write(u8 addr,u8 data) // addr 表示要写入数据的寄存器地址,data 表示要写入的数据
//{
// IIC_Start(); // 发送 I2C 起始信号,开始 I2C 通信。
//
// IIC_Send_Byte(PCA_Addr); // 发送 PCA_Addr = 0x80 ,告诉设备我们要写入数据
// IIC_NAck(); // 发送不应答信号,表示主控器不需要从设备接收更多数据。
//
// IIC_Send_Byte(addr); // 发送要写入数据的寄存器地址。
// IIC_NAck(); // 发送不应答信号。
//
// IIC_Send_Byte(data); // 发送要写入的数据。
// IIC_NAck(); // 发送不应答信号。
//
// IIC_Stop(); // 发送 I2C 停止信号,结束本次通信。
//
//}
/**
* 函 数:PCA9685读寄存器
* 参 数:RegAddress 寄存器地址,范围:参考PCA9685手册的寄存器描述
* 返 回 值:读取寄存器的数据,范围:0x00~0xFF
*/
uint8_t PCA9685_ReadReg(uint8_t RegAddress)
{
uint8_t Data;
MyI2C_Start(); //I2C起始
MyI2C_SendByte(ADDRESS); //发送从机地址,读写位为0,表示即将写入
MyI2C_ReceiveAck(); //接收应答
MyI2C_SendByte(RegAddress); //发送寄存器地址
MyI2C_ReceiveAck(); //接收应答
MyI2C_Start(); //I2C重复起始
MyI2C_SendByte(ADDRESS | 0x01); //发送从机地址,读写位为1,表示即将读取
MyI2C_ReceiveAck(); //接收应答
Data = MyI2C_ReceiveByte(); //接收指定寄存器的数据
MyI2C_SendAck(1); //发送应答,给从机非应答,终止从机的数据输出
MyI2C_Stop(); //I2C终止
return Data;
}
//u8 PCA9685_Read(u8 addr) // addr 表示要读取数据的寄存器地址
//{
// u8 data; // 声明一个无符号 8 位整数变量 data,用于存储读取到的数据。
// IIC_Start(); // 发送 I2C 起始信号,开始 I2C 通信。
// IIC_Send_Byte(PCA_Addr); // 发送 PCA_Addr = 0x80 ,告诉设备我们要写入数据
// IIC_NAck(); // 发送不应答信号,表示主控器不需要从设备接收更多数据。
// IIC_Send_Byte(addr); // 发送要读取数据的寄存器地址。
// IIC_NAck(); // 发送不应答信号。
// IIC_Stop(); // 发送 I2C 停止信号,结束本次通信。
// delay_us(10); // 延时 10 微秒,等待芯片准备好数据。
// IIC_Start(); // 发送 I2C 起始信号,开始另一次 I2C 通信。
// IIC_Send_Byte(PCA_Addr|0x01); // 发送 PCA9685 的地址,并设置最低位为 1,
// IIC_NAck(); // 发送不应答信号。
// data = IIC_Read_Byte(0); // 通过 I2C 从 PCA9685 读取一个字节的数据,并存储到变量 data 中。
// IIC_Stop(); // 发送 I2C 停止信号,结束本次通信。
// return data; // 返回读取到的数据。
//}
/**
* 函 数:PCA9685设置输出频率
* 参 数:float 频率
* 返 回 值:返回预分频的值
*/
void PCA9685_SetFreq(float freq)
{
uint8_t prescale,oldmode,newmode; //定义了三个无符号 8 位整型变量 用于存储预分频器值、旧的模式寄存器值和新的模式寄存器值
float prescaleval; //定义了一个双精度浮点型变量 prescaleval,用于计算预分频器的值。
// freq *= 0.98; //将传入的频率值乘以 0.98,这是为了微调频率值以适应 PCA9685 的实际需求
prescaleval = 25000000/4096/freq - 1;
prescale = floor(prescaleval+0.5); //将计算得到的预分频器值四舍五入取整,并将其赋值给 prescale 变量。
oldmode = PCA9685_ReadReg(MODE1); //通过调用 PCA9685_Read 函数读取当前 PCA9685 寄存器中的模式值,并将其存储在 oldmode 变量中。
newmode = (oldmode&0x7F)|0x10; //根据旧的模式值计算出新的模式值,将最高位清零(不复位)并将第5位设为1,表示将 PCA9685 设置为睡眠模式。
PCA9685_WriteReg(MODE1,newmode); //将新的模式值写入 PCA9685 的模式寄存器。
PCA9685_WriteReg(PRE_SCALE,prescale); //将计算得到的预分频器值写入 PCA9685 的预分频器寄存器。
PCA9685_WriteReg(MODE1,oldmode); //恢复旧的模式值。
Delay_us(500); // 延时 0.5 毫秒,等待 PCA9685 完全启动。
PCA9685_WriteReg(MODE1,PCA9685_ReadReg(MODE1)|0x80); //将模式值的第8( Restart enabled.).更新频率后复位使用
}
//void PCA9685_setFreq(float freq)
//{
// u8 prescale,oldmode,newmode; //定义了三个无符号 8 位整型变量 用于存储预分频器值、旧的模式寄存器值和新的模式寄存器值
//
// double prescaleval; //定义了一个双精度浮点型变量 prescaleval,用于计算预分频器的值。
//
// freq *= 0.98; //将传入的频率值乘以 0.98,这是为了微调频率值以适应 PCA9685 的实际需求
// prescaleval = 25000000; //这是 PCA9685 内部振荡器的频率
// prescaleval /= 4096; //每个周期从0计数到4095,除以 4096,得到每个计数器周期的时间,
// prescaleval /= freq; //除以所需的频率值,得到预分频器的值。
// prescaleval -= 1; //减去 1,得到最终的预分频器值
// prescale = floor(prescaleval+0.5f); //将计算得到的预分频器值四舍五入取整,并将其赋值给 prescale 变量。
// oldmode = PCA9685_Read(PCA_Model); //通过调用 PCA9685_Read 函数读取当前 PCA9685 寄存器中的模式值,并将其存储在 oldmode 变量中。
//
// newmode = (oldmode&0x7F)|0x10; //根据旧的模式值计算出新的模式值,将最高位清零(bit 7)并将第 5 位设为1(bit 4),表示将 PCA9685 设置为睡眠模式。
// PCA9685_Write(PCA_Model,newmode); //将新的模式值写入 PCA9685 的模式寄存器。
// PCA9685_Write(PCA_Pre,prescale); //将计算得到的预分频器值写入 PCA9685 的预分频器寄存器。
// PCA9685_Write(PCA_Model,oldmode); //恢复旧的模式值。
// delay_ms(5); // 延时 5 毫秒,等待 PCA9685 完全启动。
// PCA9685_Write(PCA_Model,oldmode|0xa1); //将模式值的最高位和第 1 位设为1,表示将 PCA9685 设置为正常工作模式。
//
//}
/**
* 函 数:PCA9685_SetChannel
* 参 数:0~15
* 返 回 值:无
*/
void PCA9685_SetChannel(uint8_t Channel)
{
Channel_Flag = Channel;
}
/**
* 函 数:PCA9685_setPWM
* 参 数:channel;舵机编号。on 表示置1的位置,off 表示置0位置(12位有效,值要小于4095)
* 返 回 值:无
*/
void PCA9685_SetPWM(uint16_t pulse_on,uint16_t pulse_off)
{
// 将脉冲宽度分解为ON和OFF时间
uint8_t on_l = (pulse_on & 0xFF); //低8位
uint8_t on_h = (pulse_on >> 8) & 0xFF; //高8位
uint8_t off_l = (pulse_off & 0xFF);
uint8_t off_h = (pulse_off >> 8) & 0xFF;
// 写入ON和OFF时间到对应的寄存器
PCA9685_WriteReg(LED0_ON_L + 4 * Channel_Flag, on_l);
PCA9685_WriteReg(LED0_ON_H + 4 * Channel_Flag, on_h);
PCA9685_WriteReg(LED0_OFF_L + 4 * Channel_Flag, off_l);
PCA9685_WriteReg(LED0_OFF_H + 4 * Channel_Flag, off_h);
}
//void PCA9685_setPWM(u8 num,u32 on,u32 off) //num 表示 PWM 通道号,on 表示 PWM 的起始位置,off 表示 PWM 的结束位置(即从高电平切换到低电平的时刻)
//{
// IIC_Start(); //发送 I2C 起始信号,开始 I2C 通信。
//
// IIC_Send_Byte(PCA_Addr); //发送 PCA9685 的地址,告诉设备我们要和 PCA9685 进行通信。
// IIC_Wait_Ack(); //等待应答信号,确保设备准备好接收数据。
//
// IIC_Send_Byte(LED0_ON_L+4*num); //发送 LED 寄存器的地址,根据 PWM 通道号计算出相应的寄存器地址。
// IIC_Wait_Ack(); //
//
// IIC_Send_Byte(on&0xFF); //发送 PWM 的起始位置低 8 位。
// IIC_Wait_Ack(); //等待应答信号。
//
// IIC_Send_Byte(on>>8); //发送 PWM 的起始位置高 8 位。
// IIC_Wait_Ack(); //等待应答信号。
//
// IIC_Send_Byte(off&0xFF); //发送 PWM 的结束位置低 8 位。
// IIC_Wait_Ack(); //等待应答信号。
//
// IIC_Send_Byte(off>>8); //发送 PWM 的结束位置高 8 位。
// IIC_Wait_Ack(); //等待应答信号。
//
// IIC_Stop(); //发送 I2C 停止信号,结束本次通信。
//}
/**
* 函 数:SetAngle
* 参 数:0~180°
* 返 回 值:无
*/
void PCA9685_SetAngle(uint8_t angle)
{
uint16_t pulse_on = 0;
uint16_t pulse_off = 0;
pulse_off = (uint16_t)((angle+45)*4096/1800+0.5); //四舍五入
PCA9685_SetPWM(pulse_on,pulse_off);
}
//void setAngle(u8 num,u16 angle)
//{
// u32 off = 0;
// off = (u32)(103+angle*1.13); //360度舵机,每转动一度=1.14 0.5ms -180度起始位置:103
// PCA9685_setPWM(num,0,angle);
//}
//
//
/**
* 函 数:PCA9685初始化
* 参 数:无
* 返 回 值:无
*/
void PCA9685_Init(void)
{
MyI2C_Init(); //先初始化底层的I2C
/*PCA9685寄存器初始化,需要对照PCA9685手册的寄存器描述配置,此处仅配置了部分重要的寄存器*/
PCA9685_WriteReg(MODE1, 0x21); //0使用内部时钟,1自增模式,0退出睡眠模式,1响应0x70通用地址
Delay_us(500); // 延时 0.5 毫秒,等待 PCA9685 完全启动。
}
//
//
//void PCA9685_Init(float hz,u16 angle)
//{
// u32 off = 0;
// IIC_Init();
// PCA9685_Write(PCA_Model,0x00);
// PCA9685_setFreq(hz);
// off = (u32)(103+angle*1.14); //360度舵机,每转动一度=1.14 0.5ms -180度起始位置:103
// PCA9685_setPWM(0,0,off);
// PCA9685_setPWM(1,0,off);
// PCA9685_setPWM(2,0,off);
// PCA9685_setPWM(3,0,off);
// PCA9685_setPWM(4,0,off);
// PCA9685_setPWM(5,0,off);
// PCA9685_setPWM(6,0,off);
// PCA9685_setPWM(7,0,off);
// PCA9685_setPWM(8,0,off);
// PCA9685_setPWM(9,0,off);
// PCA9685_setPWM(10,0,off);
// PCA9685_setPWM(11,0,off);
// PCA9685_setPWM(12,0,off);
// PCA9685_setPWM(13,0,off);
// PCA9685_setPWM(14,0,off);
// PCA9685_setPWM(15,0,off);
// delay_ms(100);
//
//}
/**
* 函 数:Channel函数,串口选择指定通道
* 参 数:无
* 返 回 值:无
*/
void Channel(void)
{
Serial_SendString("Run_16_Servo...\r\n"); //串口回传一个字符串
OLED_Clear();
OLED_ShowString(1, 1, "Run_16_Servo."); //OLED清除指定位置,并显示Run_Mode_1
OLED_ShowString(2, 1, "Channel;");
Serial_RxFlag = 0; //进入循环前,结束数据包处理,等待接受命令
while (1)
{
if (Serial_RxFlag == 1)
{
if (strcmp(Serial_RxPacket, "Stop") == 0) //传入Stop字符停止当前模式
{
Serial_SendString("Stop_Channel...\r\n");
OLED_Clear();
OLED_ShowString(1, 1, "Stop_Channel.");
Serial_RxFlag = 0;
break;
}
else
{
uint8_t Channel = atoi(Serial_RxPacket); //把字符串转为数字
PCA9685_SetChannel(Channel); //置通道
OLED_ShowNum(2, 9, Channel, 2);
OLED_ShowString(3, 1, "Angle;");
Serial_Printf("Channel;%d", Channel);
Serial_RxFlag = 0; //数据包接收标志位清零等待下次接收
break;
}
}
}
}
/**
* 函 数:Angle函数,串口选择指定角度
* 参 数:无
* 返 回 值:无
*/
void Angle(void)
{
while (1)
{
if (Serial_RxFlag == 1)
{
if (strcmp(Serial_RxPacket, "Stop") == 0) //传入Stop字符停止当前模式
{
Serial_SendString("Stop_16_Servo...\r\n");
OLED_Clear();
OLED_ShowString(1, 1, "Stop_16_Servo.");
Serial_RxFlag = 0;
break;
}
else //否则认为输入的是角度
{
uint8_t Angle = atoi(Serial_RxPacket); //把字符串转为数字
PCA9685_SetAngle(Angle); //置角度
OLED_ShowNum(3, 7, Angle, 3);
Serial_Printf("Angle;%d", Angle);
Serial_RxFlag = 0; //数据包接收标志位清零等待下次接收
}
}
}
}
复制代码
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include "LED.h"
#include "string.h"
#include "Servo.h"
#include "PCA9685.h"
int main(void)
{
/*模块初始化*/
OLED_Init(); //OLED初始化
Serial_Init(); //串口初始化
PCA9685_Init();
PCA9685_SetFreq(50);
while (1)
{
if (Serial_RxFlag == 1) //如果接收到数据包
{
if (strcmp(Serial_RxPacket, "16_Servo") == 0)
{
Channel();
Angle();
}
Serial_RxFlag = 0; //防止串口错误输入导致标志位一直不清零,不能接收下次指令
}
}
}
原理图: 无
仿真: 无
代码:
程序.7z
(187.3 KB, 下载次数: 0)
2024-12-14 17:02 上传
点击文件名下载附件
下载积分: 黑币 -5
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1