VScode+espidf驱动16路PWM舵机驱动板(PCA 9685)

一、vscode

1.安装ESPIDF插件

2.下载espidf库

这里我就不过多赘述了,有很多文章将这个,有的人一次成功,有的人重装系统也成功不了,我很幸运属于一次成功那种,还使用了两种不同的方法,下载了两个版本。我这次用的是v4.4.6,芯片是esp32s3.

参考:如何在VSCode搭建ESP-IDF开发ESP32_vscode创建esp32-CSDN博客

二、16路PWM舵机驱动板(PCA 9685)

1.iic设备从机地址

提到iic就不得不先说他的重要概念,分为主机从机。现在我们要使用esp32驱动PCA 9685,那么PCA 9685就是从机必然有个从机地址。

如上图右上角与下图对应:我将与A0对应的焊盘焊上那么A0=1

那么我的从机地址是1000001=0x41

2.直接上代码吧

寄存器等介绍参考:STM32--PCA9685驱动(16路舵机驱动模块)_16路舵机控制板pca9685-CSDN博客

.c文件,该代码由Arduino驱动库改编而来

#include "PCA9685_IIC_PWMServoDriver.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2c.h"
#include "esp_log.h"
#include <math.h>

#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

#define PCA9685_IIC_ADDR    0x41//PCA9685的地址
#define PCA9685_IIC_SDA     17  //SDA引脚定义,根据实际连接修改
#define PCA9685_IIC_SCL     18  //SCL引脚定义,根据实际连接修改
#define PCA9685_IIC_port    I2C_NUM_1
#define PCA9685_IIC_FREQ    100000 /* 标准模式(100 kbit/s) */ //PCA9685工作频率:40-1000HZ
// Set to true to print some debug messages, or false to disable them.
#define ENABLE_DEBUG_OUTPUT false
//ESP32 IIC INITIALIZATION FUNCTION
/*函数名宏*/
#define read8 PCA9685_IIC_PWMServoDriver_read8
#define write8 PCA9685_IIC_PWMServoDriver_write8
#define setPWM PCA9685_IIC_PWMServoDriver_setPWM
#define reset PCA9685_IIC_PWMServoDriver_reset
static const char *TAG = "PCA9685_IIC_PWMServoDriver";

#define I2C_MASTER_TX_BUF_DISABLE   0   /*!< I2C master do not need buffer */
#define I2C_MASTER_RX_BUF_DISABLE   0   /*!< I2C master do not need buffer */
void PCA9685_IIC_PWMServoDriver_Init(void) {
    
i2c_port_t i2c_master_port = PCA9685_IIC_port ;//IIC1
static i2c_config_t conf = {
    .mode = I2C_MODE_MASTER,
    .sda_io_num = PCA9685_IIC_SDA,         // select GPIO specific to your project
    .sda_pullup_en = GPIO_PULLUP_ENABLE,
    .scl_io_num = PCA9685_IIC_SCL,         // select GPIO specific to your project
    .scl_pullup_en = GPIO_PULLUP_ENABLE,
    .master.clk_speed = PCA9685_IIC_FREQ,  // select frequency specific to your project
    // .clk_flags = 0,          /*!< Optional, you can use I2C_SCLK_SRC_FLAG_* flags to choose i2c source clock here. */
};
i2c_param_config(i2c_master_port, &conf);
ESP_ERROR_CHECK(i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE , I2C_MASTER_TX_BUF_DISABLE, 0));//
printf("IIC INITIALIZATION SUCCESSFUL\n");

}

void PCA9685_IIC_PWMServoDriver_write8(uint8_t addr, uint8_t d){


    i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();//创建IIC命令链表
    i2c_master_start(i2c_cmd);//IIC开始信号发送
    i2c_master_write_byte(i2c_cmd, (PCA9685_IIC_ADDR << 1) | I2C_MASTER_WRITE, true);//IIC写入地址和写入模式
    i2c_master_write_byte(i2c_cmd, addr, true);//IIC写入寄存器地址
    i2c_master_write_byte(i2c_cmd, d, true);//IIC写入数据
    i2c_master_stop(i2c_cmd);//IIC停止信号发送
    i2c_master_cmd_begin(PCA9685_IIC_port, i2c_cmd, 1000 / portTICK_RATE_MS);//IIC命令链表开始执行,等待1000ms超时时间
    i2c_cmd_link_delete(i2c_cmd);//删除IIC命令链表
    

}

