打印
[STM32F1]

利用STM32F103精确控制步进电机

[复制链接]
楼主: 欢乐家园
手机看帖
扫描二维码
随时随地手机跟帖
21
欢乐家园|  楼主 | 2022-8-22 23:49 | 只看该作者 |只看大图 回帖奖励 |倒序浏览
定时器共产生nPDTemp3+1=6400个脉冲,电机转过的角度为6400*1.8°=11520°,即电机转了32圈。
  转动速度

使用特权

评论回复
22
欢乐家园|  楼主 | 2022-8-22 23:50 | 只看该作者
4.在32细分的情况下电机1rad/s和转1°需要的重装值为nPDTemp2=11.25,nPDTemp3=17.7778。

使用特权

评论回复
23
欢乐家园|  楼主 | 2022-8-22 23:51 | 只看该作者
四、程序实现
1.main.c程序
#include "main.h"
#include "sys.h"
#include "usart1.h"
#include "delay.h"
#include "math.h"

u16 t;  
u16 len;                    //接收到的数据长度
u16 times=0;
char receive_data[60];        //接收到的字符
char state;                                //电机正反转标志位0正转,1反转
int speed,angle;                //旋转速度,角度
int a=0;                        //判断是否接收到数据
int r,data=0;                          //用于数据转换
int type;                                //转换后的数据   
extern u16 USART_RX_STA;

/**************************
* 函数名:delay
* 描述  :延时函数
* 输入  :无
* 输出  :无
* 返回值:无
****************************/
void delay()//延时
{
         int i,j;
         for(i=0;i<2000;i++)
         for(j=0;j<1000;j++);
}

