STM32各模块学习笔记

更新时间:2023-11-07 21:40:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

STM32中断优先级和开关总中断

一,中断优先级:

STM32(Cortex-M3)中的优先级概念

STM32(Cortex-M3)中有两个优先级的概念——抢占式优先级和响应优先级,有人把响应优先级称作'亚优先级'或'副优先级',每个中断源都需要被指定这两种优先级。

具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以嵌套低抢占式优先级的中断。

当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。

既然每个中断源都需要被指定这两种优先级,就需要有相应的寄存器位记录每个中断的优先级;在Cortex-M3中定义了8个比特位用于设置中断源的优先级,这8个比特位可以有8种分配方式,如下:

所有8位用于指定响应优先级

最高1位用于指定抢占式优先级,最低7位用于指定响应优先级 最高2位用于指定抢占式优先级,最低6位用于指定响应优先级 最高3位用于指定抢占式优先级,最低5位用于指定响应优先级 最高4位用于指定抢占式优先级,最低4位用于指定响应优先级 最高5位用于指定抢占式优先级,最低3位用于指定响应优先级 最高6位用于指定抢占式优先级,最低2位用于指定响应优先级 最高7位用于指定抢占式优先级,最低1位用于指定响应优先级

这就是优先级分组的概念。

--------------------------------------------------------------------------------

Cortex-M3允许具有较少中断源时使用较少的寄存器位指定中断源的优先级,因此STM32把指定中断优先级的寄存器位减少到4位,这4个寄存器位的分组方式如下:

第0组:所有4位用于指定响应优先级

第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级 第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级 第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级 第4组:所有4位用于指定抢占式优先级

可以通过调用STM32的固件库中的函数NVIC_PriorityGroupConfig()选择使用哪种优先级分组方式,这个函数的参数有下列5种:

NVIC_PriorityGroup_0 => 选择第0组 NVIC_PriorityGroup_1 => 选择第1组 NVIC_PriorityGroup_2 => 选择第2组 NVIC_PriorityGroup_3 => 选择第3组 NVIC_PriorityGroup_4 => 选择第4组

接下来就是指定中断源的优先级,下面以一个简单的例子说明如何指定中断源的抢占式优先级和响应优先级:

// 选择使用优先级分组第1组

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

// 使能EXTI0中断

NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQChannel;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 指定抢占式优先级别1

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 指定响应优先级别0 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);

// 使能EXTI9_5中断

NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQChannel;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 指定抢占式优先级别0 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 指定响应优先级别1 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);

要注意的几点是:

1)如果指定的抢占式优先级别或响应优先级别超出了选定的优先级分组所限定的范围,将可能得到意想不到的结果;

2)抢占式优先级别相同的中断源之间没有嵌套关系;

3)如果某个中断源被指定为某个抢占式优先级别,又没有其它中断源处于同一个抢占式优先级别,则可以为这个中断源指定任意有效的响应优先级别。

二,开关总中断:

在STM32/Cortex-M3中是通过改变CPU的当前优先级来允许或禁止中断。

PRIMASK位:只允许NMI和hard fault异常,其他中断/异常都被屏蔽(当前CPU优先级=0)。 FAULTMASK位:只允许NMI,其他所有中断/异常都被屏蔽(当前CPU优先级=-1)。

在STM32固件库中(stm32f10x_nvic.c和stm32f10x_nvic.h) 定义了四个函数操作PRIMASK位和FAULTMASK位,改变CPU的当前优先级,从而达到控制所有中断的目的。

下面两个函数等效于关闭总中断: void NVIC_SETPRIMASK(void); void NVIC_SETFAULTMASK(void);

下面两个函数等效于开放总中断: void NVIC_RESETPRIMASK(void); void NVIC_RESETFAULTMASK(void);

上面两组函数要成对使用,不能交叉使用。

例如:

第一种方法:

NVIC_SETPRIMASK(); //关闭总中断 NVIC_RESETPRIMASK();//开放总中断

第二种方法:

NVIC_SETFAULTMASK(); //关闭总中断 NVIC_RESETFAULTMASK();//开放总中断

