標(biāo)題:
stm32f103c8t6單片機(jī)和PCF9685控制16路舵機(jī)通過串口調(diào)試程序
[打印本頁]
作者:
2013261071
時(shí)間:
2024-12-12 18:13
標(biāo)題:
stm32f103c8t6單片機(jī)和PCF9685控制16路舵機(jī)通過串口調(diào)試程序
#include "stm32f10x.h" // Device header
#include "MyI2C.h"
#include "PCA9685_Reg.h"
#include "math.h"
#include "Delay.h"
#include "Serial.h"
#include "OLED.h"
#include "string.h"
#include "stdlib.h"
uint8_t Channel_Flag = 0;
/**
* 函 數(shù):PCA9685寫寄存器
* 參 數(shù):RegAddress 寄存器地址,范圍:參考PCA9685手冊的寄存器描述
* 參 數(shù):Data 要寫入寄存器的數(shù)據(jù),范圍:0x00~0xFF
* 返 回 值:無
*/
void PCA9685_WriteReg(uint8_t RegAddress, uint8_t Data)
{
MyI2C_Start(); //I2C起始
MyI2C_SendByte(ADDRESS); //發(fā)送從機(jī)地址,讀寫位為0,表示即將寫入
MyI2C_ReceiveAck(); //接收應(yīng)答
MyI2C_SendByte(RegAddress); //發(fā)送寄存器地址
MyI2C_ReceiveAck(); //接收應(yīng)答
MyI2C_SendByte(Data); //發(fā)送要寫入寄存器的數(shù)據(jù)
MyI2C_ReceiveAck(); //接收應(yīng)答
MyI2C_Stop(); //I2C終止
}
//void PCA9685_Write(u8 addr,u8 data) // addr 表示要寫入數(shù)據(jù)的寄存器地址,data 表示要寫入的數(shù)據(jù)
//{
// IIC_Start(); // 發(fā)送 I2C 起始信號,開始 I2C 通信。
//
// IIC_Send_Byte(PCA_Addr); // 發(fā)送 PCA_Addr = 0x80 ,告訴設(shè)備我們要寫入數(shù)據(jù)
// IIC_NAck(); // 發(fā)送不應(yīng)答信號,表示主控器不需要從設(shè)備接收更多數(shù)據(jù)。
//
// IIC_Send_Byte(addr); // 發(fā)送要寫入數(shù)據(jù)的寄存器地址。
// IIC_NAck(); // 發(fā)送不應(yīng)答信號。
//
// IIC_Send_Byte(data); // 發(fā)送要寫入的數(shù)據(jù)。
// IIC_NAck(); // 發(fā)送不應(yīng)答信號。
//
// IIC_Stop(); // 發(fā)送 I2C 停止信號,結(jié)束本次通信。
//
//}
/**
* 函 數(shù):PCA9685讀寄存器
* 參 數(shù):RegAddress 寄存器地址,范圍:參考PCA9685手冊的寄存器描述
* 返 回 值:讀取寄存器的數(shù)據(jù),范圍:0x00~0xFF
*/
uint8_t PCA9685_ReadReg(uint8_t RegAddress)
{
uint8_t Data;
MyI2C_Start(); //I2C起始
MyI2C_SendByte(ADDRESS); //發(fā)送從機(jī)地址,讀寫位為0,表示即將寫入
MyI2C_ReceiveAck(); //接收應(yīng)答
MyI2C_SendByte(RegAddress); //發(fā)送寄存器地址
MyI2C_ReceiveAck(); //接收應(yīng)答
MyI2C_Start(); //I2C重復(fù)起始
MyI2C_SendByte(ADDRESS | 0x01); //發(fā)送從機(jī)地址,讀寫位為1,表示即將讀取
MyI2C_ReceiveAck(); //接收應(yīng)答
Data = MyI2C_ReceiveByte(); //接收指定寄存器的數(shù)據(jù)
MyI2C_SendAck(1); //發(fā)送應(yīng)答,給從機(jī)非應(yīng)答,終止從機(jī)的數(shù)據(jù)輸出
MyI2C_Stop(); //I2C終止
return Data;
}
//u8 PCA9685_Read(u8 addr) // addr 表示要讀取數(shù)據(jù)的寄存器地址
//{
// u8 data; // 聲明一個(gè)無符號 8 位整數(shù)變量 data,用于存儲讀取到的數(shù)據(jù)。
// IIC_Start(); // 發(fā)送 I2C 起始信號,開始 I2C 通信。
// IIC_Send_Byte(PCA_Addr); // 發(fā)送 PCA_Addr = 0x80 ,告訴設(shè)備我們要寫入數(shù)據(jù)
// IIC_NAck(); // 發(fā)送不應(yīng)答信號,表示主控器不需要從設(shè)備接收更多數(shù)據(jù)。
// IIC_Send_Byte(addr); // 發(fā)送要讀取數(shù)據(jù)的寄存器地址。
// IIC_NAck(); // 發(fā)送不應(yīng)答信號。
// IIC_Stop(); // 發(fā)送 I2C 停止信號,結(jié)束本次通信。
// delay_us(10); // 延時(shí) 10 微秒,等待芯片準(zhǔn)備好數(shù)據(jù)。
// IIC_Start(); // 發(fā)送 I2C 起始信號,開始另一次 I2C 通信。
// IIC_Send_Byte(PCA_Addr|0x01); // 發(fā)送 PCA9685 的地址,并設(shè)置最低位為 1,
// IIC_NAck(); // 發(fā)送不應(yīng)答信號。
// data = IIC_Read_Byte(0); // 通過 I2C 從 PCA9685 讀取一個(gè)字節(jié)的數(shù)據(jù),并存儲到變量 data 中。
// IIC_Stop(); // 發(fā)送 I2C 停止信號,結(jié)束本次通信。
// return data; // 返回讀取到的數(shù)據(jù)。
//}
/**
* 函 數(shù):PCA9685設(shè)置輸出頻率
* 參 數(shù):float 頻率
* 返 回 值:返回預(yù)分頻的值
*/
void PCA9685_SetFreq(float freq)
{
uint8_t prescale,oldmode,newmode; //定義了三個(gè)無符號 8 位整型變量 用于存儲預(yù)分頻器值、舊的模式寄存器值和新的模式寄存器值
float prescaleval; //定義了一個(gè)雙精度浮點(diǎn)型變量 prescaleval,用于計(jì)算預(yù)分頻器的值。
// freq *= 0.98; //將傳入的頻率值乘以 0.98,這是為了微調(diào)頻率值以適應(yīng) PCA9685 的實(shí)際需求
prescaleval = 25000000/4096/freq - 1;
prescale = floor(prescaleval+0.5); //將計(jì)算得到的預(yù)分頻器值四舍五入取整,并將其賦值給 prescale 變量。
oldmode = PCA9685_ReadReg(MODE1); //通過調(diào)用 PCA9685_Read 函數(shù)讀取當(dāng)前 PCA9685 寄存器中的模式值,并將其存儲在 oldmode 變量中。
newmode = (oldmode&0x7F)|0x10; //根據(jù)舊的模式值計(jì)算出新的模式值,將最高位清零(不復(fù)位)并將第5位設(shè)為1,表示將 PCA9685 設(shè)置為睡眠模式。
PCA9685_WriteReg(MODE1,newmode); //將新的模式值寫入 PCA9685 的模式寄存器。
PCA9685_WriteReg(PRE_SCALE,prescale); //將計(jì)算得到的預(yù)分頻器值寫入 PCA9685 的預(yù)分頻器寄存器。
PCA9685_WriteReg(MODE1,oldmode); //恢復(fù)舊的模式值。
Delay_us(500); // 延時(shí) 0.5 毫秒,等待 PCA9685 完全啟動。
PCA9685_WriteReg(MODE1,PCA9685_ReadReg(MODE1)|0x80); //將模式值的第8( Restart enabled.).更新頻率后復(fù)位使用
}
//void PCA9685_setFreq(float freq)
//{
// u8 prescale,oldmode,newmode; //定義了三個(gè)無符號 8 位整型變量 用于存儲預(yù)分頻器值、舊的模式寄存器值和新的模式寄存器值
//
// double prescaleval; //定義了一個(gè)雙精度浮點(diǎn)型變量 prescaleval,用于計(jì)算預(yù)分頻器的值。
//
// freq *= 0.98; //將傳入的頻率值乘以 0.98,這是為了微調(diào)頻率值以適應(yīng) PCA9685 的實(shí)際需求
// prescaleval = 25000000; //這是 PCA9685 內(nèi)部振蕩器的頻率
// prescaleval /= 4096; //每個(gè)周期從0計(jì)數(shù)到4095,除以 4096,得到每個(gè)計(jì)數(shù)器周期的時(shí)間,
// prescaleval /= freq; //除以所需的頻率值,得到預(yù)分頻器的值。
// prescaleval -= 1; //減去 1,得到最終的預(yù)分頻器值
// prescale = floor(prescaleval+0.5f); //將計(jì)算得到的預(yù)分頻器值四舍五入取整,并將其賦值給 prescale 變量。
// oldmode = PCA9685_Read(PCA_Model); //通過調(diào)用 PCA9685_Read 函數(shù)讀取當(dāng)前 PCA9685 寄存器中的模式值,并將其存儲在 oldmode 變量中。
//
// newmode = (oldmode&0x7F)|0x10; //根據(jù)舊的模式值計(jì)算出新的模式值,將最高位清零(bit 7)并將第 5 位設(shè)為1(bit 4),表示將 PCA9685 設(shè)置為睡眠模式。
// PCA9685_Write(PCA_Model,newmode); //將新的模式值寫入 PCA9685 的模式寄存器。
// PCA9685_Write(PCA_Pre,prescale); //將計(jì)算得到的預(yù)分頻器值寫入 PCA9685 的預(yù)分頻器寄存器。
// PCA9685_Write(PCA_Model,oldmode); //恢復(fù)舊的模式值。
// delay_ms(5); // 延時(shí) 5 毫秒,等待 PCA9685 完全啟動。
// PCA9685_Write(PCA_Model,oldmode|0xa1); //將模式值的最高位和第 1 位設(shè)為1,表示將 PCA9685 設(shè)置為正常工作模式。
//
//}
/**
* 函 數(shù):PCA9685_SetChannel
* 參 數(shù):0~15
* 返 回 值:無
*/
void PCA9685_SetChannel(uint8_t Channel)
{
Channel_Flag = Channel;
}
/**
* 函 數(shù):PCA9685_setPWM
* 參 數(shù):channel;舵機(jī)編號。on 表示置1的位置,off 表示置0位置(12位有效,值要小于4095)
* 返 回 值:無
*/
void PCA9685_SetPWM(uint16_t pulse_on,uint16_t pulse_off)
{
// 將脈沖寬度分解為ON和OFF時(shí)間
uint8_t on_l = (pulse_on & 0xFF); //低8位
uint8_t on_h = (pulse_on >> 8) & 0xFF; //高8位
uint8_t off_l = (pulse_off & 0xFF);
uint8_t off_h = (pulse_off >> 8) & 0xFF;
// 寫入ON和OFF時(shí)間到對應(yīng)的寄存器
PCA9685_WriteReg(LED0_ON_L + 4 * Channel_Flag, on_l);
PCA9685_WriteReg(LED0_ON_H + 4 * Channel_Flag, on_h);
PCA9685_WriteReg(LED0_OFF_L + 4 * Channel_Flag, off_l);
PCA9685_WriteReg(LED0_OFF_H + 4 * Channel_Flag, off_h);
}
//void PCA9685_setPWM(u8 num,u32 on,u32 off) //num 表示 PWM 通道號,on 表示 PWM 的起始位置,off 表示 PWM 的結(jié)束位置(即從高電平切換到低電平的時(shí)刻)
//{
// IIC_Start(); //發(fā)送 I2C 起始信號,開始 I2C 通信。
//
// IIC_Send_Byte(PCA_Addr); //發(fā)送 PCA9685 的地址,告訴設(shè)備我們要和 PCA9685 進(jìn)行通信。
// IIC_Wait_Ack(); //等待應(yīng)答信號,確保設(shè)備準(zhǔn)備好接收數(shù)據(jù)。
//
// IIC_Send_Byte(LED0_ON_L+4*num); //發(fā)送 LED 寄存器的地址,根據(jù) PWM 通道號計(jì)算出相應(yīng)的寄存器地址。
// IIC_Wait_Ack(); //
//
// IIC_Send_Byte(on&0xFF); //發(fā)送 PWM 的起始位置低 8 位。
// IIC_Wait_Ack(); //等待應(yīng)答信號。
//
// IIC_Send_Byte(on>>8); //發(fā)送 PWM 的起始位置高 8 位。
// IIC_Wait_Ack(); //等待應(yīng)答信號。
//
// IIC_Send_Byte(off&0xFF); //發(fā)送 PWM 的結(jié)束位置低 8 位。
// IIC_Wait_Ack(); //等待應(yīng)答信號。
//
// IIC_Send_Byte(off>>8); //發(fā)送 PWM 的結(jié)束位置高 8 位。
// IIC_Wait_Ack(); //等待應(yīng)答信號。
//
// IIC_Stop(); //發(fā)送 I2C 停止信號,結(jié)束本次通信。
//}
/**
* 函 數(shù):SetAngle
* 參 數(shù):0~180°
* 返 回 值:無
*/
void PCA9685_SetAngle(uint8_t angle)
{
uint16_t pulse_on = 0;
uint16_t pulse_off = 0;
pulse_off = (uint16_t)((angle+45)*4096/1800+0.5); //四舍五入
PCA9685_SetPWM(pulse_on,pulse_off);
}
//void setAngle(u8 num,u16 angle)
//{
// u32 off = 0;
// off = (u32)(103+angle*1.13); //360度舵機(jī),每轉(zhuǎn)動一度=1.14 0.5ms -180度起始位置:103
// PCA9685_setPWM(num,0,angle);
//}
//
//
/**
* 函 數(shù):PCA9685初始化
* 參 數(shù):無
* 返 回 值:無
*/
void PCA9685_Init(void)
{
MyI2C_Init(); //先初始化底層的I2C
/*PCA9685寄存器初始化,需要對照PCA9685手冊的寄存器描述配置,此處僅配置了部分重要的寄存器*/
PCA9685_WriteReg(MODE1, 0x21); //0使用內(nèi)部時(shí)鐘,1自增模式,0退出睡眠模式,1響應(yīng)0x70通用地址
Delay_us(500); // 延時(shí) 0.5 毫秒,等待 PCA9685 完全啟動。
}
//
//
//void PCA9685_Init(float hz,u16 angle)
//{
// u32 off = 0;
// IIC_Init();
// PCA9685_Write(PCA_Model,0x00);
// PCA9685_setFreq(hz);
// off = (u32)(103+angle*1.14); //360度舵機(jī),每轉(zhuǎn)動一度=1.14 0.5ms -180度起始位置:103
// PCA9685_setPWM(0,0,off);
// PCA9685_setPWM(1,0,off);
// PCA9685_setPWM(2,0,off);
// PCA9685_setPWM(3,0,off);
// PCA9685_setPWM(4,0,off);
// PCA9685_setPWM(5,0,off);
// PCA9685_setPWM(6,0,off);
// PCA9685_setPWM(7,0,off);
// PCA9685_setPWM(8,0,off);
// PCA9685_setPWM(9,0,off);
// PCA9685_setPWM(10,0,off);
// PCA9685_setPWM(11,0,off);
// PCA9685_setPWM(12,0,off);
// PCA9685_setPWM(13,0,off);
// PCA9685_setPWM(14,0,off);
// PCA9685_setPWM(15,0,off);
// delay_ms(100);
//
//}
/**
* 函 數(shù):Channel函數(shù),串口選擇指定通道
* 參 數(shù):無
* 返 回 值:無
*/
void Channel(void)
{
Serial_SendString("Run_16_Servo...\r\n"); //串口回傳一個(gè)字符串
OLED_Clear();
OLED_ShowString(1, 1, "Run_16_Servo."); //OLED清除指定位置,并顯示Run_Mode_1
OLED_ShowString(2, 1, "Channel;");
Serial_RxFlag = 0; //進(jìn)入循環(huán)前,結(jié)束數(shù)據(jù)包處理,等待接受命令
while (1)
{
if (Serial_RxFlag == 1)
{
if (strcmp(Serial_RxPacket, "Stop") == 0) //傳入Stop字符停止當(dāng)前模式
{
Serial_SendString("Stop_Channel...\r\n");
OLED_Clear();
OLED_ShowString(1, 1, "Stop_Channel.");
Serial_RxFlag = 0;
break;
}
else
{
uint8_t Channel = atoi(Serial_RxPacket); //把字符串轉(zhuǎn)為數(shù)字
PCA9685_SetChannel(Channel); //置通道
OLED_ShowNum(2, 9, Channel, 2);
OLED_ShowString(3, 1, "Angle;");
Serial_Printf("Channel;%d", Channel);
Serial_RxFlag = 0; //數(shù)據(jù)包接收標(biāo)志位清零等待下次接收
break;
}
}
}
}
/**
* 函 數(shù):Angle函數(shù),串口選擇指定角度
* 參 數(shù):無
* 返 回 值:無
*/
void Angle(void)
{
while (1)
{
if (Serial_RxFlag == 1)
{
if (strcmp(Serial_RxPacket, "Stop") == 0) //傳入Stop字符停止當(dāng)前模式
{
Serial_SendString("Stop_16_Servo...\r\n");
OLED_Clear();
OLED_ShowString(1, 1, "Stop_16_Servo.");
Serial_RxFlag = 0;
break;
}
else //否則認(rèn)為輸入的是角度
{
uint8_t Angle = atoi(Serial_RxPacket); //把字符串轉(zhuǎn)為數(shù)字
PCA9685_SetAngle(Angle); //置角度
OLED_ShowNum(3, 7, Angle, 3);
Serial_Printf("Angle;%d", Angle);
Serial_RxFlag = 0; //數(shù)據(jù)包接收標(biāo)志位清零等待下次接收
}
}
}
}
復(fù)制代碼
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include "LED.h"
#include "string.h"
#include "Servo.h"
#include "PCA9685.h"
int main(void)
{
/*模塊初始化*/
OLED_Init(); //OLED初始化
Serial_Init(); //串口初始化
PCA9685_Init();
PCA9685_SetFreq(50);
while (1)
{
if (Serial_RxFlag == 1) //如果接收到數(shù)據(jù)包
{
if (strcmp(Serial_RxPacket, "16_Servo") == 0)
{
Channel();
Angle();
}
Serial_RxFlag = 0; //防止串口錯(cuò)誤輸入導(dǎo)致標(biāo)志位一直不清零,不能接收下次指令
}
}
}
原理圖: 無
仿真: 無
代碼:
程序.7z
(187.3 KB, 下載次數(shù): 0)
2024-12-14 17:02 上傳
點(diǎn)擊文件名下載附件
下載積分: 黑幣 -5
歡迎光臨 (http://www.torrancerestoration.com/bbs/)
Powered by Discuz! X3.1