/**************************
* 函数名:Waiting_receptio
* 描述  :等待串口接收数据
* 输入  :无
* 输出  :无
* 返回值:无
****************************/
void Waiting_reception(void)
{
        while(a==0)//等待数据发送完成
        {
                delay_ms(100);
                if(a==1)//接收到数据
                {
                        if(USART_RX_STA&0x8000)//(串口接收用到了正点原子的例程)
                        {
                                len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
                                r=len;                               
                                type=0;
                                for(t=0;t<len;t++)//拷贝数据,将字符转换为十进制数
                                {
                                        receive_data[t]=USART_RX_BUF[t];
                                        data=(int)receive_data[t]-48;
                                        r=r-1;
                                        type=type+data*(pow(10,r));
                                }
                                USART_RX_STA=0;
                                a=0;
                                delay_ms(500);
                                break;
                        }
                }
        }
}
/**************************
* 函数名:KeyStates
* 描述  :监测按键状态
* 输入  :无
* 输出  :无
* 返回值:0/1
****************************/
u8 KeyStates()//按键状态
{
        static u8 i = 0;
        if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3)==0)
        {
                delay();
                if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3)==0)
                        while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3)==0);
                        i = ~i;
        }
        return i;
}
/****************
* 函数名:main
* 描述  :主函数
* 输入  :无
* 输出  :无
******************/
int main(void)
{
        NVIC_Configuration();                        //中断初始化
        GPIO_Config();                                        //IO口初始化
        USART1_Config();                                //串口初始化
    delay_init();                                   //延时函数初始化       
        GPIO_ResetBits(GPIOA, GPIO_Pin_11);//A11置零  A11——EAN+
        GPIO_ResetBits(GPIOA, GPIO_Pin_12);//A12置零  A12——DIR+
        while(1)
        {
                delay_ms(100);
                Initial_state:                printf("\r\n 请选择正反转,正转输入0,反转输入1 (以新行作为结束标志)\r\n");       
                Waiting_reception();
                state=type;//将接收到的数据给type
                if(type==0)//电机正转
                {
                        GPIO_SetBits(GPIOA, GPIO_Pin_12);//电机正转
                        printf("\r\n 电机为正转模式,请输入旋转速度(rad/s),输入0返回初始模式 \r\n");
                       
/*********************************************此模块用于配置速度参数********************************************************/

                        part1:Waiting_reception();
                        speed =type;//将接收到的数据给speed
                        if(speed==0)goto Initial_state;//如果是0则返回初始模式
                                else{
                                        if(speed>=15)
                                        {
                                                printf("\r\n 旋转速度>15rad/s,请重新选择旋转速度。\r\n");
                                                goto part1;
                                        }
                                                else printf("\r\n 电机旋转速度为%d rad/s,请输入旋转角度,输入0返回初始模式 \r\n",speed);
                                }
       
/*********************************************此模块用于配置角度参数********************************************************/               

                        Waiting_reception();
                        angle =type;//将接收到的数据给type
                        for(;;)
                        {
                                if(angle>0)//接收到的数据不是0
                                {
                                        TIM2_Master__TIM3_Slave_Configuration(speed,angle);        // 配置TIM2和TIM3的重装值 ,改变电机转动速度和角度
                                        delay_ms(20000);//电机保护
                                        printf("\r\n 电机已旋转%d °,请输入下一次旋转角度,输入0返回初始模式; \r\n",angle);
                                        angle=0;
                                        Waiting_reception();               
                                        angle =type;                                       
                                }else{
                                        if(angle==0)goto Initial_state;//返回初始状态       
                                        else {
                                                printf("\r\n 角度错误,已返回初始模式 \r\n");               
                                                goto Initial_state;
                                        }                                               
                                }
                        }
                }               
/*********************************************反转模式********************************************************/               
                else{
                        if(type==1)
                        {
                                GPIO_ResetBits(GPIOA, GPIO_Pin_12);//电机反转
                                printf("\r\n 电机为正转模式,请输入旋转速度(rad/s),输入0返回初始模式 \r\n");
                               
/*********************************************此模块用于配置速度参数********************************************************/
                                part2:                                Waiting_reception();
                                speed =type;//将接收到的数据给speed
                                if(speed==0)goto Initial_state;//如果是0则返回初始模式
                                        else{
                                                if(speed>=15)
                                                {
                                                        printf("\r\n旋转速度>15rad/s,请重新选择旋转速度。\r\n");
                                                        goto part2;
                                                }else printf("\r\n 电机旋转速度为%d rad/s,请输入旋转角度,输入0返回初始模式  \r\n",speed);
                                        }       
/*********************************************此模块用于配置角度参数********************************************************/               
                                Waiting_reception();
                                angle =type;//将接收到的数据给type
                                for(;;)
                                {
                                        if(angle>0)//接收到的数据不是0
                                        {
                                                TIM2_Master__TIM3_Slave_Configuration(speed,angle);        // 配置TIM2和TIM3的重装值 ,改变电机转动速度和角度
                                                delay_ms(20000);//电机保护
                                                printf("\r\n 电机已旋转%d °,请输入下一次旋转角度,输入0返回初始模式;\r\n",angle);
                                                angle=0;
                                                Waiting_reception();               
                                                angle =type;                                       
                                        }else{
                                                if(angle==0)goto Initial_state;//返回初始状态       
                                                else {
                                                        printf("\r\n 角度错误,已返回初始模式 \r\n");               
                                                        goto Initial_state;
                                                }                                                               
                                        }
                                }
/****************************************************************************************************************************/                                       
                }else{//if(a!=0)&(a!=1)
                        type=NULL;
                        printf("\r\n 输入无效 \r\n");
                        goto Initial_state;//返回初始状态
                        }
                }
        }
}

使用特权

评论回复
24
欢乐家园|  楼主 | 2022-8-22 23:53 | 只看该作者
2.main.h程序
#ifndef _MAIN_H
#define        _MAIN_H
#include <stm32f10x.h>
#include <usart1.h>
#include <misc.h>
#include <nvic.h>
#include <stdio.h>
#include "stm32f10x_tim.h"
#include "timer.h"
#endif

使用特权

