嵌入式系统开发的最新趋势与挑战

发表时间: 2024-06-16 10:05

2021-2022(2)嵌入式系统期末复习提纲(练习版)前言

前段时间参加了嵌入式系统的期末考试,这两天抽空把之前整理好的提纲发上来。

考试前我按照老师给的复习大纲整理了一下知识点,大纲里没有看到教学体系,所以那部分内容也没看,结果考试时出现了好几道关于教学体系的选择题,让我很郁闷。考试结束后,老师说他准备了两套期末考试试卷,我抽的那套可能是另一套。这个解释有点难以接受,不过我只要背下准备的知识点,还是可以应付期末考试的。

最后还是要强调一下,没有什么捷径,就如同复习要点的第一点就是把课本看透,背熟!

1.考试题型 精选(20分) 简答题(30分) 编程题(20分,包括代码讲解、填空等) 论述题(30分) 2.复习重点 通读教材,重点分析代码,特别是初始化部分和动作部分,理解程序 了解嵌入式处理器的选择原则(根据需求、性能、成本、开发工具、现有资源等选择) 了解嵌入式系统的发展趋势 3.主要知识点 第一章 嵌入式系统概述 1.嵌入式系统的概念、应用和特点

​ 练习:简答题:简述嵌入式系统的定义、应用及特点

定义:嵌入式系统是一种以应用为中心,以计算机为基础,软件和硬件均可定制的系统,适用于对功能、可靠性、成本、体积、功耗等有严格要求的应用系统。

用途:用于国防军事、工业控制、交通管理、信息家电、医疗保健设备、机器人等领域

特点:1.专用于特定任务,2.支持多种类型的处理器和处理器系统,3.通常极其注重成本,4.一般是实时系统,5.可扩展性好,6.可靠性高,7.大多数有功耗限制

练习:简答题:简述嵌入式系统的定义和组成

定义:嵌入式系统是一种以应用为中心,以计算机为基础,软件和硬件均可定制的系统,适用于对功能、可靠性、成本、体积、功耗等有严格要求的应用系统。

​ 组成:一般由嵌入式微处理器、外围硬件设备、嵌入式操作系统和用户应用程序四部分组成。

2.嵌入式系统硬件(CPU、外设)

​ 真题:(2016-2017年Essay第3题)目前流行的嵌入式微处理器有哪些,它们有什么特点?

​ 真题:(2017-2018论文题目3)列举至少4种目前流行的嵌入式微处理器及其特点

①ARM微处理器:ARM提供通过IP授权生产的芯片,各有特色,占据32位市场90%以上的份额

②MIPS微处理器:瞄准高性能、高端32位和64位处理器市场

③PowerPC:品种多,覆盖范围广,从高档工作站到台式计算机,从消费电子产品到大型通讯设备;

④x86:源自Intel8080,最早的嵌入式芯片,兼容性好限制了其性能发展

⑤摩托罗拉68000:CISC架构,用于通信应用

3.主要嵌入式软件系统(应用程序和操作系统)

​ 练习:简答题:嵌入式操作系统的定义

支持嵌入式系统应用程序的操作系统软件是嵌入式系统极其重要的组成部分,通常包括与硬件相关的底层驱动软件、系统内核、设备驱动接口、通信协议、图形界面等。嵌入式操作系统负责嵌入式系统所有软硬件资源的分配、任务的调度、控制以及并发活动的协调等。

练习:简答题:列出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)作为最小存取单位,当一个逻辑地址必须被分割成若干个物理单元时,就会出现先放哪个,后放哪个的问题,于是就产生了尾端问题。针对不同的存储方式,又有两种描述:大端(big endian)和小端(little endian)。

大端字节序:低地址存储高有效字节

(1000 0000)2 = 1

​ 小端序:低地址存储低有效字节

(1000 0000)2 = 128

4.CM3嵌套矢量中断控制器NVIC

​ 真题:(2016-2017、2017-2018简答题3)NVIC中端面联锁技术起什么作用?

其作用是当连续发生两次中断时,将最后一条返回指令和下一条调用指令的执行时间由42个周期减少到6个周期。

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 之间弹出和推送寄存器

⑤Cortex-M3 ISR与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引脚作为这些外设模块的功能引脚时,就叫端口复用。

​ 重映射:除了默认的引脚位置外,还可以通过配置重映射寄存器将外设的引脚映射到其他引脚位置。

第三章 嵌入式开发环境 1.嵌入式开发环境(交叉编译,开发工具:Keil μVision)

​ 真题:(2016-2017、2017-2018简答题4)什么是交叉编译?

在主机平台(如PC)上使用交叉编译器编译出可以在其他平台(如ARM)上运行的代码的过程。

​ 练习: 简答题:什么是交叉编译,为什么需要使用交叉编译?

交叉编译是指在一个平台上进行编译,生成在另一个平台上运行的可执行程序。

进行交叉编译的原因是目标平台不具备直接运行开发环境的条件。

第五章 μ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-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任务有五种状态:睡眠、就绪、运行、中断服务、等待或挂起。

第四章 STM32-A平台开发基础(理论)

​ 练习:简答题:USART、NVIC等常见术语代表什么意思?

①UART是一种用于异步通信的通用串行数据总线。该总线可以双向通信,并能实现全双工的发送和接收。

②NVIC是嵌入式向量中断控制器,提供对异常进行全面管理的中断控制器

​ 练习:问答:什么是看门狗,它的作用是什么?

① 为了检测和解决软件错误引起的故障,嵌入式处理器通常有一个称为看门狗的模块

②此模块一般为倒计时计数器,每隔一定周期产生复位信号,使系统复位,复位信号一般是在倒数到达一定值(比如0)时产生。

③在设计嵌入式系统软件时,通过在看门狗产生复位信号之前进行喂狗操作,避免看门狗倒计时到产生复位信号的值(即让看门狗倒计时值从某个设定的倒计时值重新开始)。

第四章 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);  //位带定义PE08u32 *PEO9 = (u32 *)(0x42000000 +(0x4001180C-0x40000000)*32 + 9*4);  //位带定义PE09int 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

主程序

/******************************流水灯************************* 流水灯* 现象:二极管从左至右依次全部点亮*************************************************************/#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只处理一次按键闭合,必须去除按键抖动。在按键稳定闭合时读取按键状态,必须确定按键释放稳定后才能进行处理。

密钥文件

#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;// 无按键按下}

密钥文件

#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

主程序

#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); 	}	 }

打断

退出程序

#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}

主程序

#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//091209void 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}

主程序

#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函数

/******************************************************* 双ADC通道测电压值* 测量电压应#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的值,测量的电压应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文件

/******************************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); //获取ADCvoid UltrasonicAdcInit(void);//超声波测量ADC初始化u16 GetUltrasonicAdc(u8 ch); /******************温度和光照***********************/void TemperatureAndLightAdcInit(void); //端口,ADC1初始化,通道14、15u16 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);	}	}

主程序

/******************************温度与光照测量************************* 温度与光照* 温度显示与左边,光照显示在右边******************************************************************/#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

定时器.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目录

#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											   }

主程序

#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);	}	 }

窗口看门狗

主程序

#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目录

#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; }