常常使用

NVIC_SETPRIMASK(); // Disable Interrupts NVIC_RESETPRIMASK(); // Enable Interrupts

STM32时钟系统

STM32资料 2009-09-23 14:53 阅读72 评论0 字号: 大大 中中 小小

在STM32中,有五个时钟源,为HSI、HSE、LSI、LSE、PLL。 ①、HSI是高速内部时钟,RC振荡器,频率为8MHz。

②、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。 ③、LSI是低速内部时钟,RC振荡器,频率为40kHz。 ④、LSE是低速外部时钟,接频率为32.768kHz的石英晶体。 ⑤、PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。

图1 HSE/LSE时钟源

其中40kHz的LSI供独立看门狗IWDG使用,另外它还可以被选择为实时时钟RTC的时钟源。另外,实时时钟RTC的时钟源还可以选择LSE,或者是HSE的128分频。RTC的时钟源通过RTCSEL[1:0]来选择。

STM32中有一个全速功能的USB模块,其串行接口引擎需要一个频率为48MHz的时钟源。该时钟源只能从PLL输出端获取,可以选择为1.5分频或者1分频,也就是,当需要使用USB模块时,PLL必须使能,并且时钟频率配置为48MHz或72MHz。

另外,STM32还可以选择一个时钟信号输出到MCO脚(PA8)上,可以选择为PLL输出的2分频、HSI、HSE、或者系统时钟。

系统时钟SYSCLK,它是供STM32中绝大部分部件工作的时钟源。系统时钟可选择为PLL输出、HSI或者HSE。系统时钟最大频率为72MHz,它通过AHB分频器分频后送给各模块使用,AHB分频器可选择1、2、4、8、16、64、128、256、512分频。其中AHB分频器输出的时钟送给5大模块使用: ①、送给AHB总线、内核、内存和DMA使用的HCLK时钟。 ②、通过8分频后送给Cortex的系统定时器时钟。 ③、直接送给Cortex的空闲运行时钟FCLK。 ④、送给APB1分频器。APB1分频器可选择1、2、4、8、16分频,其输出一路供APB1外设使用(PCLK1,最大频率36MHz),另一路送给定时器(Timer)2、3、4倍频器使用。该倍频器可选择1或者2倍频,时钟输出供定时器2、3、4使用。 ⑤、送给APB2分频器。APB2分频器可选择1、2、4、8、16分频,其输出一路供APB2外

设使用(PCLK2,最大频率72MHz),另一路送给定时器(Timer)1倍频器使用。该倍频器可选择1或者2倍频,时钟输出供定时器1使用。另外,APB2分频器还有一路输出供ADC分频器使用,分频后送给ADC模块使用。ADC分频器可选择为2、4、6、8分频。

在以上的时钟输出中,有很多是带使能控制的,例如AHB总线时钟、内核时钟、各种APB1外设、APB2外设等等。当需要使用某模块时,记得一定要先使能对应的时钟。

需要注意的是定时器的倍频器,当APB的分频为1时,它的倍频值为1,否则它的倍频值就为2。连接在APB1(低速外设)上的设备有:电源接口、备份接口、CAN、USB、I2C1、I2C2、UART2、UART3、SPI2、窗口看门狗、Timer2、Timer3、Timer4。注意USB模块虽然需要一个单独的48MHz时钟信号,但它应该不是供USB模块工作的时钟,而只是提供给串行接口引擎(SIE)使用的时钟。USB模块工作的时钟应该是由APB1提供的。连接在APB2(高速外设)上的设备有:UART1、SPI1、Timer1、ADC1、ADC2、所有普通IO口(PA~PE)、第二功能IO口。

下图是STM32用户手册中的时钟系统结构图,通过该图可以从总体上掌握STM32的时钟系统。

STM32外部中断之二

STM32资料 2009-09-10 21:18 阅读243 评论0 字号: 大大 中中 小小 STM32 外部中断配置

1配置中断

1、 分配中断向量表:

/* Set the Vector Table base location at 0x20000000 */

NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);