评论回复
25
欢乐家园|  楼主 | 2022-8-22 23:54 | 只看该作者
3.time.c程序
#include "timer.h"
/**************************
* 函数名:GPIO_Config
* 描述  :无
* 输入  :无
* 输出  :无
* 调用  :主函数
* 返回值:无
****************************/
void GPIO_Config(void)
{
                GPIO_InitTypeDef GPIO_InitStructure;
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //使能IOA
                RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM3, ENABLE); //使能TIM2,TIM3
               
            /* Timer2 Channel 1, PA0 */
            GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_0;
            GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用输出
            GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
            GPIO_Init(GPIOA, &GPIO_InitStructure);
                 
                GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_11|GPIO_Pin_12;
                GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;         //通用推挽输出模式
                GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;        //指定GPIO引脚可输出的最高频率为50MHZ
                GPIO_Init(GPIOA, &GPIO_InitStructure);                   //
                GPIO_ResetBits(GPIOA, GPIO_Pin_1);//指定引脚输出低电平,此时灯全灭,方向
                GPIO_ResetBits(GPIOA, GPIO_Pin_2);//指定引脚输出低电平,此时灯全灭        使能
                GPIO_SetBits(GPIOA, GPIO_Pin_11);//指定引脚输出低电平,此时灯全灭,方向
                GPIO_SetBits(GPIOA, GPIO_Pin_12);//指定引脚输出低电平,此时灯全灭        使能
       
         
                GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
                GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;         //通用推挽输出模式
                GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;        //指定GPIO引脚可输出的最高频率为50MHZ
                GPIO_Init(GPIOA, &GPIO_InitStructure);                   //
               
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
                GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
                GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;         //通用推挽输出模式
                GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;        //指定GPIO引脚可输出的最高频率为50MHZ
                GPIO_Init(GPIOB, &GPIO_InitStructure);                   //
                GPIO_ResetBits(GPIOB, GPIO_Pin_12);//指定引脚输出低电平,此时灯全灭        使能
               
                //GPIO_ResetBits  GPIO_SetBits
}

//================================================================================
/**************************
* 函数名:TIM2_Master__TIM3_Slave_Configuration
* 描述  :主从定时器配置
* 输入  :电机转速speed,转角angle
* 输出  :无
* 调用  :主函数
* 返回值:无
****************************/
void TIM2_Master__TIM3_Slave_Configuration(u32 PulseFrequency, u32 pulse)
{
        TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
        TIM_OCInitTypeDef TIM_OCInitStructure;
       
        u16 nPDTemp ;
        u16 pulse_number;
        float p=PulseFrequency;
        TIM_Cmd(TIM2, DISABLE);
        nPDTemp = (11.25/p);                            //TIM2重装值是11.25时1s转一圈(电机32细分下)
        pulse_number = (16.7778*pulse);//TIM3重装值是16.7778时转1°(电机32细分下)
       
        // 时基配置:配置PWM输出定时器——TIM2
        /* Time base configuration */
        TIM_TimeBaseStructure.TIM_Period = nPDTemp; //定时周期为nPDTemp
        TIM_TimeBaseStructure.TIM_Prescaler = 999; //预分频值1000,即f=72khz
        TIM_TimeBaseStructure.TIM_ClockDivision = 0; //时钟分频因子,会影响滤波器采样频率,与本实验无影响
        TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
        TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; //指定重复计数器值
        TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
       
        // 输出配置:配置PWM输出定时器——TIM2
        /* PWM1 Mode configuration: Channel1 */   
        TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //TIM 脉冲宽度调制模式 1
        TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //高电平有效
        TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能输出
        TIM_OCInitStructure.TIM_Pulse = nPDTemp>>1;//50% //比较tim_ccr的值,输出脉冲发生跳变
        TIM_OC1Init(TIM2, &TIM_OCInitStructure); //初始化
        TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable); //使能 TIMx 在 CCR1 上的预装载寄存器
        TIM_ARRPreloadConfig(TIM2, ENABLE); //使能或者失能 TIMx 在 ARR 上的预装载寄存器
       
        // 时基配置:配置脉冲计数寄存器——TIM3
        TIM_TimeBaseStructure.TIM_Period = pulse_number;      //0x1900是360°;//改变给电机的脉冲个数                     
        TIM_TimeBaseStructure.TIM_Prescaler = 0;
        TIM_TimeBaseStructure.TIM_ClockDivision = 0;
        TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
        TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
        TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
        // 输出配置:配置输出比较非主动模式定时器——TIM3
        // Output Compare Active Mode configuration: Channel1
        TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Inactive; //输出比较非主动模式,(匹配时设置输出引脚为无效        电平,当计数值为比较/捕获寄存器值相同时,强制输出为低电平)      
        TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
        TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
        TIM_OCInitStructure.TIM_Pulse = 0xFFFF; // 这里的配置值意义不大   
        TIM_OC1Init(TIM3, &TIM_OCInitStructure);

        // 配置TIM2为主定时器
        // Select the Master Slave Mode
        TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable); //设置 TIM2 主/从模式并使能
        // Master Mode selection  
        TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); //使用更新事件作为触发输出
       
        // 配置TIM3为从定时器
        // Slave Mode selection: TIM3
        TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Gated); //选择 TIM3为从模式   TIM_SlaveMode_Gated-当触发信号(TRGI)为高电平时计数器时钟使能
        TIM_SelectInputTrigger(TIM3, TIM_TS_ITR1); //选择 TIM3 输入触发源    TIM_TS_ITR1-TIM 内部触发 1
        TIM_ITRxExternalClockConfig(TIM3, TIM_TS_ITR1);//设置 TIM3 内部触发为外部时钟模式   TIM_TS_ITR1-TIM 内部触发 1
        TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE); //使能TIM3     TIM 捕获/比较 1 中断源          
       
        TIM_Cmd(TIM2, ENABLE);
        TIM_Cmd(TIM3, ENABLE);
}

