STM32(HAL库) I2C 实现与 PCA9685 通信,控制多个舵机

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 阻塞方式下的库函数:

/*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

具体的代码解释还没来得及做,感谢那些原作者的分享,站在巨人的肩膀上能使我们看得更远。
原文点这里哦!!

猜你喜欢

转载自blog.csdn.net/qq_52111026/article/details/119323207