传送门:STM32控制ULN2003驱动步进电机28BYJ-48最基础版
当使用STM32控制ULN2003驱动步进电机28BYJ-48时,步进电机转速变化缓慢,想要得到合适的控制速度,需要不断的调试,不断的更改代码,编辑、编译、下载到STM32中,不断的重复下载造成开发速度慢同时对单片机也是一种损耗,废话不多说直接上代码。
#define STRUCT(type) typedef struct _tag_##type type;\
struct _tag_##type
/* 类型定义 ------------------------------------------ */
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);
};
/**
* @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 .\r\n", motor->step_slice);
Step_Motor_CW(motor);
}
else
{
printf("motor ready run ccw... , period is %d .\r\n", 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[i];
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);
}
/* 串口1接收配置使能,使用串口进行调试 */
#define USART1_RCV_EN 1
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);
}
/* 类型定义 ------------------------------------------------- */
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);
}
}
#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: %#x\r\n", 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
处理函数只进行了简单处理,需要复杂功能都可以在此函数中进行处理。
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);
}
}
调试结果图
以上的调试过程中,需要注意每次输入的时候使用回车,不然程序会卡住或者下调指令执行错误,有稍微的不方便之处,可以使用定时器中断进行程序改进,各有优略。
以上代码传送门: 串口调试步进电机,再也不用担心调试重复下载啦
喜欢请点个赞哦,谢谢!欢迎指正