一、获取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拉电阻计算出来的。