文章目录
- 一、SysTick实验
- 二、定时器实验
- 三、LED呼吸灯实验
- 四、蜂鸣器实验
一、SysTick实验
- 利用 LPC1114 DevKit 开发板或者 LPC1114 MASB 最小系统板,设定 LPC1114 微控制器时钟频率 48 MHz,利用 SysTick 定时器定时 1 秒(如何才能定时1秒?提示:在中断子程序中进行计数),控制(PIO1_9)引脚LED灯的闪烁功能。编写C语言代码进行实现。写出配置子程序、中断服务子程序和主程序。在中断子程序中设置断点,观察是否能产生中断。
- LPC1114系列 ARM Cortex-M0 微控制器内核外设 SysTick 定时器为 24 位递减计时器,当计数值达到 0 时产生中断,并将从STRELOAD寄存器中自动重装载定时初值。只要不把它在 SysTick 控制及状态寄存器 STCTRL 中的使能位清除,就永不停止。要想在规定的时间点上产生中断(循环产生),就必须先将指定的时间间隔值装入STRELOAD。SysTick定时器可以为操作系统或其它系统管理软件提供固定 10ms 的中断。
在 PIO1_9 引脚上接一个LED灯,设置 PIO1_9 引脚为GPIO,引脚方向为输出,高电平灯灭,低电平灯亮。电路如下图所示:
C语言程序
#include <LPC11xx.h>
void LED_init(void) {
LPC_SYSCON->SYSAHBCLKCTRL |= (1UL << 6);
LPC_GPIO1->DIR |= (1UL << 9);
LPC_GPIO1->DATA &= ~(1UL << 9);
}
void LED_On ( void ) {
LPC_GPIO1->DATA |= (1UL << 9);
}
void LED_Off ( void ) {
LPC_GPIO1->DATA &= ~(1UL << 9);
}
void LED_Invert(void) {
int ledstate;
// Read current state of GPIO P1_0..31, which includes LED
ledstate = LPC_GPIO1->DATA;
if(ledstate&= (1 << 9)) { // Turn on LED if it is off
LPC_GPIO1->DATA &= ~ (1 <<9);
}
else{ // Turn off LED if it is on
LPC_GPIO1->DATA |= (1 << 9);
}
}
volatile uint32_t msTicks=0;
void SysTick_Handler(void) {
msTicks++;
}
int main (void)
{
LED_init();
LED_Off ( );
if (SysTick_Config(SystemCoreClock / 100)) {
while (1);
}
while (1)
{
if(msTicks==50){
LED_Invert();
msTicks=0;
}
// Turn LED on, then wait
}
return 0;
}
观察开发板LED灯,可以实现控制(PIO1_9)引脚 LED 灯每隔 1s 闪烁
二、定时器实验
利用 LPC1114 DevKit 开发板,设定 LPC1114 微控制器时钟频率48MHz,利用 16 位通用定时器 1 实现定时匹配输出控制MAT0(PIO1_9)引脚状态反转,16位定时器 1 每 1 秒产生一次匹配中断,设置 MAT0.0 引脚电平进行反转,实现 LED 灯的控制。
- 选用 LPC1114 的 16 为定时计数器 1 来定时 1s 并产生中断,在中断中完成灯的电平翻转,实现灯 1s 中翻转一次的闪烁状态。
- 首先得初始化 16 位时器1,打开时钟,清零中断标志寄存器、配置预分配值、设置匹配后操作、设置定时时间、打开定时器、使能 16 位定时器1中断,配置好上述操作后便完成了定时器的初始化配置。
- 然后写中断程序,直接在中断里面调用灯翻转函数,使用的是中断,没有用匹配输出,因此需要调用翻转函数。最后在主函数里面调用led初始化函数、16位定时计数器1初始化函数,然后用 while(1) 语句一直等待进入中断即可。
C语言程序如下:
#include <LPC11xx.h>
void LED_init(void) //LED初始化函数
{
LPC_SYSCON->SYSAHBCLKCTRL |= (1UL << 6);
LPC_GPIO1->DIR |= (1UL << 9);
LPC_GPIO1->DATA &= ~(1UL << 9);
}
void LED_Invert(void) //LED翻转函数
{
int ledstate;
// Read current state of GPIO P1_0..31, which includes LED
ledstate = LPC_GPIO1->DATA;
if(ledstate&= (1 << 9))
{ // Turn on LED if it is off
LPC_GPIO1->DATA &= ~ (1 <<9);
}
else
{ // Turn off LED if it is on
LPC_GPIO1->DATA |= (1 << 9);}}
void TMR16B1_COUNT_Init(void) //16位定时器1初始化函数
{
LPC_SYSCON->SYSAHBCLKCTRL |= (1UL << 8);
LPC_TMR16B1->IR =0x1F; //中断标寄存器写1清零
LPC_TMR16B1->PR = 999; //设置分频系数
LPC_TMR16B1->MCR = 3; //匹配后TC复位且中断
LPC_TMR16B1->MR0 = SystemCoreClock/1000; //设置定时时间
LPC_TMR16B1->TCR = 0x01; //使能定时器
NVIC_EnableIRQ(TIMER_16_1_IRQn); //设置中断并使能
}
void TIMER16_1_IRQHandler(void) //中断处理子程序
{
if((LPC_TMR16B1->IR |= 0x01)==1)
LED_Invert();
}
int main() //主函数
{
LED_init();
TMR16B1_COUNT_Init();
while(1);
return 0;
}
通过此次实验明白定时器运行的流程和熟悉各种寄存器的配置。定时器打开(通过配置定时器控制寄存器TCR)后,定时/计数器 TC 会开始每隔一个时钟周期增加1,当 TC 增加到设定的匹配值 MR 的时候,会发生一些操作例如进入中断、TC复位等(通过匹配控制寄存器 MCR 来配置),然后在发生中断的时候,中断标志寄存器 IE 里面对应的位会被置1。还有注意定时器有 4 个匹配通道,配置各个寄存器的时候要配置对应的通道位。
三、LED呼吸灯实验
- 利用 LPC1114 的 16 位定时器 1 的 PWM 输出不同占空比的 PWM 波驱动 LED 灯,来实现呼吸灯渐变的效果。
- 首先进行定时器 PWM 模式的初始化,打开 16 位定时器时钟和IO口 1_9 的时钟,然后配置 PIO1_9 复用模式为MAT0(直接是定时器输出PWM方波的端口)。然后配置通道 3 为计时,设置预分频 PR=0,MCR为0x20<<9(匹配后 TC 复位不中断),MR3=SystemCoreClock/1000,即 MR3 的定时时间为1s。然后配置 PWMC 为 0x01 (即MAT0输出PWM)。最后打开定时器,完成定时器初始化。
- 在主函数里面,调用定时器 PWM 初始化函数,然后开始主循环,我设置了一个标志变量 flag 来判断是递增还是递减,也设置了一个比例变量 x(0到1)来输出不同的占空比PWM,将 x*MR3 的值给MR0,然后递增或者递减(根据 flag 来判断)。即完成了不同的PWM输出给LED实现呼吸的效果。经过实际调整和简单计算,发现 x 每次增减值取 0.00001 时能够达到 LED 很好的渐变的效果。
#include <LPC11xx.h>
int flag=0; //占空比x,标志变量flag
float x=0;
void TMR16B1_PWM_Init(void)
{
LPC_SYSCON->SYSAHBCLKCTRL |= (1UL << 8);
LPC_SYSCON->SYSAHBCLKCTRL |= (1UL << 16);
LPC_IOCON->PIO1_9 |= 0x01; //设置P1_9复用位mat0
LPC_TMR16B1->PR = 0; //设置分频系数
LPC_TMR16B1->PWMC= 0x01; //MAT0为PWM输出
LPC_TMR16B1->MCR = 0x02 <<9; //匹配后TC复位且不中断
LPC_TMR16B1->MR3 = SystemCoreClock/1000; //设置定时时间为1ms
LPC_TMR16B1->MR0 = LPC_TMR16B1->MR3;
LPC_TMR16B1->TCR = 0x01; //使能定时器
}
int main()
{
TMR16B1_PWM_Init();
while(1)
{
if (x>=1 | x<=0)
{
flag = ~flag;
}
if(flag==0)
{
LPC_TMR16B1->MR0 = x*LPC_TMR16B1->MR3;
x=x+0.00001;
}
else
{
LPC_TMR16B1->MR0 = x*LPC_TMR16B1->MR3;
x=x-0.00001;
}
}
return 0;
}
本次实验过程中,就遇到了程序在硬件上运行速度非常快这样的问题,刚开始没有察觉到,以为是按我们正常以为时间运行,却不知道两次增减间隔可能就只有几十微秒,增量 x 取得太大就会出问题,后来找到原因所在,调试设置合理的 x 值后便得到了较好的呼吸灯效果。
四、蜂鸣器实验
-
音乐由不同音调和不同的节拍相互组合而成。音调体现在频率,节拍体现在此音调存在的时间上。故而我们可以控制蜂鸣器以不同频率的不同存在时间的有规律组合即可产生动听的音乐。
-
利用定时器输出相同占空比,不同周期的 PWM 方波驱动蜂鸣器可以实现不同频率的声音,此时不需要用到中断里面去控制蜂鸣器,而是输出的 PWM 波直接送到蜂鸣器,代码优化了一些。实现节拍通过系统节拍器定时间来作延时功能。主要有三个数组:频率数组FREQ[]、节拍数组beat[]、歌曲数组song[]。Song[]里面的数依次为频率和对应的节拍。
-
然后我们配置 16 位定时器 1 的 MR3 通道来作为定时,MR1通道作为 PWM 波,通过 PIO1_10 来输出 PWM 波送给蜂鸣器。根据数组FREQ[]里面的值来改变 MR3 定时时间,然后 MR1 根据 MR3 依次改变,实现不同频率声音。
-
主函数程序逻辑为:首先调用 16 位定时器 PWM 初始化函数,然后配置系统滴答定时器为1s,然后进行主循环,主循环里面取出 song[] 数组里面的频率和其节拍位置,然后从 FREQ[]、beat[] 取出频率和节拍的具体值,再调用 sound() 函数,根据新的频率、节拍值,调节 PWM 波周期,和存在的时间。根据 song[] 数组重复上述取值,改变 PWM 周期,延时等操作,即可实现蜂鸣器播放音乐。
#include <LPC11xx.h>
uint16_t fre,time;
volatile uint32_t msTicks=0;
uint16_t FREQ[] = {
0x106, 0x126, 0x14A, 0x15D, 0x188, 0x1B8, 0x1EE, //低音1-0,2-1,3-2,4-3,5-4,6-5,7-6,
0x20B, 0x24B, 0x293, 0x2BA, 0x310, 0x370, 0x3DC, //1-7,2-8,3-9,4-10,5-11,6-12,7-13,
0x416, 0x497, 0x526, 0x575, 0x620, 0x6E0, 0x7B8,0x00 //高音1-14,2-15,3-16,4-17,5-18,6-19,7-20,
};
uint16_t beat[] = {
0x96,0x12C,0x1C2,0x258,0x384,0x4B0,0x00 //1/4_0,2/4_1,3/4_2,4/4_3,6/4_4,8/4_5
}; //节拍
uint16_t song1[]={
4,1, 4,1, 5,3, 4,3, 7,3, 6,5, 4,1, 4,1, 5,3, 4,3, 8,3,
7,5, 4,1, 4,1, 11,3, 9,3, 7,3, 6,3, 19,3, 10,1, 10,1, 9,3, 8,3, 7,3,
9,5, 4,1, 4,1, 5,5, 21,6
}; //音乐:频率 节拍
void TMR16B1_PWM_Init(void)
{
LPC_SYSCON->SYSAHBCLKCTRL |= (1UL << 8);
LPC_SYSCON->SYSAHBCLKCTRL |= (1UL << 16);
LPC_IOCON->PIO1_10 |= 0x02; //设置P1_10复用位mat1
LPC_TMR16B1->PR = 999; //设置分频系数
LPC_TMR16B1->PWMC= 0x02; //MAT1为PWM输出
LPC_TMR16B1->MCR = 0x02 <<9; //匹配后TC复位
LPC_TMR16B1->MR3 = SystemCoreClock/1000; //设置定时时间
LPC_TMR16B1->MR1 = LPC_TMR16B1->MR3*0.5;
}
void SysTick_Handler(void) //系统滴答定时器中断,用于延时
{
msTicks++;
}
void delay(uint32_t t) //延时1ms
{
msTicks=0;
while(t!=msTicks);
}
void sound() //产生频率和节拍
{
LPC_TMR16B1->TCR=0x01;
LPC_TMR16B1->MR3 = SystemCoreClock/(1000*fre);
LPC_TMR16B1->MR1 = LPC_TMR16B1->MR3*0.5; //频率
delay(time); //节拍
LPC_TMR16B1->TCR=0x00;
}
int main()
{
unsigned char k,m, i;
TMR16B1_PWM_Init();
i=0;
if (SysTick_Config(SystemCoreClock / 1000)) //配置系统滴答定时器
{
while (1);
}
while(1)
{
k = song1[i] ; //取频率位置
fre=FREQ[k]; //取频率值
m=song2[i+1]; //取节拍位置
time =beat[m]; //取节拍值
i=i+2; //下一组频率和节拍位置
sound();
if(fre==0) i=0;
}
}
代码编译执行后没有错误,程序成功下载到板子上后,可以播放生日快乐歌。