2、 设置中断优先级:

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); //设置中断优先级

3、 初始化外部中断:

/*允许EXTI4中断 */

void WWDG_IRQHandler(void) {

WWDG_SetCounter(0x7F); //更新计数值

WWDG_ClearFlag(); //清除标志位 }

void SysTickHandler(void)

{ IWDG_ReloadCounter(); //重启计数器(喂狗) }

j) 注意事项:

i. 有狗平常没事情可以不理,但是千万别忘了喂它,否则死都不知道怎么死的!

ii. 初始化程序的调用一定要在systic的初始化之后。

iii. 独立看门狗需要systic中断来喂,但是systic做别的用处不能只做这件事,所以我写了如下几句代码,可以不影响systic的其他应用,其他systic周期代码也可参考:

第一步:在stm32f10x_it.c中定义变量

int Tic_IWDG; //喂狗循环程序的频率判断变量

第二步:将SysTickHandler中喂狗代码改为下面:

Tic_IWDG++; //变量递增

if(Tic_IWDG>=100) //每100个systic周期喂狗

{ IWDG_ReloadCounter();//重启计数器(喂狗)

Tic_IWDG=0; //变量清零 }

//IO输入,GPIOB的2、10、11脚输出

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 ;//管脚号

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //输出速度

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //输入输出模式

GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化

简单的延迟函数:

void Delay(vu32 nCount) //简单延时函数

{ for (; nCount != 0; nCount--);} //循环计数延时

完成之后再在main.c的while里面写一段:

GPIO_WriteBit(GPIOB, GPIO_Pin_2, (BitAction)0x01);//写入1

Delay(0xffff);

GPIO_WriteBit(GPIOB, GPIO_Pin_2, (BitAction)0x00);//写入0

Delay(0xffff);

就可以看到连接在PB2脚上的LED闪烁了,单片机就跑起来了。

STM32笔记之八:来跟PC打个招呼,基本串口通讯

a) 目的:在基础实验成功的基础上,对串口的调试方法进行实践。硬件代码顺利完成之后,对日后调试需要用到的printf重定义进行调试,固定在自己的库函数中。

b) 初始化函数定义:

void USART_Configuration(void); //定义串口初始化函数

c) 初始化函数调用:

void UART_Configuration(void); //串口初始化函数调用

初始化代码:

void USART_Configuration(void) //串口初始化函数 {

//串口参数初始化

USART_InitTypeDef USART_InitStructure; //串口设置恢复默认参数

//初始化参数设置

USART_InitStructure.USART_BaudRate = 9600; //波特率9600

USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长8位

USART_InitStructure.USART_StopBits = USART_StopBits_1; //1位停止字节

USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验

USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无流控制

USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//打开Rx接收和Tx发送功能

USART_Init(USART1, &USART_InitStructure); //初始化

USART_Cmd(USART1, ENABLE); //启动串口 }

RCC中打开相应串口

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE);

GPIO里面设定相应串口管脚模式

//串口1的管脚初始化

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //管脚9

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出

GPIO_Init(GPIOA, &GPIO_InitStructure); //TX初始化

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //管脚10

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入

GPIO_Init(GPIOA, &GPIO_InitStructure); //RX初始化

d) 简单应用:

发送一位字符

USART_SendData(USART1, 数据); //发送一位数据

while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET){} //等待发送完毕

接收一位字符

while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET){} //等待接收完毕

变量= (USART_ReceiveData(USART1)); //接受一个字节

发送一个字符串

先定义字符串:char rx_data[250];

然后在需要发送的地方添加如下代码

int i; //定义循环变量

while(rx_data!='\\0') //循环逐字输出,到结束字'\\0'

{USART_SendData(USART1, rx_data); //发送字符

while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET){} //等待字符发送完毕

i++;}

e) USART注意事项:

发动和接受都需要配合标志等待。

只能对一个字节操作,对字符串等大量数据操作需要写函数

使用串口所需设置:RCC初始化里面打开RCC_APB2PeriphClockCmd

