找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 975|回复: 0
打印 上一主题 下一主题
收起左侧

stm32f103c8t6单片机和PCF9685控制16路舵机通过串口调试程序

[复制链接]
跳转到指定楼层
楼主
ID:1139558 发表于 2024-12-12 18:13 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
  1. #include "stm32f10x.h"                  // Device header
  2. #include "MyI2C.h"
  3. #include "PCA9685_Reg.h"
  4. #include "math.h"
  5. #include "Delay.h"
  6. #include "Serial.h"
  7. #include "OLED.h"
  8. #include "string.h"
  9. #include "stdlib.h"

  10. uint8_t Channel_Flag = 0;

  11. /**
  12.   * 函    数:PCA9685写寄存器
  13.   * 参    数:RegAddress 寄存器地址,范围:参考PCA9685手册的寄存器描述
  14.   * 参    数:Data 要写入寄存器的数据,范围:0x00~0xFF
  15.   * 返 回 值:无
  16.   */
  17. void PCA9685_WriteReg(uint8_t RegAddress, uint8_t Data)
  18. {
  19.         MyI2C_Start();                                                //I2C起始
  20.         MyI2C_SendByte(ADDRESS);                        //发送从机地址,读写位为0,表示即将写入
  21.         MyI2C_ReceiveAck();                                        //接收应答
  22.         MyI2C_SendByte(RegAddress);                        //发送寄存器地址
  23.         MyI2C_ReceiveAck();                                        //接收应答
  24.         MyI2C_SendByte(Data);                                //发送要写入寄存器的数据
  25.         MyI2C_ReceiveAck();                                        //接收应答
  26.         MyI2C_Stop();                                                //I2C终止
  27. }

  28. //void PCA9685_Write(u8 addr,u8 data)    //   addr 表示要写入数据的寄存器地址,data 表示要写入的数据
  29. //{
  30. //        IIC_Start();                         //   发送 I2C 起始信号,开始 I2C 通信。
  31. //       
  32. //        IIC_Send_Byte(PCA_Addr);             //   发送 PCA_Addr = 0x80 ,告诉设备我们要写入数据
  33. //        IIC_NAck();                          //   发送不应答信号,表示主控器不需要从设备接收更多数据。
  34. //       
  35. //        IIC_Send_Byte(addr);                 //   发送要写入数据的寄存器地址。
  36. //        IIC_NAck();                          //   发送不应答信号。
  37. //       
  38. //        IIC_Send_Byte(data);                 //   发送要写入的数据。
  39. //        IIC_NAck();                          //   发送不应答信号。
  40. //       
  41. //        IIC_Stop();                          //   发送 I2C 停止信号,结束本次通信。
  42. //       
  43. //}

  44. /**
  45.   * 函    数:PCA9685读寄存器
  46.   * 参    数:RegAddress 寄存器地址,范围:参考PCA9685手册的寄存器描述
  47.   * 返 回 值:读取寄存器的数据,范围:0x00~0xFF
  48.   */
  49. uint8_t PCA9685_ReadReg(uint8_t RegAddress)
  50. {
  51.         uint8_t Data;
  52.        
  53.         MyI2C_Start();                                                //I2C起始
  54.         MyI2C_SendByte(ADDRESS);                        //发送从机地址,读写位为0,表示即将写入
  55.         MyI2C_ReceiveAck();                                        //接收应答
  56.         MyI2C_SendByte(RegAddress);                        //发送寄存器地址
  57.         MyI2C_ReceiveAck();                                        //接收应答
  58.        
  59.         MyI2C_Start();                                                //I2C重复起始
  60.         MyI2C_SendByte(ADDRESS | 0x01);                //发送从机地址,读写位为1,表示即将读取
  61.         MyI2C_ReceiveAck();                                        //接收应答
  62.         Data = MyI2C_ReceiveByte();                        //接收指定寄存器的数据
  63.         MyI2C_SendAck(1);                                        //发送应答,给从机非应答,终止从机的数据输出
  64.         MyI2C_Stop();                                                //I2C终止
  65.        
  66.         return Data;
  67. }

  68. //u8 PCA9685_Read(u8 addr)                //   addr 表示要读取数据的寄存器地址
  69. //{
  70. //        u8 data;                           //   声明一个无符号 8 位整数变量 data,用于存储读取到的数据。
  71. //        IIC_Start();                       //   发送 I2C 起始信号,开始 I2C 通信。
  72. //        IIC_Send_Byte(PCA_Addr);           //   发送 PCA_Addr = 0x80 ,告诉设备我们要写入数据
  73. //        IIC_NAck();                        //   发送不应答信号,表示主控器不需要从设备接收更多数据。
  74. //        IIC_Send_Byte(addr);                  //   发送要读取数据的寄存器地址。
  75. //        IIC_NAck();                           //   发送不应答信号。
  76. //        IIC_Stop();                           //   发送 I2C 停止信号,结束本次通信。
  77. //        delay_us(10);                         //   延时 10 微秒,等待芯片准备好数据。
  78. //        IIC_Start();                          //   发送 I2C 起始信号,开始另一次 I2C 通信。
  79. //        IIC_Send_Byte(PCA_Addr|0x01);         //   发送 PCA9685 的地址,并设置最低位为 1,
  80. //        IIC_NAck();                           //   发送不应答信号。
  81. //        data = IIC_Read_Byte(0);              //   通过 I2C 从 PCA9685 读取一个字节的数据,并存储到变量 data 中。
  82. //        IIC_Stop();                           //   发送 I2C 停止信号,结束本次通信。
  83. //        return data;                          //   返回读取到的数据。
  84. //}

  85. /**
  86.   * 函    数:PCA9685设置输出频率
  87.   * 参    数:float 频率
  88.   * 返 回 值:返回预分频的值
  89. */
  90. void PCA9685_SetFreq(float freq)
  91. {
  92.         uint8_t prescale,oldmode,newmode;               //定义了三个无符号 8 位整型变量 用于存储预分频器值、旧的模式寄存器值和新的模式寄存器值
  93.         float prescaleval;                        //定义了一个双精度浮点型变量 prescaleval,用于计算预分频器的值。
  94.        
  95. //        freq *= 0.98;                             //将传入的频率值乘以 0.98,这是为了微调频率值以适应 PCA9685 的实际需求       
  96.         prescaleval = 25000000/4096/freq - 1;
  97.         prescale = floor(prescaleval+0.5);       //将计算得到的预分频器值四舍五入取整,并将其赋值给 prescale 变量。
  98.        
  99.         oldmode = PCA9685_ReadReg(MODE1);        //通过调用 PCA9685_Read 函数读取当前 PCA9685 寄存器中的模式值,并将其存储在 oldmode 变量中。
  100.        
  101.         newmode = (oldmode&0x7F)|0x10;            //根据旧的模式值计算出新的模式值,将最高位清零(不复位)并将第5位设为1,表示将 PCA9685 设置为睡眠模式。
  102.         PCA9685_WriteReg(MODE1,newmode);         //将新的模式值写入 PCA9685 的模式寄存器。
  103.         PCA9685_WriteReg(PRE_SCALE,prescale);          //将计算得到的预分频器值写入 PCA9685 的预分频器寄存器。
  104.         PCA9685_WriteReg(MODE1,oldmode);         //恢复旧的模式值。
  105.         Delay_us(500);                              // 延时 0.5 毫秒,等待 PCA9685 完全启动。
  106.         PCA9685_WriteReg(MODE1,PCA9685_ReadReg(MODE1)|0x80);       //将模式值的第8( Restart enabled.).更新频率后复位使用
  107.        
  108. }

  109. //void PCA9685_setFreq(float freq)
  110. //{
  111. //        u8 prescale,oldmode,newmode;               //定义了三个无符号 8 位整型变量 用于存储预分频器值、旧的模式寄存器值和新的模式寄存器值
  112. //          
  113. //        double prescaleval;                        //定义了一个双精度浮点型变量 prescaleval,用于计算预分频器的值。
  114. //       
  115. //        freq *= 0.98;                             //将传入的频率值乘以 0.98,这是为了微调频率值以适应 PCA9685 的实际需求
  116. //        prescaleval = 25000000;                   //这是 PCA9685 内部振荡器的频率
  117. //        prescaleval /= 4096;                      //每个周期从0计数到4095,除以 4096,得到每个计数器周期的时间,
  118. //        prescaleval /= freq;                      //除以所需的频率值,得到预分频器的值。
  119. //        prescaleval -= 1;                         //减去 1,得到最终的预分频器值
  120. //        prescale = floor(prescaleval+0.5f);       //将计算得到的预分频器值四舍五入取整,并将其赋值给 prescale 变量。
  121. //        oldmode = PCA9685_Read(PCA_Model);        //通过调用 PCA9685_Read 函数读取当前 PCA9685 寄存器中的模式值,并将其存储在 oldmode 变量中。
  122. //       
  123. //        newmode = (oldmode&0x7F)|0x10;            //根据旧的模式值计算出新的模式值,将最高位清零(bit 7)并将第 5 位设为1(bit 4),表示将 PCA9685 设置为睡眠模式。
  124. //        PCA9685_Write(PCA_Model,newmode);         //将新的模式值写入 PCA9685 的模式寄存器。
  125. //        PCA9685_Write(PCA_Pre,prescale);          //将计算得到的预分频器值写入 PCA9685 的预分频器寄存器。
  126. //        PCA9685_Write(PCA_Model,oldmode);         //恢复旧的模式值。
  127. //        delay_ms(5);                              // 延时 5 毫秒,等待 PCA9685 完全启动。
  128. //        PCA9685_Write(PCA_Model,oldmode|0xa1);    //将模式值的最高位和第 1 位设为1,表示将 PCA9685 设置为正常工作模式。
  129. //       
  130. //}

  131. /**
  132.   * 函    数:PCA9685_SetChannel
  133.   * 参    数:0~15
  134.   * 返 回 值:无
  135. */
  136. void PCA9685_SetChannel(uint8_t Channel)
  137. {
  138.         Channel_Flag = Channel;
  139. }

  140. /**
  141.   * 函    数:PCA9685_setPWM
  142.   * 参    数:channel;舵机编号。on 表示置1的位置,off 表示置0位置(12位有效,值要小于4095)
  143.   * 返 回 值:无
  144. */
  145. void PCA9685_SetPWM(uint16_t pulse_on,uint16_t pulse_off)  
  146. {
  147.         // 将脉冲宽度分解为ON和OFF时间
  148.     uint8_t on_l = (pulse_on & 0xFF);                                //低8位
  149.     uint8_t on_h = (pulse_on >> 8) & 0xFF;                        //高8位
  150.     uint8_t off_l = (pulse_off & 0xFF);
  151.     uint8_t off_h = (pulse_off >> 8) & 0xFF;

  152.     // 写入ON和OFF时间到对应的寄存器
  153.     PCA9685_WriteReg(LED0_ON_L + 4 * Channel_Flag, on_l);
  154.     PCA9685_WriteReg(LED0_ON_H + 4 * Channel_Flag, on_h);
  155.     PCA9685_WriteReg(LED0_OFF_L + 4 * Channel_Flag, off_l);
  156.     PCA9685_WriteReg(LED0_OFF_H + 4 * Channel_Flag, off_h);
  157. }
  158. //void PCA9685_setPWM(u8 num,u32 on,u32 off)   //num 表示 PWM 通道号,on 表示 PWM 的起始位置,off 表示 PWM 的结束位置(即从高电平切换到低电平的时刻)
  159. //{
  160. //        IIC_Start();                              //发送 I2C 起始信号,开始 I2C 通信。
  161. //       
  162. //        IIC_Send_Byte(PCA_Addr);                  //发送 PCA9685 的地址,告诉设备我们要和 PCA9685 进行通信。
  163. //        IIC_Wait_Ack();                           //等待应答信号,确保设备准备好接收数据。
  164. //
  165. //        IIC_Send_Byte(LED0_ON_L+4*num);           //发送 LED 寄存器的地址,根据 PWM 通道号计算出相应的寄存器地址。
  166. //        IIC_Wait_Ack();                           //
  167. //       
  168. //        IIC_Send_Byte(on&0xFF);                   //发送 PWM 的起始位置低 8 位。
  169. //        IIC_Wait_Ack();                           //等待应答信号。
  170. //         
  171. //        IIC_Send_Byte(on>>8);                     //发送 PWM 的起始位置高 8 位。
  172. //        IIC_Wait_Ack();                           //等待应答信号。
  173. //       
  174. //        IIC_Send_Byte(off&0xFF);                  //发送 PWM 的结束位置低 8 位。
  175. //        IIC_Wait_Ack();                           //等待应答信号。
  176. //       
  177. //        IIC_Send_Byte(off>>8);                    //发送 PWM 的结束位置高 8 位。
  178. //        IIC_Wait_Ack();                           //等待应答信号。
  179. //       
  180. //        IIC_Stop();                              //发送 I2C 停止信号,结束本次通信。
  181. //}
  182. /**
  183.   * 函    数:SetAngle
  184.   * 参    数:0~180°
  185.   * 返 回 值:无
  186. */
  187. void PCA9685_SetAngle(uint8_t angle)
  188. {
  189.         uint16_t pulse_on = 0;
  190.         uint16_t pulse_off = 0;
  191.         pulse_off = (uint16_t)((angle+45)*4096/1800+0.5);  //四舍五入
  192.         PCA9685_SetPWM(pulse_on,pulse_off);
  193. }

  194. //void setAngle(u8 num,u16 angle)
  195. //{
  196. //        u32 off = 0;               
  197. //        off = (u32)(103+angle*1.13);  //360度舵机,每转动一度=1.14   0.5ms -180度起始位置:103
  198. //        PCA9685_setPWM(num,0,angle);
  199. //}
  200. //
  201. //

  202. /**
  203.   * 函    数:PCA9685初始化
  204.   * 参    数:无
  205.   * 返 回 值:无
  206.   */
  207. void PCA9685_Init(void)
  208. {
  209.         MyI2C_Init();                                                                        //先初始化底层的I2C
  210.        
  211.         /*PCA9685寄存器初始化,需要对照PCA9685手册的寄存器描述配置,此处仅配置了部分重要的寄存器*/
  212.         PCA9685_WriteReg(MODE1, 0x21);                                        //0使用内部时钟,1自增模式,0退出睡眠模式,1响应0x70通用地址
  213.         Delay_us(500);                                      // 延时 0.5 毫秒,等待 PCA9685 完全启动。
  214.        
  215. }
  216. //  
  217. //
  218. //void PCA9685_Init(float hz,u16 angle)
  219. //{
  220. //        u32 off = 0;
  221. //        IIC_Init();
  222. //        PCA9685_Write(PCA_Model,0x00);
  223. //        PCA9685_setFreq(hz);
  224. //    off = (u32)(103+angle*1.14);  //360度舵机,每转动一度=1.14    0.5ms -180度起始位置:103
  225. //        PCA9685_setPWM(0,0,off);
  226. //        PCA9685_setPWM(1,0,off);
  227. //        PCA9685_setPWM(2,0,off);
  228. //        PCA9685_setPWM(3,0,off);
  229. //        PCA9685_setPWM(4,0,off);
  230. //        PCA9685_setPWM(5,0,off);
  231. //        PCA9685_setPWM(6,0,off);
  232. //        PCA9685_setPWM(7,0,off);
  233. //        PCA9685_setPWM(8,0,off);
  234. //        PCA9685_setPWM(9,0,off);
  235. //        PCA9685_setPWM(10,0,off);
  236. //        PCA9685_setPWM(11,0,off);
  237. //        PCA9685_setPWM(12,0,off);
  238. //        PCA9685_setPWM(13,0,off);
  239. //        PCA9685_setPWM(14,0,off);
  240. //        PCA9685_setPWM(15,0,off);
  241. //        delay_ms(100);
  242. //       
  243. //}
  244. /**
  245.   * 函    数:Channel函数,串口选择指定通道
  246.   * 参    数:无
  247.   * 返 回 值:无
  248.   */
  249. void Channel(void)
  250. {
  251.         Serial_SendString("Run_16_Servo...\r\n");                                //串口回传一个字符串
  252.         OLED_Clear();
  253.         OLED_ShowString(1, 1, "Run_16_Servo.");                                //OLED清除指定位置,并显示Run_Mode_1
  254.         OLED_ShowString(2, 1, "Channel;");
  255.         Serial_RxFlag = 0;                                //进入循环前,结束数据包处理,等待接受命令
  256.        
  257.         while (1)
  258.         {
  259.                 if (Serial_RxFlag == 1)                       
  260.                 {
  261.                         if (strcmp(Serial_RxPacket, "Stop") == 0)                         //传入Stop字符停止当前模式
  262.                         {
  263.                                 Serial_SendString("Stop_Channel...\r\n");                               
  264.                                 OLED_Clear();
  265.                                 OLED_ShowString(1, 1, "Stop_Channel.");
  266.                                 Serial_RxFlag = 0;
  267.                                 break;
  268.                         }
  269.                         else                                                                                        
  270.                         {
  271.                                 uint8_t Channel  = atoi(Serial_RxPacket);                                //把字符串转为数字
  272.                                 PCA9685_SetChannel(Channel);                                                                //置通道
  273.                                 OLED_ShowNum(2, 9, Channel, 2);
  274.                                 OLED_ShowString(3, 1, "Angle;");
  275.                                 Serial_Printf("Channel;%d", Channel);
  276.                                 Serial_RxFlag = 0;                                                                //数据包接收标志位清零等待下次接收
  277.                                 break;
  278.                         }
  279.                 }
  280.         }
  281. }
  282. /**
  283.   * 函    数:Angle函数,串口选择指定角度
  284.   * 参    数:无
  285.   * 返 回 值:无
  286.   */
  287. void Angle(void)
  288. {
  289.         while (1)
  290.         {
  291.                 if (Serial_RxFlag == 1)                       
  292.                 {
  293.                         if (strcmp(Serial_RxPacket, "Stop") == 0)                         //传入Stop字符停止当前模式
  294.                         {
  295.                                 Serial_SendString("Stop_16_Servo...\r\n");                               
  296.                                 OLED_Clear();
  297.                                 OLED_ShowString(1, 1, "Stop_16_Servo.");
  298.                                 Serial_RxFlag = 0;
  299.                                 break;
  300.                         }
  301.                         else                                                                                                 //否则认为输入的是角度
  302.                         {
  303.                                 uint8_t Angle  = atoi(Serial_RxPacket);                                //把字符串转为数字
  304.                                 PCA9685_SetAngle(Angle);                                                                //置角度
  305.                                 OLED_ShowNum(3, 7, Angle, 3);
  306.                                 Serial_Printf("Angle;%d", Angle);
  307.                                 Serial_RxFlag = 0;                                                                //数据包接收标志位清零等待下次接收
  308.                         }
  309.                 }
  310.         }
  311. }
复制代码



#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)

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表