当前位置:   article > 正文

OpenHarmony 项目实战:基于全志 XR806 实现的简易四足行走机器狗_全志openharmony

全志openharmony

小米在 2021 年发布的自研四足仿生机器人 ——CyberDog,使得 “仿生机器人” 这个话题 再次站在风口之上 ,伴随芯片、舵机、减速器等核心部件的发展,也期待更多更 “生动” 的 “仿生机器人” 出现 ,全志 XR806 是一款支持 WiFi 和 BLE 的芯片可满足智能机器人等领域的无线连接需求,今天就先带大家看一款基于全志 XR806 开源鸿蒙开发板做的四足行走机器狗。

一、机器狗外观图

二、PCA9685 开发流程

狗子身上有 12 个舵机,一条腿接 3 个舵机,连接到一个 pca9685 舵机驱动上,舵机驱动通过 I2C 与 XR806 通信,XR806 有两个 I2C 接口,我主要是用第 1 个 (B14, B15),通过控制 PCA9685 上的寄存器来简介控制舵机。

pca9685 参数:

  • I2C 接口,支持高达 16 路 PWM 输出,每路 12 位分辨率 (4096 级);
  • 内置 25MHz 晶振,可不连接外部晶振,也可以连接外部晶振,最大 50MHz;
  • 支持 2.3V-5.5V 电压,最大耐压值 5.5V, 逻辑电平 3.3V;
  • 具有上电复位,以及软件复位等功能。

引脚图示:
 

1. pca 初始化

首先要进行 i2c 初始化:

i2c_init(i2c_id);

导入相关库:

  1. #include "driver/chip/hal_i2c.h"
  2. #define i2c_id 1 # 使用第1个i2c

i2c 初始化函数:

  1. void i2c_init(unsigned int id){
  2. I2C_InitParam initParam;
  3. initParam.addrMode = I2C_ADDR_MODE_7BIT;
  4. initParam.clockFreq = 40000;
  5. if (HAL_I2C_Init(id, &initParam) != HAL_OK) {
  6. printf("i2c init fail!\n");
  7. while(1);
  8. } else {
  9. printf("i2c init success!\n");
  10. }
  11. }

主要是调用 HAL_I2C_Init 函数对第 1 个 i2c 接口进行初始化。

初始化 pca9685:

pca9685_init(60);

pca9685_init 函数:

  1. void pca9685_init(float hz) {
  2. pca_write(pca_mode1, 0x0);
  3. pca_setfreq(hz);
  4. OS_MSleep(500);
  5. }

2. pca 操作

1. pca9685 写寄存器

  1. void pca_write(uint8_t reg_addr, uint8_t data) {
  2. uint8_t buf[1];
  3. buf[0] = data;
  4. HAL_I2C_Master_Transmit_Mem_IT(i2c_id, pca_adrr, reg_addr, I2C_MEMADDR_SIZE_8BIT, buf, 1);
  5. }

2. pca9685 读寄存器

  1. uint8_t pca_read(uint8_t reg_addr) {
  2. uint8_t buf[1];
  3. HAL_I2C_Master_Receive_Mem_IT(i2c_id, pca_adrr, reg_addr, I2C_MEMADDR_SIZE_8BIT, buf, 1);
  4. return buf[0];
  5. }

3. pca9685 设置频率

  1. void pca_setfreq(float freq) {
  2. uint8_t prescale,old_mode,new_mode;
  3. double prescaleval;
  4. freq *= 0.92;
  5. prescaleval = 25000000;
  6. prescaleval /= 4096;
  7. prescaleval /= freq;
  8. prescaleval -= 1;
  9. prescale =(uint8_t)(prescaleval + 0.5f);
  10. old_mode = pca_read(pca_mode1);
  11. new_mode = (old_mode & 0x7F) | 0x10;
  12. pca_write(pca_mode1, new_mode);
  13. pca_write(pca_pre, prescale);
  14. pca_write(pca_mode1, old_mode);
  15. OS_MSleep(2);
  16. pca_write(pca_mode1, old_mode | 0xa1);
  17. }

这里的代码不像读函数和写函数一样好理解,主要是一般情况下,在用 pca9685 内置晶振,为 25MHZ,通过配置 PRE_SCALE 寄存器进行配置。如果在舵机控制中,采用内置晶振,取 osc_clock=25000000,update_rate=50 (舵机控制频率 50Hz)。

而且在写 PRESCALE 寄存器的时候,要先设置为 Sleep 模式,也就是将 mode1 寄存器的 SLEEP 标志位设置为 1,具体可参考我上面写的代码。

3. pca 设置 pwm

  1. void pca_setpwm(uint8_t num, uint32_t on, uint32_t off) {
  2. pca_write(LED0_ON_L+4*num,on);
  3. pca_write(LED0_ON_H+4*num,on>>8);
  4. pca_write(LED0_OFF_L+4*num,off);
  5. pca_write(LED0_OFF_H+4*num,off>>8);
  6. }

