笔记(六)RCC,EXTI,SysTick 您所在的位置:网站首页 三代rcc和二代rcc有什么区别 笔记(六)RCC,EXTI,SysTick

笔记(六)RCC,EXTI,SysTick

2024-06-12 18:25| 来源: 网络整理| 查看: 265

RCC是复位和时钟控制

首先是复位

1、复位

有三种复位:系统复位、电源复位和后备域复位。

系统复位

系统复位将复位除时钟控制寄存器CSR中的复位标志和备份区域中的寄存器以外的所有寄存器

为它们的复位数值

当以下事件中的一件发生时,产生一个系统复位:

1. NRST引脚上的低电平(外部复位)

2. 窗口看门狗计数终止(WWDG复位)

3. 独立看门狗计数终止(IWDG复位)

4. 软件复位(SW复位)

5. 低功耗管理复位

可通过查看RCC_CSR控制状态寄存器中的复位状态标志位识别复位事件来源。

软件复位

通过将Cortex™-M3中断应用和复位控制寄存器中的SYSRESETREQ位置’1’,可实现软件复位。请参考Cortex™-M3技术参考手册获得进一步信息。

电源复位

当以下事件中之一发生时,产生电源复位:

1. 上电/掉电复位(POR/PDR复位)

2. 从待机模式中返回

下面是时钟

2、时钟

1.三种不同的时钟源可被用来驱动系统时钟(SYSCLK):

● HSI振荡器时钟

● HSE振荡器时钟

● PLL时钟

时钟树

●当HSI被用于作为PLL时钟的输入时,系统时钟能得到的最大频率是36MHz。

● Flash存储器编程接口时钟始终是HSI时钟。

● 全速USB OTG的48MHz时钟是从PCC VCO时钟(2xPLLCLK),和随后可编程预分频器(除3或除2)得到,这是通过RCC_CFGR寄存器的OTGFSPRE位控制。为了正常地操作USB全速OTG,应该配置PLL输出72MHz或48MHz。

● I2S2和I2S3的时钟还可以从PLL3 VCO时钟(2xPLL3CLK)得到,这是通过RCC_CFGR2寄存器的I2SxSRC位控制。更多有关PLL3的内容和如何配置I2S时钟,以得到高质量的音频效果,请参阅第23.4.3节: 时钟发生器。

● 以太网MAC的时钟(TX、 RX和RMII)是由外部PHY提供。更多有关以太网配置的详情,请见第27.4.4节: MII/RMII的选择。当使用以太网模块时, AHB时钟频率必须至少为25MHz。

RCC通过AHB时钟(HCLK)8分频后作为Cortex系统定时器(SysTick)的外部时钟。通过对SysTick控制与状态寄存器的设置,可选择上述时钟或Cortex(HCLK)时钟作为SysTick时钟。 ADC时钟由高速APB2时钟经2、 4、 6或8分频后获得。

定时器时钟频率分配由硬件按以下2种情况自动设置:

1. 如果相应的APB预分频系数是1,定时器的时钟频率与所在APB总线频率一致。

2. 否则,定时器的时钟频率被设为与其相连的APB总线频率的2倍

这一部分正点原子的讲的比较易懂,建议看正点原子,看完正点原子再看野火的就很容易明白了,也可以只看正点原子的

2.时钟源介绍

(1)STM32有五个时钟源::HSI、HSE、LSI、LSE、PLL

①、HSI是高速内部时钟,RC振荡器,频率为8MHz,精度不高,一分频 可作为系统时钟,二分频可作为PLL时钟。能够在不需要任何外部器件的条件下提供系统时钟,启动时间比HSE短,校准后精度较差。如果HSE晶体振荡器失效, HSI时钟会被作为备用时钟源。

②、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz.一分频和二分频均可作为PLL时钟。时钟控制寄存器(RCC_CR)中的HSERDY位判断是否稳定。在启动时,直到这一位被硬件置’1’,时钟才被释放出来。如果在时钟中断寄存器(RCC_CIR)中允许产生中断,将会产生相应中断。HSE晶体可以通过设置时钟控制寄存器(RCC_CR)中的HSEON位被启动和关闭。

③、LSI是低速内部时钟,RC振荡器,频率为40kHz,提供低功耗时钟。LSI RC担当一个低功耗时钟源的角色,可以在停机和待机模式下保持运行,为独立看门狗和自动唤醒单元提供时钟。

