应本订阅号一位读者要求,发布一个超声波的例程.本来打算用STM8L-DISCOVERY板子上的LCD液晶屏显示测量到的距离的.昨天写程序时发现,LCD显示的一个IO口和检测超声波回波的捕获IO都是PB0,而且TIM2的这捕获IO还不能复用到其他引脚,只有PB0这一个IO.所以不能使用LCD显示了.今天我发现,IAR功能还是很强大的,VIEW菜单下有一个LIVE WATCH功能,故名思议,就是一个实时显示变量数据的功能,用起来太爽了,比用屏幕显示还给力.第一次发现IAR的这个功能^_^

本例程,主要是通过TIM2的捕获功能,测量超声波发出后,出现的一个高电平时间,根据高电平的时间和声速340m/s来计算距离.
这里要说的是,第一次发现这个吃灰好久的超声波模块,竟然还有这么一个奇怪,我以前没发现,很方便的功能.就是超声波模块收到回波后,ECHO引脚会产生一个高电平,这个高电平的时间,就是超声波从发射到返回的时间,只要测量这个高电平的时间,然后除2,就可以得到超声波从发射处到目标处的时间了.
如下图,要想让发射一次超声波,只需给TRIG引脚一个大于10us的高电平,由于这个模块是自带了4M的晶振,所以可以自动发送8个40KHz的脉冲(这个模块的自动化程度很高啊!),模块接收到超声波回波后,会在ECHO引脚输出一个高电平,这个高电平的时间,就是超声波从发射到接收的时间,用起来很方便.以前学习51时,用过一个把超声波做在板子上的开发板,不是外接的模块,还要自己写程序发送8个40KHz的脉冲,还要自己判断开始计时时间……用起来很麻烦.这个超声波模块的信号是HC-SR04.

下图是本例程的实测波形,首先让PC7产生一个45us的高电平,让超声波模块发射超声波,然后会在PB0引脚产生一个高电平,如下图是实测的一个超声波发射和接收波形,此时高电平的时间是9.6ms,所以超声波从超声波模块到目标位置的时间为9.6ms/2=4.8ms.有了时间,还有速度340m/s,根据公式,就可以求出距离34000/1000(cm/ms)*4.8ms=163cm.

看下图,右侧部分的tmp值就是计算后的距离,这个值此刻显示的为162cm.通过IAR的这个Live Watch功能,可以很方便的查看此时的距离,我用手中的平板,在模块上方移动,能够看到距离数据会随之改变.

TIM2的捕获功能配置,和下图是一样的,IC1即捕获通道1配置为在脉冲的上升沿发生发生捕获,将TIM2的计数值复制到TIM2_CCR1寄存器中,同时计数器的值复位,从0重新开始计数,在脉冲的下降沿捕获通道2发生捕获,将此时计数器的值复制到TIM2_CCR2寄存器,此值为高电平脉冲时间.


