18级嵌入式系统复习提纲
第1讲 STM32F4体系结构
1、嵌入式系统的概念 以应用为中心、以计算机技术为基础、软硬件可裁剪,适应应用系统对功能、可靠性、成本、体积、功耗严格要求的专用计算机系统。 2、了解ARM体系结构的演变进程 1991年开始共推出7个版本,V1~V3未用于商业授权。V4T开始商业授权,T表示16位Thumb指令,ARM7TDMI,冯诺依曼结构,三级流水线,0.9MIPS/MHz。V5增强ARM和Thumb指令切换的支持,增加了DSP指令支持(后缀E)、Java支持(后缀J)V6版增强DSP和多媒体处理指令,增加SIMD指令扩展,对音、视频处理性能有极大提升,Thumb-2指令。2004年发布V7版,ARM首次为其体系结构命名,命名为Cortex,分为A(应用)、R(实时)、M(微控制器)三个系列 3、掌握STM32F4的时钟树:有哪几个时钟源、特点,正点原子战舰开发板时钟配置 5个时钟源 LSI:低速内部时钟,RC 振荡器,频率为 32kHz 左右。供独立看门狗、RTC、自动唤醒单元使用 LSE:低速外部时钟,接频率为32.768kHz 的石英晶体。主要是 RTC 的时钟源 HSI:高速内部时钟,RC 振荡器,频率为 16MHz。可以直接作为系统时钟或者用作 PLL输入 HSE:高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围4MHz~26MHz。开发板接8M晶振。HSE也可以直接做为系统时钟或者 PLL 输入。 PLL:锁相环倍频输出 。主 PLL:由 HSE 或者 HSI 提供时钟信号,并具有两个不同的输出时钟。PLLP:生成高速的系统时钟(最高168MHz)PLLQ:生成 USB OTG FS 的时钟(48MHz)、随机数发生器的时钟和 SDIO时钟。专用 PLL(PLLI2S):生成精确时钟,从而在 I2S 接口实现高品质音频性能 PLL输出频率计算方法:输入时钟先
M
M
M分频再
N
N
N倍频最后
P
P
P(或
Q
Q
Q分频)
f
o
u
t
=
f
i
n
?
N
M
?
P
f_{out}=\frac{f_{in}*N}{M*P}
fout?=M?Pfin??N? 开发板时钟配置:
f
i
n
=
8
M
H
z
f_{in}=8MHz
fin?=8MHz,设置
M
=
8
,
N
=
336
,
P
=
2
M=8,N=336,P=2
M=8,N=336,P=2 4、时钟相关函数在哪个.h和.c文件定义(其他模块同样),时钟源使能函数、外设时钟使能函数(要知道哪个外设挂在哪个总线上,截图打印下来参考)、时钟源选择函数,分频因子配置函数,外设复位函数 文件:stm32f4xx_rcc.h、stm32f4xx_rcc.c 时钟源使能函数:
void RCC_HSICmd(FunctionalState NewState);
void RCC_LSICmd(FunctionalState NewState);
void RCC_PLLCmd(FunctionalState NewState);
void RCC_PLLI2SCmd(FunctionalState NewState);
void RCC_PLLSAICmd(FunctionalState NewState);
void RCC_RTCCLKCmd(FunctionalState NewState);
外设时钟使能函数:
void RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState);
void RCC_AHB2PeriphClockCmd(uint32_t RCC_AHB2Periph, FunctionalState NewState);
void RCC_AHB3PeriphClockCmd(uint32_t RCC_AHB3Periph, FunctionalState NewState);
void RCC_APB1PeriphClockCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState);
void RCC_APB2PeriphClockCmd(uint32_t RCC_AHB2Periph, FunctionalState NewState);
使能:ENABLE.失能:DISABLE
时钟源选择函数:
void RCC_HSEConfig(uint8_t RCC_HSE);
void RCC_LSEConfig(uint8_t RCC_LSE);
void RCC_PLLConfig(uint32_t RCC_PLLSource, uint32_t PLLM, uint32_t PLLN, uint32_t PLLP, uint32_t PLLQ);
void RCC_SYSCLKConfig(uint32_t RCC_SYSCLKSource);
void RCC_HCLKConfig(uint32_t RCC_SYSCLK);
void RCC_PCLK1Config(uint32_t RCC_HCLK);
RCC_SYSCLKConfig(RCC_SYSCLKSource_HSI);
RCC_PCLK1Config(RCC_HCLK_Div2);
外设复位函数:
void RCC_AHB1PeriphResetCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState);
void RCC_AHB2PeriphResetCmd(uint32_t RCC_AHB2Periph, FunctionalState NewState);
void RCC_AHB3PeriphResetCmd(uint32_t RCC_AHB3Periph, FunctionalState NewState);
void RCC_APB1PeriphResetCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState);
void RCC_APB2PeriphResetCmd(uint32_t RCC_AHB2Periph, FunctionalState NewState);
状态参数获取清除函数
uint8_t RCC_GetSYSCLKSource(void);
void RCC_GetClocksFreq(RCC_ClocksTypeDef*RCC_Clocks);
FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG);
void RCC_ClearFlag(void);
5、ARM存储空间,大端排序、小端排序 ARM体系的存储空间:
2
32
B
2^{32}B
232B的单一、线性地址空间。地址:
0
~
2
32
?
1
0 \sim 2^{32}-1
0~232?1.可以看作由
2
30
2^{30}
230个32位的字组成的字地址空间.也可以看作由
2
31
2^{31}
231个16位的半字组成的半字地址空间 ARM体系的存储器格式:大端格式(Big Endian)高字节存储在低地址中,低字节则存放在高地址中;小端格式(Little Endian)高字节存储在高地址中,低字节则存放在低地址. 6、位带操作的含义、哪些区域可以进行位带操作,位带地址计算方法 含义:使用普通的加载/存储指令(字)对单一比特(位)进行读写的方式 区域:内部SRAM的最低1MB(0x20000000 ~ 0x200FFFFF)片内外设的最低1MB(0x40000000 ~ 0x400FFFFF) 内存SRAM区:
A
l
i
a
s
A
d
d
r
=
0
x
22000000
+
(
(
A
?
0
x
20000000
)
×
8
+
n
)
×
4
=
0
x
22000000
+
(
A
?
0
x
20000000
)
×
32
+
n
×
4
AliasAddr=0x2200 0000+((A-0x2000 0000)×8+n)×4\\ =0x2200 0000+(A-0x2000 0000)×32+n×4
AliasAddr=0x22000000+((A?0x20000000)×8+n)×4=0x22000000+(A?0x20000000)×32+n×4 外设区:
A
l
i
a
s
A
d
d
r
=
0
x
42000000
+
(
(
A
?
0
x
40000000
)
×
8
+
n
)
×
4
=
0
x
42000000
+
(
A
?
0
x
40000000
)
×
32
+
n
×
4
AliasAddr=0x4200 0000+((A-0x4000 0000)×8+n)×4\\ =0x4200 0000+(A-0x4000 0000)×32+n×4
AliasAddr=0x42000000+((A?0x40000000)×8+n)×4=0x42000000+(A?0x40000000)×32+n×4 A:SRAM或外设地址;AliasAddr:别名地址 n:位序号,若A为字节地址,则n为0…7,若A为字地址,则n为0…31
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr&0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr,bitnum) MEM_ADDR(BITBAND(addr,bitnum))
第2讲 GPIO
1、STM32F4ZGT6共有多少组多少位GPIO(其他资源也同样,比如有多少USART,这个知识点后面就不单独列了) 7组16位IO端口(GPIOAGPIOG,PAPG) 1组2位端口GPIOH(GPIOH.0/PH0、GPIOH.1/PH1) 7×16+2=114 2、GPIO的工作方式、特点 4种输入模式 浮空输入:输出关闭,上下拉关闭 上拉输入:下拉电阻关闭,上拉电阻打开。无输入时,默认高电平。 下拉输入:上拉电阻关闭,下拉电阻打开。无输入时,默认低电平。 模拟输入:一定是浮空输入,不能上下拉。 4种输出模式 开漏输出、推挽式输出、开漏复用功能、推挽式复用功能(均可上、下拉) 上电复位后,GPIO默认为浮空状态(上下拉电阻都不加),部分特殊功能引脚为特定状态。 推挽输出:可以输出强高、低电平,连接数字器件 开漏输出:只可以输出强低电平,高电平得靠外部电阻拉高。输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行。适合于做电流型的驱动,其吸收电流的能力相对强(一般20mA以内) 复用输出:数据来自复用外设的输出脚 3、打开相应GPIO的时钟(其他资源同样要求,后面不单独列出) RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); 4、GPIO相关寄存器的作用 端口模式寄存器(GPIOx_MODER)、端口输出类型寄存器(GPIOx_OTYPER)、端口输出速度寄存器(GPIOx_OSPEEDR)、端口上拉下拉寄存器(GPIOx_PUPDR)、端口输入数据寄存器(GPIOx_IDR)、端口输出数据寄存器(GPIOx_ODR)、端口置位/复位寄存器(GPIOx_BSRR)、端口配置锁存寄存器(GPIOx_LCKR)、复用功能寄存器(GPIOx_AFRL & GPIOx_AFRH) 5、库函数的操作方法:GPIO初始化;输出0、1;读取等
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOF, &GPIO_InitStructure);
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin,BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
GPIO_ReadlnputDataBit(GPIOF,GPIO_Pin_9);
GPIO_ReadlnputData(GPIOF);
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
6、寄存器方式操作方法
RCC->AHB1ENR|= 1<<5;
GPIOF->MODER &= ~(3<<2*9);
GPIOF->MODER |= 1<<(2*9);
GPIOF->OSPEEDR &= ~(3<<2*9);
GPIOF->OSPEEDR |= 3<<(2*9);
GPIOF->PUPDR &= ~(3<<2*9);
GPIOF->PUPDR |=1<<(2*9);
GPIOF->OTYPER &= ~(1<<9);
GPIOF->OTYPER |=0<<9;
GPIOF->ODR|= 1<<9;
GPIOF->ODR&=~(1<<9);
GPIOF->ODR|=1<<9;
GPIOF->BSRRH = 0x0200;
GPIOF->BSRRL = 0x0400;
uint32_t data=GPIOF->IDR
uint32_t data=GPIOF->ODR
7、位带操作
#define BITBAND(addr, bitnum) ((addr&0xF0000000)+0x2000000+((addr&0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
#define GPIOA_ODR_Addr (GPIOA_BASE+20)
#define GPIOF_ODR_Addr (GPIOF_BASE+20)
#define GPIOA_IDR_Addr (GPIOA_BASE+16)
#define GPIOF_IDR_Addr (GPIOF_BASE+16)
#define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n)
#define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n)
PFout(9)=1;
PFout(9)=0;
8、寄存器开发与固件库开发的优缺点 寄存器开发方式的特点 运行速度快、占用存储空间小;需要学习、记忆各寄存器的功能、格式、每位作用含义,STM32F4有数百个寄存器 固件库开发方式的特点 直接调用固件库函数,无须深入了解各寄存器的具体格式 运行速度较慢、占用存储空间较大
9、理解CMSIS标准 使不同芯片公司Cortex-M4内核的芯片在软件上基本兼容。Cortex Microcontroller Software Interface Standard,Cortex微控制器软件接口标准 10、GPIO与其他模块结合的应用 驱动LED、按键,中断,USART等 11、GPIO端口复用: 注意初始化GPIO时一定选择****复用功能****,即GPIO_InitStructure.GPIO_Mode = ****GPIO_Mode_AF;*并用*GPIO_PinAFConfig();****进行复用功能配置。要知道哪个资源可以复用哪些引脚,把我们学过的资源都整理出来备查
第3讲NVIC与EXTI
EXTI挂载在APB2上。 1、NVIC与EXTI的概念 NVIC:Nested Vectored Interrupt Controller,嵌入向量中断控制器 EXTI:External Interrupt/event controller,外部中断/事件控制器 2、NVIC相对VIC的优点 快,ns级响应速度,实时性强。标准,只要是Cortex内核,不同公司用法一样。 3、NVIC的中断管理规模(多少级中断、内核中断、外部中断各多少)、STM32F407的中断管理规模 256级可编程中断设置(8位),16个内核中断,240个可屏蔽中断,每个IO口都可以作为中断源。 管理中断:非屏蔽请求NMI、可屏蔽请求IRQ。中断源是IO口、外设、内核中断、系统异常中断 stm32只支持92个(10个内核中断,82个可屏蔽中断)4位16级优先管理,5组分组(没有使用Cortex内核IP的全部8位) 4、NVIC中断的优先级管理规则,理解抢占优先级、响应优先级,中断优先顺序、中断嵌套规则、中断优先级分组。STM32F4对NVIC中断优先管理上的实现 优先级分为抢占优先级(主优先级)、响应优先级(从优先级)。 中断嵌套规则:高抢占优先级嵌套低抢占优先级,同级不可嵌套,小号优先。 响应优先级不嵌套,只决定多个相同抢占优先级、不同响应优先级同时申请时的相应顺序,小号优先。 两种优先级都相同的多个中断源同时申请时,首先响应中断地址(向量)低(小号) 优先级规则针对中断通道而非中断源。 中断优先级分组:9种组合。 分组0:8位全用于响应优先级(0:8) 分组x:x位抢占优先级,8-x位响应优先级(x:8-x) STM32对NVIC中断优先管理实现:只用高4位,16个可编程优先级 5、NVIC相关寄存器的含义 在core_cm4.h ISER[8] :中断使能寄存器组 ICER[8] :中断失能寄存器组 ISPR[8] :中断挂起寄存器组 ICPR[8] :中断解挂寄存器组 IABR[8] :中断激活标志位寄存器组 (stm32只用3个) IP[240] :中断优先级寄存器组(stm32只用82个,高4位) STIR :软件触发中断寄存器组 6、固件库方式对NVIC初始化、中断优先级分组设定方法
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
typedef struct {
uint8_t NVIC_IRQChannel;
uint8_t NVIC_IRQChannelPreemptionPriority;
uint8_t NVIC_IRQChannelSubPriority;
FunctionalState NVIC_IRQChannelCmd
} NVIC_InitTypeDef;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x00;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
中断分组配置在寄存器SCB->AIRCR[10..8] 中进行。 固件库函数: void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup) 例如:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2) (在初始化时配置一次,一般不改) 7、中断与事件区别 中断:需CPU执行ISR(中断服务程序)完成相关操作 事件:靠脉冲发生器产生一个脉冲,不需CPU干预,由硬件自动完成相关操作,如触发AD转换 可以这样简单认为,事件机制提供了一个完全由硬件自动完成触发到产生结果的通道,不要软件的参与,降低了CPU的负荷,节省了中断资源,提高了响应速度,是利用硬件来提升CPU芯片处理能力的一个有效方法。 8、EXTI中断线的映射规则 23根(23个通道,16个针对输入输出端口,每个I/O都可以映射为外部中断线,同一编号的I/O口同时只能由一个映射为外部中断线) 配置:SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource2) //PE几连到中断线几 9、外部中断、内部中断的中断服务函数名 (固定的,编程用到,外部中断线9-5、10-15共用一个) 在stm32f4xx_it.c定义 void EXTI0_IRQHandler(void) void EXTI1_IRQHandler(void) void EXTI9_5_IRQHandler(void) void EXTI15_10_IRQHandler(void) 外部中断:0~4各一个,5~9共用,10~15共用 10、EXTI相关寄存器含义 中断屏蔽寄存器 (EXTI_IMR) 事件屏蔽寄存器 (EXTI_EMR) 上升沿触发选择寄存器 (EXTI_RTSR) 下降沿触发选择寄存器 (EXTI_FTSR) 软件中断事件寄存器 (EXTI_SWIER) 挂起寄存器 (EXTI_PR) 11、EXTI固件库: stm32f4xx_exti.h,stm32f4xx_exti.c void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct); //初始化中断线类型、触发方式 ITStatus EXTI_GetITStatus(uint32_t EXTI_Line); //判断中断线中断状态是否发生 EXTI_ClearITPendingBit() //清除中断线上标志位
EXTI_InitStructure.EXTI_Line = EXTI_Line2;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
中断服务函数。 EXTIx_IRQHandler(); 中断服务函数最后一步:清除中断标志位 void EXTI_ClearITPendingBit(uint32_t EXTI_Line); 示例:
void EXTI3_IRQHandler(void){
if (EXTI_GetITStatus(EXTI_Line3)!=RESET){
EXTI_ClearITPendingBit(EXTI_Line3);
}
}
12、根据电路连接图判别外部中断的触发方式
按键连接高电平,按下接通电路。则需要默认输出低电平,下拉,高有效,上升沿触发。 按键连接低电平,按下接通电路,则需要默认输出高电平,上拉,低有效,下降沿触发。
第4讲WDG
窗口看门狗挂在APB1。独立看门狗内部自带时钟,不挂载总线。 1、WDG的作用 递减器计数(定时器),溢出时,使系统复位 2、IWDG、WWDG的特点、工作原理 IWDG(独立看门狗):由专用的低速时钟(LSI,32kHz)驱动,即使主时钟发生故障它仍有效,但精度不高(15~47kHz )应用于需要看门狗作为一个在主程序之外能够完全独立工作,并且对时间精度要求较低的场合。 在键值寄存器(IWDG_KR)中写入0xCCCC(启动),开始启用独立看门狗。此时计数器开始从其复位值0xFFF递减,当计数器值计数到尾值0x000时会产生一个复位信号(IWDG_RESET)。无论何时,只要往键值寄存器IWDG_KR中写入0xAAAA(俗称喂狗), 自动重装载寄存器IWDG_RLR的值就会重新加载到计数器,从而避免看门狗复位。如果程序异常,就无法正常喂狗,则导致系统复位。
WWDG(窗口看门狗):由APB1分频(4096)后的时钟驱动,精度高。通过可配置的时间窗口来检测应用程序非正常的过迟或过早操作。最适合那些要求看门狗在精确计时窗口起作用的程序。可触发中断(提前唤醒中断)
3、IWDG、WWDG寄存器的含义 IWDG_KR :钢铁雄心4mod KaiserReich键值寄存器,只写 IWDG_RLR :每次喂狗时重载入计数器的初值,具有写保护。 IWDG_SR :状态寄存器,为1时表示正在更新,为0时表示复位 IWDG_PR :预分频寄存器。
WWDG_CR :控制寄存器,第7位为激活位,低位为计数器 WWDG_CFR :第9位为是否使能提前唤醒中断EWI(在计数值0x40时唤醒并用中断服务程序提醒主程序去喂狗),第8位第7位为定时器时基,低7位为上窗口值。 WWDG_SR :提前唤醒中断标志,计数器值达到0x40时硬件置1 4、IWDG几个键值的作用: aaaah:喂狗 5555h:去除写保护,使能对IWDG_PR和IWDG_RLR访问 cccch:启动(使能)看门狗 0000h:加写保护对IWDG_PR和IWDG_RLR
5、了解IWDG、WWDG库函数、配置流程 IWDG库函数:
void IWDG_WriteAccessCmd(uint16_t IWDG_WriteAccess);
void IWDG_SetPrescaler(uint8_t IWDG_Prescaler);
void IWDG_SetReload(uint16_t Reload);
void IWDG_ReloadCounter(void);
void IWDG_Enable(void);
FlagStatus IWDG_GetFlagStatus(uint16_t IWDG_FLAG);
配置流程:
IWDG_WriteAccessCmd();
IWDG_SetPrescaler(prer);
IWDG_SetReload(rlr);
IWDG_Enable();
IWDG_ReloadCounter();
独立看门狗溢出时间
T
o
u
t
=
(
4
?
2
p
r
e
r
?
r
l
r
)
/
32
T_{out}=(4*2^{prer}*rlr)/32
Tout?=(4?2prer?rlr)/32 WWDG库函数:
溢出时间
T
w
w
d
g
=
t
p
c
l
k
1
?
4096
?
2
w
d
g
t
b
?
(
T
[
5
:
0
]
+
1
)
(
m
s
)
T_{wwdg}=t_{pclk1}*4096*2^{wdgtb}*(T[5:0]+1)(ms)
Twwdg?=tpclk1??4096?2wdgtb?(T[5:0]+1)(ms),其中
t
p
c
l
k
1
t_{pclk1}
tpclk1?为APB1时钟周期,
w
d
g
t
b
wdgtb
wdgtb为预分频系数,
T
[
5
:
0
]
T[5:0]
T[5:0]为WWDG计数值低6位
void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);
void WWDG_SetWindowValue(uint8_t WindowValue);
void WWDG_EnableIT(void);
void WWDG_SetCounter(uint8_t Counter);
void WWDG_Enable(uint8_t Counter);
FlagStatus WWDG_GetFlagStatus(void);
void WWDG_ClearFlag(void);
配置步骤:
RCC_APB1PeriphClockCmd();
WWDG_SetPrescaler(fprer);
WWDG_SetWindowValue(wr);
WWDG_EnableIT();NVIC_Init();
WWDG_Enable(WWDG_CNT);
WWDG_SetCounter();
WWDG_IRQHandler();
第5-7讲TIM
TIM1和TIM8在APB2,TIM2~TIM5在APB1,TIM9~TIM11在APB2,TIM12~TIM14、TIM6和TIM7在APB1 1、STM32F4有哪几类定时器、特点、计数模式 STM32F40x系列总共有最多14个定时器: 通用定时器TIM2~TIM5 通用定时器TIM9~TIM14 基本定时器TIM6、TIM7 高级定时器TIM1、TIM8 看门狗(IWDG、WWDG)本质上也是定时器 TIM2、TIM5为32位,其他16位 Cortex内核的SysTick计时器(24位递减) 2、定时器原理(基本定时模块、输入捕捉模块、输出比较模块)、输入捕捉通道数量、产生PWM的通道数量 16/32位向上、向下、向上/向下(中心对齐)计数模式,自动重装载计数器。16位可编程预分频器,分频系数为1~65536,可实时修改。4个独立通道(TIMx_CH1 ~4),可用作:输入捕获/输出比较/PWM生成/单脉冲输出,可对外部信号(TIMx_ETR)计数,可以定时器级连.
输入捕捉通道数量:TIM6、7无此功能,10、11、13、14一个通道,9、12二个通道,其他4个通道 PWM输出通道数量:TIM1/TIM8产生7路PWM输出,TIM2~5同时4路PWM输出。TIM9、TIM12同时2路PWM输出,TIM10/11/13/14产生1路PWM输出 3、定时时间的计算 若APB1的分频系数是1,则TIMx的时钟等于APB1时钟(×1),否则TIMx的时钟等于APB1时钟的2倍(×2)。 (T1/T8/T9~11时钟信号来自APB2) CK_PSC为时钟周期。 CK_INT=84M
f
C
K
_
I
N
T
=
f
C
K
_
P
S
C
/
(
P
S
C
[
15
:
0
]
+
1
)
f_{CK\_INT} = f_{CK\_PSC} / (PSC[15:0] + 1)
fCK_INT?=fCK_PSC?/(PSC[15:0]+1)
T
=
(
T
I
M
_
P
e
r
i
o
d
+
1
)
?
(
T
I
M
_
P
r
e
s
c
a
l
e
r
+
1
)
/
f
C
K
_
P
S
C
T=(TIM\_Period+1)*(TIM\_Prescaler+1)/f_{CK\_PSC}
T=(TIM_Period+1)?(TIM_Prescaler+1)/fCK_PSC? 4、相关寄存器的含义、作用 TIMx_CNT :实现计数功能 TIMx_PSC :对CK_PSC预分频, TIMx_ARR :自动重载寄存器。 TIMx_CR1 :控制寄存器 TIMx_DIER :中断控制 TIMx_SR :状态寄存器,低位表示中断,其他位用于捕获/比较 TIMx_CCER :捕获比较使能寄存器 TIMx_CCMR1 :PWM输出模式 5、库函数的使用、各方式的配置流程 stm32f4xx_tim.c、stm32f4xx_tim.h
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
TIM_TimeBaseStructure.TIM_Prescaler=7199;
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = 4999;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
定时器中断
RCC_APBxPeriphClockCmd();
TIM_TimeBaseInit();
TIM_ITConfig()、NVIC_Init();
TIM_Cmd();
TIMx_IRQHandler();
输入捕获通道初始化 void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
TIM5_ICInitStructure.TIM_Channel=TIM_Channel_1;
TIM5_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;
TIM5_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM5_ICInitStructure.TIM_ICFilter = 0x00;
TIM_ICInit(TIM5, &TIM5_ICInitStructure);
通道极性设置独立函数 void TIM_OCxPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity); 获取通道捕获值函数uint32_t TIM_GetCapture1(TIM_TypeDef* TIMx); 输入捕获的一般配置步骤
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_PinAFConfig();
TIM_TimeBaseInit();
TIM_ICInit();
TIM_ITConfig(); NVIC_Init();
NVIC_PriorityGroupConfig();
TIM_Cmd();
TIMx_IRQHandler();
PWM输出通道初始化函数: void TIM_OCxInit(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
示例:
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure. TIM_Pulse=100;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
设置比较值函数 void TIM_SetCompareX(TIM_TypeDef* TIMx, uint16_t Comparex); 使能输出比较预装载 void TIM_OCxPreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload); 使能自动重装载的预装载寄存器允许位 void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState); PWM输出配置步骤
RCC_APB1PeriphClockCmd();
RCC_AHB1PeriphClockCmd ();
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14);
TIM_TimeBaseInit();
TIM_OC1Init();
TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM14,ENABLE);
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
TIM_SetCompare1();
6、输入捕捉的原理,如何测量脉冲的宽度和周期 输入阶段:消除抖动、边沿选择、通道选择、分频,得到的信号直接控制主电路。得到捕获控制信号后,读取捕获值。 上升沿捕获得到计数值1,下降沿捕获得到计数值2,计算得到脉宽。(中间可能有多次溢出)
T
=
n
?
T
溢
出
+
(
C
N
T
下
降
?
C
N
T
上
升
)
?
T
计
数
T=n*T_{溢出}+(CNT_{下降}-CNT_{上升})*T_{计数}
T=n?T溢出?+(CNT下降??CNT上升?)?T计数? 捕获到上升沿立即清除cnt时,
T
=
n
?
T
溢
出
+
C
N
T
下
降
?
T
计
数
T=n*T_{溢出}+CNT_{下降}*T_{计数}
T=n?T溢出?+CNT下降??T计数? 7、输入捕捉中的硬件滤波原理 在CK_INT的DTS时钟下控制滤波。假如DTS采样频率=计数器的时钟(CK_INT)频率,采样次数N=4,则在上升沿时采样信号进行倒计数,没记到N次时电平又低了,则不认为是有效信号。若上升沿持续了N个DTS周期,认为有效。在输入信号变低后,经过N个DTS周期,滤波后的信号也变低。时间延迟了N个DTS周期,宽度不变。 8、PWM的原理、作用,占空比、周期如何改变 PWM(Pulse Width Modulation)即脉冲宽度调制,也就是占空比和周期均可变的脉冲波形。 原理:冲量相等而形状不同的窄脉冲加在具有惯性的环节上时,其效果基本相同。 作用:PWM脉冲宽度调制是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。 改变周期可达到调频效果,改变脉宽或占空比可达到调压效果。故采用适当的控制方法即可使电压电流与频率协调变化。 改变重载值ARR可以改变周期,改变比较值CCRx可以改变脉宽(占空比) PWM频率=计数频率/重装载值 9、影子寄存器与原寄存器的关系,作用 物理上对应两个寄存器(缓冲作用),一个程序可直接操作的,另一个叫影子寄存器(真正起作用的).由TIMx_CR1.APRE控制,APRE=0两者直通,APRE=1,每次更新事件(UEV)时接通。 void TIM_ARRPreloadConfig(TIM_TypeDef *TIMx,FunctionalState NewState);
第9讲USART
USART1、USART6挂在APB2,其他APB1。 1、几种常见串行通信标准(UART、USART、SPI、I2C、1-wire)的特点
USART:引脚同UART,异步通信,全双工。
2、USART主要寄存器 USART_SR:状态寄存器 TXE:发送数据寄存器的数据是否传到移位寄存器,1表示完成 TC:0表示发送未完成,1表示发送完成 RXNE:0表示未接受,1表示已准备好读取接收到的数据 USART_DR:数据寄存器 USART_CR:控制寄存器 USART_BRR:波特率寄存器 15~4位存储USARTDIV的整数部分(PPT有误),3~0存放USART小数。小数部分的数值*16后以二进制存储。 3、波特率计算,
波
特
率
=
f
P
C
L
K
8
?
(
2
?
O
V
E
R
8
)
?
U
S
A
R
T
D
I
V
f
C
L
K
为
串
口
总
线
上
的
时
钟
频
率
,
O
V
E
R
8
为
过
采
样
率
波特率=\frac{f_{PCLK}}{8*(2-OVER8)*USARTDIV}\\ f_{CLK}为串口总线上的时钟频率, OVER8为过采样率
波特率=8?(2?OVER8)?USARTDIVfPCLK??fCLK?为串口总线上的时钟频率,OVER8为过采样率
4、常用库函数,中断方式工作、查询方式工作,接收、发送方法(含库函数与寄存器两种方式)
void USART_Init();
void USART_Cmd();
void USART_ITConfig();
查询
FlagStatus USART_GetFlagStatus();
void USART_ClearFlag();
while(USART_GetFlagStatus(USART1, UART_FLAG_TC) == RESET);
while((USART1->SR&0x40)==0);
while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
while(USART1->SR & Ox20 == 0);
中断
if(USART_GetITStatus(USART1,USART_IT_RXNE)){
res=USART_ReceiveData(USART1);
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
ITStatus USART_GetITStatus();
void USART_ClearFlag();
void USART_ClearITPendingBit();
接收
uint16_t USART_ReceiveData();
ch=USART_ReceiveData(USART1);
ch=USART1->DR;
发送
void USART_SendData();
USART_SendData(USART1,ch);
USART1->DR=ch;
5、USART的应用,与主机串口调试助手通信,结合其他模块,如GPIO、NVIC等 主机(串口调试助手)发送数据到STM32的USART1, USART1将收到的数据直接再发送到主机。
void My_USART1_Init(void){
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate=115200;
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
USART_InitStructure.USART_Parity=USART_Parity_No;
USART_InitStructure.USART_StopBits=USART_StopBits_1;
USART_InitStructure.USART_WordLength=USART_WordLength_8b;
USART_Init(USART1,&USART_InitStructure);
USART_Cmd(USART1,ENABLE);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
}
void USART1_IRQHandler(void){
u8 res;
if(USART_GetITStatus(USART1,USART_IT_RXNE)){
res=USART_ReceiveData(USART1);
USART_SendData(USART1,res);
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
}
int main(void){
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
My_USART1_Init();
while(1);
}
struct __FILE {
int handle;
};
FILE __stdout;
int fputc(int ch, FILE *f){
while((USART1->SR&0x40)==0);
USART1->DR = (u8) ch;
return ch;
}
6、STM32F407支持多少UART、多少USART 2个UART。挂在APB1:UART4、UART5. 4个USART。挂在APB1:USART2、3,挂在APB2:USART1、USART6
第10讲I2C
1、I2C简介 两线式串行总线,连接微控制器及外围设备。双向同步半双工。每个器件有唯一地址,工作在主模式或从模式,可以发送或接收。主控器能产生时钟信号,总线上开漏模式上拉。 2、I2C协议内容:空闲、开始、停止、应答、数据有效性、数据传输等 空闲:SDA和SCL都是高电平,则空闲。 开始:SCL为高期间,SDA由高到低跳变 停止:SCL为高期间,SDA由低到高跳变 应答:发送器每发送一个字节,就在第9时钟脉冲期间释放数据线,由接收器反馈一个应答信号。有效应答信号为低电平,非应答信号为高电平。对有效应答ACK要求:接收器在第9个SCL信号前的低电平期间拉低SDA,确保SCL的高电平期间为稳定的低电平。 数据有效性:SCL上升沿时采集数据,且在SCL下降沿前SDA上的数据必须稳定。 数据传输:I2C总线上传输的每个字节均为8位,首先传输MSB,每次启动一次I2C总线,其后传输的字节数是没有限制的。每传输一个字节后都必须由接收器回应一个应答(ACK或NACK)。SDA上,第一帧为寻址字节,包括7位被控器的地址和1位读写方向位,接着是被控器的应答位。紧接着是主控器和被控器的数据传输和应答,传输结束后,主控器要发停止信号。 3、用GPIO口软件仿真I2C方法
void IIC_Init(void){
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
IIC_SCL=1;
IIC_SDA=1;
}
void IIC_Start(void){
SDA_OUT();
IIC_SDA=1;
IIC_SCL=1;
delay_us(4);
IIC_SDA=0;
delay_us(4);
IIC_SCL=0;
}
void IIC_Stop(void){
SDA_OUT();
IIC_SCL=0;
IIC_SDA=0;
delay_us(4);
IIC_SCL=1;
IIC_SDA=1;
delay_us(4);
}
u8 IIC_Wait_Ack(void){
u8 ucErrTime=0;
SDA_IN();
IIC_SDA=1; delay_us(1);
IIC_SCL=1; delay_us(1);
while(READ_SDA) {
ucErrTime++;
if(ucErrTime>250){
IIC_Stop();
return 1;
}
}
IIC_SCL=0;
return 0;
}
void IIC_Ack(void){
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
void IIC_NAck(void){
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
void IIC_Send_Byte(u8 txd){
u8 t;
SDA_OUT();
IIC_SCL=0;
for(t=0;t<8;t++){
IIC_SDA=(txd&0x80)>>7;
txd<<=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
delay_us(2);
}
}
u8 IIC_Read_Byte(unsigned char ack) {
unsigned char i,receive=0;
SDA_IN();
for(i=0;i<8;i++ ){
IIC_SCL=0;
delay_us(2);
IIC_SCL=1;
receive<<=1;
if(READ_SDA) receive++;
delay_us(1);
}
if (!ack)
IIC_NAck();
else
IIC_Ack();
return receive;
}
第11讲SPI
SPI2/SPI3挂载在APB1上,其余挂载在APB2上。 1、SPI简介 高速、全双工、同步串行通信总线,只占4根线,任何时候只能有一个主机和从机通信。发送数据时必然接收到数据 2、SPI从设备的选择方法 SPI_CR1的SSM位设置硬件或软件管理NSS(从器件选择),为1时软件管理,NSS由SPI_CR1寄存器的SSI位驱动。为0时是硬件管理,根据SPI_CR2的SSOE位控制模式。 3、SPI主要寄存器 SPI_CR1:控制寄存器1. SPI_CR2:控制寄存器2,与中断相关。 SPI_SR:位1TXE,发送缓冲区空。位0RXNE,接收缓冲区非空。位7BSY,忙标志。 SPI_DR:数据寄存器。物理上分两个缓冲区:发送缓冲区和接收缓冲区。 SPI_I2SCFGR:I2S模式配置。 4、SPI常用库函数
void SPI_Init(SPI_TypeDef* SPIx,SPI_InitTypeDef* SPI_InitStruct);
void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);
void SPI_I2S_ITConfig(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT, FunctionalState NewState);
void SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState);
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);
void SPI_DataSizeConfig(SPI_TypeDef* SPIx,uint16_t SPI_DataSize);
FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);
void SPI_I2S_ClearFlag(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);
ITStatus SPI_I2S_GetITStatus(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT);
void SPI_I2S_ClearITPendingBit(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT);
5、如何判断SPI一个数据发送完成、如何判断SPI接收到一个数据 数据先于时钟,选择第一边沿采集。数据时钟同时,选择第二边沿。 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); //一个数据没发完,等待发送完成 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); //没有接受到数据,等待接收到1帧数据 SPI_I2S_SendData(SPI1,ch); //库函数方式发送数据 SPI1->DR=ch; //寄存器方式
第12讲ADC
挂载在APB2上 1、STM32F4 ADC简介:特点、性能、分辨率、数据对齐、通道数量等 特点:模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。 在转换结束、注入转换结束以及发生模拟看门狗溢出事件时产生中断。可独立设置各通道采样时间 外部触发器选项,可为规则转换和注入转换配置极性 性能:ADC电源要求:全速运行2.4V ~ 3.6V,慢速运行1.8V 分辨率:可以配置为12位、10位、8位或6位。 对齐:左对齐或右对齐的方式存储在16位数据寄存器中。 通道数量:3个AD转换器,每个AD转换器有16个外部通道,共24个外部通道。3个内部通道(ADC1) 2、规则通道、注入通道的概念 规则通道组:相当于正常运行的程序,最多16个通道。 注入通道组:相当于中断,最多4个通道。 3、工作模式:单次、连续、扫描、间断 单次:将ADC_CR2的CONT位置0,则处于单次转换模式。该模式下ADC仅执行一次转换,然后ADC停止。此模式下,可通过以下方式启动转换:将ADC_CR2的SWSTART位置1(规则通道),将JSWSTART位置1(注入通道)、外部触发。所选通道转换结束后结果保存在16位ADC_DR(ADC_JDR1)中EOC(JEOC)标志置1.EOCIE(JEOCIE)位置1时将产生中断 连续:将ADC_CR2的CONT位置1,则处于连续转换模式。该模式下ADC结束一个转换后立即启动一个新的转换。此模式下,可通过以下方式启动转换:将ADC_CR2的SWSTART位置1、外部触发。每次转换结束后转换结果存储在16位 ADC_DR中.EOC标志置1;EOCIE位置1时将产生中断 扫描:扫描一组通道。将ADC_CR1中的SCAN位置1选择扫描模式。ADC会扫描在ADC_SQRx寄存器(规则通道)或ADC_JSQR寄存器(注入通道)中选择的所有通道。为组中的每个通道都执行一次转换。自动注入模式下需要禁止外部事件触发,EXTEN/JEXTEN[1:0]=00 间断:将ADC_CR1的DISCEN位置1使能此模式。该模式可用于转换n(n≤8)个转换的短序列,该短序列是在ADC_SQRx中选择的转换序列的一部分。出现外部触发时,将启动在ADC_SQRx寄存器中选择的接下来n个转换,直到序列中的所有转换均完成为止。 4、转换时间计算 可使用ADC_SMPR1和ADC_SMPR2的SMPx[2:0]位修改采用周期数。ADC会在数个ADCCLK周期内对输入电压进行采样。 总转换时间
T
c
o
n
v
=
采
样
时
间
+
周
期
数
(
分
辨
率
12
)
?
每
个
A
D
C
C
L
K
的
时
长
T_{conv}=采样时间+周期数(分辨率12)*每个ADCCLK的时长
Tconv?=采样时间+周期数(分辨率12)?每个ADCCLK的时长 采样时间最少3周期,定义在ADC_SMPR1 5、如何启动ADC ADC启动可由软件控制,将SWSTART/JSWSTART的位置1开始转换。也可由外部事件触发。 void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState); ADC_Cmd(ADC1, ENABLE); //使能ADC1 void ADC_SoftwareStartConv(ADC_TypeDef* ADCx) ADC_SoftwareStartConv(ADC1); //软件启动ADC1规则组转换 ADC1->CR2|=1<<30 //寄存器方式 ADC_SoftwareStartInjectedConv(ADC1); //注入组 ADC1->CR2|=1<<22 //寄存器方式 6、如何读取转换结果(注入组、规则组,库函数、寄存器) uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx); return ADC_GetConversionValue(ADC1); //获取ADC1转换结果
u16 Get_Adc(u8 ch){
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_480Cycles );
ADC_SoftwareStartConv(ADC1);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));
while((ADC1->SR&(1<<1))==0)
return ADC_GetConversionValue(ADC1);
return ADC1->DR;
return ADC_GetInjectedConversionValue(ADC1,ADC_InjectedChannel_1);
return ADC1->JDR1;
}
7、常用寄存器 ADC_SR :状态寄存器,STRT和JSTRT中断无关,其余位中断有关。 ADC_CR1 :控制寄存器。 ADC_CR2 :控制寄存器。CONT 位=1位连续,CONT =0为单次 ADC_SMPR1/2 :采样时间寄存器。每个通道可以设置不同采样时间。 ADC_JOFRx :注入通道数据偏移寄存器,转换注入通道时从原始转换数据减去偏移量 ADC_HTR :上阈值寄存器。 ADC_LTR :下阈值寄存器。 ADC_SQR1/2/3 :定义通道顺序。 ADC_JSQR :注入通道顺序 ADC_DR :数据寄存器。AD转换结果。规则组数据寄存器低16位有用,可设置左右对齐。 ADC_JDR1/2/3/4 :注入通道转换结果。 ADC_CSR :3个ADC公用的状态寄存器,只读。 ADC_CCR :公用控制寄存器,可以读写。 8、常用库函数
void ADC_CommonInit(ADC_CommonInitTypeDef* ADC_CommonInitStruct);
void ADC_Init(ADC_TypeDef* ADCx,ADC_InitTypeDef* ADC_InitStruct);
void ADC_DeInit(ADC_TypeDef* ADCx)
void ADC_Cmd(ADC_TypeDef* ADCx,FunctionalState NewState);
void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT,FunctionalState NewState);
void ADC_SoftwareStartConv(ADC_TypeDef* ADCx);
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);
9、ADC数据采集程序,单通道连续转换 整体流程
RCC_AHB1PeriphClockCmd (RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
GPIO_Init();
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,DISABLE);
ADC_CommonInit();
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
ADC_RegularChannelConfig();
ADC_Cmd(ADC1, ENABLE);
ADC_SoftwareStartConv(ADC1);
ADC_GetConversionValue(ADC1);
NVIC_Init();
ADC_ITConfig(ADC1,ADC_IT_EOC,ENABLE);
ADC_IRQHandler();
void Adc_Init(void){
GPIO_InitTypeDef GPIO_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
ADC_InitTypeDef ADC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,DISABLE);
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStructure.ADC_Prescaler=ADC_Prescaler_Div4
ADC_CommonInit(&ADC_CommonInitStructure);
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConvEdge=ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE);
}
第13讲DAC
挂载在APB1上 1、STM32F4 DAC简介:特点、数据格式、分辨率等 特点:12位数字输入,电压输出型的DAC 数据格式/分辨率: 8 位右对齐:必须将数据加载到 DAC_DHR8Rx [7:0] 位(存储到DHRx[11:4] 位)。 12 位左对齐:必须将数据加载到 DAC_DHR12Lx [15:4] 位(存储到DHRx[11:0] 位)。 12 位右对齐:必须将数据加载到 DAC_DHR12Rx [11:0] 位(存储到DHRx[11:0] 位)。
2、输出电压计算方法
D
A
C
O
U
T
P
U
T
=
V
R
E
F
+
?
D
O
R
4095
DAC_{OUTPUT}=V_{REF+}*\frac{DOR}{4095}
DACOUTPUT?=VREF+??4095DOR?
V
R
E
F
+
V_{REF+}
VREF+?为参考电压输入,
D
O
R
DOR
DOR为数据输出寄存器的值 3、主要寄存器 DAC_CR :控制寄存器 DAC_SWTRIGR :软件触发寄存器 DAC_DHR yyyx :数据输入寄存器,如DAC_DHR12R1 ,DAC_DHR12L1 ,DAC_DHR8R1 DAC_DORx :数据输出寄存器,通道1的数据为DAC_DOR1 ,通道2的数据为DAC_DOR2 DAC_SR :状态寄存器
4、常用库函数 void DAC_Init(uint32_t DAC_Channel,DAC_InitTypeDef* DAC_InitStruct); //初始化 void DAC_Cmd(uint32_t DAC_Channel, FunctionalState NewState); //使能 void DAC_SetChannel1Data(uint32_t DAC_Align, uint16_t Data); //写入DHR数据 uint16_t DAC_GetDataOutputValue(uint32_t DAC_Channel); //回读输出数据
5、如何启动DAC void DAC_Cmd(uint32_t DAC_Channel, FunctionalState NewState); //CR对应位置1 定时器 TRGO 输出或外部中断线 9 上检测到上升沿时,DAC_DHRx中的数据即会加载到DAC_DORx 中。再经过三个APB1 周期,DAC_DORx 将会得到更新。 如果选择软件触发,一旦 SWTRIG 位置 1, 转换即会开始.DAC_DHRx 加载到DAC_DORx 后,SWTRIG 即由硬件复位。
6、如何将一个数据送DAC转换(库函数、寄存器) DAC_SetChannel1Data(DAC_Align_12b_R, 500); //将500送入,库函数 DAC->DHR12R1 = 500; //将500送入,寄存器 num=DAC->DOR1; 7、DAC产生相应波形输出的编程
void Dac1_Init(void){
GPIO_InitTypeDef GPIO_InitStructure; DAC_InitTypeDef DAC_InitType;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
DAC_InitType.DAC_Trigger=DAC_Trigger_None;
DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;
DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;
DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ;
DAC_Init(DAC_Channel_1,&DAC_InitType);
DAC_Cmd(DAC_Channel_1, ENABLE);
DAC_SetChannel1Data(DAC_Align_12b_R, 0);
}
void Dac1_Set_Vol(u16 vol){
double temp=vol;
temp/=1000;
temp=temp*4096/3.3;
DAC_SetChannel1Data(DAC_Align_12b_R,temp);
}
int main(void){
u16 adcx;
float temp;
u8 t=0;
u16 dacval=0;
u8 key;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init(168);
uart_init(115200);
LED_Init();
Adc_Init();
KEY_Init();
Dac1_Init();
DAC_SetChannel1Data(DAC_Align_12b_R,dacval);
while(1){
t++;
key=KEY_Scan(0);
if(key==WKUP_PRES){
if(dacval<4000)
dacval+=200;
DAC_SetChannel1Data(DAC_Align_12b_R, dacval);
}else if(key==KEY1_PRES) {
if(dacval>200)
dacval-=200;
else dacval=0;
DAC_SetChannel1Data(DAC_Align_12b_R, dacval);
}
if(t==10||key==KEY1_PRES||key==WKUP_PRES) {
adcx=DAC_GetDataOutputValue(DAC_Channel_1);
printf(“DAC寄存器的值是:%d\n”,adcx);
temp=(float)adcx*(3.3/4096);
printf(“DAC的输出电压是:%f\n”,temp);
adcx=Get_Adc_Average(ADC_Channel_5,10);
temp=(float)adcx*(3.3/4096);
printf(“ADC采集到的电压是:%f\n”,temp);
LED0=!LED0;
t=0;
}
delay_ms(10);
}
}
第14讲 RTOS
1、理解嵌入式应用软件的设计的两种方式 前后台系统方式(裸机方式):没有嵌入式操作系统支持,直接操作裸机,在裸机上写程序,比如用51单片机进行设计,基本就没有操作系统的概念。通常把程序分为两部分:前台系统和后台系统。 RTOS方式:在嵌入式操作系统的支持下进行应用程序设计,由嵌入式操作系统进行内存、任务、外围设备等管理,减轻开发人员负担。
2、对RTOS有两个基本要求:功能正确、时间正确,硬实时、软实时 能使计算机系统及时响应外部事件的请求,并能及时控制所有实时设备与实时任务协调运行,且能在规定的时间内完成对事件的处理,逻辑或功能正确,时间正确. 硬实时操作系统:必须在极严格的时间内完成任务 软实时操作系统:对完成任务的截止时间要求不是十分严格,对超时有一定的容忍度
3、RTOS内核的两种类型 不可剥夺型内核(合作型多任务内核):总是优先级别高的任务最先获得CPU使用权,每个任务必须能主动放弃CPU使用权 可剥夺型内核:CPU总是运行多个任务中优先级别最高的任务,高级别任务可以剥夺正在运行任务的CPU使用权
4、任务的组成 由三部分组成:任务堆栈、任务控制块和任务函数 任务堆栈:上下文(任务)切换的时候用来保存任务的工作环境,即STM32的内部寄存器值 任务控制块:用来记录任务的各个属性 任务函数:由用户编写的任务处理代码 5、μCOSIII的特点 μC/OS III是一个可裁剪、可剥夺型的多任务内核,而且没有任务数限制。μC/OS III提供了实时操作系统所需的所有功能,包括资源管理、同步、任务通信等。 μC/OS III是用C和汇编写的,其中绝大部分都是用C语言编写的,只有极少数的与处理器密切相关的部分代码才是用汇编写的,结构简洁,可读性很强。
|