④、LSE是低速外部时钟,接频率为32. 768kHz的石英晶体。可作为RTC时钟。为实时时钟或者其他定时功能提供一个低功耗且精确的时钟源。

⑤、PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2.倍频可选择为2~16倍, 但是其输出频率最大不得超过72MHz.

PLL有三条来源:HSI的二分频,HSE的一分频和二分频

css:时钟监控系统,HSE失效时自动切换为HSI

注意: 一旦CSS被激活,并且HSE时钟出现故障, CSS中断就产生,并且NMI也自动产生。 NMI将被不断执行,直到CSS中断挂起位被清除。因此,在NMI的处理程序中必须通过设置时钟中断寄存器(RCC_CIR)里的CSSC位来清除CSS中断。

如果HSE振荡器被直接或间接地作为系统时钟, (间接的意思是:它被作为PLL输入时钟或通过PLL2,并且PLL时钟被作为系统时钟),时钟故障将导致系统时钟自动切换到HSI振荡器,同时外部HSE振荡器被关闭。在时钟失效时,如果HSE振荡器时钟(直接的或通过PLL2)是作为PLL的输入时钟, PLL也将被关闭。

(2)系统时钟SYSCLK可来源于三个时钟源:①、HSI振荡器时钟不分频②、HSE振荡器时钟不分频③、 PLL时钟

系统复位后, HSI振荡器被选为系统时钟。目标时钟源就绪(经过启动稳定阶段的延迟或PLL稳定),从 一个时钟源到另一个时钟源的切换才会发生。在被选择时钟源没有就绪时,系统时钟的切换不会发 生。直至目标时钟源就绪,才发生切换。

在时钟控制寄存器(RCC_CR)里的状态位指示哪个时钟已经准备好了,哪个时钟目前被用作系统

时钟。

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

(4)任何一个外设在使用之前,必须首先使能其相应的时钟。

3.时钟输出

微控制器允许输出时钟信号到外部MCO引脚。

相应的GPIO端口寄存器必须被配置为相应功能。以下8个时钟信号可被选作MCO时钟:

● SYSCLK

● HSI

● HSE

● 除2的PLL时钟

● PLL2时钟

● PLL3时钟除以2

● XT1外部3~25MHz振荡器(用于以太网)

● PLL3时钟(用于以太网)

在MCO上输出的时钟必须小于50MHz(这是I/O端口的最大速度)。

时钟的选择由时钟配置寄存器(RCC_CFGR)中的MCO[3:0]位控制。

3、代码详解

RCC作用

从RCC结构体里可以看出RCC的作用(在stm32f10x.h里)

设置系统时钟 SYSCLK、设置 AHB 分频因子(决定 HCLK 等于多少) 、 设置 APB2 分频因子(决定 PCLK2 等于多少)、设置 APB1 分频因子(决定 PCLK1 等于多少)、设置各个外设的分频因子;控制AHB、 APB2 和 APB1 这三条总线时钟的开启、控制每个外设的时钟的开启。

RCC相关寄存器很重要,需要看芯片参考手册,下面的系统时钟库函数会用到

时钟系统初始化函数剖析

程序里的CL是互联型,我们用到的板子不是互联型,不用考虑CL部分的代码

启动文件的复位程序这里有一个SystemInit,从这里go to definition

就来到时钟系统初始化函数了

/* Reset the RCC clock configuration to the default reset state(for debug purpose) */ /* Set HSION bit */ RCC->CR |= (uint32_t)0x00000001; //默认初始化状态 /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */ #ifndef STM32F10X_CL RCC->CFGR &= (uint32_t)0xF8FF0000; #else RCC->CFGR &= (uint32_t)0xF0FF0000; #endif /* STM32F10X_CL */ /* Reset HSEON, CSSON and PLLON bits */ RCC->CR &= (uint32_t)0xFEF6FFFF; /* Reset HSEBYP bit */ RCC->CR &= (uint32_t)0xFFFBFFFF; /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */ RCC->CFGR &= (uint32_t)0xFF80FFFF;

①首先是CR寄存器的HSION位置一,开启内部

/* Set HSION bit */ RCC->CR |= (uint32_t)0x00000001;

②CFGR寄存器SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO这些位全部清零