(RCC_APB2Periph_USARTx);GPIO里面管脚设定:串口RX(50Hz,IN_FLOATING);串口TX(50Hz,AF_PP);

f) printf函数重定义(不必理解,调试通过以备后用)

(1) 需要c标准函数:

#include \

(2) 粘贴函数定义代码

#define PUTCHAR_PROTOTYPE int __io_putchar(int ch) //定义为putchar应用

(3) RCC中打开相应串口

(4) GPIO里面设定相应串口管脚模式

(6) 增加为putchar函数。

int putchar(int c) //putchar函数 {

if (c == '\\n'){putchar('\\r');} //将printf的\\n变成\\r

USART_SendData(USART1, c); //发送字符

while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET){} //等待发送结束

return c; //返回值 }

(8) 通过,试验成功。printf使用变量输出:%c字符,%d整数,%f浮点数,%s字

符串,/n或/r为换行。注意:只能用于main.c中。

3、 NVIC串口中断的应用

a) 目的:利用前面调通的硬件基础,和几个函数的代码,进行串口的中断输入练习。因为在实际应用中,不使用中断进行的输入是效率非常低的,这种用法很少见,大部分串口的输入都离不开中断。

b) 初始化函数定义及函数调用:不用添加和调用初始化函数,在指定调试地址的时候已经调用过,在那个NVIC_Configuration里面添加相应开中断代码就行了。

c) 过程:

i. 在串口初始化中USART_Cmd之前加入中断设置:

USART_ITConfig(USART1, USART_IT_TXE, ENABLE);//TXE发送中断,TC传输完成中断,RXNE接收中断,PE奇偶错误中断,可以是多个。

ii. RCC、GPIO里面打开串口相应的基本时钟、管脚设置

iii. NVIC里面加入串口中断打开代码:

NVIC_InitTypeDef NVIC_InitStructure;//中断默认参数

NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQChannel;//通道设置为串口1中断

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //中断占先等级0

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //中断响应优先级0

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //打开中断

NVIC_Init(&NVIC_InitStructure); //初始化

iv. 在stm32f10x_it.c文件中找到void USART1_IRQHandler函数,在其中添入执行代码。一般最少三个步骤:先使用if语句判断是发生那个中断,然后清除中断标志位,最后给字符串赋值,或做其他事情。

void USART1_IRQHandler(void) //串口1中断 {

char RX_dat; //定义字符

TIM_ClearITPendingBit(TIM2, TIM_IT_CC4); //清除中断标志

GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)(1-GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_11)));//变换LED色彩

IC4value = TIM_GetCapture4(TIM2); //获取捕捉数值

} }

i) 简单应用:

//改变占空比

TIM_SetCompare4(TIM3, 变量);

j) 注意事项:

管脚的IO输出模式是根据应用来定,比如如果用PWM输出驱动LED则应该将相应管脚设为AF_PP,否则单片机没有输出

我的测试程序可以发出不断循环三种波长并捕获,对比结果如下:

捕捉的稳定性很好,也就是说,同样的方波捕捉到数值相差在一两个数值。

捕捉的精度跟你设置的滤波器长度有关,在这里

TIM_ICInitStructure.TIM_ICFilter = 0x4; //滤波设置,经历几个周期跳变认定波形稳定0x0~0xF

这个越长就会捕捉数值越小,但是偏差几十个数值,下面是0、4、16个周期滤波的比较,out是输出的数值,in是捕捉到的。

现在有两个疑问:

1、在TIM2的捕捉输入通道初始化里面这句

TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2); //选择时钟触发源

按照硬件框图,4通道应该对应TI4FP4。可是实际使用TI1FP1,TI2FP2都行,其他均编译错误未注册。这是为什么?

2、关闭调试器和IAR程序,直接供电跑出来的结果第一个周期很正常,当输出脉宽第二次循环变小后捕捉的数值就差的远了。不知道是为什么

STM32笔记之十二:时钟不息工作不止,systic时钟应用

a) 目的:使用系统时钟来进行两项实验——周期执行代码与精确定时延迟。

b) 初始化函数定义:

void SysTick_Configuration(void);

c) 初始化函数调用:

