stm32f103+PCA9685避坑点

完整代码会放文章后面

一、IIC地址问题

在淘宝上购买PCA9685,卖家一般会给你发一个资料包,其中的一个PDF文档介绍模块使用的,在介绍IIC地址时设置部分是错的!

stm32f103+PCA9685避坑点_第1张图片

 A0其实是IIC地址的第1位,并不是第0位,第0位是读写位,写模式设置位0,读模式设置为1。

默认的地址是0x80,即A5到A0都不连接的情况

        A0连上时,写操作地址值为1000 0010   地址值:0x82

        使用寄存器自动增量读操作时第二次发送从机地址,最后一位的值为1(第一次发送从机地址最后一位时仍为0)

看一下官方文档的介绍

stm32f103+PCA9685避坑点_第2张图片

 启用寄存器自动增量读操作---->

stm32f103+PCA9685避坑点_第3张图片

 顺带补上写操作的用法

stm32f103+PCA9685避坑点_第4张图片

二、 mode1相关位的设置参考下图

stm32f103+PCA9685避坑点_第5张图片

三、 PCA9685初始化操作如下:

/*********************************
PCA9685初始化并设置舵机初始角度值

**********************************/
void pca9685_Init(){
	u8 num;
	IIC_Init();		//初始化IIC总线	
	pca9685_Write(PCA9685_MODE1,0x0);	//在频率设置之前,必须有  (MODE1所有位置0)
	setPWMFreq(50);
	for(num=0;num<15;num++){
		set_ServoAngle(num,90);
	}
}

至于为什么要将mode1置零,可能是为了清除掉上电操作自动运行的旧模式。

上代码:

.h文件

#ifndef _PCA9685_H
#define _PCA9685_H
#include "sys.h"
#include "myiic.h"

#define PCA9685_adrr 0x80
#define PCA9685_SUBADR1 0x2
#define PCA9685_SUBADR2 0x3
#define PCA9685_SUBADR3 0x4

#define PCA9685_MODE1 0x0
#define PCA9685_PRESCALE 0xFE


#define LED0_ON_L 0x6
#define LED0_ON_H 0x7
#define LED0_OFF_L 0x8
#define LED0_OFF_H 0x9

#define ALLLED_ON_L 0xFA
#define ALLLED_ON_H 0xFB
#define ALLLED_OFF_L 0xFC
#define ALLLED_OFF_H 0xFD

void pca9685_Write(unsigned char reg,unsigned char data);
u8 pca9685_Read(unsigned char reg);
void setPWMFreq(u8 freq);
void setPWM(u8 num, u16 on, u16 off);
void pca9685_Init(void);
void servo_Test(void);

#endif

.c文件

#include "PCA9685.h"

#include "usart.h"
#include "myiic.h"
#include "delay.h"
#include "math.h"

/****************
向PCA9685写一个字节
reg:寄存器的地址值
data:要写入的值
****************/
void pca9685_Write(unsigned char reg,unsigned char data){
    IIC_Start();
    IIC_Send_Byte(PCA9685_adrr);	//
    IIC_Wait_Ack();
    IIC_Send_Byte(reg);
	IIC_Wait_Ack();
    IIC_Send_Byte(data);
    IIC_Wait_Ack();
    IIC_Stop();
}
/****************
从PCA9685读一个字节
读取寄存器的值
reg:寄存器地址值
****************/
u8 pca9685_Read(unsigned char reg){
	u8 res;
	IIC_Start();
	IIC_Send_Byte(PCA9685_adrr);
	IIC_Wait_Ack();
	IIC_Send_Byte(reg);
	IIC_Wait_Ack();
	IIC_Start();
	IIC_Send_Byte(PCA9685_adrr|0x01);	//将地址值设置位读模式,地址值最后一位决定读写
	IIC_Wait_Ack();
	res=IIC_Read_Byte(0);
	IIC_Stop();
	return res;
}

/****************
设置PCA9685的PWM频率值
freq=50Hz   周期 t=1/50=0.02s=20ms
****************/
void setPWMFreq(u8 freq){
	u8 prescale,oldmode,newmode;
	double prescaleval;
	prescaleval = 25000000.0/(4096*freq*0.915);
	prescale = (u8)floor(prescaleval+0.5)-1;	//floor --->round down(向下取整)

	oldmode = pca9685_Read(PCA9685_MODE1);
	newmode = (oldmode&0x7F) | 0x10; // sleep	设置预分频需要先进入低功耗模式
	pca9685_Write(PCA9685_MODE1, newmode); // go to sleep
	pca9685_Write(PCA9685_PRESCALE, prescale); // set the prescaler
	pca9685_Write(PCA9685_MODE1, oldmode);
	delay_ms(5);
	pca9685_Write(PCA9685_MODE1, oldmode | 0xa1); 		//0xa1:1010 0001
}

/****************
设置每个通道的PWM值
num=0~15
on:
off:
****************/

void setPWM(u8 num, u16 on, u16 off) {
    pca9685_Write(LED0_ON_L+4*num,on);
    pca9685_Write(LED0_ON_H+4*num,on>>8);
    pca9685_Write(LED0_OFF_L+4*num,off);
    pca9685_Write(LED0_OFF_H+4*num,off>>8);
}

/****************
设置每个通道舵机的角度值
servoNum=0~15 
servoAngle=0~180
****************/
void set_ServoAngle(u8 servoNum,u16 servoAngle){
	u16 pwm;
	pwm=(u16)204.8*(0.5+(servoAngle*1.0/90));	//4096/20ms=204.8/1ms 
				//(舵机驱动PWM周期是20ms,高低电平变化在0.5ms~2.5ms之间才能有效驱动舵机)
	setPWM(servoNum, 0, pwm);
}

/*********************************
PCA9685初始化并设置舵机初始角度值

**********************************/
void pca9685_Init(){
	u8 num;
	IIC_Init();		//初始化IIC总线	
	pca9685_Write(PCA9685_MODE1,0x0);	//在频率设置之前,必须有  (MODE1所有位置0)
	setPWMFreq(50);
	for(num=0;num<15;num++){
		set_ServoAngle(num,90);
	}
}

后面有时间再补上PID和位置控制

你可能感兴趣的:(stm32,单片机,嵌入式硬件)