PCA9685--16路 PWM模块舵机驱动板--STM32 IIC接口模块

60 篇文章 12 订阅

目录

一、概述和硬件

1、概述

2、硬件

1、电压

2、i2c地址

3、使能脚

二、寄存器功能

 MODE1寄存器

外面调用的接口


 

 

PCA9685是一款基于IIC总线通信的12位精度16通道PWM波输出的芯片,该芯片最初由NXP推出时主要面向LED开关调光,

16路12位PWM信号发生器,可用于控制舵机、led、电机等设备,i2c通信,节省主机资源。就是想控制好几台舵机,但太占用引脚资源就想到了这个神神器

 

 

 

 

一、概述和硬件


1、概述


网上上面比较便宜。
i2c通信,只需要2根i2c线就可以控制16路pwm,周期和占空比都可控。
可以多个模块级联。箭头部分可以更改设备id。
可以控制16路输出端的开、关、pwm以及占空比。
精度是12位:

工作频率    时间分辨率    通常舵机500~2500us可分成份数    通常舵机500~2500us,旋转角180°的角度分辨率
50Hz    4.88us    410份    0.439°
60Hz    4us    492份    0.366°
驱动方式可以选择开漏输出或推挽输出。

2、硬件


1、电压


数字电路电压范围可接受3.3和5v电平。此外还有一个v+引脚,这个引脚是给舵机供电用的,可以接稍微高一点的电压。

2、i2c地址


有6个地址控制脚,通过这些引脚可以控制设备的i2c地址。
7位的I2C地址为:0x40 + A5:A0,A5到A0如果不做任何处理的话是0,想要把哪一位置1就把那个引脚焊到一起。
另外用i2cdetect检测出还有一个0x70地址一直存在,这是一个通用地址,可以给所有从机下达指令。

3、使能脚


模块有一个OE反使能脚,这个引脚低电平使能,不接的话模块内部默认已经接地使能了,所以正常使用可以不接。

二、寄存器功能

内部地址(hex)名称功能
0MODE1设置寄存器1
1MODE2设置寄存器2
2SUBADR1i2c-bus subaddress1
3SUBADR2i2c-bus subaddress2
4SUBADR3i2c-bus subaddress3
5ALLCALLADR 
6LED0_ON_L 
7LED0_ON_H 
8LED0_OFF_L 
9LED0_OFF_H 
0x06 + 4*XLEDX_ON_L 
0x06 + 4*X + 1LEDX_ON_H 
0x06 + 4*X + 2LEDX_OFF_L 
0x06 + 4*X + 3LEDX_OFF_H 
… 上面共16路通道
FAALL_LED_ON_L 
FBALL_LED_ON_H 
FCALL_LED_OFF_L 
FDALL_LED_OFF_H 
FEPRE_SCALE控制周期的寄存器
FFTestMode 

 
MODE1寄存器


位    名称    功能

名称功能
D7RESTART写1复位,写完后此位自动清除。一定要在SLEEP位写0后至少500us后才能对此位写1进行复位。
D6EXTCLOCK0-使用内部时钟(25MHz)。1-使用外部时钟引脚的时钟。修改此位前,一定要先SLEEP,再修改此位(此时SLEEP位仍然写1),再退出SLEEP。
D5AI0-内部地址读写后不自动增加。1-内部地址读写后自动增加。一般i2c设备在对从机读写后内部地址都会自动增加,这个芯片可以手动设置是否自动增加,我们一般都会设成自动增加。
D4SLEEP0-退出SLEEP模式。1-进入SLEEP模式。注:1、写0退出sleep模式后,最多等500us后即可产生稳定的时钟信号。2、写1进入sleep模式后,时钟会关闭。此时可以修改时钟源寄存器EXTCLOCK和周期寄存器PRE_SCALE,修改这两个寄存器之前必须先进入sleep模式。
D3SUB1 
D2SUB2 
D1SUB3 
D0ALLCALL0-不响应0x70通用i2c地址。1-响应0x70通用i2c地址。这个芯片除了可以通过A5:A0自定义i2c地址外,还有一个通用i2c地址0x70,此寄存器可以控制是否响应这个通用地址。注意啊:这个寄存器的设置好像掉电会保存的!


各个通道的ON和OFF寄存器
总共16个通道,每个通道都有 LEDX_ON_L、LEDX_ON_H、LEDX_OFF_L、LEDX_OFF_H 四个寄存器。
系统中有一个12位的计数ACK,ACK根据PRE_SCALE寄存器设置的周期进行增加,没增加一次就会和上述四个寄存器对比:
当发现 ACK == LEDX_ON_H[3:0]:LEDX_ON_L 时,X通道输出高电平;
当发现 ACK == LEDX_OFF_H[3:0]:LEDX_OFF_L 时,X通道输出低电平。

PRE_SCALE寄存器
这个寄存器是用来设置周期的,具体原理可以不用管,只要记住:

  freq :要设置的周期
  prescaleval :要写入的参数
 

  freq *= 0.9;  
  float prescaleval = 25000000;
  prescaleval /= 4096;
  prescaleval /= freq;
  prescaleval -= 1;

外面调用的接口

不用初始化,直接设置。

设置后没有输出的话,可能i2c写入失败,刚开始遇到过这种情况,后来重新换了i2c库之后就可以了。还是主要i2c要正确使用。

/*freq:要设置的周期*/
void setPWMFreq(float freq);