uint8_t PCA9685_IIC_PWMServoDriver_read8(uint8_t addr) {
    uint8_t read_data;

    i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();//创建IIC命令链表
    i2c_master_start(i2c_cmd);//IIC开始信号发送
    i2c_master_write_byte(i2c_cmd, (PCA9685_IIC_ADDR << 1) | I2C_MASTER_READ, true);//IIC写入地址和写入模式
    i2c_master_read_byte(i2c_cmd, &read_data, true);//IIC读取数据
    i2c_master_stop(i2c_cmd);//IIC停止信号发送
    i2c_master_cmd_begin(PCA9685_IIC_port, i2c_cmd, 1000 / portTICK_RATE_MS);//IIC命令链表开始执行,等待1000ms超时时间
    i2c_cmd_link_delete(i2c_cmd);//删除IIC命令链表
    return read_data;
}

// void PCA9685_IIC_PWMServoDriver_reset(void) {
//     PCA9685_IIC_PWMServoDriver_write8(PCA9685_MODE1, 0x0);
// }

void PCA9685_IIC_PWMServoDriver_reset(void) {
    write8(PCA9685_MODE1, 0x0);
}

void PCA9685_IIC_PWMServoDriver_setPWMFreq(float freq) {
  //Serial.print("Attempting to set freq ");
  //Serial.println(freq);
  freq *= 0.9;  // Correct for overshoot in the frequency setting (see issue #11).
  float prescaleval = 25000000;
  prescaleval /= 4096;
  prescaleval /= freq;
  prescaleval -= 1;
  if (ENABLE_DEBUG_OUTPUT) {
    ESP_LOGI(TAG,"Estimated pre-scale: %f\n",prescaleval);
  }
  uint8_t prescale = floor(prescaleval + 0.5);
  if (ENABLE_DEBUG_OUTPUT) {
    ESP_LOGI(TAG,"Final pre-scale: %d\n",prescale);
  }
  
  uint8_t oldmode = read8(PCA9685_MODE1);
  uint8_t newmode = (oldmode&0x7F) | 0x10; // sleep
  write8(PCA9685_MODE1, newmode); // go to sleep
  write8(PCA9685_PRESCALE, prescale); // set the prescaler
  write8(PCA9685_MODE1, oldmode);
  vTaskDelay(5/portTICK_PERIOD_MS); // wait for oscillator
  write8(PCA9685_MODE1, oldmode | 0xa1);  //  This sets the MODE1 register to turn on auto increment.
                                          // This is why the beginTransmission below was not working.
  //  Serial.print("Mode now 0x"); Serial.println(read8(PCA9685_MODE1), HEX);
}

void PCA9685_IIC_PWMServoDriver_setPWM(uint8_t num, uint16_t on, uint16_t off) {
  //Serial.print("Setting PWM "); Serial.print(num); Serial.print(": "); Serial.print(on); Serial.print("->"); Serial.println(off);
    i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();//创建IIC命令链表
    i2c_master_start(i2c_cmd);//IIC开始信号发送
    i2c_master_write_byte(i2c_cmd, (PCA9685_IIC_ADDR << 1) | I2C_MASTER_WRITE, true);//IIC写入地址和写入模式
    i2c_master_write_byte(i2c_cmd, LED0_ON_L+4*num, true);//IIC写入寄存器地址
    i2c_master_write_byte(i2c_cmd, on, true);//IIC写入数据
    i2c_master_write_byte(i2c_cmd, on>>8, true);//IIC写入数据
    i2c_master_write_byte(i2c_cmd, off, true);//IIC写入数据
    i2c_master_write_byte(i2c_cmd, off>>8, true);//IIC写入数据
    i2c_master_stop(i2c_cmd);//IIC停止信号发送
    i2c_master_cmd_begin(PCA9685_IIC_port, i2c_cmd, 1000 / portTICK_RATE_MS);//IIC命令链表开始执行,等待1000ms超时时间
    i2c_cmd_link_delete(i2c_cmd);//删除IIC命令链表

}


// Sets pin without having to deal with on/off tick placement and properly handles
// a zero value as completely off.  Optional invert parameter supports inverting
// the pulse for sinking to ground.  Val should be a value from 0 to 4095 inclusive.
//设置引脚,而不必处理开/关刻度位置和正确处理
// 0值表示完全关闭。可选的invert参数支持逆变
//下沉到地面的脉冲。Val应该是0到4095之间的值。

uint16_t min(uint16_t a, uint16_t b) {
  return (a < b) ? a : b;
}

