1.前言
以前写过的步进电机驱动,都是针对一个步进电机做的驱动,当到了需要两个、三个步进电机的场景。复制粘贴出几个,再修改函数名来实现。经常容易出错不说,也显得很不优雅。针对这个痛点,我决定重新编译一份通用性强,跨平台的步进电机驱动。针对跨平台、拓展需求,本次驱动设计基于面向对象思维实现。
2.驱动支持说明
驱动支持三种工作模式,模式0:支持正传、反转、停止;模式1:增量控制旋转角度,控制;模式2,指定角度运行。
3.驱动
用户需要根据平台实现引用可用的uint8_t、int16_t、int32_t、uint32_t头文件或者自行实现。
3.1数据类型引用说明
3.2数据类型实现说明
#define uint8_t unsigned char
#define uint16_t unsigned short
#define int16_t short
#define int32_t int
#define uint32_t unsigned int
3.3c文件
StepMotorObject_typ_t obj_StepMotor1 = {0};
int StepMotor_init(StepMotorObject_typ_t *obj, StepMotorPinSetType *PinHandle, uint8_t inverter, uint8_t mode)
{
if (obj == NULL)
{
return -1;
}
obj->status = 1;
obj->ctr_level_out_high = 1;
obj->ctr_level_out_low = 0;
obj->inverter = inverter;
obj->mode = mode; //工作模式
obj->index = 0;
obj->set_angle = 0; //角度设定
obj->angle_cnt = 0;
obj->run_angle_record = 0;
// -------------------------------
obj->set_fixed_angle = 0;
obj->fixed_angle_cnt = 0;
obj->directing_control = 0;
if (obj->inverter > 0)
{ //输出反向
obj->ctr_level_out_high = 0;
obj->ctr_level_out_low = 1;
}
obj->PinHandle = PinHandle;
return 0;
}
static int32_t angleToBeat(int16_t angle)
{ //转换出角度值
int32_t beat = 0;
beat = ((angle * 4096) / 360);
return beat;
}
void ctr_beat_out(StepMotorObject_typ_t *obj, uint8_t index)
{
uint8_t out_index = 0;
if (obj == NULL)
{
return;
}
if (obj->status != 1)
{ //未初始化
return;
}
// obj->PinHandle[StepMotor_A1](1);
// obj->PinHandle[StepMotor_A2](1);
// obj->PinHandle[StepMotor_B1](1);
// obj->PinHandle[StepMotor_B2](1);
// obj->PinHandle[StepMotor_A1](0);
// obj->PinHandle[StepMotor_A2](0);
// obj->PinHandle[StepMotor_B1](0);
// obj->PinHandle[StepMotor_B2](0);
out_index = index;
switch (out_index)
{
case 0:
//--------------- 1 ----------------------------
obj->PinHandle[StepMotor_A1](obj->ctr_level_out_low);
obj->PinHandle[StepMotor_A2](obj->ctr_level_out_high);
obj->PinHandle[StepMotor_B1](obj->ctr_level_out_high);
obj->PinHandle[StepMotor_B2](obj->ctr_level_out_high);
break;
case 1:
//--------------- 2 ----------------------------
obj->PinHandle[StepMotor_A1](obj->ctr_level_out_low);
obj->PinHandle[StepMotor_A2](obj->ctr_level_out_low);
obj->PinHandle[StepMotor_B1](obj->ctr_level_out_high);
obj->PinHandle[StepMotor_B2](obj->ctr_level_out_high);
break;
case 2:
//--------------- 3 ----------------------------
obj->PinHandle[StepMotor_A1](obj->ctr_level_out_high);
obj->PinHandle[StepMotor_A2](obj->ctr_level_out_low);
obj->PinHandle[StepMotor_B1](obj->ctr_level_out_high);
obj->PinHandle[StepMotor_B2](obj->ctr_level_out_high);
break;
case 3:
//--------------- 4 ----------------------------
obj->PinHandle[StepMotor_A1](obj->ctr_level_out_high);
obj->PinHandle[StepMotor_A2](obj->ctr_level_out_low);
obj->PinHandle[StepMotor_B1](obj->ctr_level_out_low);
obj->PinHandle[StepMotor_B2](obj->ctr_level_out_high);
break;
case 4:
//--------------- 5 ----------------------------
obj->PinHandle[StepMotor_A1](obj->ctr_level_out_high);
obj->PinHandle[StepMotor_A2](obj->ctr_level_out_high);
obj->PinHandle[StepMotor_B1](obj->ctr_level_out_low);
obj->PinHandle[StepMotor_B2](obj->ctr_level_out_high);
break;
case 5:
//--------------- 6 ----------------------------
obj->PinHandle[StepMotor_A1](obj->ctr_level_out_high);
obj->PinHandle[StepMotor_A2](obj->ctr_level_out_high);
obj->PinHandle[StepMotor_B1](obj->ctr_level_out_low);
obj->PinHandle[StepMotor_B2](obj->ctr_level_out_low);
break;
case 6:
//--------------- 7 ----------------------------
obj->PinHandle[StepMotor_A1](obj->ctr_level_out_high);
obj->PinHandle[StepMotor_A2](obj->ctr_level_out_high);
obj->PinHandle[StepMotor_B1](obj->ctr_level_out_high);
obj->PinHandle[StepMotor_B2](obj->ctr_level_out_low);
break;
case 7:
//--------------- 8 ----------------------------
obj->PinHandle[StepMotor_A1](obj->ctr_level_out_low);
obj->PinHandle[StepMotor_A2](obj->ctr_level_out_high);
obj->PinHandle[StepMotor_B1](obj->ctr_level_out_high);
obj->PinHandle[StepMotor_B2](obj->ctr_level_out_low);
break;
default:
obj->PinHandle[StepMotor_A1](obj->ctr_level_out_high); //关闭输出
obj->PinHandle[StepMotor_A2](obj->ctr_level_out_high);
obj->PinHandle[StepMotor_B1](obj->ctr_level_out_high);
obj->PinHandle[StepMotor_B2](obj->ctr_level_out_high);
break;
}
}
void ctr_beat_out_off(StepMotorObject_typ_t *obj)
{
if (obj == NULL)
{
return;
}
if (obj->status != 1)
{ //未初始化
return;
}
obj->PinHandle[StepMotor_A1](obj->ctr_level_out_high); //关闭输出
obj->PinHandle[StepMotor_A2](obj->ctr_level_out_high);
obj->PinHandle[StepMotor_B1](obj->ctr_level_out_high);
obj->PinHandle[StepMotor_B2](obj->ctr_level_out_high);
// obj->PinHandle[0](0);
}
void StepMotor_cycle_handle(StepMotorObject_typ_t *obj)
{
if (obj == NULL)
{
return;
}
if (obj->status != 1)
{ //未初始化
return;
}
if (obj->mode == 1)
{ //角度控制
obj->angle_cnt += angleToBeat(obj->set_angle); // 需要增加限幅
obj->set_angle = 0; //清0
if (obj->run_angle_record < obj->angle_cnt) //正传
{
if (obj->run_angle_record < 2147483647)
{
obj->run_angle_record++;
}
if (++obj->index > 7)
obj->index = 0;
}
else if (obj->run_angle_record > obj->angle_cnt) //反转
{
if (obj->run_angle_record > -2147483648)
{
obj->run_angle_record--;
}
if (obj->index != 0)
{
obj->index--;
}
else
{
obj->index = 7;
}
}
else
{
ctr_beat_out_off(obj);
return;
}
ctr_beat_out(obj, obj->index);
}
else if (obj->mode == 2)
{ //指定角度运行
obj->fixed_angle_cnt = angleToBeat(obj->set_fixed_angle); // 固定角度
if (obj->run_angle_record < obj->fixed_angle_cnt) //正传
{
if (obj->run_angle_record < 2147483647)
{
obj->run_angle_record++;
}
if (++obj->index > 7)
{
obj->index = 0;
}
ctr_beat_out(obj, obj->index);
}
else if (obj->run_angle_record > obj->fixed_angle_cnt) //反转
{
if (obj->run_angle_record > -2147483648)
{
obj->run_angle_record--;
}
ctr_beat_out(obj, obj->index);
}
else
{
ctr_beat_out_off(obj);
}
}
else
{
if (obj->directing_control == -1)
{
if (++obj->index > 7)
obj->index = 0;
ctr_beat_out(obj, obj->index);
}
else if (obj->directing_control == 1)
{
if (obj->index != 0)
{
obj->index--;
}
else
{
obj->index = 7;
}
ctr_beat_out(obj, obj->index);
}
else
{
ctr_beat_out_off(obj);
return;
}
}
}
int StepMotorSetMode(StepMotorObject_typ_t *obj, uint8_t mode)
{
if (obj == NULL)
{
return -1;
}
if (obj->status != 1)
{ //未初始化
return -2;
}
obj->mode = mode; //工作模式
return 0;
}
int StepMotorSetForwardAngle(StepMotorObject_typ_t *obj, int16_t set_angle)
{
if (obj == NULL)
{
return -1;
}
if (obj->status != 1)
{ //未初始化
return -2;
}
if (obj->mode != 1)
{
return -3;
}
if (set_angle < 0)
{
return -3;
}
obj->set_angle = set_angle;
return 0;
}
int StepMotorSetReversalAngle(StepMotorObject_typ_t *obj, int16_t set_angle)
{
if (obj == NULL)
{
return -1;
}
if (obj->status != 1)
{ //未初始化
return -2;
}
if (obj->mode != 1)
{
return -3;
}
if (set_angle < 0)
{
return -3;
}
obj->set_angle = 0 - set_angle;
return 0;
}
int StepMotorSetFixedAngle(StepMotorObject_typ_t *obj, int16_t set_fixed_angle)
{
if (obj == NULL)
{
return -1;
}
if (obj->status != 1)
{ //未初始化
return -2;
}
if (obj->mode != 2)
{
return -3;
}
obj->set_fixed_angle = set_fixed_angle;
return 0;
}
int StepMotorSetForward(StepMotorObject_typ_t *obj)
{
if (obj == NULL)
{
return -1;
}
if (obj->status != 1)
{ //未初始化
return -2;
}
if (obj->mode != 0)
{
return -3;
}
obj->directing_control = 1;
return 0;
}
int StepMotorSetReversal(StepMotorObject_typ_t *obj)
{
if (obj == NULL)
{
return -1;
}
if (obj->status != 1)
{ //未初始化
return -2;
}
if (obj->mode != 0)
{
return -3;
}
obj->directing_control = -1;
return 0;
}
int StepMotorSetStop(StepMotorObject_typ_t *obj)
{
if (obj == NULL)
{
return -1;
}
if (obj->status != 1)
{ //未初始化
return -2;
}
if (obj->mode != 0)
{
return -3;
}
obj->directing_control = 0;
return 0;
}
3.4h文件
/*定义步进电机控制引脚枚举*/
typedef enum StepMotorPin{
StepMotor_A1=0,
StepMotor_A2=1,
StepMotor_B1=2,
StepMotor_B2=3
}StepMotorPinType;
/*定义引脚操作函数指针类型*/
typedef void (* StepMotorPinSetType)(uint8_t value);
/* 定义步进电机的对象类型 */
typedef struct StepMotorObject {
uint8_t status;
uint8_t ctr_level_out_high; //输出相位1
uint8_t ctr_level_out_low; //输出相位0
uint8_t inverter; //输入输出是否为反相器 1反向 0 正常
uint8_t mode; //工作模式
uint8_t index; // 步进
int16_t set_angle; //角度设定
int32_t angle_cnt;
int32_t run_angle_record;
// -------------------------------
int16_t set_fixed_angle;
int32_t fixed_angle_cnt;
// -------------------------------
int8_t directing_control;
StepMotorPinSetType *PinHandle;
}StepMotorObject_typ_t;
extern StepMotorObject_typ_t obj_StepMotor1;
int StepMotor_init(StepMotorObject_typ_t *obj, StepMotorPinSetType *PinHandle, uint8_t inverter, uint8_t mode);
void ctr_beat_out(StepMotorObject_typ_t *obj, uint8_t index);
void ctr_beat_out_off(StepMotorObject_typ_t *obj);
void StepMotor_cycle_handle(StepMotorObject_typ_t *obj);
int StepMotorSetMode(StepMotorObject_typ_t *obj , uint8_t mode);
int StepMotorSetForwardAngle(StepMotorObject_typ_t *obj, int16_t set_angle);
int StepMotorSetReversalAngle(StepMotorObject_typ_t *obj, int16_t set_angle);
int StepMotorSetFixedAngle(StepMotorObject_typ_t *obj, int16_t set_fixed_angle);
int StepMotorSetForward(StepMotorObject_typ_t *obj);
int StepMotorSetReversal(StepMotorObject_typ_t *obj);
4使用示例
4.1接口实现
接口部分根据不同平台进项修改,这里以ESP32为例
void StepMotor1_Pin_init(void)
{
gpio_reset_pin(19);
/* Set the GPIO as a push/pull output */
gpio_set_direction(19, GPIO_MODE_OUTPUT);
gpio_reset_pin(5);
/* Set the GPIO as a push/pull output */
gpio_set_direction(5, GPIO_MODE_OUTPUT);
gpio_reset_pin(25);
/* Set the GPIO as a push/pull output */
gpio_set_direction(25, GPIO_MODE_OUTPUT);
gpio_reset_pin(15);
/* Set the GPIO as a push/pull output */
gpio_set_direction(15, GPIO_MODE_OUTPUT);
}
static void StepMotor1_A1(uint8_t value)
{
if (value)
{
gpio_set_level(19, 1);
}
else
{
gpio_set_level(19, 0);
}
}
static void StepMotor1_A2(uint8_t value)
{
if (value)
{
gpio_set_level(5, 1);
}
else
{
gpio_set_level(5, 0);
}
}
static void StepMotor1_B2(uint8_t value)
{
if (value)
{
gpio_set_level(25, 1);
}
else
{
gpio_set_level(25, 0);
}
}
static void StepMotor1_B1(uint8_t value)
{
if (value)
{
gpio_set_level(15, 1);
}
else
{
gpio_set_level(15, 0);
}
}
static StepMotorPinSetType setmotor_PIN[4] = {StepMotor1_A1, StepMotor1_A2, StepMotor1_B1, StepMotor1_B2};
4.2定时器刷新接口
static bool timer_callback(void *args){
uint64_t val;
static uint8_t index = 0;
BaseType_t pxHigherPriorityTaskWoken = pdFALSE;
val = timer_group_get_counter_value_in_isr(0, 0);
StepMotor_cycle_handle(&obj_StepMotor1); //定时刷新步进电机节拍
return pxHigherPriorityTaskWoken;
}
4.3初始化于使用
void app_main(void)
{
timer_config_t config = {
.alarm_en = 1,
.counter_en = 0,
.counter_dir = TIMER_COUNT_UP,
.auto_reload = 1,
.divider = 16,
};
timer_init(0, 0, &config);
timer_set_counter_value(0, 0, 0x00ull);
// timer_set_alarm_value(0, 0, TIMER_FREQ * 3);
// timer_set_alarm_value(0, 0, 12500); //200HZ
// timer_set_alarm_value(0, 0, 5000); //500HZ
// timer_set_alarm_value(0, 0, 25000); //100HZ
// timer_set_alarm_value(0, 0, 50000); //50HZ
timer_set_alarm_value(0, 0, 4800); //
timer_enable_intr(0, 0);
timer_isr_callback_add(0, 0, timer_callback, NULL, ESP_INTR_FLAG_IRAM);
/*启动定时器*/
timer_start(TIMER_GROUP_0,TIMER_0); //开始计数
StepMotor1_Pin_init(); //步进电机初始化端口
StepMotor_init(&obj_StepMotor1, setmotor_PIN, 1, 2); //配置步进电机工作模式
StepMotorSetFixedAngle(&obj_StepMotor1,500); //转动500°
}
5总结
以后再增加步进电机或者移植到别的的单片机钟,不需要再动驱动部分,只需要修改实现4部分接口便可以快速完成步进电机拓展。大大增加了灵活性。