精华内容
下载资源
问答
  • 2.1 STM32电机PWM控制我们这里提供左右两个电机PWM控制代码,在公众号:小白学移动机器人,发送:电机PWM控制,即可获得源码工程下载链接。2.1.1 实现工具STM32单片机、直流减速电机、TB6612双路电机驱动器、keil...

    99b4aaecbdc3d303e1491a171bb6f410.png

    2.1 STM32电机PWM控制

    我们这里提供左右两个电机PWM控制代码,在公众号:小白学移动机器人,发送:电机PWM控制,即可获得源码工程下载链接。

    2.1.1 实现工具

    STM32单片机、直流减速电机、TB6612双路电机驱动器、keil5

    2.1.2 电机实物接线图解

    具体到我们的电机,我们可以看看电机后面的图解。

    258e56caae915d13a9cb54600cb625c8.png

    中间的四根线(红绿白黑)是编码器的线,只是用于测速,和直流电机本身没有联系。

    综上所述,我们只需控制施加在黄线和棕色线两端的直流电压大小和极性即可实现调试和换向。

    2.1.3 TB6612FNG 使用说明

    要实现上面的调试和换向功能,我们可以使用单片机实现的,但是单片机IO 的带负载能力较弱,而直流电机是大电流感性负载,所以我们需要功率放大器件,在这里,我们选择了 TB6612FNG驱动器。

    TB6612FNG 是东芝半导体公司生产的一款直流电机驱动器件,它具有大电流MOSFET-H 桥结构,双通道电路输出,可同时驱动 2 个电机。也许大家更熟悉 L298N,其实这两者的使用基本一致的。而且,相比 L298N 的热耗性和外围二极管续流电路,它无需外加散热片,外围电路简单,只需外接电源滤波电容就可以直接驱动电机,利于减小系统尺寸。对于PWM信号输入频率范围,高达100KHz的频率足以满足我们大部分的要求:

    698d38160fa03cbeed488448574e69da.png

    以下是TB6612模块测试一个电机的接线图:

    1d5328597591d27c147124c70a08dc36.png

    VM直接接电池即可,VCC是内部的逻辑供电,一般给3.3v或者5v都行,模块的3个GND接任意一个就行。STBY置高模块才能正常工作。

    完成上面的接线之后,我们可以开始控制电机了,上图中红色的部分的5个引脚控制一路电机,蓝色部分控制另一路电机,这里以A路为例。A01和A02分别接电机的+和-。然后同PWMA,AIN2,AIN1控制电机。其中PWMA接到单片机的PWM引脚,一般10KHZ即可,并通过改变占空比调节电机的速度。下面是真值表:

    2f42fb79a4308f0fb2715f1b92339fa9.png

    AIN1接3.3-5v、AIN2接GND,PWMA接到3.3-5v。这样相当于控制电机满占空比正转,反转相反。

    2.1.4 部分代码分享

    我这里就用通俗的话描述一下定时器的PWM。

    首先明确一点,STM32单片机引脚PWM电压输出一般为0-3.3V。该电压属于有效值,看过波形的大家都知道,PWM一个周期内有高电平和低电平。切换电平标志就是0-自动重载值范围内的一个数,相比即可得到PWM的百分比大小。例如TIM1->CCR1这个寄存器就存放了一个这样的数字。当定时器的计数器比较当前数字等于TIM1->CCR1,立即切换引脚电平状态。

    大家都知道每一个定时器的内部都有一个16计数器,根据计数模式的不同,该计数器存在自加、自减两种情况,这里按自加描述。定时器硬件配置的时候往往需要我们填入两个参数,自动重载值 arr、预分频值psc。

    预分频值psc的作用:决定该定时器的时钟频率的大小。

    自动重载值 arr的作用:范围在0-65535之间,决定该定时器溢出的位置大小。

    举个例子,这里按照单片机系统时钟72MHz描述,如果psc=7200,那么该定时器的时钟频率为72M/7200=10KHz,也就是说该计数器每秒自加10000次数据。如果arr=1000,那么也就是说该定时器1000/10000=0.1溢出一次,也就是说该PWM的频率为1/0.1=10Hz。

    (1)TB6612驱动器引脚配置、PWM配置

    Motor_Init函数中的四个引脚选择根据每个人的情况而定,需要做必要的更改。

    PWM_Init函数中具体的PWM引脚选择也是根据每个人的情况而定,如果可以的话,可以直接按照这里的配置使用。

    #include 

    (2)PWM电机控制

    #include 

    (3)main.c

    #include 

    2.1.5 总结

    其实本篇文章就是简单的实现一下直流减速电机PWM控制。还没有对电机的速度实现闭环控制,下一篇,我们来写光电霍尔编码器的电机测速的原理以及代码实现。

    系列文章

    搭建ROS小车真的难吗?

    ROS小车软件结构以及控制流程

    50216c55ec1afe1ab2e40cec62ebec47.png
    展开全文
  • 不管是对于初学者还是对于一个玩过单片机的电子爱好者来说,或多或少都接触到过小车项目,今天给大家介绍的的一个项目基于STM32超声波避障小车。这也是我曾经的一个课设,在此开源分享给大家,全文5000多字,干货满...

    35e5c6af5e08a419d3552981c31e598f.png

    不管是对于初学者还是对于一个玩过单片机的电子爱好者来说,或多或少都接触到过小车项目,今天给大家介绍的的一个项目基于STM32超声波避障小车。这也是我曾经的一个课设,在此开源分享给大家,全文5000多字,干货满满,加油读完,保证你收货多多!

    735251b51664c986e3ff9a8b88cf465a.png
    基于STM32超声波避障小车https://www.zhihu.com/video/1208122319123783680

    处理器电路设计

    单片机是系统的CPU,是系统稳定、正常运行的重要前提,以下为单片机选型的两种方案:
    (1)传统的8位单片机,是通过超大规模集成电路对其进行集成为一个独立芯片的控制器。内部组件包括CPU、随机存储器、只读存储器、I/O接口、中断系统、计时器、串口通讯、数模转换等。STC89C52单片机是最常见的51单片机,但是资源较少,精确度低,处理速度相比STM32单片机差很多。

    (2)使用目前市面上最常见的STM32单片机,STM32系列单片机可分为ARMCortex-M3内核体系结构的不同应用领域。它可分为STM32F1系列和STM32F4系列,STM32F1系列单片机时钟频率最高可达72米,在同一产品中性能最好。单片机的基本处理速度为36米,16位单片机的性能也很好。微晶片的内建快闪记忆体相当大,范围从32kb到512kb,可视需要选择。单个设备的功耗非常低,仅360mA,32位单片机产品的功耗最低,每兆赫只有0.5安培。特别值得一提的是,内接单晶片汇流排是一种Harvard架构,可执行速度高达1.25 DMIPS/MHz的指令。此芯片越来越多地被用作主要控制器。

    通过对单片机的资源和处理时间的速度我们采用选择STM32103C8T6为本系统主控芯片,程序下载是只需要一个JLINK就可以轻松完成。控制器模块电路如下所示:

    0b2457d4cbb15dafbf399be50b2e9289.png

    电源模块设计

    本设计采用锂电池供电, 模块的供电电压一般都为5V,同时超声波模块需要较大的电流才能正常工作,所以在降压的基础上也要保证足够大的输出电流。本设计采用可调输出版本,模块的输入电压范围广,输出电压在1.25V-35V内可调,电压转换效率高,输出纹波小。降压电路如下所示:

    828c3d48beb51d4d61073a3d04aaede3.png

    电机驱动模块设计

    要完成转向是能够利用单片机实现的,然而单片机I0的带负载能力弱,因此我们选择了大功率放大器件TB6612FNG。TB6612FNG是采用MOSFET-H桥结构的双通道大电流电路输出,可以控制2个电机的驱动。相比普通的电机驱动,外围电路非常简单,只需要一个芯片和一个钽电容进行PWM输出滤波,系统尺寸小。PWM信号输入频率范围广,轻松满足本设计的需求。

    f2504e4efdc8350c5a0033893b01bf75.png

    电机驱动引脚表

     1控制芯片:TB6612
     2控制芯片数量:2
     3    1号TB6612引脚分配:
     4     VM         PWMA--------->TIM1_CH1(PA8)
     5     VCC        AIN2--------->GPIOB_12
     6     GND        AIN1--------->GPIOB_13
     7     AO1        STBY--------->GPIOB_14
     8     AO2        BIN1--------->GPIOB_15
     9     BO2        BIN2--------->GPIOA_12
    10     BO1        PWMB--------->TIM1_CH2(PA9)
    11     GND        GND
    12    2号TB6612引脚分配:
    13     VM         PWMA--------->TIM1_CH3(PA10)
    14     VCC        AIN2--------->GPIOB_5
    15     GND        AIN1--------->GPIOB_6
    16     AO1        STBY--------->GPIOB_7
    17     AO2        BIN1--------->GPIOB_8
    18     BO2        BIN2--------->GPIOA_9
    19     BO1        PWMB--------->TIM1_CH4(PA11)
    20     GND        GND
    21真值表
    22     AIN1   0     1     0     1
    23     AIN2   0     0     1     1
    24     BIN1   0     1     0     1
    25     BIN2   0     0     1     1
    26           停止  正转  反转  刹车

    电机所用到的定时器配置

     1//初始化TIMX,设置TIMx的ARR,PSC
     2//arr:自动重装载初值,psc为预分频值,两者配合控制定时器时钟的周期
     3//定时器选择TIM1
     4static void TB6612_ADVANCE_TIM1_Mode_Config(TIM_TypeDef* TIMx,uint16_t arr,uint16_t psc,uint16_t duty)
     5{
     6    //-----------------时基结构体初始化-------------------------/
     7    TIM_TimeBaseInitTypeDef TIM_TimeStructure;
     8    /*开启定时器1时钟,即内部时钟CK_INT=72M*/
     9    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
    10    TIM_DeInit(TIMx);
    11    /*内部时钟作为计数器时钟,72MHZ*/
    12    TIM_InternalClockConfig(TIMx);
    13    /*自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断*/
    14    TIM_TimeStructure.TIM_Period=arr;
    15    /*时钟预分频系数为71,则驱动计数器的时钟CK_CNT=CK_INT/(71+1)=1MHZ*/
    16    TIM_TimeStructure.TIM_Prescaler=psc-1;
    17    /*设置时钟分割,TIM_CKD_DIV1=0,PWM波不延时*/
    18    TIM_TimeStructure.TIM_ClockDivision=TIM_CKD_DIV1;
    19    /*向上计数模式*/
    20    TIM_TimeStructure.TIM_CounterMode=TIM_CounterMode_Up;
    21    /*重复计数器*/
    22    TIM_TimeStructure.TIM_RepetitionCounter=0;
    23    /*初始化定时器*/
    24    TIM_TimeBaseInit(TIMx,&TIM_TimeStructure);
    25    /*使能ARR预装载寄存器(影子寄存器)*/
    26    TIM_ARRPreloadConfig(TIMx,ENABLE);
    27    //-----------------输出比较结构体初始化-----------------------/
    28    TIM_OCInitTypeDef   TIM_OCInitStructure;
    29    /*PWM模式设置,设置为PWM模式1*/
    30    TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
    31    /*PWM输出使能相应的IO口输出信号*/
    32    TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
    33    /*设置占空比大小,CCR1[15:0]: 捕获/比较通道1的值,若CC1通道配置为输出:CCR1包含了装入当前捕获/比较1寄存器的值(预装载值)。*/
    34    TIM_OCInitStructure.TIM_Pulse=duty;
    35    /*输出通道电平极性设置*/
    36    TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;
    37    /*初始化输出比较参数*/
    38    TIM_OC1Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道1
    39    TIM_OC2Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道2
    40    TIM_OC3Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道3
    41    TIM_OC4Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道4
    42    /*自动重装载*/
    43    TIM_OC1PreloadConfig(TIMx,TIM_OCPreload_Enable);
    44    TIM_OC2PreloadConfig(TIMx,TIM_OCPreload_Enable);
    45    TIM_OC3PreloadConfig(TIMx,TIM_OCPreload_Enable);
    46    TIM_OC4PreloadConfig(TIMx,TIM_OCPreload_Enable);
    47    /*使能计数器*/
    48    TIM_Cmd(TIMx,ENABLE);
    49    /*主输出使能,如果设置了相应的使能位(TIMx_CCER寄存器的CCxE、CCxNE位),则开启OC和OCN输出。*/
    50    TIM_CtrlPWMOutputs(TIMx,ENABLE);    
    51}
    52//高级定时器输出通道初始化函数
    53static void TB6612_ADVANCE_TIM_Gpio_Config()
    54{
    55    GPIO_InitTypeDef  GPIO_InitStruct;
    56    /*----------通道1配置--------------*/
    57    /*定时器1输出比较通道*/
    58    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    59    /*配置为复用推挽输出*/
    60    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
    61    GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8;
    62    GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
    63    GPIO_Init(GPIOA,&GPIO_InitStruct);
    64    /*-----------通道二配置-------------*/
    65    /*定时器1输出比较通道*/
    66    /*配置为复用推挽输出*/
    67    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
    68    GPIO_InitStruct.GPIO_Pin=GPIO_Pin_11;
    69    GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
    70    GPIO_Init(GPIOA,&GPIO_InitStruct);
    71     /*-----------通道三配置-------------*/
    72    /*定时器1输出比较通道*/
    73    /*配置为复用推挽输出*/
    74    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
    75    GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
    76    GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
    77    GPIO_Init(GPIOA,&GPIO_InitStruct);
    78     /*-----------通道四配置-------------*/
    79    /*定时器1输出比较通道*/
    80    /*配置为复用推挽输出*/
    81    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
    82    GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;
    83    GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
    84    GPIO_Init(GPIOA,&GPIO_InitStruct);
    85}

    超声波模块

    采用HC-SR04超声波模块,该芯片具有较高的集成度以及良好的稳定性,测度距离十分精确,十分稳定。供电电压为DC5V供电电流小于10mA,探测距离为0.010m-3.5m一共有四个引脚VCC(DC5V)、Triger(发射端)、Echo(接收端)、GND(地)。HC-SR04实物图如下:

    879a177170d1f8b6ab8d5a5b48be4769.png

    该模块是利用单片机的IO触发电平测距,单片机内部利用普通定时器产生一个高电平信号之后,超声波就可以自主发送频率为40khz的方波,然后等待信号的返回;若有信号返回,单片机IO口就立刻输出一高电平,利用高电平产生的时间可以计算小车与障碍物的距离。最终距离就是高电平持续时间乘以声音在空气中传播的速度再除以2,可以反复测量距离。

    在程序开始首先初始化超声波,利用定时器并设置时基的自动重装载初值1000,psc为预分频值72,这样的话我们产生一次中断的时间是1ms,并设置抢占优先级0,子优先级3。HC_SR04_Echo引脚接收到高电平,打开定时器,且每1ms进入一次中断。在测量时首先让Trig发送一个大于10us的高电平,然后拉高HC_SR04_Trig,当Echo为0时打开定时器计时,当Echo为1时关闭定时器,通过公式计算距离。

    模块工作原理:
    (1)单片机触发引脚,输出高电平信号;
    (2)模块发送端自动发送特定频率的方波;
    (3)如果有信号返回,通过IO输出一高电平,高电平持续的时间就是超声波的发射时长;
    (4)测试距离=(高电平时间*声速(340M/S))/2。

    注意:在硬件操作上需要首先让模块地端先连接,否则会影响模块工作。测距时,被测物体的摆放不能太过于杂乱,否则会影响测试结果。

    f7a0d5b5870a925ec4ddae105fc914a1.png

    超声波重要代码(可参考)

     1/* 获取接收到的高电平的时间(us*/
     2uint32_t Get_HC_SR04_Time(void)
     3{
     4    uint32_t t=0;
     5    t=Acoustic_Distance_Count*1000;//us
     6    t+=TIM_GetCounter(TIM2);//获取us
     7    TIM2->CNT =0;
     8    Acoustic_Distance_Count=0;
     9    Systic_Delay_us(100);
    10    return t;
    11}
    12/*获取距离*/
    13void Get_HC_SR04_Distance(void)
    14{
    15    static uint16_t count=0;
    16    switch(count)
    17    {
    18        case 1:
    19        {
    20            GPIO_SetBits(Acoustic_Port,HC_SR04_Trig);//Trig发送一个大于10us的高电平
    21        }break;
    22
    23        case 15:
    24        {
    25            count=0;
    26            GPIO_ResetBits(Acoustic_Port,HC_SR04_Trig);
    27            while(GPIO_ReadInputDataBit(Acoustic_Port,HC_SR04_Echo)==0);//当Echo为0时打开定时器 计时
    28            Open_Tim2();
    29            while(GPIO_ReadInputDataBit(Acoustic_Port,HC_SR04_Echo)==1);//当Echo为0时打开定时器 计时
    30            Close_Tim2();
    31            HC_SR04_Distance=(float)(Get_HC_SR04_Time()/5.78);
    32
    33        }break;
    34        default:break;
    35    }
    36    count++;
    37}

    舵机模块

    本系统使用的是SG90型号的舵机,舵机是一种常见的角度驱动器,本系统需要判断不同位置的障碍物可以且对转向的力度小。舵机可以理解为方向盘称,方向盘是一个常见的名字。它实际上是一个伺服马达。舵机实物图如下:

    bd3b32b58dab70359413d8fed2b37696.png

    舵机模块接口简单,舵机模块只有三个引脚。分别引引出了三根线左右两边是电源正负接口线,中间一根是PWM信号线直接连接单片机的控制引脚。通过控制单片机的引脚输出的脉冲宽度进而控制舵机旋转的角度。舵机每增加0.1ms 舵机对应增加9度。

    0.5ms---------0

    1.0ms---------45

    1.5ms---------90

    2.0ms---------135

    2.5ms-----------180

    20ms的时基脉冲,如果想让舵机转90度,就应该发生一个高电平持续时间为1.5ms,周期为20ms的方波,duty=1.5/20=7.5%。在这里设置定时器自动重装载寄存器arr的值为1000,所以当占空比为百分之75是,在程序中就要设置占空比为75/1000=7.5%, 这就是具体的算法。

    舵机重要代码(可参考)

     1/**PWM引脚初始化*/
     2static void SERVO_Gpio_Init(void)
     3{
     4    GPIO_InitTypeDef  GPIO_InitStruct;
     5    /*----------通道2配置--------------*/
     6    /*定时器3输出比较通道*/
     7    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
     8    /*配置为复用推挽输出*/
     9    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
    10    GPIO_InitStruct.GPIO_Pin=GPIO_Pin_7;
    11    GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
    12    GPIO_Init(GPIOA,&GPIO_InitStruct); 
    13}
    14//定时器3初始化,设置TIMx的ARR,PSC
    15//arr:自动重装载初值,psc为预分频值,两者配合控制定时器时钟的周期
    16static void SERVO_TIM_Config(TIM_TypeDef* TIMx,uint16_t arr,uint16_t psc,uint16_t duty)
    17{
    18    //-----------------时基结构体初始化-------------------------/
    19    TIM_TimeBaseInitTypeDef TIM_TimeStructure;
    20    /*开启定时器3时钟,即内部时钟CK_INT=72M*/
    21    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
    22    TIM_DeInit(TIMx);
    23    /*内部时钟作为计数器时钟,72MHZ*/
    24    TIM_InternalClockConfig(TIMx);
    25    /*自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断*/
    26    TIM_TimeStructure.TIM_Period=arr;//1000 当定时器从0计数到999,即1000次,为一个定时周期
    27    /*时钟预分频系数为71,则驱动计数器的时钟CK_CNT=CK_INT/(1440-1+1)=0.05MHZ*/
    28    TIM_TimeStructure.TIM_Prescaler=psc-1;;//1400  //即定时器的频率为5KHZ   
    29    /*设置时钟分割,TIM_CKD_DIV1=0,PWM波不延时*/
    30    TIM_TimeStructure.TIM_ClockDivision=TIM_CKD_DIV1;
    31    /*向上计数模式*/
    32    TIM_TimeStructure.TIM_CounterMode=TIM_CounterMode_Up;
    33    /*重复计数器*/
    34    TIM_TimeStructure.TIM_RepetitionCounter=0;
    35        /*初始化定时器*/
    36    TIM_TimeBaseInit(TIMx,&TIM_TimeStructure);
    37    /*使能ARR预装载寄存器(影子寄存器)*/
    38    TIM_ARRPreloadConfig(TIMx,ENABLE);
    39    //-----------------输出比较结构体初始化 开始-----------------------/
    40    TIM_OCInitTypeDef   TIM_OCInitStructure;
    41    /*PWM模式设置,设置为PWM模式1*/
    42    TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
    43    /*PWM输出使能相应的IO口输出信号*/
    44    TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
    45    /*设置占空比大小,CCR1[15:0]: 捕获/比较通道1的值,若CC1通道配置为输出:CCR1包含了装入当前捕获/比较1寄存器的值(预装载值)。*/
    46    TIM_OCInitStructure.TIM_Pulse=duty;   //占空比大小
    47    /*输出通道电平极性设置*/
    48    TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;
    49    /*初始化输出比较参数*/
    50    TIM_OC2Init(TIMx,&TIM_OCInitStructure);
    51    //-----------------输出比较结构体初始化 结束-----------------------/    
    52    /*自动重装载*/
    53    TIM_OC2PreloadConfig(TIMx,TIM_OCPreload_Enable);
    54    /*使能计数器*/
    55    TIM_Cmd(TIMx,ENABLE);
    56    /*主输出使能,如果设置了相应的使能位(TIMx_CCER寄存器的CCxE、CCxNE位),则开启OC和OCN输出。*/
    57    TIM_CtrlPWMOutputs(TIMx,ENABLE);    
    58}
    59/*舵机PWM初始化
    60   每增加0.1ms 舵机对应增加9度
    610.5ms---------0
    621.0ms---------45   
    631.5ms---------90
    642.0ms---------135
    652.5ms-----------180
    662.1ms    turn_left=150
    670.8ms    turn_right=25
    681.3ms    turn_front=75
    6920ms的时基脉冲,如果想让舵机转90度,就应该发生一个高电平为1.5ms,周期为20ms的方波,duty=1.5/20=7.5% ,而定时器自动重装载寄存器arr的值为 1000 ,所以duty=75,时占空比为75/1000=7.5%. 
    70*/
    71void SERVO_Init(void)
    72{
    73    SERVO_Gpio_Init();
    74    SERVO_TIM_Config(TIM3,1000,1440,turn_front);
    75/** 我们把定时器设置自动重装载寄存器 arr 的值为 1000,设置时钟预分频器为 1440,则
    76驱动计数器的时钟:CK_CNT = CK_INT / (1440-1+1)=0.05M,则计数器计数一次的时间等于:
    771/CK_CNT=20us,当计数器计数到 ARR 的值 1000 时,产生一次中断,则中断一次的时间
    78为:1/CK_CNT*ARR=20ms。
    79PWM 信号的频率 f = TIM_CLK/{(ARR+1)*(PSC+1)}  TIM_CLK=72MHZ
    80               = 72 000 000/(1000*1440)=5KHZ    
    81*/    
    82}
    83/*舵机角度控制*/
    84void SERVO_Angle_Control(uint16_t Compare2)
    85{
    86    TIM_SetCompare2(TIM3,Compare2);
    87}

    编码器模块

    调节小车前进的速度和避障快慢我们采用EC11旋转式编码器,可以用于光度、湿度、音量调节等参数的调节。EC11编码器的形状类似于电位器,中心有一个旋钮可以调节PWM信号,光电码盘利用光电转换原理输出三组方波脉冲。EC11编码器的实物图如下:

    4d70b11c6a04cfa143cae1e70a92acb4.png

    OLED显示模块

    用来显示小车转速,以及左右编码器数值和电池电压等参数所用的是OLED显示模块,分辨率较高,而且功耗低,正常显示时仅0.06W,供电电压范围在3.3V-5V,有IIC和SPI两种通信协议可供选择。显示模块的亮度和对比度可以通过程序设置。由于它使用寿命长以及其他的优点,OLED更加适合小系统,本系统由于单片机引脚有限,不适合利用简单的LCD1602或者12864来显示,在多方对比之下OLED效果更好。OLED显示部分相对比较简单,大家参考中景园的例程就可以实现。

    微信公众号:果果小师弟

    个人网站:http://www.liuyao-blog.cn

    展开全文
  • 在使用STM32F103产生固定频率、固定占空比的PWM波时,虽然有官方以及众多开发板提供的例程,但是关于有点问题并没有说的很清晰,并且《STM32F10X参考手册》的中文翻译可能容易造成歧义,所以一开始并没有理解,这里...

    433beaa39355b12824a3dd87f00b27e5.png

    在使用STM32F103产生固定频率、固定占空比的PWM波时,虽然有官方以及众多开发板提供的例程,但是关于有点问题并没有说的很清晰,并且《STM32F10X参考手册》的中文翻译可能容易造成歧义,所以一开始并没有理解,这里就梳理一下我的理解,如果有误解的情况,希望交流指正。

    1. 遇到的问题

    先直接上段配置代码,这段代码是产生一个20kHz固定频率,50%固定占空比的方波信号,典型的配置过程,一般来说也不会有什么太多的疑问。但是我逐步了解背后的定时器工作逻辑的时候,就产生了一些疑问,也没有找到合理、清晰的解答。先说明一下,只有通用定时器和高级定时器才有PWM模式,基本定时器没有。
    static void PWM_Mode_Config(void)
    {
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;
    // 基本定时器配置
    TIM_DeInit(TIM3);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // 开启定时器时钟,即内部时钟CK_INT=72M
    TIM_TimeBaseStructure.TIM_Period = 49; // 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
    TIM_TimeBaseStructure.TIM_Prescaler = 71; // 时钟预分频数为
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 设置时钟分频系数:不分频(这里用不到)
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // 初始化定时器
    TIM_ClearFlag(TIM3, TIM_FLAG_Update); // 清除计数器更新标志位
    // PWM模式配置
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // 配置为PWM模式1
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 使能输出
    TIM_OCInitStructure.TIM_Pulse = 25; // 设置初始PWM脉冲宽度为25,实际上就是配置占空比(捕获比较寄存器1,CCR1)
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; // 当定时器计数值小于CCR1_Val时为低电平
    TIM_OC2Init(TIM3, &TIM_OCInitStructure ); // 使能通道2
    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable ); // 使能预装载(使能CCR1的预装载)
    TIM_ARRPreloadConfig(TIM3, ENABLE); // 使能自动重载寄存器ARR的预装载
    // 开启TIM3
    TIM_Cmd(TIM3, ENABLE); // 使能定时器
    TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); // 使能定时器中断

    1.1 什么是清除标志位

    TIM_ClearFlag(TIM3, TIM_FLAG_Update);  // 清除计数器更新标志位
    代码中有这样一条,有人会问函数 TIM_ClearFlag() 和函数 TIM_ClearITPendingBit() 有什么区别?其实重点在Flag和IT,前者是外设的状态标志,而后者是外设的中断标志。状态标志就是一个外设它有自身的一些标志位(Flag),来表明它处于什么状态,下图就是定时器的状态标记。中断标志就是使能外设的中断后,每次发生一次中断,它会表明发生了什么样的中断,同样中断也有相应的标记。两者分别靠函数 TIM_GetFlagStatus() 和函数 TIM_GetITStatus() 来获取。 没有使能中断时,是可以读取该外设的状态标志的。同理,串口外设也有此区别。
    9244c50e2b227922ee744ebb35a065e5.png

    1.2 模式1和模式2的区别

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;// 配置为PWM模式1
    模式1 在向上计数时,一旦TIMx_CNT < TIMx_CCR1时通道1为有效电平,否则为无效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为无效电平(OC1REF=0),否则为有效电平(OC1REF=1)。 模式2 在向上计数时,一旦TIMx_CNT < TIMx_CCR1时通道1为无效电平,否则为有效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为有效电平,否则为无效电平。 实际上可以看到,模式1和模式2并无本质的区别,只是在同样的有效电平情况下,输出的波形电平相反而已。那么什么又是有效电平?后面有机会单独说明。

    1.3 什么是自动重装载和预装载寄存器?

    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable );// 使能预装载(使能CCR1的预装载)
    TIM_ARRPreloadConfig(TIM3, ENABLE); // 使能自动重载寄存器ARR的预装载
    这个问题才是我写这篇博客的源动力,网上有很多人问,但是回答者没能详细说明,《STM32F10X参考手册》中的描述也有歧义。我只能从别人回答的只言片语中摸索,然后根据自己的理解来解释。下面就看第二节的基本知识。

    2. PWM波产生的过程

    ad966562dba745049ec082e9f2d77762.png
    《STM32F10X参考手册》对此的描述如下: 自动装载寄存器是预先装载的,写或读自动重装载寄存器将访问预装载寄存器。根据在TIMx_CR1寄存器中的自动装载预装载使能位(ARPE)的设置,预装载寄存器的内容被立即或在每次的更新事件UEV时传送到影子寄存器。当计数器达到溢出条件(向下计数时的下溢条件)并当TIMx_CR1寄存器中的UDIS位等于’0’时,产生更新事件。更新事件也可以由软件产生。随后会详细描述每一种配置下更新事件的产生。 我在这个通用定时器框图里面找不到预装载寄存器,这就更增加疑惑了。后来翻了很多问答帖,大概明白了其中的意思,以下是我的总结以及理解。 从框图里面看到自动重装载寄存器(Auto Reload Register,ARR)和捕获/比较寄存器组(Capture/Compare Register,CCR)的框下面有阴影,这就说明这两种寄存器含有影子寄存器(Shadow Register)。影子寄存器实际上才是真正直接参与定时器工作的寄存器,具有即时性。我们配置的ARR和CCR相当于都是上层的寄存器,芯片在工作中,自动将这些上层寄存器的值传递到各自的影子寄存器,从而参与计数或者比较过程。 那么,预装载寄存器在哪里呢? 在参考文献《关于STM32影子寄存器和预装载寄存器和TIM_ARRPreloadConfig》中发现实际上ARR和CCR(可能)在物理底层上是两个寄存器的组合,即预装载寄存器(Preload Register)和影子寄存器。实际上,我们在代码层面,对ARR和CCR赋值,最终传递到直接参与工作的影子寄存器,中间还要经历一个预装载寄存器的传递,它在这里相当于缓存的作用。但是在手册中并没找到确实再物理硬件上存在预装载寄存器的蛛丝马迹(只查到缓存器一说),那么也可以这样理解。所谓的预装载寄存器实际上只是ARR和CCR在他们需要向影子寄存器传递值的时候的一种“功能化”的别称,也就是说在需要传值的时候,ARR和CCR就是各自影子寄存器的预装载寄存器。 预装载寄存器的概念应该是相对于影子寄存器来说的。影子寄存器是即时其作用的,而预装载寄存器的值只有传递到影子寄存器才能起作用,你可以把它理解为一个缓存。就程序员的角度观察,两者共用一个地址,无法直接区别访问,只能通过另外的办法来设置对该地址操作的具体行为。
    作为类比,你可以把预装载寄存器理解为内存中的cache,数据写入了cache,却是不一定写入内存的。当然你可以通过MMU设置为写穿(write through)或写回(write back)模式。 那么ARR和CCR各自的影子寄存器的值在什么时候更新呢? 可以立即将值传入或者每次事件更新的时传入,依赖于相关的控制位。 以ARR为例,控制寄存器TIMx_CR1的位7——ARPE(自动重装载预装载允许位 ,Auto-reload preload enable),写“0”时,TIMx_ARR寄存器没有缓冲;写“1”时,TIMx_ARR寄存器被装入缓冲器。也就是说ARPE写1,则影子寄存器立即被更新,否则只有等到每次事件发生时,才更新,这就是 TIM_ARRPreloadConfig(TIM3, ENABLE); 的含义。
    void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState)
    {
    /* Check the parameters */
    assert_param(IS_TIM_ALL_PERIPH(TIMx));
    assert_param(IS_FUNCTIONAL_STATE(NewState));
    if (NewState != DISABLE)
    {
    /* Set the ARR Preload Bit */
    TIMx->CR1 |= TIM_CR1_ARPE;
    }
    else
    {
    /* Reset the ARR Preload Bit */
    TIMx->CR1 &= (uint16_t)~((uint16_t)TIM_CR1_ARPE);
    }
    }
    同理,CCR的影子寄存器也有相应操作。我选择的是TIM3的通道2,其控制寄存器CCMR1的OC2PE位(CCMR1控制通道1和通道2),相应的操作可以对照库函数来了解。 看到有人回复“如果不改变频率和占空比,纯粹输出PWM波,则可以不需要使能预装载”,那是不是意味着以下两句代码可以不写?这需要试一下……
    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable );	         // 使能预装载(使能CCR1的预装载)
    TIM_ARRPreloadConfig(TIM3, ENABLE); // 使能自动重载寄存器ARR的预装载
    那这样设计预装载和影子寄存器有什么好处呢? 参考如下,我的简单理解就是将定时器的赋值过程和工作过程独立开。 设计preload register和shadow register的好处是,所有真正需要起作用的寄存器(shadow register)可以在同一个时间(发生更新事件时)被更新为所对应的preload register的内容,这样可以保证多个通道的操作能够准确地同步。如果没有shadow register,或者preload register和shadow register是直通的,即软件更新preload register时,同时更新了shadow register,因为软件不可能在一个相同的时刻同时更新多个寄存器,结果造成多个通道的时序不能同步,如果再加上其它因素(例如中断),多个通道的时序关系有可能是不可预知的。 天呐,终于稍微理顺了,虽然不一定对,而且定时器需要死磕的细节太多,精力有限,慢慢来~~ 精彩文章推荐: STM32单片机:独立看门狗、窗口看门狗的配置 stm32-hal库开发简介 STM32如何配置外部中断? cf39dcf28fe59d9bbd193bf82dd4d10c.png
    展开全文
  • STM32利用定时器1的通道1输出PWM波注:本程序基于正点原子的第33讲PWM输出pwm.h文件下的代码:#ifndef __PWM_H #define __PWM_H #include "sys.h" void TIM1_PWM_Init(u16 arr,u16 psc); #endif pwm.c文件下的...

    STM32利用定时器1的通道1输出PWM波

    注:本程序基于正点原子的第33讲PWM输出

    0cf6d81cc59a1f92c4f33b3f7acad0dd.png

    38a9ca1a6ec401856a04bb14bf5f46e1.png

    pwm.h文件下的代码:

    #ifndef __PWM_H
    #define __PWM_H
    #include "sys.h"
    
    void TIM1_PWM_Init(u16 arr,u16 psc);
    
    
    
    #endif
    

    pwm.c文件下的代码:

    #include 

    过程中遇到的问题:

    这句话//TIM_ForcedOC1Config(TIM1,TIM_ForcedAction_Active);不能有,有这句软件仿真结果就一直是高电平。

    使用此函数:TIM_ForcedOC1Config(TIM1, TIM_ForcedAction_InActive);可以将定时器端口强制置为低电平,但是PWM开启时使用TIM_ForcedOC1Config(TIM12, TIM_ForcedAction_Active);PWM就无效了。

    主函数main.c的代码:

    #include 

    软件仿真

    aabea1447e585318915fc1a89ae70827.png

    076cc2bc3e30e0a6e1c798c53c5c8d75.png

    c7609543cb7322264b71d6b776062069.png

    4152c7e4909d82d95ba6745b6b58ec8f.png

    1fb34ba30941fd62486892d9b5bb05a2.png

    443c0f2501d643db17dc41ab9a91353f.png

    d9144deb613cb715c4278194dc60cbb7.png

    这里要选择:周期性刷新逻辑分析仪屏幕,才能显示波形

    428058c880068c89344aa9efbe361f30.png

    c82d90f052197c2574515557d530829a.png

    3f3148a0f5852ebb90b8726a6a19dce2.png

    观察得到是1MHz的方波

    频率的计算:

    ba53473729b196f533b96a77e05342f7.png

    频率设置的相关参数:

    (1)TIM_TimeBaseStructure.TIM_Period ====== ARR

    (2)TIM_TimeBaseStructure.TIM_Prescaler ======PSC

    (3)TIM_OCInitStructure.TIM_Pulse ====== (ARR+1)/2 ------>占空比为50%

    这里TIM_Pulse是设置占空比的

    这里:(ARR+1)*(PSC+1) = 72MHz/1MHz =72000000/1000000 =72

    (ARR+1)(PSC+1) = 72 = 8*9 → ARR = 7 , PSC = 8,

    设置占空比为50%,则TIM_Pulse=(ARR+1)/2 = (7+1)/2 = 4

    展开全文
  • STM32定时器是 ST MCU 内部最基础且常用的外设,实际应用尤为普遍。去年,电堂推出了《STM32 TIMER基础及常规应用介绍》,为大家梳理了 STM32 TIMER 的庞大内容,涵盖 TIMER 的基本应用原理、常规应用等。现在将课程...
  • STM32定时器是 ST MCU 内部最基础且常用的外设,实际应用尤为普遍。去年,电堂推出了《STM32 TIMER基础及常规应用介绍》,为大家梳理了 STM32 TIMER 的庞大内容,涵盖 TIMER 的基本应用原理、常规应用等。现在将课程...
  • 创新放飞梦想,实践铸就未来 小黑上回说到通过STM32 GPIO口加上延时函数实现跑马灯的教程(【跑马灯教程 】),一开始文章的题目是呼吸灯,后来才纠正过来(被锤警告),这回才是真正的呼吸灯! 由于小黑个人技术知识...
  • 不管是对于初学者还是对于一个玩过单片机的电子爱好者来说,或多或少都接触到过小车项目,今天给大家介绍的的一个项目基于STM32超声波避障小车。这也是我曾经的一个课设,在此开源分享给大家,全文5000多字,干货满...
  • STM32定时器是 ST MCU 内部最基础且常用的外设,实际应用尤为普遍。去年,电堂推出了《STM32 TIMER基础及常规应用介绍》,为大家梳理了 STM32 TIMER 的庞大内容,涵盖 TIMER 的基本应用原理、常规应用等。现在将课程...
  • STM32定时器是 ST MCU 内部最基础且常用的外设,实际应用尤为普遍。去年,电堂推出了《STM32 TIMER基础及常规应用介绍》,为大家梳理了 STM32 TIMER 的庞大内容,涵盖 TIMER 的基本应用原理、常规应用等。现在将课程...
  • PA15 H2(本节不用) PB3 H3(本节不用) PB10 为了更加简单地介绍清楚BLDC 6 步方波控制的具体实现,为了实现电机的速度控制,本节直接使用STM32的TIM1产生三路PWM波对应信号IN1、IN2、IN3,通过调节PWM波占空比实现...
  • 一、stm32的pwm输出引脚是使用的IO口的复用功能。二、T2~T5这4个通用定时器均可输出4路PWM——CH1~CH4。三、我们以tim3的CH1路pwm输出为例来进行图文讲解(其它类似),并在最后给出tim3的ch1和ch2两路pwm输出的c代码...
  • 讲完互补PWM,就要说一下COM事件是个什么东西了,在说这个之前需要理解一些寄存器的意思: CCRx : 比较捕获寄存器,控制占空比 CCxE : PWM通道 CCxNE : 互补PWM通道 具体参考STM32参考手册的产生六步PWM章节,参考...
  • 下面的这个是stm32的定时器逻辑图,上来有助于理解: TIM3的ARR寄存器和PSC寄存器, 确定PWM频率。 这里配置的这两个定时器确定了PWM的频率,我的理解是:PWM的周期(频率)就是ARR寄存器值与PSC寄存器值相乘得...
  • 本资源为stm32f103输入捕获得到占空比与周期源代码,keil5工程,在原子哥代码基础上改的,增加了一个获得周期的逻辑,可以得到周期时间和高电平时间,进而得到占空比。亲测准确无误
  • STM32 定时器捕获PWM波 以及占空比
  • STM32F103输入捕获实验 可改占空比 之前做电设时候用到的
  • 博文基于STM32F103ZET6芯片,标准固件库3.5.0和MDK5编写; 博文并不讨论PWM的基础知识,而是直接去谈对PWM灵活的控制问题;想全面了解的可以看看我写的专门针对PWM的博客: ...
  • stm32 进行端口的捕获计数高低电平的时间得到端口的频率和占空比,利用的是定时器2的ch1和ch2
  • STM32 输入捕获 测量频率 PWM占空比

    千次阅读 2020-08-04 16:43:31
    看了网上关于STM32输入捕获的资料,有几篇介绍的很不错,但是内容上还有一点问题,稍加修改,大家可以参考一下。 重要概念理解(对于理解输入捕获功能很重要,特别看了数据手册CCR1\CCR2\CCR3\CCR3云里雾里) PWM...
  • 只需一路定时器的输入捕获即可精确地测量 PWM 频率和占空比,测试精度与定时器的频率有关,测试结果使用串口1打印出来,单位为 us
  • 在比赛提供的官方固件库的基础上进行修改,暂且用了两块板子,板子A烧录的之前写的输出比较输出两路频率和占空比可变的PWM作为信号发生源供烧入捕获的板子B检测。 板子A一路是1Khz作为基频的频率可调,另一路是10Khz...
  • 基于STM32F407ZGT6的输入捕获测量频率以及占空比的完整工程,可直接使用
  • STM32F746G eclipse环境 通过分析PWM 读取传感器TMP05的占空比 并计算温度值 将温度值实时显示在LCD屏幕上
  • stm32标准库输入捕获测频率、占空比.rar
  • STM32输入捕获

    2018-01-05 21:13:11
    STM32的输入捕获类型实验 定时器的设定 PWM 的设定 占空比的选择
  • STM32输入捕获.zip

    2021-08-11 16:02:46
    1.PWM输入捕获占空比和频率。 2.针对不同占空比做出不同响应。
  • 使用CubeMX生成PWM波形,并且进行PWM波形的频率和占空比测量,使用硬件来进行两次捕获,降低代码的工作量。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,405
精华内容 562
关键字:

stm32捕获占空比