/*

 num:设置第num个输出口输出量变化

 on与off:配合产生占空比 0~on为低电平 ,到on为高电平,到off转为低电平 

*/
void setPWM(u8 num, u16 on, u16 off);

上面的用的不方便就封装为简单的  直接输入需要设置的占空比 周期50,off:15表示1.5ms控制舵机方便

void set_pwm(u8 num,  u8 off);

void setPWMFreq(float freq) {

	freq *= 0.9;  
	float prescaleval = 25000000;
	prescaleval /= 4096;
	prescaleval /= freq;
	prescaleval -= 1;

	uint8_t oldmode = read8(PCA9685_MODE1);

	uint8_t newmode = (oldmode&0x7F) | 0x10; //准备进入sleep,设置时钟前必须先进入sleep模式
	Delay_ms(5);

	write8(PCA9685_MODE1, newmode); 
	Delay_ms(5);

	write8(PCA9685_PRESCALE, prescaleval);  p
	Delay_ms(5);

	oldmode &= 0xef;	//清除sleep位
	write8(PCA9685_MODE1, oldmode);

	Delay_ms(5);
	write8(PCA9685_MODE1, oldmode | 0xa1);  //  This sets the MODE1 register to turn on auto increment.

}
void setPWM(uint8_t num, uint16_t on, uint16_t off) {
    write8(LED0_ON_L+4*num,on);
    write8(LED0_ON_H+4*num,on>>8);
    write8(LED0_OFF_L+4*num,off);
    write8(LED0_OFF_H+4*num,off>>8);

}
void set_pwm(u8 num,  u8 off)
{
  setPWM(num, 0, (int)(off*20.48));

}
uint8_t read8(uint8_t reg_addr) {
	u8 data=0;
	I2C_BufferRead(PCA9685_SLAVE_ADDRESS,&data,reg_addr,1);
     return data;
}

void write8(uint8_t reg_addr, uint8_t reg_dat) {
	I2C_ByteWrite(PCA9685_SLAVE_ADDRESS,reg_dat,reg_addr); 
}

由于经常使用i2c接口就写了一个库方便移植,以后每次使用i2c就可以只用稍加修改封装下,主要还是使用一下两个接口 I2C_BufferRead(PCA9685_SLAVE_ADDRESS,&data,reg_addr,1);与 I2C_ByteWrite(PCA9685_SLAVE_ADDRESS,reg_dat,reg_addr); 
 

 

 

  • 33
    点赞
  • 259
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
以下是使用I2C2驱动PCA968516通道PWM代码,需要先初始化I2C2和PCA9685: ```c #include "stm32f4xx.h" #define PCA9685_ADDR 0x40 // PCA9685I2C地址 void I2C2_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; I2C_InitTypeDef I2C_InitStruct; RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOB, &GPIO_InitStruct); GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_I2C2); GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_I2C2); I2C_DeInit(I2C2); I2C_InitStruct.I2C_Mode = I2C_Mode_I2C; I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStruct.I2C_OwnAddress1 = 0x00; I2C_InitStruct.I2C_Ack = I2C_Ack_Enable; I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStruct.I2C_ClockSpeed = 100000; I2C_Init(I2C2, &I2C_InitStruct); I2C_Cmd(I2C2, ENABLE); } void PCA9685_Init(void) { uint8_t data[2]; data[0] = 0x00; // MODE1寄存器地址 data[1] = 0x01; // 设置PCA9685为普通模式 I2C_GenerateSTART(I2C2, ENABLE); while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C2, PCA9685_ADDR, I2C_Direction_Transmitter); while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(I2C2, data[0]); while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_SendData(I2C2, data[1]); while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_GenerateSTOP(I2C2, ENABLE); data[0] = 0xFE; // PRE_SCALE寄存器地址 data[1] = 0x03; // 设置PWM时钟频率为50Hz I2C_GenerateSTART(I2C2, ENABLE); while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C2, PCA9685_ADDR, I2C_Direction_Transmitter); while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(I2C2, data[0]); while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_SendData(I2C2, data[1]); while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_GenerateSTOP(I2C2, ENABLE); data[0] = 0x00; // MODE1寄存器地址 data[1] = 0x81; // 设置PCA9685为自动增量模式 I2C_GenerateSTART(I2C2, ENABLE); while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C2, PCA9685_ADDR, I2C_Direction_Transmitter); while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(I2C2, data[0]); while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_SendData(I2C2, data[1]); while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_GenerateSTOP(I2C2, ENABLE); } void PCA9685_SetPWM(uint8_t channel, uint16_t on, uint16_t off) { uint8_t data[5]; data[0] = 0x06 + 4 * channel; // LED0_ON_L寄存器地址 data[1] = on & 0xFF; // LED0_ON_L data[2] = on >> 8; // LED0_ON_H data[3] = off & 0xFF; // LED0_OFF_L data[4] = off >> 8; // LED0_OFF_H I2C_GenerateSTART(I2C2, ENABLE); while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C2, PCA9685_ADDR, I2C_Direction_Transmitter); while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(I2C2, data[0]); while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_SendData(I2C2, data[1]); while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_SendData(I2C2, data[2]); while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_SendData(I2C2, data[3]); while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_SendData(I2C2, data[4]); while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_GenerateSTOP(I2C2, ENABLE); } int main(void) { I2C2_Init(); PCA9685_Init(); while (1) { // 设置PWM输出 PCA9685_SetPWM(0, 0, 2048); // 50%占空比 PCA9685_SetPWM(1, 0, 4096); // 100%占空比 } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值