void PCA9685_IIC_PWMServoDriver_setPin(uint8_t num, uint16_t val, bool invert)
{
  // Clamp value between 0 and 4095 inclusive.
  val = min(val, 4095);
  if (invert) {
    if (val == 0) {
      // Special value for signal fully on.
      setPWM(num, 4096, 0);
    }
    else if (val == 4095) {
      // Special value for signal fully off.
      setPWM(num, 0, 4096);
    }
    else {
      setPWM(num, 0, 4095-val);
    }
  }
  else {
    if (val == 4095) {
      // Special value for signal fully on.
      setPWM(num, 4096, 0);
    }
    else if (val == 0) {
      // Special value for signal fully off.
      setPWM(num, 0, 4096);
    }
    else {
      setPWM(num, 0, val);
    }
  }
}

void PCA9685_IIC_PWMServoDriver_begin(void) {
    PCA9685_IIC_PWMServoDriver_Init();
    reset();
    PCA9685_IIC_PWMServoDriver_setPWMFreq(50);
}

.h

/*
 * @Author: lonesix 111976810+lonesix@users.noreply.github.com
 * @Date: 2024-04-23 15:05:23
 * @LastEditors: lonesix 111976810+lonesix@users.noreply.github.com
 * @LastEditTime: 2024-04-23 17:17:29
 * @FilePath: \voiceHelperDemoV2\main\servo\PCA9685_IIC_PWMServoDriver.h
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#ifndef _PCA9685_IIC_PWMSERVODRIVER_H_
#define _PCA9685_IIC_PWMSERVODRIVER_H_
// 在 .h 文件中声明:
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif

// void my_cpp_func(void);
void PCA9685_IIC_PWMServoDriver_begin(void);
void PCA9685_IIC_PWMServoDriver_setPin(uint8_t num, uint16_t val, bool invert);
void PCA9685_IIC_PWMServoDriver_setPWM(uint8_t num, uint16_t on, uint16_t off);
#ifdef __cplusplus
}
#endif

// // 在 .cpp 文件中进行定义:
// extern "C" void my_cpp_func(void) {
//     // ...
// }
#endif /*_PCA9685_IIC_PWMSERVODRIVER_H_ */

3.测试例程

随便写的效果不是太好

/*
 * @Author: lonesix 111976810+lonesix@users.noreply.github.com
 * @Date: 2024-04-23 17:18:14
 * @LastEditors: lonesix 111976810+lonesix@users.noreply.github.com
 * @LastEditTime: 2024-04-24 15:19:37
 * @FilePath: \voiceHelperDemoV2\main\servo\servo_task.c
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#include "servo_task.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#define SERVOMIN  4096*(0.5/20) // this is the 'minimum' pulse length count (out of 4096)
//这是“最小”脉冲长度计数(在4096)中
#define SERVOMAX  4096*(2.5/20) // this is the 'maximum' pulse length count (out of 4096)
//这是“最大”脉冲长度计数(在4096中)

void servo_task(void *pvParameters){
    PCA9685_IIC_PWMServoDriver_begin();
    while(1){

        for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; ) {
            PCA9685_IIC_PWMServoDriver_setPWM(0, 0, pulselen);
            vTaskDelay(50 / portTICK_PERIOD_MS); // 延时1毫秒
            pulselen=pulselen+4096/180; // 增加脉冲长度,以控制舵机的角度变化
        }
        
        for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; ) {
            PCA9685_IIC_PWMServoDriver_setPWM(0, 0, pulselen);
            vTaskDelay(50 / portTICK_PERIOD_MS); // 延时1毫秒
            pulselen=pulselen-4096/180; // 减少脉冲长度,以控制舵机的角度变化
        }
    }
}

void Action_management(){
    xTaskCreate(servo_task, "servo_task", 4096, NULL, 5, NULL);    // 创建servo_task任务,优先级为5,堆栈大小为2048字节。
}

4.直接角度驱动

早期版本凑合用

该函数放置于二、2的.c文件中

void PCA9685setAngle(uint8_t num,uint8_t angle)
{
	uint32_t off = 0;
  if (angle<=90 && angle>=0)
  {
    /* code */
    // printf("angle:%d\n !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",angle);
    // off = (uint32_t)(102.4+angle*2.28); 
    off=(uint32_t)(4095*((angle/180.0)*2.0+0.5)/20.0);
    setPWM(num,0,off);  
  }else if (angle>90 && angle<=180){
    
    // off = (uint32_t)(512-(180-angle)*2.28); 
    off=(uint32_t)(4095*((angle/180.0)*2.0+0.5)/20.0);
    setPWM(num,0,off);
  }
  // printf("off:%d\n !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",off);
	
	
}

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值