/****************************************************
* 函数名:Output_Pulse
* 描述  :无
* 输入  :无
* 输出  :无
* 返回值:无
******************************************************/
void Output_Pulse(u16 Num)
{
                GPIO_ResetBits(GPIOA, GPIO_Pin_2);//指定引脚输出低电平,此时灯全灭        使能
                TIM3->CCR1 = Num;
                TIM3->CNT = 0;
                TIM_Cmd(TIM3, ENABLE);
                TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE);
                TIM_Cmd(TIM2, ENABLE);
}

/****************************************************
* 函数名:angle_set
* 描述  :无
* 输入  :无
* 输出  :无
* 返回值:无
******************************************************/
void angle_set(u8 dir,u8 angle)
{
        if(dir==0)
                                GPIO_ResetBits(GPIOA, GPIO_Pin_1);//指定引脚输出低电平,此时灯全灭,方向
        else
                                GPIO_SetBits(GPIOA, GPIO_Pin_1);//指定引脚输出低电平,此时灯全灭,方向
       
        Output_Pulse(angle*6400);
}

使用特权

评论回复
26
欢乐家园|  楼主 | 2022-8-22 23:55 | 只看该作者
4.time.h程序
#ifndef __TIMER_H
#define        __TIMER_H
#include "main.h"
extern unsigned char Flag;
extern unsigned char TIM2_Pulse_TIM3_Counter_OK;
void GPIO_Config(void);
void TIM2_Master__TIM3_Slave_Configuration(u32 PulseFrequency,u32 pulse);
void Frequence_Setting(u32 PulseFrequency);
void Output_Pulse(u16 Num);
void angle_set(u8 dir,u8 angle);
#endif

使用特权

评论回复
27
欢乐家园|  楼主 | 2022-8-22 23:56 | 只看该作者
5.usart1.c程序
#include <main.h>
#include <usart1.h>
#include <string.h>
#include <math.h>

/******************************************************
* 函数名:USART1_Config
* 描述  :USART1 GPIO 配置,工作模式配置
* 输入  :无
* 输出  : 无
* 调用  :外部调用
***************************************************** */
void USART1_Config(void)
{
        GPIO_InitTypeDef GPIO_InitStructure;
        USART_InitTypeDef USART_InitStructure;
       
        /* config USART1 clock */
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

        /* USART1 GPIO config */
        /* Configure USART1 Tx (PA.09) as alternate function push-pull */
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStructure);   
        /* Configure USART1 Rx (PA.10) as input floating */
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
       
        /* USART1 mode config */
        USART_InitStructure.USART_BaudRate = 115200;
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;
        USART_InitStructure.USART_StopBits = USART_StopBits_1;
        USART_InitStructure.USART_Parity = USART_Parity_No ;
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
        USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
        USART_Init(USART1, &USART_InitStructure);
        USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
       
        USART_Cmd(USART1, ENABLE);
}