SysTick_Configuration();

d) 初始化函数:

void SysTick_Configuration(void) {

SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);//时钟除8

SysTick_SetReload(250000); //计数周期长度

SysTick_CounterCmd(SysTick_Counter_Enable); //启动计时器

SysTick_ITConfig(ENABLE); //打开中断 }

e) 在NVIC的初始化函数里面增加以下代码打开相关中断:

NVIC_SystemHandlerPriorityConfig(SystemHandler_SysTick, 1, 0);//中断等级设置,一般设置的高一些会少受其他影响

f) 在stm32f10x_it.c文件中找到void SysTickHandler 函数

void SysTickHandler(void) {

执行代码 }

g) 简单应用:精确延迟函数,因为systic中断往往被用来执行周期循环代码,所以一些例程中使用其中断的启动和禁止来编写的精确延时函数实际上不实用,我自己编写了精确计时函数反而代码更精简,思路更简单。思路是调用后,变量清零,然后使用时钟来的曾变量,不断比较变量与延迟的数值,相等则退出函数。代码和步骤如下:

i. 定义通用变量:u16 Tic_Val=0; //变量用于精确计时

ii. 在stm32f10x_it.c文件中相应定义:

extern u16 Tic_Val;//在本文件引用MAIN.c定义的精确计时变量

iii. 定义函数名称:void Tic_Delay(u16 Tic_Count);//精确延迟函数

iv. 精确延时函数:

void Tic_Delay(u16 Tic_Count) //精确延时函数

{ Tic_Val=0; //变量清零

while(Tic_Val != Tic_Count){printf(\计时 }

v. 在stm32f10x_it.c文件中void SysTickHandler 函数里面添加

Tic_Val++;//变量递增

vi. 调用代码:Tic_Delay(10); //精确延时

vii. 疑问:如果去掉计时行那个没用的printf(\函数将停止工作,这个现象很奇怪

C语言功底问题。是的,那个“注意事项”最后的疑问的原因就是这个

Tic_Val应该改为vu16

while(Tic_Val != Tic_Count){printf(\计时

就可以改为:

while(Tic_Val != Tic_Count); //检查变量是否计数到位

STM32笔记之十三:恶搞,两只看门狗

a) 目的:

了解两种看门狗(我叫它:系统运行故障探测器和独立系统故障探测器,新手往往被这个并不形象的象形名称搞糊涂)之间的区别和基本用法。

b) 相同:

都是用来探测系统故障,通过编写代码定时发送故障清零信号(高手们都管这个代码叫做“喂狗”),告诉它系统运行正常。一旦系统故障,程序清零代码(“喂狗”)无法执行,其计数器就会计数不止,直到记到零并发生故障中断(狗饿了开始叫唤),控制CPU重启整个系统(不行啦,开始咬人了,快跑……)。

c) 区别:

独立看门狗Iwdg——我的理解是独立于系统之外,因为有独立时钟,所以不受系统影响的系统故障探测器。(这条狗是借来的,见谁偷懒它都咬!)主要用于监视硬件错误。

窗口看门狗wwdg——我的理解是系统内部的故障探测器,时钟与系统相同。如果系统时钟不走了,这个狗也就失去作用了。(这条狗是老板娘养的,老板不干活儿他不管!)主要用于监视软件错误。

d) 初始化函数定义:鉴于两只狗作用差不多,使用过程也差不多初始化函数栓一起了,用的时候根据情况删减。

void WDG_Configuration(void);

e) 初始化函数调用:

WDG_Configuration();

f) 初始化函数

void WDG_Configuration() //看门狗初始化 {

//软件看门狗初始化

WWDG_SetPrescaler(WWDG_Prescaler_8); //时钟8分频4ms

// (PCLK1/4096)/8= 244 Hz (~4 ms)

WWDG_SetWindowValue(65); //计数器数值

WWDG_Enable(127); //启动计数器,设置喂狗时间

// WWDG timeout = ~4 ms * 64 = 262 ms

WWDG_ClearFlag(); //清除标志位

WWDG_EnableIT(); //启动中断

