1 项目目标

利用定时器产生周期为1ms的中断,作为系统时基。并在数码管上显示开机时间(分辨率为0.1秒);同时实现LED4、LED5、LED6和LED7分别以300ms、220ms、450ms和700ms为周期闪烁。

2 硬件电路

3 中断系统

3.1 什么是中断

中断就是正常执行的工作被一些突发的事件所打断。比如我们在看书,电话铃响了。看书是我们正在进行的事情,电话铃响就是一个突发事件。电话铃响了,我们就会停下手头的事情,去接电话。接电话这个动作,就是对这个中断事件的响应。电话接完了,接着看书,回归正常工作。

中断这种工作机制,主要是为提高效率,另外一种方式叫查询式。还是以接电话为例,比如手机处于免打扰状态,电话来的时候没有铃声提醒。如果我们不想漏过电话,只能一会儿抽空看一眼手机,有没有电话打进来。这种工作方式相对中断式工作效率偏低。

中断方式和查询方式相比,好处在于加快了响应速度,一旦发生某个事件,可以快速响应。提高了系统的实时性能。

中断方式有个问题是,如何保证中断响应完成后能无缝继续刚刚被打断的工作。当然人没有这个问题,单片机内部只有一个CPU,就需要专门设计的方式来保证这种工作方式的正确性。

AT32的中断系统非常强大,所有的外设几乎都可以工作于中断方式。

3.2 中断系统的几个概念

中断源 即中断是来源于哪个外设的哪个事件,比如定时器时间到、外部引脚电生了电平跳变、UART收到了一个数据等等

中断请求 当有事件发生时,中断系统向CPU发出执行中断请求

中断响应 当发生了一个中断事件时,需要去做的事情,就是对这个中断的响应

中断优先级 中断事件可以打断当前的正在执行的程序,如果两个中断同时发生了,先去响应哪一个呢?为解决这个问题,给中断定义不同的优先级,首先响应优先级高的中断。如果两个相同优先级的中断同时来了,怎么办?简单点,按事先规定的顺序(就像我们经常说的按姓氏笔画,排名不分先后)。

3.3 中断的执行过程

4 Timer定时中断

AT32F407VGT6有14个寄存器,功能对比如下:

本例采用基本定时器TMR6,作为1ms的时基定时器。

4.1 AT32F407系统时钟系统

本例采用内部RC振荡器时钟,然后通过PLL(锁相环)倍频后作为系统时钟。可以在雅特力官网下载AT32_New_Clock_Configuration工具自动生成时钟配置代码。本例系统时钟为64MH,APB1、APB2的分频系数为2,其频率都为32MHz。

/*** @brief  system clock config program* @note   the system clock is configured as follow:*         system clock        = hick / 12 * pll_mult*         system clock source = pll (hick)*         sclk                = 64000000*         ahbdiv              = 1*         ahbclk              = 64000000*         apb1div             = 2*         apb1clk             = 32000000*         apb2div             = 2*         apb2clk             = 32000000*         pll_mult            = 16*         pll_range           = LE72MHZ (less than 72 mhz or equal to 72 mhz)* @param  none* @retval none*/
void system_clock_config(void)
{/* reset crm */crm_reset();/* enable hick */crm_clock_source_enable(CRM_CLOCK_SOURCE_HICK, TRUE);/* wait till hick is ready */while(crm_flag_get(CRM_HICK_STABLE_FLAG) != SET){}/* config pll clock resource */crm_pll_config(CRM_PLL_SOURCE_HICK, CRM_PLL_MULT_16, CRM_PLL_OUTPUT_RANGE_LE72MHZ);/* enable pll */crm_clock_source_enable(CRM_CLOCK_SOURCE_PLL, TRUE);/* wait till pll is ready */while(crm_flag_get(CRM_PLL_STABLE_FLAG) != SET){}/* select pll as system clock source */crm_sysclk_switch(CRM_SCLK_PLL);/* wait till pll is used as system clock source */while(crm_sysclk_switch_status_get() != CRM_SCLK_PLL){}/* config ahbclk */crm_ahb_div_set(CRM_AHB_DIV_1);/* config apb2clk */crm_apb2_div_set(CRM_APB2_DIV_2);/* config apb1clk */crm_apb1_div_set(CRM_APB1_DIV_2);/* update system_core_clock global variable */system_core_clock_update();
}