#ifndef STM32F10X_CL RCC->CFGR &= (uint32_t)0xF8FF0000; #else RCC->CFGR &= (uint32_t)0xF0FF0000; #endif /* STM32F10X_CL */

根据数值对应寄存器的每一位就可以知道,每一行代码的含义

③HSEON, CSSON,PLLON清零

/* Reset HSEON, CSSON and PLLON bits */ RCC->CR &= (uint32_t)0xFEF6FFFF;

这里视频里都有着带着看,就按照这种方式对应就好

然后就来到SetSysClock();这个函数

这个函数的作用是,根据这个宏定义,选择了系统时钟配置为72M的函数

static void SetSysClockTo72(void) { __IO uint32_t StartUpCounter = 0, HSEStatus = 0; /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/ /* Enable HSE */ RCC->CR |= ((uint32_t)RCC_CR_HSEON); /* Wait till HSE is ready and if Time out is reached exit */ //等待时钟稳定 do { HSEStatus = RCC->CR & RCC_CR_HSERDY;//判断CR寄存器的第17位是否为1 StartUpCounter++; } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); if ((RCC->CR & RCC_CR_HSERDY) != RESET) { HSEStatus = (uint32_t)0x01; } else { HSEStatus = (uint32_t)0x00; } if (HSEStatus == (uint32_t)0x01) { //闪存手册,不需要理解 /* Enable Prefetch Buffer */ FLASH->ACR |= FLASH_ACR_PRFTBE; /* Flash 2 wait state */ FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY); FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2; //两个等待状态 //配置三个时钟源的选择,系统时钟,APB1,APB2 /* HCLK = SYSCLK */ //go to definition RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; /* PCLK2 = HCLK */ RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; /* PCLK1 = HCLK/2 */ RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; #ifdef STM32F10X_CL /* Configure PLLs ------------------------------------------------------*/ /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */ /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */ RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL | RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC); RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 | RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5); /* Enable PLL2 */ RCC->CR |= RCC_CR_PLL2ON; /* Wait till PLL2 is ready */ while((RCC->CR & RCC_CR_PLL2RDY) == 0) { } /* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */ RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL); RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | RCC_CFGR_PLLMULL9); #else /* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */ RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL)); RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);//9倍 #endif /* STM32F10X_CL */ /* Enable PLL */ RCC->CR |= RCC_CR_PLLON; /* Wait till PLL is ready */ while((RCC->CR & RCC_CR_PLLRDY) == 0) { } /* Select PLL as system clock source */ RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; /* Wait till PLL is used as system clock source */ //等待系统时钟切换完成,SWS检测位 while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) { } } else { /* If HSE fails to start-up, the application will have wrong clock configuration. User can add here some code to deal with this error */ } } #endif

SW:选择系统时钟来源

SWS:SW位选择好时钟来源以后,SWS里面的相应为会被置一,读取SWS位,就可以确保当前时钟设置完毕

//配置三个时钟源的选择,AHB,APB1,APB2

本身注释就很清楚,go to definition去定义位置,后面注释都写了,可以自己根据宏定义的值对应一下相关寄存器

这个函数我只对下面这部分有点疑问,其余部分均能与寄存器对应上

/* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */

??//HSE 作为PLL输入时钟,2分频,PLL倍频保留

RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |

RCC_CFGR_PLLMULL));

HSE不分频进入锁相环

//选择HSE 作为PLL输入时钟

RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);//9倍

当设置好时钟以后,会有SystemCoreClock,可以通过SystemCoreClock在外部获取到设置的系统时钟是多少

HSE时钟输入(bsp_rcc.c)