//独立看门狗初始化

IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);//启动寄存器读写

IWDG_SetPrescaler(IWDG_Prescaler_32);//40K时钟32分频

IWDG_SetReload(349); //计数器数值

IWDG_ReloadCounter(); //重启计数器

IWDG_Enable(); //启动看门狗 }

g) RCC初始化:只有软件看门狗需要时钟初始化,独立看门狗有自己的时钟不需要但是需要systic工作相关设置。

RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);

h) 独立看门狗使用systic的中断来喂狗,所以添加systic的中断打开代码就行了。软件看门狗需要在NVIC打开中断添加如下代码:

NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQChannel; //通道

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //占先中断等级

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //响应中断优先级

NVIC_Init(&NVIC_InitStructure); //打开中断

i) 中断程序,软件看门狗在自己的中断中喂狗,独立看门狗需要使用systic的定时中断来喂狗。以下两个程序都在stm32f10x_it.c文件中。

void WWDG_IRQHandler(void) {

WWDG_SetCounter(0x7F); //更新计数值

WWDG_ClearFlag(); //清除标志位 }

void SysTickHandler(void)

{ IWDG_ReloadCounter(); //重启计数器(喂狗) }

j) 注意事项:

i. 有狗平常没事情可以不理,但是千万别忘了喂它,否则死都不知道怎么死的!

ii. 初始化程序的调用一定要在systic的初始化之后。

iii. 独立看门狗需要systic中断来喂,但是systic做别的用处不能只做这件事,所以我写了如下几句代码,可以不影响systic的其他应用,其他systic周期代码也可参考:

第一步:在stm32f10x_it.c中定义变量

int Tic_IWDG; //喂狗循环程序的频率判断变量

第二步:将SysTickHandler中喂狗代码改为下面:

Tic_IWDG++; //变量递增

if(Tic_IWDG>=100) //每100个systic周期喂狗

{ IWDG_ReloadCounter();//重启计数器(喂狗)

Tic_IWDG=0; //变量清零 }

我的理解——可以输出和利用系统时钟的计数、状态。

基础应用1,精确计时的延时子函数。推荐使用的代码。

用法:

static vu32 TimingDelay; //全局变量声明 void SysTick_Config(void) //systick初始化函数 {

SysTick_CounterCmd(SysTick_Counter_Disable); //停止系统定时器

SysTick_ITConfig(DISABLE); //停止systick中断

SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //systick使用HCLK作为时钟源,频率值除以8。

SysTick_SetReload(9000); //重置时间1毫秒(以72MHz为基础计算)

SysTick_ITConfig(ENABLE); //开启systic中断 } void Delay (u32 nTime) //延迟一毫秒的函数 {

SysTick_CounterCmd(SysTick_Counter_Enable); //systic开始计时

TimingDelay = nTime; //计时长度赋值给递减变量

while(TimingDelay != 0); //检测是否计时完成

SysTick_CounterCmd(SysTick_Counter_Disable);

//关闭计数器

SysTick_CounterCmd(SysTick_Counter_Clear); //清除计数值 }

void TimingDelay_Decrement(void) //递减变量函数,函数名由“stm32f10x_it.c”中的中断响应函数定义好了。 {

if (TimingDelay != 0x00) //检测计数变量是否达到0

{ TimingDelay--; //计数变量递减

} }

注:建议熟练后使用,所涉及知识和设备太多,新手出错的可能性比较大。新手可用简化的延时函数代替: void Delay(vu32 nCount) //简单延时函数 {

for(; nCount != 0; nCount--); //循环变量递减计数 }

当延时较长,又不需要精确计时的时候可以使用嵌套循环:

void Delay(vu32 nCount) //简单的长时间延时函数

{int i; //声明内部递减变量

for(; nCount != 0; nCount--) //递减变量计数 {for (i=0; i<0xffff; i++)}

//内部循环递减变量计数 }

9、 阅读gpio:I/O设置函数

我的理解——所有输入输出管脚模式设置,可以是上下拉、浮空、开漏、模拟、推挽模式,频率特性为2M,10M,50M。也可以向该管脚直接写入数据和读取数据。

基础应用1,gpio初始化函数。所有程序必须。

用法:void GPIO_Configuration(void) {

GPIO_InitTypeDef GPIO_InitStructure; //GPIO状态恢复默认参数

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_标号 | GPIO_Pin_标号 ; //管脚位置定义,标号可以是NONE、ALL、0至15。

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;//输出速度2MHz

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入模式

GPIO_Init(GPIOC, &GPIO_InitStructure); //C组GPIO初始化

//注:以上四行代码为一组,每组GPIO属性必须相同,默认的GPIO参数为:ALL,2MHz,FLATING。如果其中任意一行与前一组相应设置相同,那么那一行可以省略,由此推论如果前面已经将此行参数设定为默认参数(包括使用GPIO_InitTypeDef GPIO_InitStructure代码),本组应用也是默认参数的话,那么也可以省略。以下重复这个过程直到所有应用的管脚全部被定义完毕。 …… }

基础应用2,向管脚写入0或1

用法:GPIO_WriteBit(GPIOB, GPIO_Pin_2, (BitAction)0x01); //写入1

基础应用3,从管脚读入0或1

用法:GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6)

STM32笔记之七:让它跑起来,基本硬件功能的建立

0、 实验之前的准备

a) 接通串口转接器

b) 下载IO与串口的原厂程序,编译通过保证调试所需硬件正常。