pwm 通道寄存器如下图:


由图可知,对于每一个通道,有 4 个寄存器,在设置 PWM 占空比的时候,首先配置舵机的示例如下图所示 (ON < OFF 的情况):

狗子运行图:
 

核心代码:

  1. #include <stdio.h>
  2. #include "ohos_init.h"
  3. #include "kernel/os/os.h"
  4. // #include "iot_i2c.h"
  5. #include "driver/chip/hal_i2c.h"
  6. #include "iot_errno.h"
  7. #include "math.h"
  8. #define i2c_id 1
  9. #define pca_adrr 0x40 // pca9685设备地址
  10. #define pca_mode1 0x0
  11. #define pca_pre 0xFE
  12. #define LED0_ON_L 0x6
  13. #define LED0_ON_H 0x7
  14. #define LED0_OFF_L 0x8
  15. #define LED0_OFF_H 0x9
  16. static OS_Thread_t g_main_thread;
  17. void pca_write(uint8_t reg_addr, uint8_t data) {
  18. uint8_t buf[1];
  19. buf[0] = data;
  20. HAL_I2C_Master_Transmit_Mem_IT(i2c_id, pca_adrr, reg_addr, I2C_MEMADDR_SIZE_8BIT, buf, 1);
  21. }
  22. uint8_t pca_read(uint8_t reg_addr) {
  23. uint8_t buf[1];
  24. HAL_I2C_Master_Receive_Mem_IT(i2c_id, pca_adrr, reg_addr, I2C_MEMADDR_SIZE_8BIT, buf, 1);
  25. return buf[0];
  26. }
  27. void pca_setfreq(float freq) {
  28. uint8_t prescale,old_mode,new_mode;
  29. double prescaleval;
  30. freq *= 0.92;
  31. prescaleval = 25000000;
  32. prescaleval /= 4096;
  33. prescaleval /= freq;
  34. prescaleval -= 1;
  35. prescale =(uint8_t)(prescaleval + 0.5f);
  36. old_mode = pca_read(pca_mode1);
  37. new_mode = (old_mode & 0x7F) | 0x10;
  38. pca_write(pca_mode1, new_mode);
  39. pca_write(pca_pre, prescale);
  40. pca_write(pca_mode1, old_mode);
  41. OS_MSleep(2);
  42. pca_write(pca_mode1, old_mode | 0xa1);
  43. }
  44. void pca_setpwm(uint8_t num, uint32_t on, uint32_t off) {
  45. pca_write(LED0_ON_L+4*num,on);
  46. pca_write(LED0_ON_H+4*num,on>>8);
  47. pca_write(LED0_OFF_L+4*num,off);
  48. pca_write(LED0_OFF_H+4*num,off>>8);
  49. }
  50. void pca9685_init(float hz) {
  51. pca_write(pca_mode1, 0x0);
  52. pca_setfreq(hz);
  53. OS_MSleep(500);
  54. }
  55. void pca_rotate(uint8_t num,uint8_t angle) {
  56. uint32_t off=0;
  57. off=floor(angle * 2 + angle / 5 + 158);
  58. pca_setpwm(num, 0, off);
  59. }
  60. void i2c_init(unsigned int id){
  61. I2C_InitParam initParam;
  62. initParam.addrMode = I2C_ADDR_MODE_7BIT;
  63. initParam.clockFreq = 40000;
  64. if (HAL_I2C_Init(id, &initParam) != HAL_OK) {
  65. printf("i2c init fail!\n");
  66. while(1);
  67. } else {
  68. printf("i2c init success!\n");
  69. }
  70. }
  71. static void MainThread(void *arg) {
  72. uint8_t i = 0;
  73. i2c_init(i2c_id);
  74. pca9685_init(60);
  75. printf("i2c and pca9685 init done.\n");
  76. while(1){
  77. pca_rotate(0, 0);
  78. OS_MSleep(1000);
  79. pca_rotate(0, 180);
  80. OS_MSleep(1000);
  81. }
  82. }
  83. void PCAMain(void) {
  84. printf("PCA9685 Motor Start.\n");
  85. if (OS_ThreadCreate(&g_main_thread, "MainThread", MainThread, NULL,
  86. OS_THREAD_PRIO_APP, 10 * 1024) != OS_OK) {
  87. printf("[ERR] Create MainThread Failed\n");
  88. }
  89. }
  90. SYS_RUN(PCAMain);

狗子的相关步态算法还在调试,使用的舵机是 MG90S,走得还不是很流畅,狗腿子会抖,后面需要继续调试,而且供电也是个大问题,不过已经焊了个供电模块稍微解决了,后面还要给狗子加个壳。

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/288256
推荐阅读
相关标签
  

闽ICP备14008679号