/******************************************************
* 函数名:fputc
* 描述  :重定向c库函数printf到USART1
* 输入  :无
* 输出  :无
* 调用  :由printf调用
***************************************************** */
int fputc(int ch, FILE *f)
{
        /* 将Printf内容发往串口 */
        USART_SendData(USART1, (unsigned char) ch);
        while (!(USART1->SR & USART_FLAG_TXE));
       
        return (ch);
}

/*-------------------------------------------------------------------------------*/
/******************************************************
* 函数名:USART1_IRQHandler
* 描述  :USART1中断服务函数
* 输入  :无
* 输出  :无
* 调用  :中断调用
***************************************************** */
u8 Res;
extern int a;
u16 USART_RX_STA=0;       //接收状态标记         
u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
void USART1_IRQHandler(void)                        //串口1中断服务程序
        {
               
                Res =USART_ReceiveData(USART1);        //读取接收到的数据
               
                if((USART_RX_STA&0x8000)==0)//接收未完成
                        {
                        if(USART_RX_STA&0x4000)//接收到了0x0d
                                {
                                if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
                                else USART_RX_STA|=0x8000;        //接收完成了
                                }
                        else //还没收到0X0D
                                {       
                                if(Res==0x0d)USART_RX_STA|=0x4000;
                                else
                                        {
                                        USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
                                        USART_RX_STA++;
                                        if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收          
                                        }                 
                                }
                        }               
                                a=1;         
        }
#if SYSTEM_SUPPORT_OS         //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
        OSIntExit();                                                                                           
#endif

/******************* (C) COPYRIGHT 2012 WildFire Team *****END OF FILE************/

使用特权

评论回复
28
欢乐家园|  楼主 | 2022-8-22 23:56 | 只看该作者
6.usart1.h程序
#ifndef __USART1_H
#define        __USART1_H
#include <main.h>
#include <stdio.h>

#define USART_REC_LEN                          200          //定义最大接收字节数 200
#define EN_USART1_RX                         1                //使能(1)/禁止(0)串口1接收
extern u8  USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u16 USART_RX_STA;                         //接收状态标记
int simple_atoi(char *source);
void USART1_Config(void);
#endif /* __USART1_H */

使用特权

评论回复
29
欢乐家园|  楼主 | 2022-8-22 23:57 | 只看该作者
源代码:
链接:https://pan.baidu.com/s/1PkmxkoeHfSfX06Gnj4Dr3A
提取码:eas7

使用特权

评论回复
30
Uriah| | 2022-9-30 09:30 | 只看该作者

紫外光复位保护电路是不行的

使用特权

评论回复
31
Bblythe| | 2022-9-30 12:29 | 只看该作者

让尽可能多的变量使用直接寻址,提高速度

使用特权

评论回复
32
万图| | 2022-12-25 08:27 | 只看该作者

STM32芯片中有多个工作时钟源的外设很常见

使用特权

评论回复
33
帛灿灿| | 2022-12-25 11:26 | 只看该作者

需要将阻焊漆(阻焊油墨)覆盖在最外层的PCB设计布线上

使用特权

评论回复
34
Bblythe| | 2022-12-25 12:29 | 只看该作者

让尽可能多的变量使用直接寻址,提高速度

使用特权

评论回复
35
周半梅| | 2022-12-25 14:25 | 只看该作者

要将多余空间去除。对于第一个字符从第一行开始向下扫描,把那些一行中所有的点的灰度值全为0的点去掉,直到扫描到有一行不全为0时为止。

使用特权

评论回复
36
Pulitzer| | 2022-12-25 15:28 | 只看该作者

每个Strategy交由Spring管理,并在构造后注册

使用特权

评论回复
37
童雨竹| | 2022-12-25 17:24 | 只看该作者

标志寄存器用于反应处理器的状态和运算结果的某些特征以及控制指令的执行。

使用特权

评论回复
38
Wordsworth| | 2022-12-25 18:27 | 只看该作者

硬件设计和软件设计本来就是鱼和熊掌的关系,两者不可兼得

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则