1、 flash,lib,nvic,rcc和GPIO,基础程序库编写

a) 这几个库函数中有一些函数是关于芯片的初始化的,每个程序中必用。为保障程序品质,初学阶段要求严格遵守官方习惯。注意,官方程序库例程中有个platform_config.h文件,是专门用来指定同类外设中第几号外设被使用,就是说在main.c里面所有外设序号用x代替,比如USARTx,程序会到这个头文件中去查找到底是用那些外设,初学的时候参考例程别被这个所迷惑住。

b) 全部必用代码取自库函数所带例程,并增加逐句注释。

c) 习惯顺序——Lib(debug),RCC(包括Flash优化),NVIC,GPIO

d) 必用模块初始化函数的定义:

void RCC_Configuration(void); //定义时钟初始化函数

void GPIO_Configuration(void); //定义管脚初始化函数

void NVIC_Configuration(void); //定义中断管理初始化函数

void Delay(vu32 nCount); //定义延迟函数

e) Main中的初始化函数调用:

RCC_Configuration(); //时钟初始化函数调用

NVIC_Configuration(); //中断初始化函数调用

GPIO_Configuration(); //管脚初始化函数调用

f) Lib注意事项:

属于Lib的Debug函数的调用,应该放在main函数最开始,不要改变其位置。

g) RCC注意事项:

Flash优化处理可以不做,但是两句也不难也不用改参数……

根据需要开启设备时钟可以节省电能

时钟频率需要根据实际情况设置参数

h) NVIC注意事项

注意理解占先优先级和响应优先级的分组的概念

i) GPIO注意事项

注意以后的过程中收集不同管脚应用对应的频率和模式的设置。

作为高低电平的I/O,所需设置:RCC初始化里面打开RCC_APB2

PeriphClockCmd(RCC_APB2Periph_GPIOA);GPIO里面管脚设定:IO输出(50MHz,Out_PP);IO输入(50MHz,IPU);

j) GPIO应用

GPIO_WriteBit(GPIOB, GPIO_Pin_2, Bit_RESET);//重置

GPIO_WriteBit(GPIOB, GPIO_Pin_2, (BitAction)0x01);//写入1

GPIO_WriteBit(GPIOB, GPIO_Pin_2, (BitAction)0x00);//写入0

GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6) ;//读入IO

k) 简单Delay函数

void Delay(vu32 nCount)//简单延时函数

{for(; nCount != 0; nCount--);}

实验步骤:

RCC初始化函数里添加:RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB , ENABLE);

不用其他中断,NVIC初始化函数不用改

GPIO初始化代码:

本文来源:https://www.bwwdw.com/article/nr62.html

Top