void Sys72_Config(void){ // 把RCC外设初始化成复位状态,这句是必须的 RCC_DeInit(); //使能HSE RCC_HSEConfig(RCC_HSE_ON); //等待时钟稳定,rcc.c里有检测始终是否启动的函数,直接调用就好 while(RCC_WaitForHSEStartUp()==ERROR){ } if (RCC_GetFlagStatus(RCC_FLAG_HSERDY) == (uint32_t)0x01) { //笑死,在rcc里找半天没找着,flash当然在flah.c里找了 //看注释找对应函数 /* Enable Prefetch Buffer */ FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); /* Flash 2 wait state */ FLASH_SetLatency(FLASH_Latency_2); /* HCLK = SYSCLK */ RCC_HCLKConfig(RCC_SYSCLK_Div1); /* PCLK2 = HCLK */ RCC_PCLK2Config(RCC_HCLK_Div1); /* PCLK1 = HCLK/2 */ RCC_PCLK1Config(RCC_HCLK_Div2); /* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */ RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); /* Enable PLL */ RCC_PLLCmd(ENABLE); /* Wait till PLL is ready */ while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) { } /* Select PLL as system clock source */ RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); /* Wait till PLL is used as system clock source */ while (RCC_GetSYSCLKSource() != (uint32_t)0x08) { } } else { /* If HSE fails to start-up, the application will have wrong clock configuration. User can add here some code to deal with this error */ } while (1) {} }

改错&修补

我和例程写的不一样在于,例程写的是有参数的函数,我写的函数参数是void

这句话忘写了 // 把RCC外设初始化成复位状态,这句是必须的 RCC_DeInit(); //等待时钟稳定我和例程写的不太一样,如果这样写的话闪灯频率会变慢,现在我没有示波器,波形看不到,就目前的现象来看没有问题,都对应的上 //下面是例程里的 // 等待 HSE 启动稳定 HSEStartUpStatus = RCC_WaitForHSEStartUp(); // 只有 HSE 稳定之后则继续往下执行,这里我写的也不太好,固件库有固件库的 //我写成这样 if (RCC_WaitForHSEStartUp()==SUCCESS) { //例程是这样 if (HSEStartUpStatus == SUCCESS) { //例程里的第二个参数我写的是RCC_PLLMul_9,我看h文件定义如下,我写的是RCC_PLLMul_9 //-----------------设置各种频率主要就是在这里设置-------------------// // 设置PLL时钟来源为HSE,设置PLL倍频因子 // PLLCLK = 8MHz * pllmul RCC_PLLConfig(RCC_PLLSource_HSE_Div1, pllmul); //------------------------------------------------------------------//

等待PLL稳定的这块例程里和系统时钟初始化函数写的是一样的,但RCC_GetSYSCLKSource() 在stm32f10x_rcc.c已经定义好了,所以可以直接用,但是HSERDY并没有(但是可以自己算)

我的main.c里也不需要参数Sys72_Config();,例程里是需要参数的,例程这种放法只改函数里的参数就可以改变频率,我这种需要到函数里改变PLLCLK这里的值

HSI时钟输入

HSI输入相比HSE输入改的地方不多,换成HSI使能,判断是否开启用寄存器,PLL时钟源换成HSI的2分频即可,HSI用的和例程一样是带参数的

void HSI_Config(uint32_t RCC_PLLMul_x){ __IO uint32_t HSIStatus = 0; // 把RCC外设初始化成复位状态,这句是必须的 RCC_DeInit(); //使能HSI RCC_HSICmd(ENABLE); HSIStatus = RCC->CR & RCC_CR_HSIRDY; if (RCC_FLAG_HSIRDY==SET) { //笑死,在rcc里找半天没找着,flash当然在flah.c里找了 //看注释找对应函数 FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); FLASH_SetLatency(FLASH_Latency_2); RCC_HCLKConfig(RCC_SYSCLK_Div1); RCC_PCLK2Config(RCC_HCLK_Div1); RCC_PCLK1Config(RCC_HCLK_Div2); RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_x); /* Enable PLL */ RCC_PLLCmd(ENABLE); /* Wait till PLL is ready */ while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) { } /* Select PLL as system clock source */ RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); /* Wait till PLL is used as system clock source */ while (RCC_GetSYSCLKSource() != (uint32_t)0x08) { } } else { /* 如果HSI 启动失败,用户可以在这里添加处理错误的代码 */ } }

现在我还没有示波器,回头用示波器在检验一下现象

中断

数值越小,优先级越高

1。中断类型

NVIC简介

外设的寄存器在stm32f10x.h,内核相关寄存器在core_cm3.c和misc.c

最常使用中断优先级寄存器

NVIC函数实现在misc.c里

中断优先级的定义

只用高四位,低四位不使用;先比较主优先级,在比较子优先级,如果都一样,就比较硬件编号,硬件终端编号就是前面说的向量表

表55 其它STM32F10xxx产品(小容量、中容量和大容量)的向量表

分组表在misc.h里

1.

先配置NVIC的中断使能寄存器,在配置对应外设的中断位,串口为例

2.

配置SCB_AIRCR位,通过固件库里NVIC_PriorityGroupConfig函数进行分组

3.

初始化结构体(misc.c)

中断源在stm32f10x.h里

下面编程选的是EXTILine0,所以中断源选EXTI0_IRQn,需要注意的是,EXTI0-4有单独的中断源,5-9,10-15是在一块的用的时候别写错了

4.中断服务函数

与启动函数里中断向量表里的中断函数同名,否则会执行启动文件里的,并在此一直循环(因为B.启动文件那一张有说)

中断服务函数写错会报错

在stm32f10x_it.c里写

EXIT(GPIO中断)

框图

信号线上打斜杠并标注“ 23”的字样,表示在控制器内部类似的信号线路有 23 个,这与 EXTI 总共有 23 个中断/事件线是吻合的。

EXTI 功能框图

EXTI 可分为两大部分功能,一个是产生中断,另一个是产生事件,这两个功能从硬件上就有所不同。

1.1中断

红色虚线指示的电路流程。它是一个产生中断的线路,最终信号流入到 NVIC 控制器内

编号 1 是输入线, EXTI 控制器有 20 个中断/事件输入线,这些输入线可以通过寄存器设置为任意一个 GPIO,EXTI0 至 EXTI15 用于 GPIO,通过编程控制可以实现任意一个 GPIO 作为 EXTI 的输入源,还有另外四根用于特定的外设事件,输入线一般是存电平变化的信号。

EXTI0 至 EXTI15 用于 GPIO,通过编程控制可以实现任意一个 GPIO 作为 EXTI 的输入源。输入源的选择映像

另外四个EXTI线的连接方式如下:

● EXTI线16连接到PVD输出

● EXTI线17连接到RTC闹钟事件

● EXTI线18连接到USB唤醒事件

● EXTI线19连接到以太网唤醒事件(只适用于互联型产品)

EXTI0 可以通过 AFIO 外部中断配置寄存器AFIO_EXTICR1的 EXTI0[3:0]位配置

根据上面的讲解可以回答下面的问题

20,15个GPIO,PVD,RTC,USB,以太网

AFIO

选择好输入源以后,输入源的电平信号会到达边沿检测器,也就是编号2的位置,边沿检测器可以是只有上升沿触发、只有下降沿触发或者上升沿和下降沿都触发。边沿检测器将检测到的信号传给编号3电路,有效为1,无效为0。

编号3是一个或门,接收编号 2 电路和软件中断事件寄存器(EXTI_SWIER)的指令 ,EXTI_SWIER 可以通过程序控制就可以启动中断/事件线,该位写’1’将设置EXTI_PR中相应的挂起位,这两个输入随便一个有有效信号 1 就可以输出 1 给编号 4 和编号 6 电路。

编号4是与门,当中屏蔽寄存器和中断挂起寄存器全为1的时候,才会将 EXTI_PR 寄存器内容输出到 NVIC 内,从而实现系统中断事件控制

1.2事件

绿色虚线指示电路它是一个产生事件的线路,最终输出一个脉冲信号(内部)。

之前电路都是共用的。编号6 电路是一个与门,它一个输入编号 3 电路,另外一个输入来自事件屏蔽寄存器(EXTI_EMR)。当中屏蔽寄存器和中断挂起寄存器全为1时,脉冲发生器会产生一个脉冲,脉冲在单片机内部,这个脉冲信号可以给其他外设电路使用,比如定时器 TIM、模拟数字转换器 ADC 等等。

初始化结构体(stm32f10x_exti.h里)

1) EXTI_Line: EXTI 中断/事件线选择,可选 EXTI0 至 EXTI22,可参考表 17-1 选择。

2) EXTI_Mode: EXTI 模式选择,可选为产生中断(EXTI_Mode_Interrupt)或者产生事件(EXTI_Mode_Event)。

