一个LM3S811的EV板项目,涉及PWM、计数、字符液晶,超声测距,计时采用的是SYSTICK(比TIMER省事),附带时钟显示(简易的,不是32768的RTC)。
接口如下:
PD口接LCD1602的8根数据线;
PB0、1、2分别接LCD1602的RS、RW、E;
PB3、4分别接DYP-ME007超声模块的TRIG、ECHO;
PB6做外部发光二极管指示;
PC5做PWM的输出;
PC6做计数器的输入;
【当然,用PC6计数器接口测量PC5的PWM脉冲(这里只测频率,不测脉宽,不测占空比),所以PC5和PC6要短接】
PB7禁用,以防JTAG锁死。
最后请注意,要在startup.s文件中开通PB引脚中断和定时器1B计数扑捉中断以及SYSTICK计时中断,共三个,如下
extern SysTick_ISR
extern Timer1_B_ISR
extern Int_GPIO_PORTB
//================================================================
//================================================================
/*本实例演示超声模块测距、PWM信号产生、利用计数器测频、利用systick定时作时钟、LCD1602显示*/
#include "lm3sxxx.h"
#define RS GPIO_PORTB_BASE,GPIO_PIN_0 //PB0为LCD命令口
#define RW GPIO_PORTB_BASE,GPIO_PIN_1 //PB1为LCD命令口
#define EN GPIO_PORTB_BASE,GPIO_PIN_2 //PB2为LCD命令口
#define GPIO_WR GPIOPinWrite //简化库函数名
#define GPIO_RD GPIOPinRead //简化库函数名
#define GPIO_IN GPIOPinTypeGPIOInput //简化库函数名,周立功中文库函数说明书中居然没有对应的说明
#define GPIO_OUT GPIOPinTypeGPIOOutput//简化库函数名,周立功中文库函数说明书中居然没有对应的说明
#define LCD_DATA GPIO_PORTD_BASE//lcd数据口
#define CHAOSHENG_T GPIO_PIN_3//PB3超声驱动,DYP-ME007是超声模块
#define CHAOSHENG_R GPIO_PIN_4//PB4超声状态接收
#define LED GPIO_PIN_6//PB6计数器捕获指示,每次捕获都取反
#define LCD_BUSY GPIO_PIN_7//PD7数据口第七位测LCD忙
#define PWM_OUT GPIO_PIN_5//PC5即CCP1=PWM输出
#define COUNT_IN GPIO_PIN_6//PC6即CCP3=计数输入
#define PWM_PORT GPIO_PORTC_BASE
#define COUNT_PORT GPIO_PORTC_BASE
#define LCD_PORT GPIO_PORTB_BASE//PB0\PB1\PB2给液晶用,PB5未用,PB7给JTAG用,尽量避免作IO
#define LED_PORT GPIO_PORTB_BASE//PB6给LED用,秒闪烁
#define CHAOSHENG_PORT GPIO_PORTB_BASE//PB3、PB4给超声用
/*=================================================================*/
unsigned long pinlv_vlue=1000;//产生的频率可在这里设置,1MHz误差10%,100KHz误差1.6%,10KHz误差0.1%,1KHz~92Hz没有误差,91以下显示0,没有深入研究。
unsigned long count_vlue=65535;//TIME1B计数器count从最大值开始向下计数
unsigned long systick_vlue=6000000;//6MHz晶振,1s
unsigned char systick_flag,echo_flag=0,count_flag;
unsigned char string1[16]={" mm Hz"}; //16个字符
unsigned char string2[16]={" "}; //16个字符
unsigned long frequency,second=86360;
/*============================延时===============================*/
void delay(unsigned long delay_clok_1us)//1us延时
{
while(delay_clok_1us)delay_clok_1us--;
}
/*=======================测试LCD忙碌状态=========================*/
unsigned char LCD_check_busy(void)//测试LCD忙碌状态,返回字节型
{
unsigned char busy;
GPIO_WR(RS,0); //RS=0
GPIO_WR(RW,2); //RW=1
GPIO_WR(EN,4); //EN=1
GPIO_IN(LCD_DATA , 0XFF); //设置数据口为输入
busy=GPIO_RD(LCD_DATA,LCD_BUSY)&0x80;//读取第八位忙闲标志
GPIO_OUT(LCD_DATA,0XFF); //重新设置数据口为输出
GPIO_WR(EN, 0); //EN=0
return busy; //返回检测信号
}
/*================为液晶LCD1602服务的函数===========================*/
void lcd_write(unsigned char cd,unsigned char temp)//写入到LCD
{
while(LCD_check_busy());//cheker busy
if(cd)GPIO_WR(RS, 1); //当写数据时使RS=1
else GPIO_WR(RS, 0); //当为写指令时RS=0
GPIO_WR(RW, 0);
GPIO_WR(EN, 0);
GPIO_WR(LCD_DATA,0xFF,temp);//送数据到LCD
GPIO_WR(EN, 4); //delay(2);//此延时必须要,不然不能显示
GPIO_WR(EN, 0);
}
/*================为液晶LCD1602服务的函数===========================*/
void lcd_strwdat( unsigned char x,unsigned char y,unsigned char *str)//准备显示内容
{
if(x<16){if(y==0)x=0x80+x;else x=0xc0+x;lcd_write(0,x);} //显示位置( x, y);
while(*str!='\0')lcd_write(1,*str++);
}
/*====================定时器1B_count中断===========================*/
void Timer1_B_ISR (void)//定时器1B_count中断处理程序,下降沿触发计数。
{
TimerLoadSet(TIMER1_BASE, TIMER_B,count_vlue);//重装定时器装载值
TimerEnable(TIMER1_BASE, TIMER_B);//使能定时器1B
TimerIntClear(TIMER1_BASE, TIMER_CAPB_MATCH);//清除定时器1B中断
count_flag++;
}
/*====================定时器systick中断,每1秒===========================*/
void SysTick_ISR (void)//systick中断
{ unsigned long temp0;
temp0=TimerValueGet(TIMER1_BASE, TIMER_B);
TimerLoadSet(TIMER1_BASE, TIMER_B,count_vlue);//重装定时器装载值
TimerEnable(TIMER1_BASE, TIMER_B);//使能定时器1B
GPIO_WR(LED_PORT,LED,GPIO_RD(LED_PORT,LED)^LED);//翻转PB6,指示秒闪烁
temp0=count_vlue-temp0;
frequency=count_flag*count_vlue+temp0;
count_flag=0;
systick_flag=1;
}
/*===========液晶的ECHO引脚送入MCUPB4引脚引起GPIO中断服务程序============*/
void Int_GPIO_PORTB(void) //PB4引脚中断服务程序中,液晶的ECHO引脚送入
{
echo_flag=1;
GPIOPinIntClear(CHAOSHENG_PORT, CHAOSHENG_R);//清除引脚中断标志
}
/*=================定时器Timer1B做count计数的初始化init===================*/
void Timer1B_count_init(void)
{
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1 );//使能定时器1外设
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB );//使能GPIOB口外设
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC );//使能GPIOC口外设
GPIOPinTypeTimer(COUNT_PORT, COUNT_IN);//设置PC6为CCP3功能输入口,做计数器输入口
GPIO_OUT(LED_PORT,LED);//设置 GPIO PB6为输出口,代替GPIODirModeSet(LED_PORT, LED, GPIO_DIR_MODE_OUT)完全可以
GPIO_WR(LED_PORT,LED,0);//点亮LED/
TimerConfigure(TIMER1_BASE,TIMER_CFG_16_BIT_PAIR|TIMER_CFG_B_CAP_COUNT);//设置定时器1.B为16位定时器配置,边沿计数捕获模式
TimerControlEvent(TIMER1_BASE,TIMER_B,TIMER_EVENT_NEG_EDGE);//设置为下降沿捕获
TimerLoadSet(TIMER1_BASE,TIMER_B,count_vlue);//设置定时器装载值为
TimerMatchSet(TIMER1_BASE,TIMER_B,0);//设置定时器匹配值
TimerIntEnable(TIMER1_BASE, TIMER_CAPB_MATCH);//GPTM捕获B的匹配中断使能
TimerEnable(TIMER1_BASE, TIMER_B);//使能定时器并开始等待边沿事件
IntEnable(INT_TIMER1B);//使能定时器1中断
}
/*=================定时器Timer0B做PWM的初始化init===================*/
void Timer0B_PWM_init(void)
{
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);//使能定时器0外设
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);//使能GPIOC口外设
GPIOPinTypePWM(PWM_PORT, PWM_OUT);//设置PC5输出PWM波形
TimerConfigure(TIMER0_BASE,TIMER_CFG_16_BIT_PAIR|TIMER_CFG_B_PWM);//设置T0B为16-PWMA模式
TimerLoadSet(TIMER0_BASE, TIMER_B,systick_vlue/pinlv_vlue);//pwm_vlue);//设置PWM的装载值
TimerMatchSet(TIMER0_BASE, TIMER_B,systick_vlue/pinlv_vlue/2);//设置PWM的匹配值 占空比50%
TimerEnable(TIMER0_BASE, TIMER_B);//PWMA模式使能
}
/*==========================液晶+超声初始化init=======================*/
void LCD_UltraWave_init(void)//液晶+超声初始化程序
{
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB|SYSCTL_PERIPH_GPIOD);//端口使能声明
GPIO_IN(CHAOSHENG_PORT, CHAOSHENG_R);//设置输入引脚(接超声模块ECHO)
GPIOPinIntEnable(CHAOSHENG_PORT, CHAOSHENG_R);//作为外部引脚中断
GPIOIntTypeSet(CHAOSHENG_PORT, CHAOSHENG_R, GPIO_RISING_EDGE);//上升沿触发
IntEnable(INT_GPIOB);//使能PB口中断(仅PC4中断被使能)
SysTickPeriodSet(systick_vlue);//设置SysTick初值,最大为16777216,用于计时
SysTickEnable(); //启动SysTick
SysTickIntEnable(); //systick系统定时器中断使能
GPIO_OUT(LCD_DATA,0xff);//设置LCD数据口(PD)全为输出
GPIO_OUT(LCD_PORT,0x0f);//设置控制口(PB)低4位(LCD3位,超声模块1位)为输出
lcd_write(0,0x38); //delay(1); //八位数据、双行显示、5X7点阵
lcd_write(0,0x0C); //delay(1); //显示开、关光标
lcd_write(0,0x06); //delay(1); //数据读、写操作后,地址指针AC自动增一
lcd_write(0,0x01); //delay(1); //清屏
}
/*==========================主函数=================================*/
main(void) //
{
long i;
unsigned long j,ulm,ulm0,ulm1,hour,mon;
SysCtlClockSet(SYSCTL_SYSDIV_1|SYSCTL_USE_OSC|SYSCTL_OSC_MAIN|SYSCTL_XTAL_6MHZ);//系统时钟
Timer0B_PWM_init(); //PWM初始化 产生PWM波,经CCP1(PC5)输出
Timer1B_count_init();//计数器初始化 检测CCP3(PC6)的脉冲,分频后从PB6输出
LCD_UltraWave_init();//液晶+超声初始化
while(1)
{
if(echo_flag==1)//超声波模块接收到信号的中断标志
{
/*===========================以下处理测距离数据==========================*/
echo_flag=0;//中断标志
ulm0=SysTickValueGet( );//读取SysTic计数值
i=0; //i暂时当计时用
LoopG00:
j=GPIO_RD(CHAOSHENG_PORT, CHAOSHENG_R);//读出超声波引脚送出的电平
j=j&CHAOSHENG_R;//第四位00010000B=0x10保留,其余位(还有31位)清零
i++; //计时
if(i>60000)goto LoopG01;//防止超时停机
if(j==0x10)goto LoopG00;//如果超声波引脚送出的电平还是高电平则转再读引脚
LoopG01:
ulm1=SysTickValueGet( );//再次读取计数值
if(ulm0
ulm=(ulm0-ulm1)/32.32;//计算两次读取SysTick计数值的差所对应的距离
/*========================以下写入测距离数据至数组1=======================*/
i=3;//先显示最低位在液晶的4位上,后面是mm
while(i>-1)//左边顶格
{
string1[i--]=ulm%10+0x30; //每次都取除10的余数,下一个i--往左移动一位显示
ulm=ulm/10; //每次都去掉最低位
}
}
/*===================以下为每一个秒钟到达是的事件处理======================*/
if(systick_flag==1)//systick中断标志,表示又一秒到
{
systick_flag=0;
/*===========================以下写入测频数据==============================*/
i=13;//先显示最低位在液晶的10位上,后面是mm,右边留3个空
while(i>6)//左边也留3个空
{
string1[i--]=frequency%10+0x30; //每次都取除10的余数,下一个i--往左移动一位显示
frequency=frequency/10; //每次都去掉最低位
}
/*===============================以下处理时钟==============================*/
second++; //秒数加1
if(second>86399)second=0; //一昼夜=86400秒
hour=second/3600; //取出小时
j=second%3600; //取出去除小时后的秒
mon=j/60; //取出分钟
j=j%60; //取出秒(临时的)
i=4; //左边空4个,在液晶的第五、六位显示小时
string2[i++]=hour/10+0x30; //写入小时的十位
string2[i++]=hour%10+0x30; //写入小时的个位
string2[i++]=':'; //写入":"
string2[i++]=mon/10+0x30; //写入分钟的十位
string2[i++]=mon%10+0x30; //写入分钟的个位
string2[i++]=':'; //写入":"
string2[i++]=j/10+0x30; //写入秒钟的十位
string2[i++]=j%10+0x30; //写入秒钟的个位
/*=====================以下驱动液晶显示==============================*/
lcd_strwdat(0,0,string1); //显示第1行
lcd_strwdat(0,1,string2); //显示第2行
/*================以下向超声波模块发出驱动电平=======================*/
GPIO_WR(CHAOSHENG_PORT, CHAOSHENG_T,CHAOSHENG_T);//输出测距模块触发信号为高电平
delay(13);//高电平延时10us
GPIO_WR(CHAOSHENG_PORT, CHAOSHENG_T,~CHAOSHENG_T);//输出测距模块触发信号为低电平
}
}
}
/*===================================================================*/