定时器使用 APB1 总线时钟,特别地,当 APB 预分频系数是 1 时,定时器的时钟频率等于 APB1 的时钟频率;当 APB 预分频系数不为 1 时,定时器的时钟频率等于 APB1 时钟频率的 2 倍。本例APB2分频系统为2,所以TMR6的时钟频率为 32*2 = 64MHz。

4.2 TMR6定时器溢出中断工作方式

基本定时器仅提供向上计数模式。其内部拥有一个 16 位计数器,计数器的值可由周期寄存器(TMRx_PR)载入。周期寄存器寄存器的值为16时,计数器如下图方式工作。

向上计数模式中,当计数值达到 TMRx_PR 值时,重新从 0 向上计数,计数器上溢并产生溢出事件,同时 OVFIF 位置 1。

定时器工作时钟为64MHz,将分频系数设置为64000(注意分频系统最大为65536),计数的输入时钟周期为1us,周期设置为1000,则得到1ms。需要注意的时,实际设置分频系数和周期寄存器时,都需要将计算值减1。

/**
* TMR6初始化 周期为1ms
*/
void tmr6_init(void)
{/* enable tmr14 clock */crm_periph_clock_enable(CRM_TMR6_PERIPH_CLOCK, TRUE);/* 64000000/64/1000 = 1000Hz*/tmr_base_init(TMR6, 1000 - 1, 64 - 1);tmr_cnt_dir_set(TMR6, TMR_COUNT_UP);/* overflow interrupt enable */tmr_interrupt_enable(TMR6, TMR_OVF_INT, TRUE);/* tmr1 overflow interrupt nvic init */nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);nvic_irq_enable(TMR6_GLOBAL_IRQn, 0, 0);/* enable tmr1 */tmr_counter_enable(TMR6, TRUE);
}

4.3 中断响应函数

中断响应函数每次定时器溢出,也就是每隔1ms,就会被调用一次。我们在这儿进行数码管显示刷新,全局变量gSysTick加1。退出中断之前,要把中断标志清除,否则中断函数会被不停调用。所有中断响应函数名称在startup_at32f403a_407.s中已经预定义。

void TMR6_GLOBAL_IRQHandler(void)
{if(tmr_flag_get(TMR6, TMR_OVF_FLAG) != RESET){gSysTick++;display_scan();tmr_flag_clear(TMR6, TMR_OVF_FLAG);}
}

4.4 独立控制4个LED的方法

全局变量gSysTick每隔1ms会加1,作为系统的时基,它的值就是系统开始运行到现在毫秒数。我们可以利用这个变量作为控制4个LED的闪烁的基准时间。由于4个LED闪烁周期都不相同,我们定义4个函数分别控制LED4-LED7。下面代码以LED4为例来说明。

void led4_ctrl(void)
{static uint32_t tick = 0;if(gSysTick - tick >= 300/2){tick = gSysTick;if(gpio_output_data_bit_read(GPIOC, GPIO_PINS_0)==SET)gpio_bits_reset(GPIOC, GPIO_PINS_0);elsegpio_bits_set(GPIOC, GPIO_PINS_0);}
}

static表示这个变量会持续存在,不会在函数退出时被销毁。

5 参考程序

main.c

