2021-2022嵌入式系统期末复习提纲(含习题)
2021-2022(2)嵌入式系统期末复习提纲 (习题版)
前言
前段时间考完嵌入式系统的期末考试,这两天抽出空来把之前整理的提纲发出来。
在考试前按照老师给的复习提纲整理了一份知识点,看到提纲里没有指令系统,就没有去看那一小节的内容,结果考试的时候选择题考了好几题指令系统,心态都炸了。考完后老师说准备了两套期末试卷,可能抽中的是另一套,这个解释有点让人难以接受,不过按照我下面准备的知识点来背还是可以应付期末考试的。
最后我还是想强调一下,没有什么投机取巧的道路,正如复习要点第一点中的通读教材。好好背!
一、考试题型
- 选择(20 分)
- 简答(30 分)
- 程序题(20 分,内容包括代码解释、填空等)
- 论述题(30 分)
二、复习要点
- 通读教材,突出重点
- 分析代码,尤其初始化部分,动作部分,理解程序
- 了解嵌入式处理器的选型原则(根据需求,性能,成本,开发工具,已有资源)
- 了解嵌入式系统的发展趋势
三、主要知识点
第 1 章 嵌入式系统概述
1.嵌入式系统概念、应用与特点
练习题:简答题:简述嵌入式系统的定义、应用和特点
定义:嵌入式系统是以应用为中心,以计算机技术为基础,并且在软、硬件方面可裁剪,适用于应用系统对功能、可靠性、成本、体积、功耗有严格要求的专用计算机系统。
应用:应用于国防军事、工业控制、交通管理、信息家电、医疗保健设备、机器人等领域
特点:1.专用于特定任务、2.多类型处理器和处理器系统支持、3.通常极其关注成本、4.一般是实时系统、5.可裁剪性好、6.可靠性高、7.大多有功耗约束
练习题:简答题:简要叙述嵌入式系统的定义及组成
定义:嵌入式系统是以应用为中心,以计算机技术为基础,并且在软、硬件方面可裁剪,适用于应用系统对功能、可靠性、成本、体积、功耗有严格要求的专用计算机系统。
组成:它一般由嵌入式微处理器、外围硬件设备、嵌入式操作系统以及用户的应用程序等四个部分组成。
2.嵌入式系统的硬件(CPU、外设)
真题:(2016-2017问答题 第3题) 目前流行的嵌入式微处理器有哪几种,各有何特点?
真题:(2017-2018问答题 第3题) 写出至少4种目前流行的嵌入式微处理器及特点
①ARM微处理器:ARM公司提供IP授权生产的芯片,各具特色,占据32位市场份额90%以上
②MIPS微处理器:面向高性能、高档次的32位和64位处理器市场
③PowerPC:品种良多,覆盖范围广,从高端工作站到桌面计算机,从消费类电子产品到大型通信设备,无所不包;
④x86:源自Intel8080,最早的嵌入式芯片,良好的兼容性限制了其性能发展
⑤Motorola 68000: CISC架构,面向通信应用
3.主要嵌入式软件系统(应用及 OS)
练习题:简答题:嵌入式操作系统的定义
一种支持嵌入式系统应用的操作系统软件,是嵌入式系统极为重要的组成部分。通常包括与硬件相关的底层驱动软件、系统内核、设备驱动接口、通信协议、图形界面等,嵌入式操作系统负责嵌入式系统的全部软、硬件资源的分配、任务调度,控制、协调并发活动。
练习题:简答题:列出常见的4种嵌入式操作系统
①μC/OS –II ②嵌入式Linux ③Windows Embedded ④VxWorks
练习题:简答题:实时操作系统的评价指标有哪些
①上下文切换时间 ②抢占时间 ③中断延时 ④信息量混洗时间
练习题:简答题:简述嵌入式操作系统与通用操作系统的区别
通用操作系统可运行在不同的硬件平台,而嵌入式操作系统与一般的Windows操作系统不同,其是一种专用、可定制的操作系统。
4.嵌入式系统的发展趋势
①嵌入式开发是一项系统工程
②网络化、信息化的要求
③网络互联、移动互联成为必然趋势
④精简系统内核、算法,降低功耗和软硬件成本
⑤提供友好的多媒体人机界面。
第 2 章 ARM Cortex-M3微处理器基础
1.ARM 处理器特点及应用
练习题:简答题:ARM处理器有什么特点?
ARM处理器的三大特点是:耗电少功能强、16位/32位双指令集和合作伙伴众多
①体积小、低功耗、低成本、高性能
②支持Thumb(16位)/ARM(32位)双指令集,能很好的兼容8位/16位器件
③大量使用寄存器,指令执行速度更快
④大多数数据操作都在寄存器中完成
⑤寻址方式灵活简单,执行效率高
⑥指令长度固定
2.CM3 微控制器简介
练习题:简答题:ARM Cortex-M3采用的哪一种架构,能够执行的指令集有哪些?
①ARMv7-M架构
②16位Thumb指令集和32位Thumb-2指令集
练习题:简答题:ARM Cortex-M3处理器系统的主要模块包括?
①处理器内核
②嵌套向量中断控制器NVIC
③总线矩阵
④存储器保护单元MPU
⑤系统调试组件和调度端口
⑥唤醒中断控制器WIC
练习题:简答题:Cortex-M3的处理器有哪两种工作模式和状态?如何进行工作模式和状态的切换?
①工作模式:线程模式和处理模式。
在复位时处理器进入线程模式,异常返回时也会进人该模式,特权模式和用户(非特权)模式代码能够在线程模式下运行。
出现异常时处理器进人处理模式,在处理模式下,所有代码都是特权访问的。
工作状态:Thumb 状态和调试状态。
Thumb 状态是 16 位和 32 位 ” 半字对齐 ” 的 Thumb 和 Thumb-2 指令的执行状态。
调试状态是处理器停止并进行调试时所进人的状态。
②通过Bx Rn指令来进行两种状态的切换
练习题:简答题:Cortex-M3的处理器支持哪两种工作模式?特权模式有哪些?非特权模式有哪些?
①线程模式和处理模式
②特权模式:系统模式、一般中断模式、快速中断模式、管理模式、中止模式、未定义指令中止模式
③非特权模式:用户模式
练习题:简答题:ARM微处理器的运行模式有哪几种,请列举并说明
ARM微处理器的运行模式有7种,分别为:
①用户模式:正常程序执行时,ARM处理器所处的状态
②快速中断模式:用于快速数据传输和通道处理
③外部中断模式:用于通常的中断处理
④特权模式:供操作系统使用的一种保护模式
⑤数据访问中止模式:当数据或指令预取终止时进入该模式,用于虚拟存储及存储保护
⑥未定义指令终止模式:用于支持硬件协处理器软件仿真
⑦系统模式:用于运行特权级的操作系统任务
3.CM3 存储格式类型
练习题:简答题:什么是大端格式?什么是小端格式?
在大端格式中,字数据的高字节存储在低地址中,而字数据的低字节则存放在高地址中。
与大端存储格式相反,在小端存储格式中,低地址中存放的是字数据的低字节,高地址存放的是字数据的高字节。
练习题:问答题:ARM里的大端格式和小端格式分别是什么意思?举例画图说明。
当前的存储器,多以byte为访问的最小单元,当一个逻辑上的地址必须分割为物理上的若干单元时就存在了先放谁后放谁的问题,于是端的问题应运而生了,对于不同的存储方法,就有大端和小端两个描述。
大端:低地址存放高有效字节
(1000 0000) 2 = 1
小端:低地址存放低有效字节
(1000 0000) 2 = 128
4.CM3 嵌套向量中断控制器 NVIC
真题:(2016-2017、2017-2018简答题 第3题) NVIC中的末尾连锁技术的作用是什么
作用是在连续两次中断时,将上一次的返回指令和下一次的调用指令执行所花费的时间,由42个Cycles降低到6个Cycles
5.CM3 寄存器组织
真题:(2016-2017问答题 第2题) ARM Cortex-3的通用寄存器是如何组织的?
真题:(2017-2018问答题 第2题) ARM Cortex-3的通用寄存器有哪些?各有何作用?
ARM Cortex-3拥有16个32位通用寄存器,名称R0-R15,分为
①低寄存器:R0-R7,可被指定的所有指令访问,复位初始值不可知;
②高寄存器:R8-R12,能被指定的所有32位指令访问,复位初始值不可知;
③堆栈指针SP:R13,作为SP自动与字对齐([1:0]位忽略)。对应两个不会被同时访问的物理寄存器SP_main和SP_process;
④链接寄存器LR:R14,执行分支指令时存储一个返回地址;
⑤程序计数器PC:R15,指向当前的程序地址。
6.CM3 存储映射
练习题:简答题:说明ARM Cortex-M3处理器的存储器系统特点及映射分区
ARM Cortex-M3的存储器映射是预定义的,他的存储器系统支持位带操作,实现了在特殊的存储器区域对单一比特的原子操作。
映射分区有:①代码区、②片上SRAM区、③片上外设区、④片外RAM区和片外外设区、⑤私有外设区、⑥芯片商指定区
7.CM3 异常与中断
真题:(2016-2017、2017-2018简答题 第1题) 辨析STM32中的异常和中断概念
在STM32中,正常的程序执行流程发生暂时停止的情形,统称为异常;而由外部事件导致的程序执行流程改变的情形也可以称为中断。因此,异常的概念包含了中断,或者说,中断是一种特殊的异常。
练习题:简答题:ARM Cortex-M3处理器的中断响应过程分为哪几个步骤?
①入栈 ②取向量 ③更新寄存器
练习题:问答题:ARM Cortex-M3为什么能够高效且低延迟地对异常进行处理?
①自动的状态保存和恢复
②优先级屏蔽支持临界区
③自动读取代码存储区或SRAM中包含ISR地址的向量表入口
④支持末尾锁链,在末尾锁链中,处理器在两个ISR之间没有对寄存器进行出栈和压栈操作的情况下处理背对背中断
⑤ISR Cortex-M3与NVIC之间采用紧耦合接口,通过该接口可以尽早地对中断和高优先级的迟来中断进行处理
⑥中断优先级可动态重新设置
⑦中断数目可设置为1-240
⑧中断优先级可配置为1-8位
⑨处理模式与线程模式具有独立的堆栈和特权等级
⑩使用C/C++标准的调用规范:ARM架构的过程调用标准执行ISR控制传输
练习题:问答题:ARM 处理器的异常主要有哪些表现形式
①快速中断请求 ②中断请求 ③软件中断 ④预取指中止 ⑤ 数据中止 ⑥未定义指令 ⑦复位
8.STM32 时钟源与时钟树
真题:(2016-2017、2017-2018问答题 第1题) STM32平台中提供哪些时钟源,其各自的频率范围是多少?
①高速外部时钟(HSE),频率4-16MHz
②低速外部时钟(LSE),频率32.768kHz
③高速内部时钟(HSI),频率8MHz
④低速内部时钟(LSI), 频率40kHz
⑤锁相环时钟(PLL) ,频率72MHz
练习题:简答题:STM32有哪几种时钟源?他们的来源和作用分别是什么?
①高速外部时钟(HSE),来源:外部晶体/陶瓷振荡器,或用户外部时钟, 作用:驱动系统时钟
②低速外部时钟(LSE),来源:外部晶体或陶瓷振荡器,或外部时钟,作用:驱动RTC
③高速内部时钟(HSI),来源:内部8MHz的RC振荡器,作用:驱动系统时钟
④低速内部时钟(LSI),来源:内部RC振荡器,作用:为独立看门狗、RTC和自动唤醒单元提供时钟
⑤锁相环时钟(PLL),来源:HSI或HSE,作用:驱动系统时钟
9.STM32F10 系列的 GPIO
练习题:简答题:什么是GPIO?
GPIO是一个灵活的由软件控制的数字信号,每个GPIO都代表一个连接到CPU特定引脚的一个位。STM32的GPIO端口的每一位都可以由软件配置成多种模式:输入浮空、输入上拉、输入下拉、模拟输入、开漏输出、推挽输出、推挽式复用功能、开漏复用功能。
练习题:简答题:STM32的GPIO端口可以配置成哪几种模式?
输入浮空、输入上拉、输入下拉、模拟输入、开漏输出、推挽输出、推挽式复用功能、开漏复用功能。
练习题:简答题:STM32F103VBT6一共有多少组I/O口?每组有多少个I/O口?并简述STM32的GPIO端口的特点(至少三点)
①5组
②16个
③特点如下:
1.通用输入/输出功能,可驱动LED等
2.单独的位设置和位清除,每个端口可被独立配置
3.所有端口都具有外部中断能力
4.复用功能
5.软件重映射端口复用
6.GPIO锁定机制
练习题:简答题:STM32F103VBT6的各个部分的含义是什么,有多少个IO?分成多少组?
①有80个IO,分为5组,每组16个IO
②STM32F103VBT6微控制器属于STM32F103xx 增强系列。这个名字中的字母V表示有100脚封装、字母B表示带有128KB的闪存存储器,T表示控制器采取QFP封装,6表示工业级温度范围(-40℃-85℃)
练习题:简答题:STM32F10系列的GPIO的端口复用和重映射的含义。
端口复用:在STM32中,有许多I/O端口,同时也内置了许多外设,为了节约引出管脚,这些内置外设引出管脚是与通用I/O管脚公用的,当I/O管脚作为这些外设模块的功能引脚时就叫端口复用功能。
重映射:一个外设的引脚除了具有默认的引脚位外,还可以通过配置重映射寄存器的方式,把这个外设的引脚映射其他的引脚位。
第 3 章 嵌入式开发环境
1.嵌入式开发环境(交叉编译、 开发工具:Keil μVision)
真题:(2016-2017、2017-2018简答题 第4题) 什么是交叉编译?
在某个主机平台上(如PC)用交叉编译器编译出可在其他平台上(如ARM)运行的代码的过程。
练习题:简答题:什么是交叉编译,为什么要采用交叉编译?
所谓交叉编译,是指在一个平台上编译生成在另一个平台上运行的可执行程序。
之所以采用交叉编译,是因为目标平台上不具备直接运行开发环境的条件。
第 5 章 μC/OS –II及应用开发
1.嵌入式操作系统概念、特点及应用
练习题:问答题:什么是嵌入式操作系统?嵌入式操作系统的基本特点是什么?为什么需要嵌入式操作系统?
①一种支持嵌入式系统应用的操作系统软件,是嵌入式系统极为重要的组成部分。通常包括与硬件相关的底层驱动软件、系统内核、设备驱动接口、通信协议、图形界面等,嵌入式操作系统负责嵌入式系统的全部软、硬件资源的分配、任务调度,控制、协调并发活动。
②具有通用操作系统的基本特点:能够有效管理越来越复杂的系统资源,能够把硬件虚拟化,简化的驱动程序移植和维护,能够提供库函数、驱动程序、工具集以及应用程序。
③嵌入式处理器性能提高,资源增多
④任务要求复杂,实时、多任务、编程困难
⑤许多的任务需求是相同的,完全可以将一些相同的功能作为一个通用模块实现,这就是操作系统,它屏蔽了底层硬件的细节,方便用户,提供了标准的、可裁剪的系统服务软组件。
练习题:问答题:请介绍一下μC/OS –II操作系统,并简述一下μC/OS –II操作系统的特点,并列出μC/OS –II的内核和内核结构?
①μC/OS –II是一个实时操作系统内核,它包含任务调度,任务管理,时间管理,内存管理和任务间的通信与同步等基本功能,而没有输入输出管理,文件系统,网络之类的服务。
②μC/OS –II特点:
1.公开源代码:μC/OS –II的全部源代码全部公布出来,能够在网上查询。
2.可移植性:绝大部分的μC/OS –II的源码使用移植性很强的ANSI C写的,只有与微处理器硬件相关用汇编语言写的。使用汇编写的已经缩减到最小,使得μC/OS –II便于移植
3.可固化:μC/OS –II是为嵌入式应用而设计的,因此,只要读者有固化手段就能嵌入到读者的产品中
4.可裁剪性:μC/OS –II可以只使用应用程序需要的那些服务
5.占先式:μC/OS –II占先式的实时内核,这表示μC/OS –II总是运行就绪状态下优先级最高的任务
6.多任务:μC/OS –II可管理64个任务,其中8个任务留给系统,应用程序最多有56个任务,但每个任务的优先级必须不同
7.可确定性:全部μC/OS –II的函数调用和服务的执行时间具有可确定性,也就是:全部μC/OS –II的函数调用和服务的执行时间可预知的
8.任务栈:μC/OS –II允许每个任务有不同的栈空间,每个任务有自己单独的栈,以便压低应用程序对RAM的需求
9.系统服务:μC/OS –II提供了很多系统服务,例如邮箱,消息队列,信号量,块大小固定的内存的申请与释放等
10.中断管理:中断可以使正在执行的任务暂时挂起,允许中断嵌套,中断嵌套层数可达255层
11.稳定性和可靠性:μC/OS –II是基于μC/OS 的,μC/OS 自使用以来已经被好几百个商业应用
③μC/OS –II的内核:
1.任务管理 2.时间管理 3.任务之间的通信与同步 4.内存管理 5.μC/OS的中断处理 6.μC/OS –II的初始化 7.μC/OS –II的启动
④μC/OS –II的内核结构:
1.临界段 2.任务 3.任务状态 4.任务控制块 5.任务调度 6.空闲任务
练习题:简答题:μC/OS –II通过哪些方法实现任务之间的通信与同步,并简要说明。
①事件控制块ECB:所有的通信信号都被看成是事件,μC/OS –II通过μC/OS –II.H中定义的一个被称为事件控制块的数据结构OS_EVENT来表征每一个具体的事件
②信号量:μC/OS –II系统中信号量由两部分组成,信号量的计数值和等待该信号任务的等待任务表。信号量在多任务系统中用于控制共享资源的使用权,标志事件的发生,使两个任务的行为同步
③邮箱:邮箱是μC/OS –II系统中另一种通信机制,他可以使一个任务或者中断服务的子程序向另一个任务发送一个指针型的变量
④消息队列:消息队列也是μC/OS –II中的一种通信机制,它可以使一个任务或者中断服务子程序向另一个任务发送以指针方式定义的变量
2.任务状态
(2017-2018 填空题) μC/OS –II的任务共有休眠、就绪、运行、中断服务以及等待或挂起五个状态
第 4 章 STM32-A平台开发基础(理论)
练习题:简答题:USART、NVIC等常见名词分别是指什么?
①UART是一种通用串行数据总线,用于异步通信,该总线双向通信,可以实现全双工传输和接收
②NVIC是内嵌向量中断控制器,提供中断控制器,用于总体管理异常
练习题:问答题:什么是看门狗,看门狗的作用是什么?
①为了检测和解决由软件错误引起的故障,嵌入式处理器通常具有一种叫做看门狗的模块
②这种模块一般是倒计数器,其作用是以一定的周期产生复位信号使系统复位,产生复位信号的时刻一般是倒计数到某个值(例如0)的时刻
③在设计嵌入式系统软件时,通过在看门狗产生复位信号前执行喂狗操作(即让看门狗的倒计数值重新从某个设定的倒计数值开始)来避免看门狗倒计数到产生复位信号的值
第 4 章 STM32-A平台开发基础(实验)
P105-P116
GPIO 8种模式:输入浮空、输入下、输入上拉、模拟输入、开漏输出、推挽式输出、推挽式复用功能、开漏复用功能;
3个最大输出速度:2MHz、10MHz、50MHz
位带操作
typedef unsigned long u32;
#define RCC_APB2ENR ((u32 *)0x40021018) //定义APB2ENR寄存器 地址
#define AFIO_MAPR ((u32 *)0x40010004) //定义APIO的MAPR寄存器
#define GPIOB_CRL (*(u32 *)0x40010C00) //定义GPIOB_CRL寄存器 值
#define GPIOB_ODR (*(u32 *)0x40010C0C) //定义GPIOB_ODR寄存器
#define GPIOE_CRH (*(u32 *)0x40011804) //定义GPIOE_CRH寄存器
#define GPIOE_ODR (*(u32 *)0x4001180C) //定义GPIOE_ODR寄存器
u32 *gpio_odr=((u32 *)0x4001180c); //定义成地址变量
u32 *PEO8 = (u32 *)(0x42000000 +(0x4001180C-0x40000000)*32 + 8*4); //位带定义PE08
u32 *PEO9 = (u32 *)(0x42000000 +(0x4001180C-0x40000000)*32 + 9*4); //位带定义PE09
int delay(int Time)
{ //简单延时程序
unsigned short t,i,j;
for(t=0;t<Time;t++)
for(i=0;i<1000;i++)
for(j=0;j<1000;j++)
;
return 0;
}
int main(void)
{
*RCC_APB2ENR|=1<<0; //使能AFIO
*RCC_APB2ENR|=1<<3; //使能PORTB时钟
*RCC_APB2ENR|=1<<6; //使能PORTE时钟
*AFIO_MAPR |= 0x02000000; //设置PB.3为I/O口可用,且可以SW仿真
GPIOB_CRL &= 0xFFFF0FFF; //清除PB.3原先配置
GPIOB_CRL = GPIOB_CRL | 0x00003000; //PB.3配置为推挽输出
GPIOB_ODR |= 0x00000008; //PB.3输出高,选择控制LED灯
GPIOE_CRH &=0X00000000; //清除PE.8-15原先配置
GPIOE_CRH |= 0X33333333; //PE.8-15配置为推挽输出
GPIOE_ODR |= 0x0000FF00; //PE.8-15输出高,八个LED灯全亮
delay(2);
while(1)
{
GPIOE_ODR = 0x00000100; //仅LED1亮,其他灯灭
delay(2);
*PEO8 = 0; //LED1灭
delay(2);
*PEO8 = 1; //LED1亮
delay(2);
*PEO9 = 1; //LED2亮
delay(2);
GPIOE_ODR = 0x00000400; //仅LED3亮,其他灯灭
delay(2);
GPIOE_ODR = 0x00000800; //仅LED4亮,其他灯灭
delay(2);
GPIOE_ODR = 0x00001000; //仅LED5亮,其他灯灭
delay(2);
GPIOE_ODR = 0x00002000; //仅LED6亮,其他灯灭
delay(2);
(*(u32 *)0x4001180C) = 0x00004000; //仅LED7亮,其他灯灭
delay(2);
*gpio_odr = 0x00008000; //仅LED8亮,其他灯灭
delay(2);
}
//永远不会执行到这
return 0;
}
寄存器版本
led.c
#include "stm32f10x_map.h"
#include "stm32f10x_nvic.h"
#include "led.h"
void LED_Init(void)
{
RCC->APB2ENR|=1<<0; //使能AFIO
RCC->APB2ENR|=1<<3; //使能PORTB时钟
RCC->APB2ENR|=1<<6; //使能PORTE时钟
AFIO->MAPR |= 0x02000000; //设置PB.3为I/O口可用,且可以SW仿真
GPIOB->CRL &= 0xFFFF0FFF;
GPIOB->CRL |= 0x00003000; //PB.3推挽输出
GPIOB->ODR |= 0x00000008; //PB.3输出高
GPIOE->CRH&=0X00000000;
GPIOE->CRH|=0X33333333; //PE.8-15推挽输出
GPIOE->ODR|=0x0000FF00; //PE.8-15输出高
}
led.h
#ifndef __LED_H
#define __LED_H
#include "sys.h"
//LED驱动代码
//LED端口定义
#define LED_SEL PBout(3) //PB3
//位选
#define SEL0 PBout(0)
#define SEL1 PBout(1)
#define SEL2 PBout(2)
//段选
#define LED0 PEout(8)
#define LED1 PEout(9)
#define LED2 PEout(10)
#define LED3 PEout(11)
#define LED4 PEout(12)
#define LED5 PEout(13)
#define LED6 PEout(14)
#define LED7 PEout(15)
void LED_Init(void);//初始化
#endif
main.c
/******************************流水灯************************
* 流水灯
* 现象:二极管从左至右依次全部点亮
*************************************************************/
#include "sys.h"
#include "delay.h"
#include "led.h"
u8 light;
int main( void )
{
Stm32_Clock_Init( 6 ); //6倍频
delay_init( 72 ); //12M外部晶振
LED_Init();
GPIOE->ODR &= ~(0xff<<8);
LED_SEL = 1; //选择二极管
light = 0x01;
while( 1 )
{
GPIOE->ODR |= (light<<8);
delay_ms( 300 );
light = light<<1;
if( light==0x00 )
{
GPIOE->ODR &= ~(0xff<<8);
delay_ms( 300 );
light = 0x01;
}
}
}
2.中断基本概念与中断程序设计
P116-P128
按键
通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,电压信号波型如下图。由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,如下图。抖动时间的长短由按键的机械特性决定,一般为5ms~10ms。这是一个很重要的时间参数,在很多场合都要用到。按键稳定闭合时间的长短则是由操作人员的按键动作决定的,一般为零点几秒至数秒。键抖动会引起一次按键被误读多次。为确保CPU对键的一次闭合仅作一次处理,必须去除键抖动。在键闭合稳定时读取键的状态,并且必须判别到键释放稳定后再作处理。
key.c
#include "key.h"
#include "delay.h"
//按键输入 驱动代码
//PC0.1.2 设置成输入
void KEY_Init(void)
{
RCC->APB2ENR|=1<<4; //使能PORTC时钟
GPIOC->CRL&=0XFFFFF000; //PC0-2设置成输入
GPIOC->CRL|=0X00000444; //用上拉输入或者浮空输入都可以,模拟输入不行。
//GPIOC->ODR|=0x7; //上拉输入//ODR复位值为0,缺省为下拉输入。
}
//按键处理函数
//返回按键值
//0,没有任何按键按下
//1,KEY1按下
//2,KEY2按下
//3,KEY3按下
//注意此函数有响应优先级,KEY1>KEY2>KEY3!!
u8 KEY_Scan(void)
{
static u8 key_up=1;//按键按松开标志
if(key_up && (KEY1==0 || KEY2==0 || KEY3==0))
{
delay_ms(10);//去抖动
key_up=0;
if(KEY1==0)
{
return 1;
}
else if(KEY2==0)
{
return 2;
}
else if(KEY3==0)
{
return 3;
}
}
else if(KEY1==1 && KEY2==1 && KEY3==1)
key_up=1;
return 0;// 无按键按下
}
key.h
#ifndef __KEY_H
#define __KEY_H
#include "sys.h"
//
//按键输入 驱动代码
//********************************************************************************
//V1.1修改说明
//修改按键扫描函数,使整个代码可以支持SWD下载。
//
#define KEY1 PCin(2) //PC2
#define KEY2 PCin(1) //PC1
#define KEY3 PCin(0) //PC0
void KEY_Init(void);//IO初始化
u8 KEY_Scan(void); //按键扫描函数
#endif
main.c
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "key.h"
//按键输入实验
int main(void)
{
u8 t;
Stm32_Clock_Init(9); //系统时钟设置
delay_init(72); //延时初始化
LED_Init(); //初始化与LED连接的硬件接口
KEY_Init(); //初始化与按键连接的硬件接口
while(1)
{
t=KEY_Scan();//得到键值
if(t)
{
switch(t)
{
case 1:
LED0=!LED0;
break;
case 2:
LED3=!LED3;
break;
case 3:
LED7=!LED7;
break;
}
}else delay_ms(10);
}
}
中断
exit.c
#include "exti.h"
#include "led.h"
#include "key.h"
#include "delay.h"
//
//外部中断 驱动代码
//
//外部中断0服务程序
void EXTI0_IRQHandler(void)
{
delay_ms(10);//消抖
if(KEY3==0) //按键3
{
LED7=!LED7;
}
EXTI->PR=1<<0; //清除LINE0上的中断标志位
}
//外部中断1服务程序
void EXTI1_IRQHandler(void)
{
delay_ms(10);//消抖
if(KEY2==0) //按键2
{
LED3=!LED3;
}
EXTI->PR=1<<1; //清除LINE1上的中断标志位
}
//外部中断2服务程序
void EXTI2_IRQHandler(void)
{
delay_ms(10);//消抖
if(KEY1==0) //按键1
{
LED0=!LED0;
}
EXTI->PR=1<<2; //清除LINE2上的中断标志位
}
//外部中断初始化程序
//初始化PC0-2为中断输入.
void EXTIX_Init(void)
{
RCC->APB2ENR|=1<<4; //使能PORTC时钟
GPIOC->CRL&=0XFFFFF000;//PC0-2设置成输入
GPIOC->CRL|=0X00000888;
Ex_NVIC_Config(GPIO_C,0,FTIR);//下降沿触发
Ex_NVIC_Config(GPIO_C,1,FTIR);//下降沿触发
Ex_NVIC_Config(GPIO_C,2,FTIR);//下降沿触发
MY_NVIC_Init(2,2,EXTI0_IRQChannel,2);//抢占2,子优先级2,组2
MY_NVIC_Init(2,1,EXTI1_IRQChannel,2);//抢占2,子优先级1,组2
MY_NVIC_Init(2,0,EXTI2_IRQChannel,2);//抢占2,子优先级1,组2
}
main.c
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "exti.h"
//外部中断实验
//注意,此代码还是无法进行SWD仿真!因为使用了中断,没法用普通的方法来考虑间歇复用SWD口!
int main(void)
{
Stm32_Clock_Init(9); //系统时钟设置
delay_init(72); //延时初始化
LED_Init(); //初始化与LED连接的硬件接口
EXTIX_Init(); //外部中断初始化
while(1)
{
delay_ms(100);
}
}
3.串口通信原理与程序设计
P128-P135
#include "sys.h"
#include "usart.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//********************************************************************************
//V1.3
//支持适应不同频率下的串口波特率设置.
//加入了对printf的支持
//增加了串口接收命令功能.
//修正了printf第一个字符丢失的bug
//
//
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
/* Whatever you require here. If the only file you are using is */
/* standard output using printf() for debugging, no file handling */
/* is required. */
};
/* FILE is typedef’ d in stdio.h. */
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
_sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif
//end
//
#ifdef EN_USART1_RX //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[64]; //接收缓冲,最大64个字节.
//接收状态
//bit7,接收完成标志
//bit6,接收到0x0d
//bit5~0,接收到的有效字节数目
u8 USART_RX_STA=0; //接收状态标记
void USART1_IRQHandler(void)
{
u8 res;
if(USART1->SR&(1<<5))//接收到数据
{
res=USART1->DR;
if((USART_RX_STA&0x80)==0)//接收未完成
{
if(USART_RX_STA&0x40)//接收到了0x0d
{
if(res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x80; //接收完成了
}else //还没收到0X0D
{
if(res==0x0d)USART_RX_STA|=0x40;
else
{
USART_RX_BUF[USART_RX_STA&0X3F]=res;
USART_RX_STA++;
if(USART_RX_STA>63)USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
}
#endif
//初始化IO 串口1
//pclk2:PCLK2时钟频率(Mhz)
//bound:波特率
//CHECK OK
//091209
void uart_init(u32 pclk2,u32 bound)
{
float temp;
u16 mantissa;
u16 fraction;
temp=(float)(pclk2*1000000)/(bound*16);//得到USARTDIV
mantissa=temp; //得到整数部分
fraction=(temp-mantissa)*16; //得到小数部分
mantissa<<=4;
mantissa+=fraction;
RCC->APB2ENR|=1<<2; //使能PORTA口时钟
RCC->APB2ENR|=1<<14; //使能串口时钟
GPIOA->CRH&=0XFFFFF00F;
GPIOA->CRH|=0X000008B0;//IO状态设置
RCC->APB2RSTR|=1<<14; //复位串口1
RCC->APB2RSTR&=~(1<<14);//停止复位
//波特率设置
USART1->BRR=mantissa; // 波特率设置
USART1->CR1|=0X200C; //1位停止,无校验位.
#ifdef EN_USART1_RX //如果使能了接收
//使能接收中断
USART1->CR1|=1<<8; //PE中断使能
USART1->CR1|=1<<5; //接收缓冲区非空中断使能
MY_NVIC_Init(3,3,USART1_IRQChannel,2);//组2,最低优先级
#endif
}
main.c
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
int main( void )
{
/*u8 i = 1;
Stm32_Clock_Init( 6 ); //9倍频
delay_init( 72 ); //12M外部晶振
uart_init( 72, 9600 );
while( 1 )
{
//printf( "i = %d\r\n", i );
i++;
delay_ms( 500 );
}
*/
u8 t;
u8 len;
u16 times=0;
Stm32_Clock_Init(6); //系统时钟设置
delay_init(72); //延时初始化
uart_init(72,9600); //串口初始化为9600
LED_Init(); //初始化与LED连接的硬件接口
while(1)
{
if(USART_RX_STA&0x80)
{
len=USART_RX_STA&0x3f;//得到此次接收到的数据长度
printf("\n Your MSG: \n");
for(t=0;t<len;t++)
{
USART1->DR=USART_RX_BUF[t];
while((USART1->SR&0X40)==0);//等待发送结束
}
printf("\n\n");//插入换行
USART_RX_STA=0;
}else
{
times++;
if(times%5000==0)
{
printf("\nSTM32A Usart\n");
}
if(times%200==0) printf("Please Input end with return\n");
if(times%30==0) LED0=!LED0;//闪烁LED,提示系统正在运行.
delay_ms(10);
}
}
}
4.模数转换原理与程序设计
P139-P149
adc.c
/******************************************************
* 双ADC通道测电压值
* 测量电压应<3.3 PA0或PA1接正极,负极接地
* PA0测量的电压显示与左边,PA1测量的电压值显示与右边
******************************温度与光照测量************************
* 温度与光照
* 温度显示与左边,光照显示在右边
***********************************END******************************
作者:宁晓兰
******************************************************************/
#include "adc.h"
#include "math.h"
/****************初始化函数********************
* 初始化
* IO口初始化、ADC1初始化、ADC2初始化
******************************************************/
void VoltageAdcInit(void)
{
//初始化IO口
RCC->APB2ENR |= 1<<2; //使能PORTA口时钟
GPIOA->CRL &= 0xffffff00; //PA0 1 模拟输入
RCC->CFGR &= ~(3<<14); //分频因子清零
RCC->CFGR |= 2<<14; //6分频 SYSCLK/DIV2=12M ADC时钟设置为12M,ADC1最大时钟不能超过14M!
VoltageAdc1Init();
VoltageAdc2Init();
}
/****************初始化函数********************
* ADC1初始化
******************************************************/
void VoltageAdc1Init(void)
{
RCC->APB2ENR |= 1<<9; //ADC1时钟使能
RCC->APB2RSTR |= 1<<9; //ADC1复位
RCC->APB2RSTR &= ~(1<<9); //复位结束
ADC1->CR1 &= 0xf0ffff; //工作模式清零
ADC1->CR1 |= 0<<16; //独立工作模式
ADC1->CR1 &= ~(1<<8); //非扫描模式
ADC1->CR2 &= ~(1<<1); //单次转换模式
ADC1->CR2 &= ~(7<<17);
ADC1->CR2 |= 7<<17; //SWSTART:软件控制转换
ADC1->CR2 |= 1<<20; //使用外部触发(SWSTART),必须使用一个事件来触发
ADC1->CR2 &= ~(1<<11); //右对齐
ADC1->SQR1 &= ~(0xf<<20);
ADC1->SQR1 &= 0<<20; //1个转换在规则序列中,也就是只转换规则序列1
ADC1->SMPR2 &= 0xfffffff0; //通道0采样时间清空
ADC1->SMPR2 |= 7<<0; //通道0 239.5周期,提高采用时间可以提高精确度
ADC1->CR2 |= 1<<0; //开启AD转换器
ADC1->CR2 |= 1<<3; //使能复位校准
while( ADC1->CR2 & 1<<3 )
; //等待校准结束
ADC1->CR2 |= 1<<2; //开启AD校准
while( ADC1->CR2 & 1<<2 )
; //等待校准结束
}
/****************初始化函数********************
* ADC2初始化
******************************************************/
void VoltageAdc2Init(void)
{
RCC->APB2ENR |= 1<<10; //ADC1时钟使能
RCC->APB2RSTR |= 1<<10; //ADC1复位
RCC->APB2RSTR &= ~(1<<10); //复位结?
ADC2->CR1 &= 0xf0ffff; //工作模式清零
ADC2->CR1 |= 0<<16; //独立工作模式
ADC2->CR1 &= ~(1<<8); //非扫描模式
ADC2->CR2 &= ~(1<<1); //单次转换模式
ADC2->CR2 &= ~(7<<17);
ADC2->CR2 |= 7<<17; //SWSTART:软件控制转换
ADC2->CR2 |= 1<<20; //使用外部触发(SWSTART),必须使用一个事件来触发
ADC2->CR2 &= ~(1<<11); //右对齐
ADC2->SQR1 &= ~(0xf<<20);
ADC2->SQR1 &= 0<<20; //1个转换在规则序列中,也就是只转换规则序列1
ADC2->SMPR2 &= ~(7<<3); //通道1采样时间清空
ADC2->SMPR2 |= 7<<3; //通道1 239.5周期,提高采用时间可以提高精确度
ADC2->CR2 |= 1<<0; //开启AD转换器
ADC2->CR2 |= 1<<3; //使能复位校准
while( ADC2->CR2 & 1<<3 )
; //等待校准结束
ADC2->CR2 |= 1<<2; //开启AD校准
while( ADC2->CR2 & 1<<2 )
; //等待校准结束
}
/****************获取ADC值函数********************
* 获取ADC的值,测量的电压应<3.3 PA0或PA1接正极,负极接地
* adcx: 1表示ADC1; 2表示ADC2
* ch: 通道值
* 返回得到的ADC的值
******************************************************/
u16 GetAdc(u8 adcx, u8 ch)
{
u16 adcValue = 0;
if( adcx==1 )
{
//设置转换序列
ADC1->SQR3 &= 0xffffffe0; //规则序列1 通道ch
ADC1->SQR3 |= ch;
ADC1->CR2 |= 1<<22; //启动规则转换通道
while( !(ADC1->SR & 1<<1) )
; //等待转换结束
adcValue = ADC1->DR;
}
else if( adcx==2 )
{
//设置转换序列
ADC2->SQR3 &= 0xffffffe0; //规则序列1 通道ch
ADC2->SQR3 |= ch;
ADC2->CR2 |= 1<<22; //启动规则转换通道
while( !(ADC2->SR & 1<<1) )
; //等待转换结束
adcValue = ADC2->DR;
}
return adcValue; //返回ADC的值
}
/****************获取电压值函数********************
* ADC转化为电压值
* adcx: 1表示ADC1; 2表示ADC2
* ch: 通道值
* 返回电压值
******************************************************/
float GetVoltage(u8 adcx, u8 ch)
{
u16 adcValue = 0;
float vol = 0;
adcValue = GetAdc( adcx, ch );
vol = 3.3*(float)adcValue/4096;
return vol;
}
/****************显示对应端口的电压值函数********************
* 显示,占三位
* adcx: 1表示ADC1; 2表示ADC2
* vol: 电压值
* PA0测量的电压显示与左边,PA1测量的电压值显示与右边
******************************************************/
void VoltageDisplay(u8 adcx, float vol)
{
u8 baiWei, shiWei, geWei;
baiWei = (u8)vol % 10;
shiWei = (u8)(vol*10)%10;
geWei = (u8)(vol*100)%10;
if( adcx==1 )
{
PortationDisplay(0, baiWei);
delay_ms(1);
SetLed(1, shiWei);
delay_ms(1);
SetLed(2, geWei);
delay_ms(1);
}
else if( adcx==2 )
{
PortationDisplay(5, baiWei);
delay_ms(1);
SetLed(6, shiWei);
delay_ms(1);
SetLed(7, geWei);
delay_ms(1);
}
}
/***************温度和光照ADC的初始化函数********************
* 初始化ADC1,通道14 15
******************************************************/
void TemperatureAndLightAdcInit(void)
{
//初始化IO口
RCC->APB2ENR |= 1<<4; //使能PORTC口时钟
GPIOA->CRL &= 0xff00ffff; //PC4 5 模拟输入
RCC->CFGR &= ~(3<<14); //分频因子清零
RCC->CFGR |= 2<<14; //6分频 SYSCLK/DIV2=12M ADC时钟设置为12M,ADC1最大时钟不能超过14M!
RCC->APB2ENR |= 1<<9; //ADC1时钟使能
RCC->APB2RSTR |= 1<<9; //ADC1复位
RCC->APB2RSTR &= ~(1<<9); //复位结束
ADC1->CR1 &= 0xf0ffff; //工作模式清零
ADC1->CR1 |= 0<<16; //独立工作模式
ADC1->CR1 &= ~(1<<8); //非扫描模式
ADC1->CR2 &= ~(1<<1); //单次转换模式
ADC1->CR2 &= ~(7<<17);
ADC1->CR2 |= 7<<17; //SWSTART:软件控制转换
ADC1->CR2 |= 1<<20; //使用外部触发(SWSTART),必须使用一个事件来触发
ADC1->CR2 &= ~(1<<11); //右对齐
ADC1->SQR1 &= ~(0xf<<20);
ADC1->SQR1 &= 0<<20; //1个转换在规则序列中,也就是只转换规则序列1
ADC1->SMPR1 &= 0xfffc0fff; //通道14,15采样时间清空
ADC1->SMPR1 |= 7<<12; //通道14 239.5周期,提高采用时间可以提高精确度
ADC1->SMPR1 |= 7<<15; //通道15 239.5周期,提高采用时间可以提高精确度
ADC1->CR2 |= 1<<0; //开启AD转换器
ADC1->CR2 |= 1<<3; //使能复位校准
while( ADC1->CR2 & 1<<3 )
; //等待校准结束
ADC1->CR2 |= 1<<2; //开启AD校准
while( ADC1->CR2 & 1<<2 )
; //等待校准结束
}
/***************获取温度的ADC的值函数********************
* 获取ADC1的ADC值
* ch为通道值
* 返回ADC1的ADC值
******************************************************/
u16 GetTemperatureAdc(u8 ch)
{
u16 adcValue = 0;
adcValue = GetAdc( 1,ch );
return adcValue;
}
/***************ADC值转换成温度值函数********************
* 通过ADC值计算温度值
* 返回温度值
******************************************************/
float GetTemperature( void )
{
u16 temperatureAdc = 0;
float temperature = 0.0;
temperatureAdc = GetTemperatureAdc( 15 ); //通道15注入的AD值
temperature = (float)temperatureAdc*(3.3/4096); //当前温度电压值
temperature = temperature *10000/(3.3-temperature)/1000; //温度电阻阻值
temperature = (float)1.0/( (float)log(temperature*1000/10000)/3950 + 1.0/(273.16+25) ) - 273.16; //计算温度
return temperature;
}
/***************光照强度的ADC值函数********************
* 光照强度的ADC值
* ch为通道值
* 返回光照的ADC值
******************************************************/
u16 GetLightAdc(u8 ch)
{
u16 adcValue = 0;
adcValue = GetAdc(1, ch);
return adcValue;
}
adc.h
/******************************ADC头文件************************
* 作者:宁晓兰
******************************************************************/
#ifndef _ADC_H
#define _ADC_H
#include "sys.h"
#include "led.h"
#include "delay.h"
#define PA0 PAin(0)
#define PA1 PAin(1)
#define V_Ro PCin(4) //测光
#define V_Rt PCin(5) //测温
void VoltageAdcInit(void); //端口初始化,ADC1初始化,ADC2初始化
void VoltageAdc1Init(void); //电压测量ADC1初始化
void VoltageAdc2Init(void); //电压测量ADC2初始化
float GetVoltage(u8 adcx, u8 ch); //获取电压值
void VoltageDisplay(u8 adcx, float vol); //显示电压值
u16 GetAdc(u8 adcx, u8 ch); //获取ADC
void UltrasonicAdcInit(void);//超声波测量ADC初始化
u16 GetUltrasonicAdc(u8 ch);
/******************温度和光照***********************/
void TemperatureAndLightAdcInit(void); //端口,ADC1初始化,通道14、15
u16 GetTemperatureAdc(u8 ch);
float GetTemperature(void);
u16 GetLightAdc(u8 ch);
#endif
/******************************温度与光照测量************************
* 温度与光照
* 温度显示与左边,光照显示在右边
******************************************************************/
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "adc.h"
/***************************主函数*****************************/
int main()
{
float adcx = 0.0;
u16 adcValue = 0;
u8 i = 0;
Stm32_Clock_Init( 6 );
delay_init( 72 );
TemperatureAndLightAdcInit();
LED_Init();
LED_SEL = 0;
adcx = GetTemperature(); //使用PC5 ADC1, 通道15
adcValue = GetLightAdc(14); //使用PC4 ADC1, 通道14
while(1)
{
i++;
if( i > 50 ) //大约每隔100个循环周期重新扫描一次ADC的值
{
adcx = GetTemperature(); //使用PC5 ADC1, 通道15
adcValue = GetLightAdc(14); //使用PC4 ADC1, 通道14
i = 0;
}
//温度
SetLed(0, adcx/10);
delay_ms(1);
PortationDisplay(1,(u8)adcx%10);
delay_ms(1);
SetLed(2, (u8)(adcx*10)%10);
delay_ms(1);
//光照
SetLed(4, adcValue/1000);
delay_ms(1);
SetLed(5, adcValue%1000/100);
delay_ms(1);
SetLed(6, adcValue%100/10);
delay_ms(1);
SetLed(7, adcValue%10);
delay_ms(1);
}
}
main.c
/******************************温度与光照测量************************
* 温度与光照
* 温度显示与左边,光照显示在右边
******************************************************************/
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "adc.h"
/***************************主函数*****************************/
int main()
{
float adcx = 0.0;
u16 adcValue = 0;
u8 i = 0;
Stm32_Clock_Init( 6 );
delay_init( 72 );
TemperatureAndLightAdcInit();
LED_Init();
LED_SEL = 0;
adcx = GetTemperature(); //使用PC5 ADC1, 通道15
adcValue = GetLightAdc(14); //使用PC4 ADC1, 通道14
while(1)
{
i++;
if( i > 50 ) //大约每隔100个循环周期重新扫描一次ADC的值
{
adcx = GetTemperature(); //使用PC5 ADC1, 通道15
adcValue = GetLightAdc(14); //使用PC4 ADC1, 通道14
i = 0;
}
//温度
SetLed(0, adcx/10);
delay_ms(1);
PortationDisplay(1,(u8)adcx%10);
delay_ms(1);
SetLed(2, (u8)(adcx*10)%10);
delay_ms(1);
//光照
SetLed(4, adcValue/1000);
delay_ms(1);
SetLed(5, adcValue%1000/100);
delay_ms(1);
SetLed(6, adcValue%100/10);
delay_ms(1);
SetLed(7, adcValue%10);
delay_ms(1);
}
}
5.定时器与看门狗原理与程序设计
P154-P167
timer.c
/****************定时器实现********************
*
* 作者:宁晓兰
***************************************************/
#include "timer.h"
//数字钟的时,分、秒
u8 hour = 0, minute = 0, second = 0;
/****************普通按键初始化函数********************
* 通用定时器中断初始化
* 这里时钟选择为APB1的2倍,而APB1为36M
* arr:自动重装值。
* psc:时钟预分频数
* 这里使用的是定时器3!
******************************************************/
void TimerxInit(u16 arr, u16 psc)
{
RCC->APB1ENR |= 1<<1; //TIM3时钟使能
TIM3->ARR = arr; //设定计数器自动重装值,10为1ms
TIM3->PSC = psc; //预分频器7200,得到10KHZ的计数时钟
TIM3->DIER |= 1<<0; //允许更新中断
TIM3->CR1 |= 0x01; //使能定时器3
MY_NVIC_Init(1, 3, TIM3_IRQChannel, 2); //抢占1,子优先级3,组2
}
/****************定时器3的中断函数********************
* 定时器3的中断函数
* 每次中断,second加一
******************************************************/
void TIM3_IRQHandler( void )
{
if( TIM3->SR & 0x0001) //溢出中断
{
second++;
if( second>59 )
{
second = 0;
minute++;
if( minute>59 )
{
minute = 0;
hour++;
if( hour>23 )
hour = 0;
}
}
}
TIM3->SR &= ~(1<<0); //清除中断标志位
}
/*****************************************************
* 数字钟显示函数
******************************************************/
void DisplayDigitalClock(void)
{
SetLed(0, hour/10); //显示 时
delay_ms(1);
SetLed(1, hour%10);
delay_ms(1);
SetLed(2, 10); //显示"-"符号
delay_ms(1);
SetLed(3, minute/10); //显示 分
delay_ms(1);
SetLed(4, minute%10);
delay_ms(1);
SetLed(5, 10); //显示"-"符号
delay_ms(1);
SetLed(6, second/10); //显示 秒
delay_ms(1);
SetLed(7, second%10);
delay_ms(1);
}
独立看门狗
wdg.c
#include "wdg.h"
//
//看门狗 驱动代码
//
//初始化独立看门狗
//prer:分频数:0~7(只有低3位有效!)
//分频因子=4*2^prer.但最大值只能是256!
//rlr:重装载寄存器值:低11位有效.
//时间计算(大概):Tout=((4*2^prer)*rlr)/40 (ms).
void IWDG_Init(u8 prer,u16 rlr)
{
IWDG->KR=0X5555;//使能对IWDG->PR和IWDG->RLR的写
IWDG->PR=prer; //设置分频系数
IWDG->RLR=rlr; //从加载寄存器 IWDG->RLR
IWDG->KR=0XAAAA;//reload
IWDG->KR=0XCCCC;//使能看门狗
}
//喂独立看门狗
void IWDG_Feed(void)
{
IWDG->KR=0XAAAA;//reload
}
main.c
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
#include "key.h"
//#include "exti.h"
#include "wdg.h"
//独立看门狗实验
int main(void)
{
Stm32_Clock_Init(9); //系统时钟设置
delay_init(72); //延时初始化
uart_init(72,9600); //串口初始化
LED_Init(); //初始化与LED连接的硬件接口
KeyInit(); //按键初始化
delay_ms(300); //让人看得到灭
IWDG_Init(4,625); //与分频数为64,重载值为625,溢出时间为1s
LED0=1; //点亮LED0
LED3=1;
while(1)
{
if(KeyScan()==1) IWDG_Feed();//如果K1按下,则喂狗
delay_ms(10);
}
}
窗口看门狗
main.c
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
#include "wdg.h"
//窗口看门狗实验
int main(void)
{
Stm32_Clock_Init(9); //系统时钟设置
delay_init(72); //延时初始化
uart_init(72,9600); //串口初始化
LED_Init(); //初始化与LED连接的硬件接口
LED0=0;
delay_ms(300);
WWDG_Init(0X7F,0X5F,3);//计数器值为7f,窗口寄存器为5f,分频数为8
while(1)
{
LED0=1;
}
}
wdg.c
#include "wdg.h"
#include "led.h"
//看门狗 驱动代码
//保存WWDG计数器的设置值,默认为最大.
u8 WWDG_CNT=0x7f;
//初始化窗口看门狗
//tr :T[6:0],计数器值
//wr :W[6:0],窗口值
//fprer:分频系数(WDGTB),仅最低2位有效
//Fwwdg=PCLK1/(4096*2^fprer).
void WWDG_Init(u8 tr,u8 wr,u8 fprer)
{
RCC->APB1ENR|=1<<11; //使能wwdg时钟
WWDG_CNT=tr&WWDG_CNT; //初始化WWDG_CNT.
WWDG->CFR|=fprer<<7; //PCLK1/4096再除2^fprer
WWDG->CFR&=0XFF80;
WWDG->CFR|=wr; //设定窗口值
WWDG->CR|=WWDG_CNT; //设定计数器值
WWDG->CR|=1<<7; //开启看门狗
MY_NVIC_Init(2,3,WWDG_IRQn,2);//抢占2,子优先级3,组2
WWDG->SR=0X00; //清除提前唤醒中断标志位
WWDG->CFR|=1<<9; //使能提前唤醒中断
}
//重设置WWDG计数器的值
void WWDG_Set_Counter(u8 cnt)
{
WWDG->CR =(cnt&0x7F);//重设置7位计数器
}
//窗口看门狗中断服务程序
void WWDG_IRQHandler(void)
{
WWDG_Set_Counter(WWDG_CNT);//重设窗口看门狗的值!
WWDG->SR=0X00;//清除提前唤醒中断标志位
LED1=!LED1;
}