- #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)
|