HAl库PCA9685基于I2C通信

本文详细介绍了使用PCA9685模块控制舵机的过程,包括频率设置及角度调整技巧。作者通过实践总结出合理的延时时间以确保角度准确调整,并分享了具体的HAL库配置与代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

   写这篇文章的目的主要是为了记录一下我在接触PCA9685时遇到的一些问题。

   首先我在学习别人的文章时看到了很多开头初始化PCA,其实主要目的都是为了设置他的频率为50HZ,所以为了简洁方便,这里我的代码开头就直接设置频率,然后在while中周期性调整角度。值得注意的一点是,我在调整角度中发现调整角度过程中假如延时过短,那么就会在调整上一个角度的过程中转换成调整下一个角度,所以我选择了延时3s。

      HAL库的配置我选择的是软件I2C通信的模式,所以选择了PB6作为时钟线,PB7作为数据线,还开启了一个串口方便调试。

 

  首先开始一个舵控板的头文件,定义一些地址

#define mode1         0x00
#define mode2         0x01
#define LED0_ON_L     0x06
#define LED0_ON_H     0x07
#define LED0_OFF_L    0x08
#define LED0_OFF_H    0x09
#define address_write 0x80
#define address_read  0x81
#define Pre_scale     0xFE

  然后在C文件中攥写代码

  首先是基于I2C协议的关于读写的代码(关于其中I2C的底层逻辑代码我也不多赘述)

void Servo_Write(uint8_t address_config,uint16_t data)
{
	if(I2C_Start() == 0) 
	{
		I2C_Stop();
	}
	
	I2C_SendByte(address_write);
	if(!I2C_WaitAck())          
	{
		I2C_Stop();
	}
	
	I2C_SendByte(address_config);            
	if(!I2C_WaitAck())
	{
		I2C_Stop();
	}
	
	I2C_SendByte(data);          
	if(!I2C_WaitAck())
	{
		I2C_Stop();
	}
	
	I2C_Stop();
}

uint16_t Servo_Read(uint8_t address_config)
{
	uint8_t data;
	if(I2C_Start() == 0)        
	{
		I2C_Stop();
	}
	I2C_SendByte(address_write);            
	if(!I2C_WaitAck())
	{
		I2C_Stop();
	}
	I2C_SendByte(address_config);            
	if(!I2C_WaitAck())
	{
		I2C_Stop();
	}
	if(I2C_Start() == 0)          
	{
		I2C_Stop();
	}
	I2C_SendByte(address_read);         
	if(!I2C_WaitAck())
	{
		I2C_Stop();
	}
	
	data = I2C_ReceiveByte();
	I2C_SendACK(1);
	I2C_Stop();
	
	return data;
}

  接下来是舵控板的设置频率代码和设置OFF值代码(我们一般将ON设为0)

/*Mode1寄存器
  D7(RESTART):写1复位,写完后此位自动清除。一定要在SLEEP位写0后至少500us后才能对此位写1进行复位
  D6(EXTCLOCK):0-使用内部时钟。1-使用外部时钟引脚的时钟。修改此位前,一定要先SLEEP,
                    再修改此位(此时SLEEP位仍然写1),再退出SLEEP
  D5(AI):0-内部地址读写后不自动增加。1-内部地址读写后自动增加
  D4(SLEEP):0-退出SLEEP模式(退出后等待500us)。1-进入SLEEP模式(更改D6和频率必须go to sleep)
  D3(SUB1) D2(SUB2) D1(SUB3)
  D0(ALLCALL):0-不响应通用i2c地址。1-响应通用i2c地址(0x70)*/
  
//设置PWM频率
void PWM_SetFreq(uint8_t freq)
{
	Servo_Write(mode1,0x00);
	float Lprescal ;
	uint8_t old_mode,now_mode,Nprescal;
	freq *= 0.915;                                       //误差
	Lprescal = (float)25000000.0 / 4096 / freq - 1;      //计算频率
	Nprescal = floor(Lprescal + 0.5f);                   //向上取整
	old_mode = Servo_Read(mode1);                        //读出mode1的值(0x00)
	now_mode = (old_mode&0x7F)|0x10;                     
	/*0x7F:0111 1111
	  0x10:0001 0000*/
	Servo_Write(mode1,now_mode);                         //让舵控板睡觉
	Servo_Write(Pre_scale,Nprescal);                     //设置PWM频率
	Servo_Write(mode1,old_mode);                         //叫醒舵控板
	HAL_Delay(5);
	Servo_Write(mode1,old_mode|0xa1);
	/*0xa1:1010 0001
	  将RESTART,AI,ALLCALL置1*/
}

//一般ON直接设0,使用OFF的值与4096的比例
void Set_Pwm(uint8_t num,uint32_t on,uint32_t off)
{
	Servo_Write(LED0_ON_L + 4 * num,on);          //写入ON低八位
	Servo_Write(LED0_ON_H + 4 * num,on>>8);       //写入ON高四位
	Servo_Write(LED0_OFF_L + 4 * num,off);        //写入OFF低八位
	Servo_Write(LED0_OFF_H + 4 * num,off>>8);     //写入OFF高四位
}

  最后就是在main里面初始化频率以及设置角度

PWM_SetFreq(50);
/*90度脉宽=0.5ms+(90/180)*(2.5ms-0.5ms)=1.5ms
	  1.5ms/20ms=off/4096
	  off=307*/
	 Set_Pwm(0,0,239);
	 HAL_Delay(3000);
	 Set_Pwm(0,0,307);
	 HAL_Delay(3000);

   其中存在的问题和不足希望读者在评论区加以点评。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值