/*硬件连接*/
//PC7 ---->TRIG
// PB0<----ECHO
/****************************************************************************************
*开发环境:IAR for stm8 v6.5.3
*硬件平台:STM8L-DISCOVERY
*功能说明:本例程,
*作 者:茗风
****************************************************************************************/
#include"iostm8l152c6.h"
#include"stdint.h"
#include"stdbool.h"
uint16_t tim2_ccr1=0,tim2_ccr2=0;
bool bFinish_Flag=false;
#define a 0x01
#define b 0x02
#define c 0x04
#define d 0x08
#define e 0x10
#define f 0x20
#define g 0x40
#define m 0x80
/******************************************************************************************************
* 名 称:void GPIO_Init(void)
* 功 能:初始化PC7为高速推挽输出
* 入口参数:无
* 出口参数:无
* 说 明:
* 范 例:无
******************************************************************************************************/
void GPIO_Config(void)
{
PC_CR1_C17 =1;//推挽输出
PC_CR2_C27 =1;//高速输出
PC_DDR_DDR7 =1;//PC7输出
PC_ODR_ODR7 =0;//输出低电平
}
/******************************************************************************************************
* 名 称:void delay(void)
* 功 能:延时
* 入口参数:无
* 出口参数:无
* 说 明:系统频率为3M时,理论计算延时时间为78ms
* 范 例:无
******************************************************************************************************/
void delay(void)
{
uint8_t i=255,j;
while(i--)//255*1.2+255*1.2*255
{
for(j=0;j<255;j++);
}
}
/******************************************************************************************************
* 名 称:void CLOCK_Init(void)
* 功 能:系统时钟切换为HSE,12M/4=3M
* 入口参数:无
* 出口参数:无
* 说 明:
* 范 例:无
******************************************************************************************************/
void CLOCK_Config(void)
{
CLK_CKDIVR =0x2;//分频值为4
/*000: System clock source/1
001: System clock source /2
010: System clock source /4
011: System clock source /8
100: System clock source /16
101: System clock source /32
110: System clock source /64
111: System clock source /128*/
//自动切换
CLK_SWCR_SWEN =1;//允许切换时钟
CLK_SWR =0x04;//写入一个八位的值,用于选择目标时钟源
while(CLK_SWCR_SWBSY);//等待时钟切换完成
//手动切换
// CLK_SWR =0x08;//写入一个八位的值,用于选择目标时钟源
// while(!CLK_ECKR_LSERDY);//等待目标时钟源稳定
// CLK_SWCR_SWEN =1;//允许切换时钟
/*
*0x01:HSI selected as system clock source
*0x02:LSI selected as system clock source
*0x04:HSE selected as system clock source
*0x08:LSE selected as system clock source
*/
}
/******************************************************************************************************
* 名 称:void TIM2_Config(void)
* 功 能:STM8定时器2捕获功能初始化
* 入口参数:无
* 出口参数:无
* 说 明:PB0为捕获输入引脚,使用通道0
* 范 例:无
******************************************************************************************************/
void TIM2_Config(void)
{
PB_DDR_DDR0 =0;//输入
PB_CR1_C10 =0;//浮空输入
// PB_CR1_C10 =1;//上拉输入
PB_CR2_C20 =0;//禁止外部中断功能
CLK_PCKENR1_PCKEN10=1;//打开定时器2时钟
//------设置TIM2时钟分频值------
//我的电路板上,HSE接的是12M,四分频后为3M
//正常情况应该接16M,这样分频后不会出现3M这样不利于计算时间的频率
TIM2_PSCR_PSC=0;//分频值 3M/2^0=3M/1=3000000Hz
//-重装值,TIM2从0计数到此值,发生溢出-
// TIM2_ARRH=0;
// TIM2_ARRL=100;
//----AUTO_RELOAD 预装载使能----
TIM2_CR1_ARPE =0;//不通过预装载寄存器
TIM2_CR1_URS=1;//仅当计数器溢出时才发生中断请求
TIM2_CR1_UDIS=1;//禁止更新事件//计数器溢出属于更新事件
TIM2_CR1_DIR=0;//向上计数
// TIM2_CCER1_CC1E=0;//清零使能位,为了配置寄存器
// TIM2_CCER1_CC2E=0;//清零使能位,为了配置寄存器
TIM2_CCMR1=0;
// TIM2_CCMR1=0x00; //IC1F=0000 输入信号不分频
TIM2_CCMR1 |=0x01;//CC1S=01 IC1 is mapped on TI1FP1
/*00: CC1 channel is configured as output
01: CC1 channel is configured as input, IC1 is mapped on TI1FP1
10: CC1 channel is configured as input, IC1 is mapped on TI2FP1
11: Reserved */
TIM2_CCMR2=0;
// TIM2_CCMR2=0x00;//IC1PSC=0 输入信号不分频
TIM2_CCMR2 |=0x02;// TIM2_CCMR2_CC2S=0x2
/* 00: CC2 channel is configured as output
01: CC2 channel is configured as input, IC2 is mapped on TI2FP2
10: CC2 channel is configured as input, IC2 is mapped on TI1FP2
11:CC2 channel is configured as input, IC2 is mapped on TRC */
TIM2_CCER1_CC1P=0;//上升沿时发生捕获
TIM2_CCER1_CC2P=1;//下降沿时发生捕获
TIM2_SMCR=0x54;//
// TIM2_SMCR_TS=0x05;//101 iput 1(TI1FP1) 头文件有错误
// TIM2_SMCR_SMS=0x04;//复位触发模式
TIM2_CCER1_CC1E=1;//使能捕获功能
TIM2_CCER1_CC2E=1;//使能捕获功能
TIM2_IER_CC1IE=1;//开启捕获中断
TIM2_IER_CC2IE=1;//开启捕获中断
TIM2_CR1_CEN=1;//开启计数
}
void main(void)
{
static uint16_t tmp=0;
uint8_t cnts=0;
CLOCK_Config();//系统时钟切换为HSE(12MHz/4=3MHz)
GPIO_Config();//超生波触发引脚
// LCD_Config();
TIM2_Config();
asm("rim"); //enable interrupts
while(1)
{
PC_ODR_ODR7 =1;//保证最短10us的低电平
for(cnts=15;cnts>0;cnts--);
PC_ODR_ODR7 =0;
for(cnts=2;cnts>0;cnts--)
{
delay();
}
if(bFinish_Flag==true)
{
//1000000us/3000000=0.33us
//340(m/s)=34/1000(cm/us)=0.034cm/us
tmp=(float)tim2_ccr2*0.3333*0.017;
bFinish_Flag=false;
}
// asm("wfi");
}
}
#pragma vector=TIM2_CAPCOM_CC1IF_vector
__interrupt void TIM2_CAPCOM_CC1IF_ISR(void)
{
if(TIM2_SR1_CC1IF==1)
{
TIM2_SR1_CC1IF=0;//清除中断标志位
// tim2_ccr1=TIM2_CCR1H;
// tim2_ccr1<<=8;
// tim2_ccr1+=TIM2_CCR1L;
}
if(TIM2_SR1_CC2IF==1)
{
TIM2_SR1_CC2IF=0;//清除中断标志位
tim2_ccr2=TIM2_CCR2H;
tim2_ccr2<<=8;
tim2_ccr2+=TIM2_CCR2L;
bFinish_Flag=true;
//tim2_ccr2得到的时间为高电平持续时间
}
}
|