STM32 PCA9685 HAL库 舵机扩展板
概述
最近在学习机械臂控制,由于单片机的IO口有限,如果机械臂的舵机都使用单片机控制,过于占用资源,从网上购买了PCA9685这款舵机扩展板,通过I2C与单片机通信,实现对舵机的控制,节省了单片机的IO口。对于PCA9685的使用网上的资料很少,卖家给的资料也全部都是英文版的,目前还没有完全搞清楚,都是如果仅仅用于控制舵机啥的,不需要太关心PCA9685,以后有时间再好好研究研究。
I2C通信
STM32的I2C通信有两种方式,一种是硬件I2C,还有一种是模拟I2C,这两种的区别大概就是硬件I2C直接使用库函数进行操作,模拟I2C根据I2C的工作时序,自己写相应的函数,操作单片机,包括起始信号,停止信号,应答信号等,跟串口相同的地方是I2C同样有中断和DMA,但是在这里仅仅使用阻塞方式就行,I2C的具体原理已经有很多的资料可以参考了,HAL库的I2C使用硬件I2C更加方便,因为HAL库已经提供了比较完整的库函数,不需要自己写函数操作单片机,更加便捷。 主要HAL库I2C 阻塞方式下的库函数:
HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
还有许多的其他库函数,但是这里用不到,就暂时不加啦!
#include "stm32_pca9685.h"
#include "math.h"
#include "i2c.h"
uint8_t pca_read(uint8_t startAddress) {
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) {
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)
{
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;
pca_write(pca_mode1, newmode);
pca_write(pca_pre, prescale);
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);
}
void PCA_Servo_Init(float hz,uint8_t angle)
{
uint32_t off=0;
pca_write(pca_mode1,0x0);
pca_setfreq(hz);
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);
}
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);
}
#ifndef __STM32PCA9685_H
#define __STM32PCA9685_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
#define jdMAX 590
#define jd000 130
#define jd180 520
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
具体的代码解释还没来得及做,感谢那些原作者的分享,站在巨人的肩膀上能使我们看得更远。 原文点这里哦!!
|