一、硬件说明
1. 610空心杯电机模块
1.1 tc214b驱动芯片
1.1.1 tc214b 用途
1.1.2 tc214b 真值表
1.1.3 tc214b 输入输出波形
1.1.4 tc214b 推荐工作条件
2 九联 Unionpi Tiger
2.1 从九联给出的文档来看,内存、存储、以及ubuntu系统是可以满足安装ROS1中noetic版本的。
2.2 根据tc214b驱动芯片来看通过两个PWM可以做到正、反转、刹车、停、以及调速功能,根据九联手册中的给出了如下信息:
3 硬件连接
将空心杯电机G、V、INA、INB与PWM接口GND、3V3、PWM_1、PWM_2相连。
软件说明
1. 参考九联gitee的操作文档,更改完后如下:
#ifndef UM_PWM_H
#define UM_PWM_H
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#define PWM_ERR (-1)
#define PWM_WRONOG_CHANNEL (-2)
#define PWM_FILE_NOT_EXIST (-3)
#define PWM_NOT_ENABLED 0
#define PWM_IS_ENABLED 1
#define PWM_POLARITY_NORMAL 0
#define PWM_POLARITY_INVERSED 1
#define PWM1 1
#define PWM2 2
#define PWM1_PEX "/sys/class/pwm/pwmchip0"
#define PWM2_PEX "/sys/class/pwm/pwmchip2"
#undef LOG_DOMAIN
#undef LOG_TAG
#define LOG_DOMAIN 0
#define LOG_TAG "Pwm_Test"
namespace tc214b{
class UmPwm
{
public:
UmPwm(){}
~UmPwm(){}
int init_pwm(int pwmChannel)
{
char pwm_file_name[128] = {0};
(void)memset(pwm_file_name, 0, sizeof(pwm_file_name));
if (PWM1 == pwmChannel) {
(void)sprintf(pwm_file_name, "%s/export", PWM1_PEX);
} else if (PWM2 == pwmChannel) {
(void)sprintf(pwm_file_name, "%s/export", PWM2_PEX);
} else {
return PWM_WRONOG_CHANNEL;
}
if (access(pwm_file_name, F_OK) != 0) {
return PWM_FILE_NOT_EXIST;
}
FILE *fp = NULL;
fp = fopen(pwm_file_name, "w");
if (!fp) {
return PWM_FILE_NOT_EXIST;
}
(void)fprintf(fp, "%d", 0);
(void)fclose(fp);
fp = NULL;
return 0;
}
int set_pwm_period(int pwmChannel, int period)
{
char pwm_file_name[128] = {0};
(void)memset(pwm_file_name, 0, sizeof(pwm_file_name));
if (PWM1 == pwmChannel) {
(void)sprintf(pwm_file_name, "%s/pwm0/period", PWM1_PEX);
} else if (PWM2 == pwmChannel) {
(void)sprintf(pwm_file_name, "%s/pwm0/period", PWM2_PEX);
} else {
return PWM_WRONOG_CHANNEL;
}
if (access(pwm_file_name, F_OK) != 0) {
return PWM_FILE_NOT_EXIST;
}
FILE *fp = NULL;
fp = fopen(pwm_file_name, "r+");
if (!fp) {
return PWM_FILE_NOT_EXIST;
}
if (period) {
fprintf(fp, "%d", period);
}
(void)fclose(fp);
fp = NULL;
return 0;
}
int set_pwm_dutyCycle(int pwmChannel, int dutyCycle)
{
char pwm_file_name[128] = {0};
(void)memset(pwm_file_name, 0, sizeof(pwm_file_name));
if (PWM1 == pwmChannel) {
(void)sprintf(pwm_file_name, "%s/pwm0/duty_cycle", PWM1_PEX);
} else if (PWM2 == pwmChannel) {
(void)sprintf(pwm_file_name, "%s/pwm0/duty_cycle", PWM2_PEX);
} else {
return PWM_WRONOG_CHANNEL;
}
if (access(pwm_file_name, F_OK) != 0) {
return PWM_FILE_NOT_EXIST;
}
FILE *fp = NULL;
fp = fopen(pwm_file_name, "r+");
if (!fp) {
return PWM_FILE_NOT_EXIST;
}
fprintf(fp, "%d", dutyCycle);
(void)fclose(fp);
fp = NULL;
return 0;
}
int set_pwm_polarity(int pwmChannel, int polarity)
{
char pwm_file_name[128] = {0};
(void)memset(pwm_file_name, 0, sizeof(pwm_file_name));
if (PWM1 == pwmChannel) {
(void)sprintf(pwm_file_name, "%s/pwm0/polarity", PWM1_PEX);
} else if (PWM2 == pwmChannel) {
(void)sprintf(pwm_file_name, "%s/pwm0/polarity", PWM2_PEX);
} else {
return PWM_WRONOG_CHANNEL;
}
if (access(pwm_file_name, F_OK) != 0) {
return PWM_FILE_NOT_EXIST;
}
FILE *fp = NULL;
fp = fopen(pwm_file_name, "rw+");
if (!fp) {
return PWM_FILE_NOT_EXIST;
}
if (polarity == PWM_POLARITY_NORMAL) {
fprintf(fp, "%s", "normal");
} else if (polarity == PWM_POLARITY_INVERSED) {
fprintf(fp, "%s", "inversed");
}
(void)fclose(fp);
fp = NULL;
return 0;
}
int set_pwm_enable(int pwmChannel, int isEnable)
{
char buffer[256] = {0};
char pwm_file_name[128] = {0};
(void)memset(pwm_file_name, 0, sizeof(pwm_file_name));
if (PWM1 == pwmChannel) {
(void)sprintf(pwm_file_name, "%s/pwm0/enable", PWM1_PEX);
} else if (PWM2 == pwmChannel) {
(void)sprintf(pwm_file_name, "%s/pwm0/enable", PWM2_PEX);
} else {
return PWM_WRONOG_CHANNEL;
}
if (access(pwm_file_name, F_OK) != 0) {
return PWM_FILE_NOT_EXIST;
}
(void)snprintf(buffer, sizeof(buffer), "echo %d > %s", isEnable, pwm_file_name);
system(buffer);
return 0;
}
int get_pwm_period(int pwmChannel)
{
int ret = 0;
char pwm_file_name[128] = {0};
(void)memset(pwm_file_name, 0, sizeof(pwm_file_name));
if (PWM1 == pwmChannel) {
(void)sprintf(pwm_file_name, "%s/pwm0/period", PWM1_PEX);
} else if (PWM2 == pwmChannel) {
(void)sprintf(pwm_file_name, "%s/pwm0/period", PWM2_PEX);
} else {
return PWM_WRONOG_CHANNEL;
}
if (access(pwm_file_name, F_OK) != 0) {
return PWM_FILE_NOT_EXIST;
}
FILE *fp = NULL;
char buffer[32] = {0};
(void)memset(buffer, 0, sizeof(buffer));
fp = fopen(pwm_file_name, "r");
if (!fp) {
return PWM_FILE_NOT_EXIST;
}
(void)fread(buffer, sizeof(buffer), 1, fp);
(void)fclose(fp);
fp = NULL;
ret = atoi(buffer);
return ret;
}
int get_pwm_dutyCycle(int pwmChannel)
{
int ret = 0;
char pwm_file_name[128] = {0};
(void)memset(pwm_file_name, 0, sizeof(pwm_file_name));
if (PWM1 == pwmChannel) {
(void)sprintf(pwm_file_name, "%s/pwm0/duty_cycle", PWM1_PEX);
} else if (PWM2 == pwmChannel) {
(void)sprintf(pwm_file_name, "%s/pwm0/duty_cycle", PWM2_PEX);
} else {
return PWM_WRONOG_CHANNEL;
}
if (access(pwm_file_name, F_OK) != 0) {
return PWM_FILE_NOT_EXIST;
}
FILE *fp = NULL;
char buffer[32] = {0};
(void)memset(buffer, 0, sizeof(buffer));
fp = fopen(pwm_file_name, "r");
if (!fp) {
return PWM_FILE_NOT_EXIST;
}
(void)fread(buffer, sizeof(buffer), 1, fp);
(void)fclose(fp);
fp = NULL;
ret = atoi(buffer);
return ret;
}
int get_pwm_polarity(int pwmChannel)
{
int ret = 0;
char pwm_file_name[128] = {0};
(void)memset(pwm_file_name, 0, sizeof(pwm_file_name));
if (PWM1 == pwmChannel) {
(void)sprintf(pwm_file_name, "%s/pwm0/polarity", PWM1_PEX);
} else if (PWM2 == pwmChannel) {
(void)sprintf(pwm_file_name, "%s/pwm0/polarity", PWM2_PEX);
} else {
return PWM_WRONOG_CHANNEL;
}
if (access(pwm_file_name, F_OK) != 0) {
return PWM_FILE_NOT_EXIST;
}
FILE *fp = NULL;
char buffer[32] = {0};
(void)memset(buffer, 0, sizeof(buffer));
fp = fopen(pwm_file_name, "r");
if (!fp) {
return PWM_FILE_NOT_EXIST;
}
(void)fread(buffer, sizeof(buffer), 1, fp);
(void)fclose(fp);
fp = NULL;
if (strstr(buffer, "normal") != NULL) {
ret = PWM_POLARITY_NORMAL;
} else if (strstr(buffer, "inversed") != NULL) {
ret = PWM_POLARITY_INVERSED;
} else {
ret = PWM_ERR;
}
return ret;
}
int is_pwm_enabled(int pwmChannel)
{
int ret = 0;
char pwm_file_name[128] = {0};
(void)memset(pwm_file_name, 0, sizeof(pwm_file_name));
if (PWM1 == pwmChannel) {
(void)sprintf(pwm_file_name, "%s/pwm0/enable", PWM1_PEX);
} else if (PWM2 == pwmChannel) {
(void)sprintf(pwm_file_name, "%s/pwm0/enable", PWM2_PEX);
} else {
return PWM_WRONOG_CHANNEL;
}
if (access(pwm_file_name, F_OK) != 0) {
return PWM_FILE_NOT_EXIST;
}
FILE *fp = NULL;
char buffer[32] = {0};
(void)memset(buffer, 0, sizeof(buffer));
fp = fopen(pwm_file_name, "r");
if (!fp) {
return PWM_FILE_NOT_EXIST;
}
(void)fread(buffer, sizeof(buffer), 1, fp);
(void)fclose(fp);
fp = NULL;
ret = atoi(buffer);
return ret;
}
};
}
#endif
2. ROS的常规操作
$ catkin_create_pkg tc214b roscpp std_msgs 创建ROS包
$ catkin_make 编译包
$ source 编译目录下的devel/detup.bash
$ roscore 启动master
$rosrun tc214b tc214n_node
另外重新定义以下消息格式
uint8 TC214B_WAIT = 0
uint8 TC214B_HEAD = 1
uint8 TC214B_BACK = 2
uint8 TC214B_BRAKE =3
uint8 cmd
uint32 duty_cycle
3. 部分软件代码说明
3.1 头文件中主要说明
boost::shared_ptr<tc214b::UmPwm> um_pwm_;
uint32_t a_period_;
uint32_t b_period_;
uint32_t a_duty_cycle_;
uint32_t b_duty_cycle_;
3.2 cpp文件主要说明
um_pwm_ = boost::shared_ptr<tc214b::UmPwm>(new tc214b::UmPwm());
a_period_ = um_pwm_->get_pwm_period(PWM1);
b_period_ = um_pwm_->get_pwm_period(PWM2);
a_period_ = 1000000;
b_period_ = 1000000;
um_pwm_->set_pwm_period(PWM1, a_period_);
um_pwm_->set_pwm_period(PWM2, b_period_);
a_duty_cycle_ = um_pwm_->get_pwm_dutyCycle(PWM1);
b_duty_cycle_ = um_pwm_->get_pwm_dutyCycle(PWM2);
a_duty_cycle_ = 0;
b_duty_cycle_ = 0;
um_pwm_->set_pwm_dutyCycle(PWM1, a_duty_cycle_);
um_pwm_->set_pwm_dutyCycle(PWM2, b_duty_cycle_);
switch (msg->cmd) {
case tc214b::Tc214bCmd::TC214B_WAIT:
a_duty_cycle_ = 0;
b_duty_cycle_ = 0;
um_pwm_->set_pwm_dutyCycle(PWM1, a_duty_cycle_);
um_pwm_->set_pwm_dutyCycle(PWM2, b_duty_cycle_);
break;
case tc214b::Tc214bCmd::TC214B_BACK:
a_duty_cycle_ = 0;
b_duty_cycle_ = msg->duty_cycle % b_period_;
um_pwm_->set_pwm_dutyCycle(PWM1, a_duty_cycle_);
um_pwm_->set_pwm_dutyCycle(PWM2, b_duty_cycle_);
break;
case tc214b::Tc214bCmd::TC214B_BRAKE:
a_duty_cycle_ = a_period_;
b_duty_cycle_ = b_period_;
um_pwm_->set_pwm_dutyCycle(PWM1, a_duty_cycle_);
um_pwm_->set_pwm_dutyCycle(PWM2, b_duty_cycle_);
break;
case tc214b::Tc214bCmd::TC214B_HEAD:
a_duty_cycle_ = msg->duty_cycle % a_period_;
b_duty_cycle_ = 0;
um_pwm_->set_pwm_dutyCycle(PWM1, a_duty_cycle_);
um_pwm_->set_pwm_dutyCycle(PWM2, b_duty_cycle_);
break;
default:
ROS_WARN("cmd error");
break;
}
3.3 利用systemd将PWM初始化,其中主要配置如下
echo 0 > /sys/class/pwm/pwmchip0/export
sudo chmod a+rw /sys/class/pwm/pwmchip0/pwm0/polarity
sudo chmod a+rw /sys/class/pwm/pwmchip0/pwm0/enable
sudo chmod a+rw /sys/class/pwm/pwmchip0/pwm0/period
sudo chmod a+rw /sys/class/pwm/pwmchip0/pwm0/duty_cycle
echo normal > /sys/class/pwm/pwmchip0/pwm0/polarity
echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/period
echo 0 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable
echo 0 > /sys/class/pwm/pwmchip2/export
sudo chmod a+rw /sys/class/pwm/pwmchip2/pwm0/polarity
sudo chmod a+rw /sys/class/pwm/pwmchip2/pwm0/enable
sudo chmod a+rw /sys/class/pwm/pwmchip2/pwm0/period
sudo chmod a+rw /sys/class/pwm/pwmchip2/pwm0/duty_cycle
echo normal > /sys/class/pwm/pwmchip2/pwm0/polarity
echo 1000000 > /sys/class/pwm/pwmchip2/pwm0/period
echo 0 > /sys/class/pwm/pwmchip2/pwm0/duty_cycle
echo 1 > /sys/class/pwm/pwmchip2/pwm0/enable
三、实验现象
四、总结
4.1 本实验借助电位器输入的电压值来控制占空比的输出,来实现调速
4.2 正反转、刹车都可以通过cmd来控制,暂时未通过电位器来控制,可以通过ROS指令来操作。
4.3 ros的节点的启动也可以通过systemd来控制,因为是做的整个项目,故不贴出是如何启动。
4.4 该操作可以做到上电启动就可以通过电位器来操作,后续会增加甲醛、温度的判断。