完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
概述
最近在学习机械臂控制,由于单片机的IO口有限,如果机械臂的舵机都使用单片机控制,过于占用资源,从网上购买了PCA9685这款舵机扩展板,通过I2C与单片机通信,实现对舵机的控制,节省了单片机的IO口。对于PCA9685的使用网上的资料很少,卖家给的资料也全部都是英文版的,目前还没有完全搞清楚,都是如果仅仅用于控制舵机啥的,不需要太关心PCA9685,以后有时间再好好研究研究。 I2C通信 STM32的I2C通信有两种方式,一种是硬件I2C,还有一种是模拟I2C,这两种的区别大概就是硬件I2C直接使用库函数进行操作,模拟I2C根据I2C的工作时序,自己写相应的函数,操作单片机,包括起始信号,停止信号,应答信号等,跟串口相同的地方是I2C同样有中断和DMA,但是在这里仅仅使用阻塞方式就行,I2C的具体原理已经有很多的资料可以参考了,HAL库的I2C使用硬件I2C更加方便,因为HAL库已经提供了比较完整的库函数,不需要自己写函数操作单片机,更加便捷。 主要HAL库I2C 阻塞方式下的库函数: /*I2C写数据的函数*/ HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout); /* I2C_HandleTypeDef *hi2c :也就是你所设置的那个实例,比如I2C1 &hi2c1 uint16_t DevAddress : 你要写入数据的地址,比如0xA0 uint8_t *pData :存放你要写的数据 uint16_t Size :数据的大小 uint32_t Timeout :最大的传输时间 例如 HAL_I2C_Master_Transmit(&hi2c1,0xA1,(uint8_t*)TxData,2,1000) */ /*I2C读数据的函数*/ HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout); /* I2C_HandleTypeDef *hi2c :也就是你所设置的那个实例,比如I2C1 &hi2c1 uint16_t DevAddress : 你要读入数据的地址,比如0xA0 uint8_t *pData :存放你要读的数据 uint16_t Size :数据的大小 uint32_t Timeout :最大的传输时间 */ 还有许多的其他库函数,但是这里用不到,就暂时不加啦! /*PCA9685.c*/ #include "stm32_pca9685.h" #include "math.h" #include "i2c.h" uint8_t pca_read(uint8_t startAddress) { //Send address to start reading from. uint8_t tx[1]; uint8_t buffer[1]; tx[0]=startAddress; HAL_I2C_Master_Transmit(&hi2c1,pca_adrr, tx,1,10000); HAL_I2C_Master_Receive(&hi2c1,pca_adrr,buffer,1,10000); return buffer[0]; } void pca_write(uint8_t startAddress, uint8_t buffer) { //Send address to start reading from. uint8_t tx[2]; tx[0]=startAddress; tx[1]=buffer; HAL_I2C_Master_Transmit(&hi2c1,pca_adrr, tx,2,10000); } void pca_setfreq(float freq)//设置PWM频率 { uint8_t prescale,oldmode,newmode; double prescaleval; freq *= 0.92; prescaleval = 25000000; prescaleval /= 4096; prescaleval /= freq; prescaleval -= 1; prescale =floor(prescaleval + 0.5f); oldmode = pca_read(pca_mode1); newmode = (oldmode&0x7F) | 0x10; // sleep pca_write(pca_mode1, newmode); // go to sleep pca_write(pca_pre, prescale); // set the prescaler pca_write(pca_mode1, oldmode); HAL_Delay(2); pca_write(pca_mode1, oldmode | 0xa1); } void pca_setpwm(uint8_t num, uint32_t on, uint32_t off) { pca_write(LED0_ON_L+4*num,on); pca_write(LED0_ON_H+4*num,on>>8); pca_write(LED0_OFF_L+4*num,off); pca_write(LED0_OFF_H+4*num,off>>8); } /*num:舵机PWM输出引脚0~15,on:PWM上升计数值0~4096,off:PWM下降计数值0~4096 一个PWM周期分成4096份,由0开始+1计数,计到on时跳变为高电平,继续计数到off时 跳变为低电平,直到计满4096重新开始。所以当on不等于0时可作延时,当on等于0时, off/4096的值就是PWM的占空比。*/ /* 函数作用:初始化舵机驱动板 参数:1.PWM频率 2.初始化舵机角度 */ void PCA_Servo_Init(float hz,uint8_t angle) { uint32_t off=0; // IIC_Init(); pca_write(pca_mode1,0x0); pca_setfreq(hz);//设置PWM频率 off=(uint32_t)(145+angle*2.4); pca_setpwm(0,0,off);pca_setpwm(1,0,off);pca_setpwm(2,0,off);pca_setpwm(3,0,off); pca_setpwm(4,0,off);pca_setpwm(5,0,off);pca_setpwm(6,0,off);pca_setpwm(7,0,off); pca_setpwm(8,0,off);pca_setpwm(9,0,off);pca_setpwm(10,0,off);pca_setpwm(11,0,off); pca_setpwm(12,0,off);pca_setpwm(13,0,off);pca_setpwm(14,0,off);pca_setpwm(15,0,off); HAL_Delay(500); } /* 函数作用:控制舵机转动; 参数:1.输出端口,可选0~15; 2.起始角度,可选0~180; 3.结束角度,可选0~180; 4.模式选择,0 表示函数内无延时,调用时需要在函数后另外加延时函数,且不可调速,第五个参数可填任意值; 1 表示函数内有延时,调用时不需要在函数后另外加延时函数,且不可调速,第五个参数可填任意值; 2 表示速度可调,第五个参数表示速度值; 5.速度,可填大于 0 的任意值,填 1 时速度最快,数值越大,速度越小; 注意事项:模式 0和1 的速度比模式 2 的最大速度大; */ void PCA_Servo(uint8_t num,uint8_t end_angle) { uint32_t off=0; off=(uint32_t)(158+end_angle*2.2); pca_setpwm(num,0,off); } /*PCA9685.h*/ #ifndef __STM32PCA9685_H #define __STM32PCA9685_H //#include "stm32f10x.h" #include "stm32f4xx_hal.h" #define pca_adrr 0x80 #define pca_mode1 0x0 #define pca_pre 0xFE #define LED0_ON_L 0x6 #define LED0_ON_H 0x7 #define LED0_OFF_L 0x8 #define LED0_OFF_H 0x9 #define jdMIN 115 // minimum #define jdMAX 590 // maximum #define jd000 130 //0度对应4096的脉宽计数值 #define jd180 520 //180度对应4096的脉宽计算值 void pca_write(uint8_t adrr,uint8_t data); uint8_t pca_read(uint8_t adrr); void PCA_Servo_Init(float hz,uint8_t angle); void pca_setfreq(float freq); void pca_setpwm(uint8_t num, uint32_t on, uint32_t off); void PCA_Servo(uint8_t num,uint8_t end_angle); #endif 具体的代码解释还没来得及做,感谢那些原作者的分享,站在巨人的肩膀上能使我们看得更远。 原文点这里哦!! |
|
|
|
只有小组成员才能发言,加入小组>>
3309 浏览 9 评论
2988 浏览 16 评论
3490 浏览 1 评论
9050 浏览 16 评论
4085 浏览 18 评论
1169浏览 3评论
602浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
594浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2331浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1894浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-20 03:08 , Processed in 1.021334 second(s), Total 80, Slave 61 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号