3) EXTI_Trigger: EXTI 边沿触发事件,可选上升沿触发(EXTI_Trigger_Rising)、下降 沿 触 发 ( EXTI_Trigger_Falling) 或 者 上 升 沿 和 下 降 沿 都 触 发( EXTI_Trigger_Rising_Falling)。

4) EXTI_LineCmd:控制是否使能 EXTI 线,可选使能 EXTI 线(ENABLE)或禁用(DISABLE)。对应中断屏蔽寄存器或事件屏蔽起寄存器

EXIT既然是GPIO的中断,那么GPIO里会有对应的函数,GPIO_EXITConfig()

编程

3.1编程要点

GPIO和EXTI的初始化可以写到一个初始化函数里

1、GPIO初始化,和前面按键的一样

2、EXTI初始化,

首先设置gpio.h里有关EXTI的部分,前面说过,这一部分在AFIO寄存器里配置,

然后打开中断的时钟,中断时钟在APB2总线上

GPIO的中断由AFIO控制

然后就是EXTI结构体的初始化,在stm32f10x_exti.h里

EXTI_InitStruct.EXTI_Line=EXTI_Line0; EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt; EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Rising; EXTI_InitStruct.EXTI_LineCmd=ENABLE; EXTI_Init(&EXTI_InitStruct);