#include "at32f403a_407_conf.h"#include "display.h"uint32_t gSysTick = 0;/*** @brief  system clock config program* @note   the system clock is configured as follow:*         system clock        = hick / 12 * pll_mult*         system clock source = pll (hick)*         sclk                = 64000000*         ahbdiv              = 1*         ahbclk              = 64000000*         apb1div             = 2*         apb1clk             = 32000000*         apb2div             = 2*         apb2clk             = 32000000*         pll_mult            = 16*         pll_range           = LE72MHZ (less than 72 mhz or equal to 72 mhz)* @param  none* @retval none*/
void system_clock_config(void)
{/* reset crm */crm_reset();/* enable hick */crm_clock_source_enable(CRM_CLOCK_SOURCE_HICK, TRUE);/* wait till hick is ready */while(crm_flag_get(CRM_HICK_STABLE_FLAG) != SET){}/* config pll clock resource */crm_pll_config(CRM_PLL_SOURCE_HICK, CRM_PLL_MULT_16, CRM_PLL_OUTPUT_RANGE_LE72MHZ);/* enable pll */crm_clock_source_enable(CRM_CLOCK_SOURCE_PLL, TRUE);/* wait till pll is ready */while(crm_flag_get(CRM_PLL_STABLE_FLAG) != SET){}/* select pll as system clock source */crm_sysclk_switch(CRM_SCLK_PLL);/* wait till pll is used as system clock source */while(crm_sysclk_switch_status_get() != CRM_SCLK_PLL){}/* config ahbclk */crm_ahb_div_set(CRM_AHB_DIV_1);/* config apb2clk */crm_apb2_div_set(CRM_APB2_DIV_2);/* config apb1clk */crm_apb1_div_set(CRM_APB1_DIV_2);/* update system_core_clock global variable */system_core_clock_update();
}/**
* TMR6初始化 周期为1ms
*/
void tmr6_init(void)
{/* enable tmr14 clock */crm_periph_clock_enable(CRM_TMR6_PERIPH_CLOCK, TRUE);/* 64000000/64/1000 = 1000Hz*/tmr_base_init(TMR6, 1000 - 1, 64 - 1);tmr_cnt_dir_set(TMR6, TMR_COUNT_UP);/* overflow interrupt enable */tmr_interrupt_enable(TMR6, TMR_OVF_INT, TRUE);/* tmr1 overflow interrupt nvic init */nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);nvic_irq_enable(TMR6_GLOBAL_IRQn, 0, 0);/* enable tmr1 */tmr_counter_enable(TMR6, TRUE);
}/**
* TMR6中断响应函数
*/
void TMR6_GLOBAL_IRQHandler(void)
{if(tmr_flag_get(TMR6, TMR_OVF_FLAG) != RESET){gSysTick++;display_scan();tmr_flag_clear(TMR6, TMR_OVF_FLAG);}
}/**
* 显示gSysTick
*/
void display_tickcount(void)
{static uint32_t tick = 0;if(gSysTick - tick >= 100){display_dec_int(gSysTick/100);tick = gSysTick;}
}/**
* LED闪烁控制
*/
void led_gpio_init(void)
{//打开GPIO时钟crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, TRUE);//配置PD2~PD5, PE0~PE7为输出gpio_init_type gpio_init_struct;gpio_init_struct.gpio_pins  = GPIO_PINS_0 | GPIO_PINS_1 | GPIO_PINS_2 | GPIO_PINS_3;gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;gpio_init_struct.gpio_pull = GPIO_PULL_NONE;gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;gpio_init(GPIOC, &gpio_init_struct);
}
void led4_ctrl(void)
{static uint32_t tick = 0;if(gSysTick - tick >= 300/2){tick = gSysTick;if(gpio_output_data_bit_read(GPIOC, GPIO_PINS_0)==SET)gpio_bits_reset(GPIOC, GPIO_PINS_0);elsegpio_bits_set(GPIOC, GPIO_PINS_0);}
}
void led5_ctrl(void)
{static uint32_t tick = 0;if(gSysTick - tick >= 220/2){tick = gSysTick;if(gpio_output_data_bit_read(GPIOC, GPIO_PINS_1)==SET)gpio_bits_reset(GPIOC, GPIO_PINS_1);elsegpio_bits_set(GPIOC, GPIO_PINS_1);}
}
void led6_ctrl(void)
{static uint32_t tick = 0;if(gSysTick - tick >= 450/2){tick = gSysTick;if(gpio_output_data_bit_read(GPIOC, GPIO_PINS_2)==SET)gpio_bits_reset(GPIOC, GPIO_PINS_2);elsegpio_bits_set(GPIOC, GPIO_PINS_2);}
}
void led7_ctrl(void)
{static uint32_t tick = 0;if(gSysTick - tick >= 700/2){tick = gSysTick;if(gpio_output_data_bit_read(GPIOC, GPIO_PINS_3)==SET)gpio_bits_reset(GPIOC, GPIO_PINS_3);elsegpio_bits_set(GPIOC, GPIO_PINS_3);}
}int main(void)
{system_clock_config();led_gpio_init();DISP_gpio_pins_init();tmr6_init();while(1){display_tickcount();led4_ctrl();led5_ctrl();led6_ctrl();led7_ctrl();}
}

