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