**《117组嵌入式分享聚合》**

一、获取STM32代码运行时间的技巧

    测试代码的运行时间的两种方法:

  • 使用单片机内部定时器,在待测程序段的开始启动定时器,在待测程序段的结尾关闭定时器。为了测量的准确性,要进行多次测量,并进行平均取值。

  • 借助示波器的方法是:在待测程序段的开始阶段使单片机的一个GPIO输出高电平,在待测程序段的结尾阶段再令这个GPIO输出低电平。用示波器通过检查高电平的时间长度,就知道了这段代码的运行时间。显然,借助于示波器的方法更为简便。

  •  借助示波器方法的实例

        Delay_us函数使用STM32系统滴答定时器实现:

    #include "systick.h"
    /* SystemFrequency / 1000    1ms中断一次 * SystemFrequency / 100000     10us中断一次 * SystemFrequency / 1000000 1us中断一次 */
    #define SYSTICKPERIOD                    0.000001#define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)
    /**  * @brief  读取SysTick的状态位COUNTFLAG  * @param  无  * @retval The new state of USART_FLAG (SET or RESET).  */static FlagStatus SysTick_GetFlagStatus(void) {if(SysTick->CTRL&SysTick_CTRL_COUNTFLAG_Msk)     {return SET;    }else    {return RESET;    }}
    /**  * @brief  配置系统滴答定时器 SysTick  * @param  无  * @retval 1 = failed, 0 = successful  */uint32_t SysTick_Init(void){/* 设置定时周期为1us  */if (SysTick_Config(SystemCoreClock / SYSTICKFREQUENCY))     { /* Capture error */return (1);    }
    /* 关闭滴答定时器且禁止中断  */    SysTick->CTRL &= ~ (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);                                                  return (0);}
    /**  * @brief   us延时程序,10us为一个单位  * @param  *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us  * @retval  无  */void Delay_us(__IO uint32_t nTime){     /* 清零计数器并使能滴答定时器 */    SysTick->VAL   = 0;      SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;     
    for( ; nTime > 0 ; nTime--)    {/* 等待一个延时单位的结束 */while(SysTick_GetFlagStatus() != SET);    }
    /* 关闭滴答定时器 */    SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;}

        检验Delay_us执行时间中用到的GPIO(gpio.h、gpio.c)的配置:

    #ifndef __GPIO_H#define    __GPIO_H
    #include "stm32f10x.h"
    #define     LOW          0#define     HIGH         1
    /* 带参宏,可以像内联函数一样使用 */#define TX(a)                if (a)    \                                            GPIO_SetBits(GPIOB,GPIO_Pin_0);\else        \                                            GPIO_ResetBits(GPIOB,GPIO_Pin_0)void GPIO_Config(void);
    #endif
    #include "gpio.h"
    /**  * @brief  初始化GPIO  * @param  无  * @retval 无  */void GPIO_Config(void){        /*定义一个GPIO_InitTypeDef类型的结构体*/        GPIO_InitTypeDef GPIO_InitStructure;
    /*开启LED的外设时钟*/        RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE); 
            GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;            GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;             GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;         GPIO_Init(GPIOB, &GPIO_InitStructure);    }

        在main函数中检验Delay_us的执行时间:

    示波器的观察结果:

     可见Delay_us(100),执行了大概102us,而Delay_us(1)执行了2.2us。

        更改一下main函数的延时参数:

     

     示波器的观察结果:

     

       可见Delay_us(100),执行了大概101us,而Delay_us(10)执行了11.4us。

        结论:此延时函数基本上还是可靠的。

    使用定时器方法的实例

        Delay_us函数使用STM32定时器2实现:

    #include "timer.h"
    /* SystemFrequency / 1000            1ms中断一次 * SystemFrequency / 100000     10us中断一次 * SystemFrequency / 1000000         1us中断一次 */
    #define SYSTICKPERIOD                    0.000001#define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)
    /**  * @brief  定时器2的初始化,,定时周期1uS  * @param  无  * @retval 无  */void TIM2_Init(void){    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    /*AHB = 72MHz,RCC_CFGR的PPRE1 = 2,所以APB1 = 36MHz,TIM2CLK = APB1*2 = 72MHz */    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    /* Time base configuration */    TIM_TimeBaseStructure.TIM_Period = SystemCoreClock/SYSTICKFREQUENCY -1;    TIM_TimeBaseStructure.TIM_Prescaler = 0;    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
        TIM_ARRPreloadConfig(TIM2, ENABLE);
    /* 设置更新请求源只在计数器上溢或下溢时产生中断 */    TIM_UpdateRequestConfig(TIM2,TIM_UpdateSource_Global);     TIM_ClearFlag(TIM2, TIM_FLAG_Update);}
    /**  * @brief   us延时程序,10us为一个单位  * @param    *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us  * @retval  无  */void Delay_us(__IO uint32_t nTime){     /* 清零计数器并使能滴答定时器 */    TIM2->CNT   = 0;      TIM_Cmd(TIM2, ENABLE);     
    for( ; nTime > 0 ; nTime--)    {/* 等待一个延时单位的结束 */while(TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != SET);     TIM_ClearFlag(TIM2, TIM_FLAG_Update);    }
        TIM_Cmd(TIM2, DISABLE);}

        在main函数中检验Delay_us的执行时间:

    #include "stm32f10x.h"#include "Timer_Drive.h"#include "gpio.h"#include "systick.h"
    TimingVarTypeDef Time;
    int main(void){        TIM2_Init();        SysTick_Init();    SysTick_Time_Init(&Time);
    for(;;)    {        SysTick_Time_Start();         Delay_us(1000);        SysTick_Time_Stop();    }     }

        怎么去看检测结果呢?用调试的办法,打开调试界面后,将Time变量添加到Watch一栏中。然后全速运行程序,既可以看到Time中保存变量的变化情况,其中TimeWidthAvrage就是最终的结果。

    可以看到TimeWidthAvrage的值等于0x119B8,十进制数对应72120,滴答定时器的一个滴答为1/72M(s),所以Delay_us(1000)的执行时间就是72120*1/72M (s) = 0.001001s,也就是1ms。验证成功。

        备注:定时器方法输出检测结果有待改善,你可以把得到的TimeWidthAvrage转换成时间(以us、ms、s)为单位,然后通过串口打印出来,不过这部分工作对于经常使用调试的人员来说也可有可无。

    两种方法对比

    软件测试方法

        操作起来复杂,由于在原代码基础上增加了测试代码,可能会影响到原代码的工作,测试可靠性相对较低。由于使用32位的变量保存systick的计数次数,计时的最大长度可以达到2^32/72M = 59.65 s。

    示波器方法

        操作简单,在原代码基础上几乎没有增加代码,测试可靠性很高。由于示波器的显示能力有限,超过1s以上的程序段,计时效果不是很理想。但是,通常的单片机程序实时性要求很高,一般不会出现程序段时间超过秒级的情况。

    二、12V风扇转速控制电路

    直流12V风扇转速控制电路

        利用这种电路可以控制小汽车内的12V直流风扇转速。电路主要元器件为555定时器。它连接成振荡器工作模式。振荡器的输出连接至场效应管IRF540(T1),风扇则连接在T1的漏极D和电池正端之间。

        C1并接在风扇两端以稳定转速,二极管D1用来保护T1免受反电动势的冲击。2A容量的保险丝保护电路过载。

        电位器VR1可以改变振荡器输出波形的占空比,从而改变风扇的转速。如果风扇转速的低速,高速范围太小,可以增加,减小 C2(0.47μF) 的值,来减少/增加风扇的转速。

        本例电路利用555定时器芯片,可实现对风扇的调速功能。

        本例电路中,555芯片用作典型的多谐振荡器来使用,发光二极管LED为风扇工作指示二极管,当风扇工作时,二极管LED被点亮。

        MOS管T1为风扇电机的开关管,M为风扇电机。 whaosoft aiot http://143ai.com

    三、8种PLC常见常见错误类型

    1、CPU反常

    CPU反常报警时,应查看CPU单元衔接于内部总线上的一切器材。具体方法是顺次替换可能存在问题的单元,找出问题单元,并作相应处理。

    2、存储器反常

    存储器反常报警时,如果是程序存储器的问题,经过从头编程后还是无法解决,这种状况可能是噪声的搅扰引起程序的改变,否则应替换存储器。

    3、输入/输出单元反常、扩展单元反常

    发作这类报警时,应首要查看输入/输出单元和扩展单元衔接器衔接状况、电缆衔接状况,断定问题发作的某单元之后,再替换单元。

    4、不执行程序

    一般状况下可依照输入——程序执行情况——输出的过程进行查看。

    · 输入查看是运用输入LED指示灯辨认,或用写入器构成的输入监视器查看。当输入LED不亮时,可开始断定是外部输入体系问题,再配合万用表查看。如果输出电压不正常,就可断定是输入单元问题。当LED亮而内部监视器无显现时,则可认为是输入单元、CPU单元或扩展单元的问题。

    · 程序进行查看是经过写入器上的监视器查看。当梯形图的接点状况与成果不一致时,则是程序错误(例如内部继电器两层运用等),或是运算部分出现问题。

    · 输出查看可用输出LED指示灯辨认。当运算成果正确而输出LED指示错误时,则可认为是CPU单元、I/O接口单元的问题。当输出LED是亮的而无输出,则可判别是输出单元问题,或是外部负载体系出现问题。

    由于PLC机型不同,I/O与LED衔接方法的不一样(有的接于I/O单元接口上,有的接于I/O单元上)。所以,依据LED判别的问题规模也有不同。

    5、部分程序不执行

    检查方法与前一项相同,但是,如果计数器、步进控制器等的输入时刻过短,则会呈现无呼应问题,这时应该校验输入时刻是否足够大,校验可按输入时刻(输入单元的最大呼应时刻+运算扫描时刻乘以2)的联系进行。

    6、电源短时掉电,程序内容也会消失

    · 首先查看电池是否存在问题。

    · 经过反复通断PLC本身电源来查看。为使微处理器正确启动,PLC中设有初始复位点电路和电源断开时的保存程序电路。这种电路发作问题时,就不能保存程序。所以可用电源的通、断进行查看。

    · 如果在替换电池后依然呈现电池反常报警,就可判定是存储器或是外部回路的漏电流异常增大所造成的。

    · 电源的通断总是与机器体系同步发作,这时可查看机器体系发作的噪声影响。由于电源的断开是常与机器体系工作同时出现的问题,绝大部分是因为电机或绕组所发作的强噪声所造成的。

    7、PROM不能工作

    先查看PROM连接是否良好,然后判断是否需要替换芯片。

    8、电源重启或复位后,动作停止

    四、STM32中的上/下拉电阻

    STM32中的GPIO

     

     以STM32中的GPIO为例,如上图是GPIO的结构图。

        从上图中标号2处可以看到,上拉和下拉电阻上都有一个开关,通过配置上下拉电阻开关,可以控制引脚的默认电平,这里有三种状态:

  • 开启上拉时,引脚默认电压为高电平

  • 开启下拉时,引脚默认电压为低电平

  • 上拉和下拉不开启时,这种状态我们称为浮空模式

  •     关于STM32的GPIO文章,请移步此处:STM32的GPIO电路原理。STM32上下拉及浮空模式的配置是通过GPIOx_CRL和GPIOx_CRH寄存器控制的,可以通过《STM32F1xx 中文参考手册》查阅。

    开启上拉电阻或下拉电阻的作用

        STM32内部的上拉其实是一个弱上拉,也就是说通过此上拉电阻输出的电流很小,如果想要输出一个大电流。那么就需要外接上拉电阻了,其实就是增加导线的输出电流。

        下拉电阻情况相反,让STM32的CPU引脚输出低电平,结果由于后续电路影响输出的低电平达不到GND。所以接个下拉电阻,其实就是为了降低导线的输出电流。

        另外当上下拉电阻都不开启,此时是浮空模式,引脚的电压是不确定的,此模式下的管脚电压会时不时改变。

        所以为了防止引脚悬空,产生积累电荷、静电荷,造成电路不稳定。一般情况下,我们都会给引脚设置成上拉或者下拉模式,使它有一个确定的默认电平状态。

        以上拉电阻举例,在STM32刚上电的时候,芯片引脚电平是不确定的。特别引脚是接按键的时候,必须给他个确定的电平。下拉电阻的作用就是,强制让电平保持在低电平。

    上下拉电阻阻值的大小    

        根据拉电阻的阻值大小,可以分为强拉或弱拉(weak pull-up/down)。拉电阻阻值越小则表示电平能力越强,为强拉,可以抵抗外部噪声的能力也越强,相应的功耗也越大。

        举个例子:

        按键的上拉电阻可以选择3.3k、4.7k、5.1k、10k等,但是电阻越小,电流越大,功耗也越大。10k的上拉电阻带来的电流,是大多数芯片所能识别到的引脚电流,如果电阻太大,电流太小,引脚识别不了,所以10k是个折中的方案。这里的电流,简单来说是根据公式VDD/R拉电阻计算出来的。

    物联沃分享整理
    物联沃-IOTWORD物联网 » **《117组嵌入式分享聚合》**

    发表评论