display.h

#ifndef __DISPLAY_H
#define __DISPLAY_Hvoid DISP_gpio_pins_init(void);void display_scan();void display_dec_int(int num);#endif

display.c

#include "at32f403a_407_conf.h"void DISP_gpio_pins_init(void)
{//打开GPIO时钟crm_periph_clock_enable(CRM_GPIOE_PERIPH_CLOCK, TRUE);crm_periph_clock_enable(CRM_GPIOD_PERIPH_CLOCK, TRUE);//配置PD2~PD5, PE0~PE7为输出gpio_init_type gpio_init_struct;gpio_init_struct.gpio_pins  = GPIO_PINS_0 | GPIO_PINS_1 | GPIO_PINS_2 | GPIO_PINS_3 | GPIO_PINS_4 | GPIO_PINS_5 | GPIO_PINS_6 | GPIO_PINS_7;gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;gpio_init_struct.gpio_pull = GPIO_PULL_NONE;gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;gpio_init(GPIOE, &gpio_init_struct);gpio_init_struct.gpio_pins  = GPIO_PINS_2 | GPIO_PINS_3 | GPIO_PINS_4 | GPIO_PINS_5;gpio_init(GPIOD, &gpio_init_struct);
}uint8_t disp_buf[4];
void display_dec_int(int num)
{static uint8_t tab[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e};disp_buf[0] = tab[num/1000%10];disp_buf[1] = tab[num/100%10];disp_buf[2] = tab[num/10%10];disp_buf[3] = tab[num%10];
}void display_scan()
{static int cur_digit = 0;//全关gpio_bits_set(GPIOD, GPIO_PINS_2 | GPIO_PINS_3 | GPIO_PINS_4 | GPIO_PINS_5); //输出字形码gpio_bits_reset(GPIOE, 0xff); //PE0~PE7 = 0gpio_bits_set(GPIOE, disp_buf[cur_digit]); //打开对应位开关gpio_bits_reset(GPIOD, GPIO_PINS_2 << cur_digit);//更新cur_digit, 准备下一次扫描cur_digit = (cur_digit + 1) % 4;
}

