湖南科技大学2023-2024(2)嵌入式系统期末复习提纲
一、 主要知识点
第 1 章:
-
定义:嵌入式系统是以应用为中心、以计算机技术为基础、软件硬件可裁减、适应应用系统对功能、可靠性、成本、体积、功耗严格要求的专用计算机系统。
-
嵌入式系统的应用:①国防军事领域 ②工业控制领域 ③交通管理领域 ④信息家电领域 ⑤办公自动化领域 ⑥POS网络及电子商务领域 ⑦医疗保健设备领域 ⑧环境工程与自然领域 ⑨机器人领域 ⑩移动终端设备领域
-
特点:①专用于特定任务 ②多类型处理器和处理器系统支持 ③通常极其关注成本 ④一般是实时系统 ⑤可裁剪性好 ⑥可靠性高 ⑦大多有功耗约束
-
(1)嵌入式处理器
嵌入式系统的核心是各种类型的嵌入式处理器,嵌入式处理器与通用处理器最大的不同点在于,嵌入式CPU大多工作在为特定用户群所专门设计的系统中,它将通用CPU中许多由板卡完成的任务集成到芯片内部,从而有利于嵌入式系统在设计时趋于小型化,同时还具有很高的效率和可靠性。
目前常用的嵌入式处理器可分为如下几种:
-
嵌入式微控制器(Micro Controller Unit , MCU):即单片机,如intel8051
-
嵌入式微处理器(Embedded Micro Processor Unit,EMPU):一般基于通用微处理器,从8位、16位直到64位,目前以32位为主。与通用微处理器相比,嵌入式微处理器体积小、重量轻、成本低、可靠性高、功耗低、工作温度、抗电磁干扰等方面有所增强。目前市场上最为广泛使用的嵌入式微处理器有ARM系列、MIPS系列、X86系列等。
-
嵌入式DSP处理器(Embedded Digital Signal Processor,EDSP):专门用于高速实时信号处理,分为通用DSP和专用DSP两种。嵌入式DSP处理器对系统结构和指令进行特殊设计,使其适合于执行DSP算法,编译效率较高,指令执行速度也快。如TI的TMS320C30。
-
嵌入式片上系统(System on Chip,SoC):将微处理器、模拟IP核、数字IP核和存储器(或片外存储控制接口)集成在单一芯片上,进一步降低了功耗,减少了开发成本。它通常是客户定制的(CSIC),或是面向特定用途的标准产品(ASSP),如 Intel的PXA 255等。
-
(2)嵌入式外围设备
-
在嵌入式硬件系统中,除了中心控制部件(MCU、DSP、EMPU、SOC)以外,用于完成存储、通信、调试、显示等辅助功能的其他部件,事实上都可以算作嵌入式外围设备。目前常用的嵌入式外围设备按功能可以分为存储设备、通信设备和显示设备等。
-
存储设备主要用于各类数据的存储,常用的有静态易失型存储器(RAM、SRAM)、动态存储器(DRAM)和非易失型存储器(ROM、EPROM、EEPROM、FLASH)三种,其中FLASH凭借其可擦写次数多、存储速度快、存储容量大、价格便宜等优点,在嵌入式领域内得到了广泛应用。
-
目前存在的绝大多数通信设备都可以直接在嵌入式系统中应用,包括RS-232接口(串行通信接口)、SPI(串行外围设备接口)、IrDA(红外线接口)、I2C(现场总线)、USB(通用串行总线接口)、Ethernet(以太网接口)等。
-
由于嵌入式应用场合的特殊性,通常使用的是阴极射线管(CRT)、液晶显示器(LCD)、触摸板(TouchPanel)和键盘等外围I/O设备。
-
嵌入式操作系统:
①传统的经典RTOS
②嵌入式Linux操作系统
③Android系统
④WindowsCE嵌入式操作系统
⑤μC/OS-Ⅱ实时操作系统 -
嵌入式应用软件:
①嵌入式支撑软件:支撑软件是用于帮助和支撑软件开发的软件,通常包括数据库和开发工具。
②应用程序
①嵌入式开发是一项系统工程,因此要求嵌入式厂商不仅要提供嵌入式软硬件系统本身,同时还需要提供强大的硬件开发工具和软件包支持。
②网络化、信息化的要求,要求芯片设计厂商在芯片上集成更多的功能。
③网络互联、移动互联成为必然趋势。
④精简系统内核、算法,降低功耗和软硬件成本。
⑤提供友好的多媒体人机界面。
第 2 章:
-
特点:①体积小、低功耗、低成本、高性能;②支持Thumb(16位)/ARM或Thumb-2(32位)指令集,能很好地兼容8位/16位器件;③大量使用寄存器,指令执行速度更快;④大多数数据操作都在寄存器中完成;⑤寻址方式灵活简单,执行效率高;⑥指令长度固定。
-
应用:①工业控制领域②无线通讯领域③网络应用④消费类电子产品⑤成像和安全产品
- ARMCortex-M3处理器采用ARMv7-M架构,它包括所有的16位Thumb指令集和基本的32位Thumb-2指令集架构,具有存储器保护单元(MPU)和嵌套中断向量控制器(NVIC)。要注意的是,ARMCortex-M3处理器不能执行ARM指令集中的指令,只能执行Thumb指令集和Thumb-2指令集中的指令。
- ARMCortex-M3处理器支持两种工作模式:线程模式和处理模式
- ARMCortex-M3处理器有两种工作状态,它们分别是:Thumb状态和调试状态。
-
CM3 存储格式类型
有一个字数据W1为0x12345678,定义W1_3、W1_2、W1_1、W1_0分别为该字数据从高到低的4个字节,则有W1_3、W1_2、W1_1、W1_0的值分别为0x12、0x34、0x56、0x78。
- 在大端格式中,字数据的高字节存储在低地址中,而字数据的低字节则存放在高地址中。
例题:存储一个32位数0x2168465到2000H~2003H四个字节单元中,若以大端模式存储,则2000H存储单元的内容为(D)
A.0x21 B.0x65 C.0x05 D.0x02
- 在小端存储格式中,低地址中存放的是字数据的低字节,高地址存放的是字数据的高字节
例题:存储一个32位数0x2168465到2000H~2003H四个字节单元中,若以小端模式存储,则2000H存储单元的内容为(C)
A.0x21 B.0x68 C.0x65 D.0x02
- 嵌套向量中断控制器(NVIC)是ARMCortex-M3处理器中一个完整的部分。ARMCortex-M3的所有中断机制都由NVIC实现。它提供了一个非屏蔽中断(NMI)和32个通用物理中断,这些中断带有8级的抢占优先权。NVIC可以通过综合选择配置为1到240个物理中断中的任何一个,并带有多达256个优先级。
- NVIC支持中断嵌套,允许通过提高中断的优先级对某个中断进行提前处理。它还支持中断的动态优先级重置。
- CM3使用了一个可以重定位的向量表,表中包含了执行函数的入口地址。中断接收后,处理器通过总线可以获取地址。
- CM3处理器使用尾末连锁技术简化了激活的和未决的中断之间的切换。
- NVIC集成了24位系统嘀答定时器(SysTick)。
- NVIC还采用了支持内置睡眠模式的Cortex-M3处理器的电源管理方案。
-
通用寄存器
- ①低寄存器。寄存器R0~R7可以被指定通用寄存器的所有指令访问,复位后的初始值是不可预知的。
- ②高寄存器。寄存器R8~R12可以被指定通用寄存器的所有32位指令访问,但不能被16位指令访问,复位后的初始值是不可预知的。
- ③堆栈指针。寄存器R13用做堆栈指针(SP)。由于SP忽略了写入位[1:0]的值,因此它自动与字(即4字节边界)对齐。堆栈指针对应两个物理寄存器SP_main和SPprocess,处理模式始终使用SP_main,而线程模式可配置为SP_main或SP_process。尽管有两个SP,但在某个时刻只能看到其中的一个,这也就是所谓的“banked”寄存器。
- ④链接寄存器。寄存器R14是子程序的链接寄存器(LR)。在执行分支(branch)和链接(BL)指令或带有交换的分支和链接指令(BLX)时,LR用于接收来自PC的返回地址。LR也用于异常返回。其他任何时候都可以将R14看做一个通用寄存器。
- ⑤程序计数器。寄存器R15为程序计数器PC,指向当前的程序地址。该寄存器的位0始终为0,因此,指令始终与字或半字边界对齐。如果修改它的值,就能达到改变程序执行流程的目的。
-
特殊功能寄存器
-
(1) 程序状态寄存器:应用程序PSR(APSR),中断号PSR(IPSR),执行PSR(EPSR)
-
(2) 中断屏蔽寄存器组:PRIMASK、FAULTMASK、BASEPRI
-
(3) 控制寄存器:用于定义特权级别和堆栈指针的选择,由两个比特来行使这两个职能。
- 位带
- ARMCortex-M3处理器的存储器映射中包括两个位带区,它们分别是:①SRAM区域中的最低的1MB;②外设存储区域中的最低的1MB。这两个位带区的起始地址分别是0x20000000和0x40000000。与这两个位带区域相对应,ARMCortexM3存储器映射中有两个32MB的位带别名区。这两个位带别名区的起始地址分别是0x22000000和0x42000000,它们被映射到前述的两个1MB位带。
- 映射公式
例题:在STM32内存中0x20000000的第2位(位从0算起)的位带别名地址是(C)
A.0x20000002 B.0x22000002 C.0x22000008 D.0x20000008
解析:Bit_word_offset=[(Byte_offset x 8)+Bit_number] x 4 = [(0 x 8) + 2] x 4 =0x08
Bit_word_addr=Bit_band_base+Bit_word_offset = 0x22000000 + 0x08 = 0x22000008
例题:在STM32内存中0x20000001的第2位(位从0算起)的位带别名地址是(C)
A.0x20000012 B.0x22000012 C.0x22000028 D.0x20000028
解析:Bit_word_offset=[(Byte_offset x 8)+Bit_number] x 4 = [(1 x 8) + 2] x 4 = 0x28 (十进制为40)
Bit_word_addr=Bit_band_base+Bit_word_offset = 0x22000000 + 0x08 = 0x22000028
-
中断:是指正在执行的程序流程被某个事件打断、处理器转而去执行与事件有关的处理程序。
-
ARM Cortex-M3是支持240个外部中断的,但具体使用了多少个是由芯片生产厂商决定。
-
异常:当正常的程序执行流程发生暂时的停止时,称之为异常,例如处理一个外部的中断请求。
-
NVIC除了支持240 个外部中断之外,还支持11个内部异常源。
-
区别与联系:广义的异常包括ARM Cortex-M3内核活动产生的异常和(在硬件支持下)外部事件导致的程序流程中断。前者称为内部异常(或系统异常,内核异常),后者称为外部中断。所以中断是一种特殊的异常。
-
异常类型
-
异常相关寄存器:
-
应用程序中断及复位控制寄存器
-
外部中断优先级寄存器
-
系统异常优先级寄存器
-
外部中断使能与除能寄存器
-
外部中断悬起与解旋寄存器
-
外部中断活动寄存器:每个外部中断都有一个活动状态位。在处理器执行了其ISR的第一条指令后,它的活动位就被置1,并且直到ISR返回时才由硬件清零。由于支持嵌套,允许高优先级异常抢占某个ISR。因此,即使中断被抢占,其活动状态位的值仍然为1。它们是只读的,共占8个字(地址范围0xE000E300~0xE000E31C),能按字、半字或字节访问。
- 异常优先级:在ARM Cortex-M3中,优先级对于异常来说很关键,它会决定一个异常是否被屏蔽,以及在未屏蔽的情况下何时可以响应。异常的优先级的数值越小,则优先级越高。ARMCortex-M3支持中断嵌套,使得高优先级异常会抢占低优先级异常占用的CPU资源。ARM Cortex-M3存在3个系统异常:复位、NMI和硬错误,这三个优先级个是固定值,其他的异常优先级是可编程的。
- ARM Cortex-M3处理器中异常的优先级分为硬件优先级和软件优先级。NVIC支持由软件指定的优先级。
- 优先级寄存器的使用
对于表2-22中所示的时钟源,相关的寄存器位于片上外设存储区,对应的地址范围是0x4002_1000~0x4002_13FF,这些寄存器有:
- ①时钟控制寄存器(RCC_CR)。复位值:0x000XX83,X代表未定义。偏移量0x0。
- ②时钟配置寄存器(RCC_CFGR)。复位值:0x00000000。偏移量0x4。
- ③时钟中断寄存器(RCC_CIR)。复位值:0x00000000。偏移量0x8。
- ④备份域控制寄存器(RCC_BDCR)。复位值:0x00000000。偏移量0x20。
- ⑤控制/状态寄存器(RCC_CSR)。复位值:0x0C000000。偏移量0x24。
STM32的时钟结构逻辑框图(时钟树)如图2-18所示。从图中可以看出,可以选择不同的时钟源作为图中右边所示硬件的时钟源,所选择的时钟源经过不同的分频电路可以得到不同的时钟来驱动相应外设的工作。此外,不同的片上外设的时钟源也可以不同,例如看门狗、RTC。与该图中的时钟电路相关时钟配置寄存器有: - ①APB1外设时钟使能寄存器(RCC_APB1ENR)。其复位值为0x00000000,偏移量为0x1C。
- ②APB2外设时钟使能寄存器(RCC_APB2ENR)。其复位值为0x00000000,偏移量为0x18。
- ③APB1外设复位寄存器(RCC_APB1RSTR)。其复位值为0x00000000,偏移量为0x10。
- ④APB2外设复位寄存器(RCC_APB2RSTR)。其复位值为0x00000000,偏移量为0x0C。
- ⑤AHB外设时钟使能寄存器(RCC_AHBENR)。其复位值为0x00000014,偏移量为0x14。
这些寄存器中,RCC_AHBENR、RCC_APB1ENR和RCC_APB2ENR用来开关各个外设模块的时钟。每一个外设模块在这些寄存器中有相应的时钟使能位,当该位为1时对应的时钟开启且相应的外设开始工作,当该位为0时对应的时钟关闭从而相应的外设停止工作。在运行模式下,任何时候都可以通过停止为外设和内存提供时钟来减少功耗。当外设时钟没有启用时,软件不能读出外设寄存器的数值,返回的数值始终是0x0。
-
GPIO概述:通用输入/输出口(GPIO)是一个灵活的由软件控制的数字信号,每个GPIO都代表一个连接到CPU特定引脚的一个位。STM32的GPIO端口的每一位都可以由软件配置成多种模式:输入浮空、输入上拉、输入下拉、模拟输入、开漏输出、推挽式输出、推挽式复用功能、开漏复用功能。
-
端口复用和重映射
-
在STM32中,有许多通用I/O端口,同时也内置了许多外设,如USART、CAN、SPI、ADC等等,为了节约引出管脚,这些内置外设引出管脚是与通用I/O管脚共用的,当I/O管脚作为这些外设模块的功能引脚时就叫端口复用功能。
-
在STM32中,每个内置外设都有若干个输入输出引脚,一般这些外设的输出引脚都有默认I/O端口,为了让设计工程师拥有更大的灵活性以便可以更好地安排外设引脚功能,在STM32中引入了外设引脚重映射(Remap)的概念,即一个外设的引脚除了具有默认的引脚位外,还可以通过配置重映射寄存器的方式,把这个外设的引脚映射到其他的引脚位。例如,管脚PB10除了可以用作通用I/O功能外还可以复用作I2C2_SCL或USART3_TX,还可以重映射为TIM2_CH3,管脚PB11除了可以用作标准I/O功能外还可以复用作I2C2_SDA或USART3_RX,还可以重映射为TIM2_CH4。USART3_TX的默认引出脚是PB10,USART3_RX的默认引出脚是PB11,但经过重映射后,可以变更USART3_TX的引出脚为PD8,变更USART3_RX的引出脚为PD9。
第 3 章:
- 交叉编译
交叉编译(cross-compilation)是指在某个主机平台上(比如PC上)用交叉编译器编译出可在其他平台上(比如ARM上)运行的代码的过程。
- 在Windows平台上,利用SDT、ADS、KEIL、WINARM等集成开发工具,可编译出针对ARMCPU的可执行代码。
- 在Linux平台上,利用arm-linux-gcc、arm-elf-gcc编译器,可编译出针对LinuxARM平台的可执行代码。
- 在Windows平台上,利用cygwin环境,模拟Linux环境运行arm-elf-gcc等编译器,可编译出针对ARMCPU的可执行代码。
- 用的ARM交叉开发软件
- ARM DeveloperSuite
- ARM REALVIEW DEVELOPER SUITE
- IAR EW ARM
- KEIL ARM-MDK
- WINARM(GCCARM)
- ARM GCC
- CooCox
- 常见的嵌入式软件调试方法
- 指令集模拟器
- 驻留监控软件
- JTAG仿真器
- 在线仿真器
第 4 章:
- LED初始化
void LED_Init(void) //LED初始化
{
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输出高
}
- 八段数码管
- 0-9和带小数点的0-9的值
/***************************数码管段选***************************/
u8 segTable[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; //0-9
u8 segTablePortation[] = {0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef}; //带小数点的0-9
- 按键初始化和按键扫描
void KEY_Init(void) //按键初始化
{
RCC->APB2ENR|=1<<4; //使能PORTC时钟
GPIOC->CRL&=0XFFFFF000;//PC0-2设置成输入
GPIOC->CRL|=0X00000888;
}
//按键处理函数
//返回按键值
//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;// 无按键按下
}
- 外部中断初始化和服务程序
//外部中断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
}
- 串口通信(接收数据)
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);
}
}
- AD转化(获取温度和光照)
/******************************************************
* 双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;
}
- 温度和光照的显示
/******************************温度与光照测量************************
* 温度与光照
* 温度显示与左边,光照显示在右边
******************************************************************/
#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 "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);
}
- 独立看门狗
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
}
- 窗口看门狗
//初始化独立看门狗
//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
}
//保存WWDG计数器的设置值,默认为最大.
u8 WWDG_CNT=0x7f;
//初始化窗口看门狗
//tr :T[6:0],用于存储计数器的值
//wr :W[6:0],用于存储窗口值
//fprer:窗口看门狗的实际设置
//低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|=1<<9; //使能提前唤醒中断
WWDG->CFR&=0XFF80;
WWDG->CFR|=wr; //设定窗口值
WWDG->CR|=WWDG_CNT|(1<<7); //开启看门狗,设置7位计数器
MY_NVIC_Init(2,3,WWDG_IRQChannel,2);//抢占2,子优先级3,组2
}
//重设置WWDG计数器的值
void WWDG_Set_Counter(u8 cnt)
{
WWDG->CR|=(cnt&0x7F); //重设置7位计数器
}
//窗口看门狗中断服务程序
void WWDG_IRQHandler(void)
{
u8 wr,tr;
wr=WWDG->CFR&0X7F;
tr=WWDG->CR&0X7F;
if(tr<wr)WWDG_Set_Counter(WWDG_CNT);//只有当计数器的值,小于窗口寄存器的值才能写CR!!
WWDG->SR=0X00; //清除提前唤醒中断标志位
LED1=!LED1;
}
第 5 章:
- 概念:pC/OS-Ⅱ实际上是一个实时操作系统内核,它只包含了任务调度、任务管理、时间管理、内存管理和任务间的通信与同步等基本功能,而没有提供输入输出管理、文件系统、网络之类的额外服务
- 特点:(1)公开源代码(2)可移植性(3)可固化(4)可裁剪(5)占先式(6)多任务(7)可确定性(8)任务栈(9)系统服务(10)中断管理(11)稳定性与可靠性
- 应用:路由器、智能手表、智能医疗设备、飞行器及工业控制