STM32使用USART串口调试ULN2003驱动步进电机

本文详细介绍了如何通过STM32单片机配合ULN2003驱动,优化步进电机28BYJ-48的控制代码,实现快速响应的转速调整,减少重复下载和调试时间。通过改进电机控制结构和串口处理,提升开发效率并简化调试过程。
摘要由CSDN通过智能技术生成

传送门: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 .\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);
}

串口部分代码改进

  • 在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: %#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

处理函数只进行了简单处理,需要复杂功能都可以在此函数中进行处理。

  • 在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);
	}
}

调试结果

  • 使用串口调试助手进行调试
    在这里插入图片描述

调试结果图

以上的调试过程中,需要注意每次输入的时候使用回车,不然程序会卡住或者下调指令执行错误,有稍微的不方便之处,可以使用定时器中断进行程序改进,各有优略。


以上代码传送门: 串口调试步进电机,再也不用担心调试重复下载啦


喜欢请点个赞哦,谢谢!欢迎指正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值