传送门:STM32控制ULN2003驱动步进电机28BYJ-48最基础版
使用串口调试步进电机28BYJ-48获得需要转速
当使用STM32控制ULN2003驱动步进电机28BYJ-48时,步进电机转速变化缓慢,想要得到合适的控制速度,需要不断的调试,不断的更改代码,编辑、编译、下载到STM32中,不断的重复下载造成开发速度慢同时对单片机也是一种损耗,废话不多说直接上代码。
定义struct.h进行宏定义,如下:
#define STRUCT(type) typedef struct _tag_##type type; struct _tag_##type 电机部分代码改进
在step_motor.h中定义类型,使用对象可以使数据组织起来使用更加方面,如下:
/* 类型定义 ------------------------------------------ */ STRUCT(StepMotor_t) { /* *state: bit0 0 表示电机处于非运行态; 1 表示运行态 * bit1 0 表示电机正转 1 反转 */ uint8_t state; /* 每步时间片 */ uint16_t step_slice; /* 总步数 */ u32 step_num; void (*run)(StepMotor_t *motor); };
在step_motor.c中定义电机运行函数,如下:
/** * @name: Step_Motor_Run * @description: step motor run. * @param {StepMotor_t} *motor * @return {*} */ void Step_Motor_Run(StepMotor_t *motor) { /* 判断正反转 */ if(!(motor-》state & 0x02)) { printf(“motor ready run cw.。。 , period is %d 。rn”, motor-》step_slice); Step_Motor_CW(motor); } else { printf(“motor ready run ccw.。。 , period is %d 。rn”, motor-》step_slice); Step_Motor_CCW(motor); } motor-》state &= ~0x01; }
同时进行运行正反转的函数定义:
/** * @name: Step_Motor_CW * @description: 电机正转函数 * @param {uint32_t} nms * @return {*} */ static void Step_Motor_CW(StepMotor_t *motor) { volatile uint8_t i; uint32_t j, pieces; /* pieces 表示8个位一组的脉冲一共多少组 */ uint8_t temp = 0; pieces = motor-》step_num / 8; for(j = 0; j 《 pieces; j++) for(i = 0; i 《 8; i++) { temp = steps; LA = (uint8_t)((temp&PLA) 》》 0); LB = (uint8_t)((temp&PLB) 》》 1); LC = (uint8_t)((temp&PLC) 》》 2); LD = (uint8_t)((temp&PLD) 》》 3); delay_us(motor-》step_slice); } Step_Motor_Stop(&StepMotor); } 串口部分代码改进
在usart.h中进行宏配置,可以方便的开关USART接收中断,配置如下:
/* 串口1接收配置使能,使用串口进行调试 */ #define USART1_RCV_EN 1
在usart.c中进行USART初始化,在初始化中开启接收中断,并对中断进行配置
void Usart1_Init(unsigned int baud) { GPIO_InitTypeDef gpio_initstruct; USART_InitTypeDef usart_initstruct; NVIC_InitTypeDef nvic_initstruct; /* 时钟使能 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); /* gpio口配置 */ //PA9 TXD gpio_initstruct.GPIO_Mode = GPIO_Mode_AF_PP; gpio_initstruct.GPIO_Pin = GPIO_Pin_9; gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &gpio_initstruct); //PA10 RXD gpio_initstruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; gpio_initstruct.GPIO_Pin = GPIO_Pin_10; gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &gpio_initstruct); /* 串口配置 */ usart_initstruct.USART_BaudRate = baud; /* 波特率设置 */ usart_initstruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控 usart_initstruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //接收和发送 usart_initstruct.USART_Parity = USART_Parity_No; //无校验 usart_initstruct.USART_StopBits = USART_StopBits_1; //1位停止位 usart_initstruct.USART_WordLength = USART_WordLength_8b; //8位数据位 USART_Init(USART1, &usart_initstruct); USART_Cmd(USART1, ENABLE); //使能串口 /* 接收中断使能 */ USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //使能接收中断 /* 接收中断配置 */ nvic_initstruct.NVIC_IRQChannel = USART1_IRQn; nvic_initstruct.NVIC_IRQChannelCmd = ENABLE; nvic_initstruct.NVIC_IRQChannelPreemptionPriority = 0; nvic_initstruct.NVIC_IRQChannelSubPriority = 2; NVIC_Init(&nvic_initstruct); }
设置一个结构类型,用来处理数据和USART接收中的状态
/* 类型定义 ------------------------------------------------- */ STRUCT(RcvDta_t) { uint8_t rcv_dta[13];/* 接收数据 */ uint8_t worked_dta[13];/* 处理数据 */ /* state: bit0 ~ bit6 记录data数量 * bit7 : 0 表示接收状态 * 1 表示处理状态 */ uint8_t state; void (*func)(RcvDta_t *rcv_ptr); };
定义结构体数据
/* 变量定义 ---------------------------------------------- */ #if USART1_RCV_EN RcvDta_t RcvMsg; #endif
串口中断函数,在串口中接收并判断是不是接收完成
void USART1_IRQHandler(void) { if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断 { #if USART1_RCV_EN uint8_t ch = USART_ReceiveData(USART1); /* 结束条件判断,在发送数据的时候需要在末尾加上回车 */ if(ch == ‘n’) RcvMsg.state |= 0x80; if(!(RcvMsg.state & 0x80)) { if((RcvMsg.state & 0x7f) 《 sizeof(RcvMsg.rcv_dta) - 1) { RcvMsg.rcv_dta[RcvMsg.state++] = ch; } } #endif USART_ClearFlag(USART1, USART_FLAG_RXNE); } }
定义app.c和app.h,并在app.c中定义串口接收处理函数
#if USART1_RCV_EN /** * @name: Dubug_MsgHandler * @description: 处理串口接收数据,将接收数据处理为电机的数据 * @param {RcvDta_t} *rcv_ptr 串口数据结构体指针 * @param {StepMotor_t} *motor 电机结构体指针 * @return {*} */ void Dubug_MsgHandler(RcvDta_t *rcv_ptr, StepMotor_t *motor) { char *ret = NULL; char *is_space = NULL; int speed; /* 将内容复制到worked_dta, rcv_dta将清空下次接收 */ strncpy((char *)rcv_ptr-》worked_dta, (char *)rcv_ptr-》rcv_dta, 11); // printf(“usart rcv: %s state: %#xrn”, rcv_ptr-》worked_dta, rcv_ptr-》state); /* 指令处理 ------------------------------------ */ /* 电机运转 --------- */ ret = strstr((char *)rcv_ptr-》worked_dta, “run”); if(ret != NULL) { motor-》state |= 0x01; motor-》state &= ~(1《《1); ret = NULL; } /* 电机正转 --------- */ ret = strstr((char *)rcv_ptr-》worked_dta, “run_cw”); if(ret != NULL) { motor-》state |= 0x01; motor-》state &= ~(1《《1); ret = NULL; } /* 电机反转 --------- */ ret = strstr((char *)rcv_ptr-》worked_dta, “run_ccw”); if(ret != NULL) { motor-》state |= 0x01; motor-》state |= (1《《1); ret = NULL; } /* 电机速度设置 --------- */ ret = strstr((char *)rcv_ptr-》worked_dta, “speed”); if(ret) { ret += strlen(“speed”); ret++; is_space = strchr(ret, ‘ ’); if(is_space) ret++; speed = atoi(ret); motor-》step_slice = speed; ret = NULL; } memset(rcv_ptr-》rcv_dta, 0, sizeof(rcv_ptr-》rcv_dta)); rcv_ptr-》state &= 0x00; } #endif 处理函数只进行了简单处理,需要复杂功能都可以在此函数中进行处理。
在main.c中调用
int main(void) { uint32_t t = 0; initSysTick(); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); Usart1_Init(115200); LED1_Init(); Step_Motor_Init(); for(;;) { /* 串口接收是否接受到 */ if(RcvMsg.state & 0x80) { Dubug_MsgHandler(&RcvMsg, &StepMotor); delay_ms(1); } /* 是否启动电机 */ if(StepMotor.state & 0x01) { StepMotor.run(&StepMotor); } if(t % 100 《 50) LED1_Open(); else LED1_Close(); if(t 》 1000) t = 0; t++; delay_ms(10); } } 调试结果
使用串口调试助手进行调试
以上的调试过程中,需要注意每次输入的时候使用回车,不然程序会卡住或者下调指令执行错误,有稍微的不方便之处,可以使用定时器中断进行程序改进,各有优略。
以上代码传送门: 串口调试步进电机,再也不用担心调试重复下载啦。
传送门:STM32控制ULN2003驱动步进电机28BYJ-48最基础版
使用串口调试步进电机28BYJ-48获得需要转速
当使用STM32控制ULN2003驱动步进电机28BYJ-48时,步进电机转速变化缓慢,想要得到合适的控制速度,需要不断的调试,不断的更改代码,编辑、编译、下载到STM32中,不断的重复下载造成开发速度慢同时对单片机也是一种损耗,废话不多说直接上代码。
定义struct.h进行宏定义,如下:
#define STRUCT(type) typedef struct _tag_##type type; struct _tag_##type 电机部分代码改进
在step_motor.h中定义类型,使用对象可以使数据组织起来使用更加方面,如下:
/* 类型定义 ------------------------------------------ */ STRUCT(StepMotor_t) { /* *state: bit0 0 表示电机处于非运行态; 1 表示运行态 * bit1 0 表示电机正转 1 反转 */ uint8_t state; /* 每步时间片 */ uint16_t step_slice; /* 总步数 */ u32 step_num; void (*run)(StepMotor_t *motor); };
在step_motor.c中定义电机运行函数,如下:
/** * @name: Step_Motor_Run * @description: step motor run. * @param {StepMotor_t} *motor * @return {*} */ void Step_Motor_Run(StepMotor_t *motor) { /* 判断正反转 */ if(!(motor-》state & 0x02)) { printf(“motor ready run cw.。。 , period is %d 。rn”, motor-》step_slice); Step_Motor_CW(motor); } else { printf(“motor ready run ccw.。。 , period is %d 。rn”, motor-》step_slice); Step_Motor_CCW(motor); } motor-》state &= ~0x01; }
同时进行运行正反转的函数定义:
/** * @name: Step_Motor_CW * @description: 电机正转函数 * @param {uint32_t} nms * @return {*} */ static void Step_Motor_CW(StepMotor_t *motor) { volatile uint8_t i; uint32_t j, pieces; /* pieces 表示8个位一组的脉冲一共多少组 */ uint8_t temp = 0; pieces = motor-》step_num / 8; for(j = 0; j 《 pieces; j++) for(i = 0; i 《 8; i++) { temp = steps; LA = (uint8_t)((temp&PLA) 》》 0); LB = (uint8_t)((temp&PLB) 》》 1); LC = (uint8_t)((temp&PLC) 》》 2); LD = (uint8_t)((temp&PLD) 》》 3); delay_us(motor-》step_slice); } Step_Motor_Stop(&StepMotor); } 串口部分代码改进
在usart.h中进行宏配置,可以方便的开关USART接收中断,配置如下:
/* 串口1接收配置使能,使用串口进行调试 */ #define USART1_RCV_EN 1
在usart.c中进行USART初始化,在初始化中开启接收中断,并对中断进行配置
void Usart1_Init(unsigned int baud) { GPIO_InitTypeDef gpio_initstruct; USART_InitTypeDef usart_initstruct; NVIC_InitTypeDef nvic_initstruct; /* 时钟使能 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); /* gpio口配置 */ //PA9 TXD gpio_initstruct.GPIO_Mode = GPIO_Mode_AF_PP; gpio_initstruct.GPIO_Pin = GPIO_Pin_9; gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &gpio_initstruct); //PA10 RXD gpio_initstruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; gpio_initstruct.GPIO_Pin = GPIO_Pin_10; gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &gpio_initstruct); /* 串口配置 */ usart_initstruct.USART_BaudRate = baud; /* 波特率设置 */ usart_initstruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控 usart_initstruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //接收和发送 usart_initstruct.USART_Parity = USART_Parity_No; //无校验 usart_initstruct.USART_StopBits = USART_StopBits_1; //1位停止位 usart_initstruct.USART_WordLength = USART_WordLength_8b; //8位数据位 USART_Init(USART1, &usart_initstruct); USART_Cmd(USART1, ENABLE); //使能串口 /* 接收中断使能 */ USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //使能接收中断 /* 接收中断配置 */ nvic_initstruct.NVIC_IRQChannel = USART1_IRQn; nvic_initstruct.NVIC_IRQChannelCmd = ENABLE; nvic_initstruct.NVIC_IRQChannelPreemptionPriority = 0; nvic_initstruct.NVIC_IRQChannelSubPriority = 2; NVIC_Init(&nvic_initstruct); }
设置一个结构类型,用来处理数据和USART接收中的状态
/* 类型定义 ------------------------------------------------- */ STRUCT(RcvDta_t) { uint8_t rcv_dta[13];/* 接收数据 */ uint8_t worked_dta[13];/* 处理数据 */ /* state: bit0 ~ bit6 记录data数量 * bit7 : 0 表示接收状态 * 1 表示处理状态 */ uint8_t state; void (*func)(RcvDta_t *rcv_ptr); };
定义结构体数据
/* 变量定义 ---------------------------------------------- */ #if USART1_RCV_EN RcvDta_t RcvMsg; #endif
串口中断函数,在串口中接收并判断是不是接收完成
void USART1_IRQHandler(void) { if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断 { #if USART1_RCV_EN uint8_t ch = USART_ReceiveData(USART1); /* 结束条件判断,在发送数据的时候需要在末尾加上回车 */ if(ch == ‘n’) RcvMsg.state |= 0x80; if(!(RcvMsg.state & 0x80)) { if((RcvMsg.state & 0x7f) 《 sizeof(RcvMsg.rcv_dta) - 1) { RcvMsg.rcv_dta[RcvMsg.state++] = ch; } } #endif USART_ClearFlag(USART1, USART_FLAG_RXNE); } }
定义app.c和app.h,并在app.c中定义串口接收处理函数
#if USART1_RCV_EN /** * @name: Dubug_MsgHandler * @description: 处理串口接收数据,将接收数据处理为电机的数据 * @param {RcvDta_t} *rcv_ptr 串口数据结构体指针 * @param {StepMotor_t} *motor 电机结构体指针 * @return {*} */ void Dubug_MsgHandler(RcvDta_t *rcv_ptr, StepMotor_t *motor) { char *ret = NULL; char *is_space = NULL; int speed; /* 将内容复制到worked_dta, rcv_dta将清空下次接收 */ strncpy((char *)rcv_ptr-》worked_dta, (char *)rcv_ptr-》rcv_dta, 11); // printf(“usart rcv: %s state: %#xrn”, rcv_ptr-》worked_dta, rcv_ptr-》state); /* 指令处理 ------------------------------------ */ /* 电机运转 --------- */ ret = strstr((char *)rcv_ptr-》worked_dta, “run”); if(ret != NULL) { motor-》state |= 0x01; motor-》state &= ~(1《《1); ret = NULL; } /* 电机正转 --------- */ ret = strstr((char *)rcv_ptr-》worked_dta, “run_cw”); if(ret != NULL) { motor-》state |= 0x01; motor-》state &= ~(1《《1); ret = NULL; } /* 电机反转 --------- */ ret = strstr((char *)rcv_ptr-》worked_dta, “run_ccw”); if(ret != NULL) { motor-》state |= 0x01; motor-》state |= (1《《1); ret = NULL; } /* 电机速度设置 --------- */ ret = strstr((char *)rcv_ptr-》worked_dta, “speed”); if(ret) { ret += strlen(“speed”); ret++; is_space = strchr(ret, ‘ ’); if(is_space) ret++; speed = atoi(ret); motor-》step_slice = speed; ret = NULL; } memset(rcv_ptr-》rcv_dta, 0, sizeof(rcv_ptr-》rcv_dta)); rcv_ptr-》state &= 0x00; } #endif 处理函数只进行了简单处理,需要复杂功能都可以在此函数中进行处理。
在main.c中调用
int main(void) { uint32_t t = 0; initSysTick(); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); Usart1_Init(115200); LED1_Init(); Step_Motor_Init(); for(;;) { /* 串口接收是否接受到 */ if(RcvMsg.state & 0x80) { Dubug_MsgHandler(&RcvMsg, &StepMotor); delay_ms(1); } /* 是否启动电机 */ if(StepMotor.state & 0x01) { StepMotor.run(&StepMotor); } if(t % 100 《 50) LED1_Open(); else LED1_Close(); if(t 》 1000) t = 0; t++; delay_ms(10); } } 调试结果
使用串口调试助手进行调试
以上的调试过程中,需要注意每次输入的时候使用回车,不然程序会卡住或者下调指令执行错误,有稍微的不方便之处,可以使用定时器中断进行程序改进,各有优略。
以上代码传送门: 串口调试步进电机,再也不用担心调试重复下载啦。
举报