(GPIO_EXITLineConfig()没用,别忘记打开EXTI的时钟)

3、NVIC初始化

NVIC的初始化单独用一个函数NVIC_Config()完成,KEY1_EXTI_Config调用NVIC_Config(),写在后面前面别忘了声明一下,首先用NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup),根据需求选择,这里选择组1,其余根据misc.h里的结构体定义初始化,为了防止别的文件调用,在前面加一下static

中断源的选择

4、中断服务函数

名字要和启动文件里的同名,里面写你具体要干什么,去启动文件里中断服务函数部分找对应的名字

中断服务函数写到it.c里,写什么呢

我们希望检测到高电平时,灯可以反转一次,那这里就对电平进行判断,检测到一次高电平,反转一次

中断标志位在exti.h里有一个检测标志位的函数

EXTI_GetITStatus(EXTI_Line0)为1,时,反转,灯的反转用ODR的异或,判断完后,还需要清除中断

作业

有一个小问题

看报错,初始化语句不能放在执行语句后,main函数里没有问题,问题在

这行语句挪到61行或62行都行

还有一个小问题,有点乱了,中断服务函数里是灯的反转,所以是GPIOB,写道GPIOC里去了

stm32f10x_it.c

void EXTI0_IRQHandler(void){ if(EXTI_GetITStatus(EXTI_Line0)==1){ LED_G_Toggle; } EXTI_ClearITPendingBit(EXTI_Line0); } void EXTI15_10_IRQHandler(void){ if(EXTI_GetITStatus(EXTI_Line13)!=RESET){ GPIOB->ODR^=GPIO_Pin_1; } EXTI_ClearITPendingBit(EXTI_Line13); }

bsp_key.c按键中断,我写是的这个名字,对应例程bsp_exti.c

#include "bsp_key.h" #include "stm32f10x_exti.h" static void NVIC_KEY1_Config(void){ NVIC_InitTypeDef NVIC_InitStruct; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); NVIC_InitStruct.NVIC_IRQChannel=EXTI0_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1; NVIC_InitStruct.NVIC_IRQChannelSubPriority=1; NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStruct); } static void NVIC_KEY2_Config(void){ NVIC_InitTypeDef NVIC_InitStruct; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); NVIC_InitStruct.NVIC_IRQChannel=EXTI15_10_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1; NVIC_InitStruct.NVIC_IRQChannelSubPriority=1; NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStruct); } void KEY1_EXTI_Config(void) { //初始化GPIO GPIO_InitTypeDef GPIO_InitStruct; EXTI_InitTypeDef EXTI_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStruct); //初始化NVIC NVIC_KEY1_Config(); //初始化EXTI GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); EXTI_InitStruct.EXTI_Line=EXTI_Line0; EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt; EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Rising; EXTI_InitStruct.EXTI_LineCmd=ENABLE; EXTI_Init(&EXTI_InitStruct); } //不需要按键扫描函数 void KEY2_EXTI_Config(void){ //初始化GPIO GPIO_InitTypeDef GPIO_InitStruct; EXTI_InitTypeDef EXTI_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStruct.GPIO_Pin=GPIO_Pin_13; GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOC, &GPIO_InitStruct); //初始化NVIC NVIC_KEY2_Config(); //初始化EXTI GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource13); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); EXTI_InitStruct.EXTI_Line=EXTI_Line13; EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling; EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt; EXTI_Init(&EXTI_InitStruct); }

"bsp_key.h"

这里第五行,可以写成下面这样,之前不是说define后面不能有分号,有分号的话需要用大括号括起来