掌上实验室V8系列教程(五)定时器中断及应用相关推荐

  1. 掌上实验室V8系列教程(四)定时器PWM输出

    1 项目功能 三色RGB LED 通过PWM 调色 2 电路原理 我们可以通过3路PWM波分别调整RGB三个二极管的亮度,即调整RGB的比例,合成不同的颜色. PWM,英文名Pulse Width M ...

  2. 掌上实验室V8系列教程(八)ADC模数转换

    目录 1 项目功能 2 ADC工作原理 3 电路原理图 4 AT32 ADC应用 4.1 ADC基础操作流程 4.2 ADC触发 4.3 ADC采样和转换时间 4.4 读取ADC转换结果 5 示例程序 ...

  3. 掌上实验室V8系列教程(二)跑马灯

    目录 1 项目功能 2 电路原理 3 软件流程 4 程序设计 5.参考资源 1 项目功能 循环点亮LED4, LED5, LED6, LED7 2 电路原理 根据硬件电路图,PC0输出"0& ...

  4. 掌上实验室V8系列教程(九)光电编码器转速测量

    1 项目功能 通过光电编码器进行转速测量 2 光电编码器工作原理 光电编码器,是一种通过光电转换将输出轴上的机械几何位移量转换成脉冲或数字量的传感器.这是应用最多的传感器,光电编码器是由光源.光码盘和 ...

  5. 掌上实验室V8系列教程(七)I2C应用 HP203B

    目录 1 项目功能 2 电路原理图 3 传感器HP203B 3.1 HP203B功能 3.2 HP203B技术指标 3.3 HP203B I2C总线接口命令 4 示例代码 1 项目功能 通过HB203 ...

  6. 掌上实验室V8系列教程(三)数码管动态显示

    目录 1 项目功能 2 电路原理 3 动态显示原理 4 示例代码 6 扩展功能 7 参考资源 1 项目功能 4位数码管动态显示 2 电路原理 3 动态显示原理 一位八段式数码管内部有8个LED,习惯上 ...

  7. CubeMX系列教程——9 定时器中断

    复制上篇工程,并打开 选择定时器,查看参数,1ms定时. 允许定时器中断 设置定时器中断优先级 生成初始化代码,打开工程 在time.c文件中添加测试代码,当定时器1ms时间到时进入中断回调函数. 在 ...

  8. 基于雅特力AT32的 《掌上实验室》V8

    1 项目介绍 掌上实验室V8是由中国计量大学现代科技学院和雅特力共同研发的一款ARM学习板,内置AT-Link-Ez仿真器. 主MCU采用雅特力AT32F407VGT7. 板上资源: 1.通信接口 1 ...

  9. C#微信公众号开发系列教程五(接收事件推送与消息排重)

    C#微信公众号开发系列教程五(接收事件推送与消息排重) 原文:C#微信公众号开发系列教程五(接收事件推送与消息排重) 微信公众号开发系列教程一(调试环境部署) 微信公众号开发系列教程一(调试环境部署续 ...

最新文章

  1. C++ unique and erase问题处理
  2. linux mysql 5.7 双机热备_2017年5月5日 星红桉liunx动手实践mysql 主主双机热备
  3. 运行Python程序的2种方式
  4. laravel 任务队列_Laravel5.5之事件监听、任务调度、队列
  5. 浅谈MaxCompute资源规划管理及评估
  6. python发邮件详解_python:利用smtplib发送邮件详解
  7. 搜索、推荐、广告中的曝光偏差问题
  8. 你见过使用寿命最长的手机有多少年?
  9. CCF为何能吸引6.8万付费会员?
  10. Python-变分模态分解(VMD)python代码及其测试用例
  11. Samba瞎折腾一下
  12. 爱心代码(带字的奥)
  13. 高德地图和百度地图生成网址
  14. 固态硬盘raw格式数据能恢复吗(图文)
  15. Eolution登录live邮箱
  16. 牛年牛人侃电脑 (素材来自网络)改编 大风
  17. python中flatten_Python中flatten( )函数及函数用法详解
  18. 迪赛智慧数——其他图表(平行坐标图):世界杯历史个人进球排名TOP10
  19. 四川大学计算机学院陈宇老师,吕建成(四川大学计算机学院(软件学院)院长)_百度百科...
  20. 英汉互译在线翻译器-大家都在用的英汉互译翻译器

热门文章

  1. HTML5权威指南读书笔记12(第21章)--创建布局postion,z-index,column-count,display:flex,box-flex、algin、pack,table
  2. word文档如何插入求和Σ公式
  3. labview 转标准c语言,LabVIEW编程之字符串转换为双精度数
  4. CATIA转的STP打开什么都没有_第五篇:STP
  5. i5 10500和i7 9700k哪个好
  6. python读取excel汉字转成拼音_Python语言之用Python将全部中文姓名转为拼音
  7. Linux虚拟机:Centos6.5设置锁屏时间
  8. gif动态图片怎样裁剪后还是动图?
  9. 【EasyExcel】模板填充List
  10. 小米致歉并紧急叫停小米9发售:备货不足