#ifndef __BSP_KEY_H #define __BSP_KEY_H #include "stm32f10x.h" #define LED_G_Toggle GPIOB->ODR^=GPIO_Pin_0 //#define LED_G_Toggle {GPIOB->ODR^=GPIO_Pin_0;} void KEY1_EXTI_Config(void); void KEY2_EXTI_Config(void); #endif /* __BSP_KEY_H */

bsp_led.c

// bsp :board support package 板级支持包 #include "bsp_led.h" void LED_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(LED_G_GPIO_CLK, ENABLE); GPIO_InitStruct.GPIO_Pin = LED_G_GPIO_PIN; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(LED_G_GPIO_PORT, &GPIO_InitStruct); } void LED_B_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStruct); }

bsp_led.h

#ifndef __BSP_LED_H #define __BSP_LED_H #include "stm32f10x.h" #define LED_G_GPIO_PIN GPIO_Pin_0 #define LED_G_GPIO_PORT GPIOB #define LED_G_GPIO_CLK RCC_APB2Periph_GPIOB #define ON 1 #define OFF 0 // \ C语言里面叫续行符,后面不能有任何的东西 #define LED_G(a) if(a) \ GPIO_ResetBits(LED_G_GPIO_PORT, LED_G_GPIO_PIN); \ else GPIO_SetBits(LED_G_GPIO_PORT, LED_G_GPIO_PIN); void LED_GPIO_Config(void); void LED_B_GPIO_Config(void); #endif /* __BSP_LED_H */

SysTick

1.简介

SysTick—系统定时器是属于 CM4 内核中的一个外设,内嵌在 NVIC 中。系统定时器是一个 24bit 的向下递减的计数器,计数器每计数一次的时间为 1/SYSCLK。当重装载数值寄存器的值递减到 0 的时候,系统定时器就产生一次中断,以此循环往复。

因为 SysTick 是属于 CM4 内核的外设,所以所有基于 CM4 内核的单片机都具有这个系统定时器,使得软件在 CM4 单片机中可以很容易的移植。系统定时器一般用于操作系统,用于产生时基,维持操作系统的心跳。

2.SysTick 寄存器介绍

SysTick—系统定时有 4 个寄存器,简要介绍如下。在使用 SysTick 产生定时的时候,只需要配置前三个寄存器,最后一个校准寄存器不需要使用。

STK_CTRL寄存器

COUNTFLAG:递减计数器递减到零时为1,当我们读取这个位以后会清零,然后重新开始计数

CLKSOURCE:可以选择系统时钟,对应时钟树部分如下

TICKINT:0不产生中断,1产生中断

STK_LOAD寄存器

24位有效,计数到零,重装载

STK_VAL寄存器

可以实时读取当前计数的值

功能框图

递减计数器在时钟驱动下,从重装载寄存器的初值开始往下递减计数到0,递减到0时会产生中断,若使能中断,则会执行中断服务程序,并且COUTFLAG会置一。如果未关闭,则从头开始递减并循环下去。递减的值可以从ST_VAL里实时读到。

3.SysTick 定时时间计算

reload:重装载计数器的值

程序一般是毫秒级的,一般设置毫秒

4.SysTick固件库定义

4.1结构体(core_cm3.h)

typedef struct { __IO uint32_t CTRL; /*!< Offset: 0x00 SysTick Control and Status Register */ __IO uint32_t LOAD; /*!< Offset: 0x04 SysTick Reload Value Register */ __IO uint32_t VAL; /*!< Offset: 0x08 SysTick Current Value Register */ __I uint32_t CALIB; /*!< Offset: 0x0C SysTick Calibration Register */ } SysTick_Type;

4.2SysTick配置函数

static __INLINE uint32_t SysTick_Config(uint32_t ticks) { //判断初值ticks,go to definiti过去可知初值是2^24,如果大于初值返回1,不符合规则 #define SysTick_LOAD_RELOAD_Msk (0xFFFFFFul SysTick_LOAD_RELOAD_Msk) return (1); //否则把初值ticks装载到STK_LOAD寄存器里 SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; //配置中断优先级 NVIC_SetPriority (SysTick_IRQn, (1CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; return (0); }

配置中断优先级

首先看函数

static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) { if(IRQn < 0) { SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority IP[(uint32_t)(IRQn)] = ((priority


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有