前言
硬件方面有很多东西长时间不接触容易忘记,复习的时候记录一下自己的想法,这样便于快速恢复
时钟系统
SPI的初始化中需要配置波特率分频系数,需要了解stm32的时钟和总线等基础结构知识才能搞清楚SPI的实际可配置的工作频率。此外,在网上查找有关SPI的峰值工作频率出现了很多超频会跑飞的情况,大部分的说法是虽然STM32的SPI支持很多不同的工作频率,但最终的选择会受到硬件等诸多因素的影响。
STM32之所以会有相比51单片机复杂很多的时钟系统,是因为stm32f4的结构和外设非常的丰富和复杂,不同的外设所需要的时钟频率是不一样的,为了降低功耗需要因地制宜……STM32的时钟系统有很多功能如果不是使用到的话很难记忆,所以暂时先只熟悉一下默认情况下的STMf4的时钟配置信息。
这张图应该是学习STM32必定会看到一张图,其实里面很多东西不需要记忆,就算记住了长时间不接触也会忘掉。。所以只能能大概看懂我觉得就可以了。。
虽然STM32提供了很多不同的时钟源选择,但是最常见的是 采用HSE外部高速时钟,外接一个8Mhz的晶振-》经过PLL后产生168Mhz提供给系统时钟 。SYSCLK:系统时钟,总线、外设等所有的时钟均是以它为基础的,它是整个芯片系统的脉搏。在STM32执行main函数之前会先执行SysTickInit:选择HSE并等待它稳定-》设定AHB、APB1/2分频系数-》设定PLL的参数(PLL的时钟源以及分频系数等)并且等待它稳定-》选择PLL倍频为系统的时钟源。这些步骤结束后,整个系统的时钟就完全确定了。
PLL=SYSCLK(系统时钟)=HCLK(AHB总线时钟)=168Mhz;
PCLK1(APB1总线时钟)=HCLK/4=42Mhz;
PCLK2(APB2总线时钟)=HCLK/2=84Mhz;
以上是对默认情况下STM32的时钟系统的简单回顾,其实是学习STM32时的一个很基础的内容。
SPI时钟频率问题
stm32f4系列的SPI1外设挂载在SPB2总线上,所以SPI1的基础时钟频率是84Mhz。在对SPI1的初始化中,需要设置SPI波特率发生器的预分频系数,如下图:SPI1的波特率理论上最高可达PCLK2/2即42Mhz。
该楼主发现在STM32F4的datasheet上SPI的器件特性写明最高支持到37.5Mhz:
所以对于主频仍采用168Mhz的情况下,采用4分频即21Mhz会比较稳妥。如果仍采用2分频即42Mhz就相当于是计算机的超频使用,偶尔一两次没问题,但不适合作为产品设计或者是长时间高稳定性的使用场景。所以采用21Mhz相当于是为了稳定和安全的一种妥协吧。
顺便搜索了一下为什么大部分串口的波特率都采用115200或者9600:串口的波特率越高,有效传输距离就越短,对于115200大概只有5M左右,而且波特率越大越容易出错,所以和SPI是同一个道理。串口通信的种种限制是导致它们带宽有限的主要原因吧……
SYSTICK滴答定时器
systick是一个非常简单的定时器,arm官方要求芯片中必须有这样一个定时器。它占用资源很少,一旦使能后就会一直工作,即使在睡眠模式下也正常运转。
本质上就是一个递减计数器,当递减到0时从重装载寄存器中读取新的数值继续递减;
如果使能了中断就会在每次递减到0时触发systick中断;
时钟源可以是HCLK/8或者HCLK,对于F4芯片,HCLK=168Mhz;
正点原子的延时函数没有使用systick中断,它的原理是:
初始化systick的时钟源为HCLK/8;
获取到用户想要延时的时长并根据systick的时钟源确定一个装载值;
将val当前值寄存器清空,使能systick定时器;
systick定时器加载一个值后进行递减;
程序始终监视CTRL寄存器中的countflag标志位是否为1;
如果为1就立即关闭systick定时器
外部中断
STM32F405ZGT6有7组GPIO(A-G),每组有16个GPIO口(0-15),一共144个GPIO口。外部中断先EXTI有16个(0-15)
GPIOX.0映射到EXTI0以此类推。其实用多了以后很好理解,外部可以是板上外设例如按键,也可以是网络模块,它们在某一时刻会触发电平的变化,而外部中断就可以捕获它们并执行相应的中断函数。
比较重要的一个概念是,对于EXTI0,GPIOA0-GPIOG0中同时只能有一个映射到EXTI0。
中断服务函数和中断向量表有关,我的理解是,当触发中断时,会到中断向量表中找到指定的函数地址,进而去执行相应的处理函数。因为中断向量表中只分配了7个,所以只有7个中断服务函数,如下:
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
EXTI9_5_IRQHandler
EXTI15_10_IRQHandler
void EXTIX_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//ʹÄÜSYSCFGʱÖÓ
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource4);//PE4 Á¬½Óµ½ÖжÏÏß4
/* ÅäÖÃEXTI_Line2,3,4 */
EXTI_InitStructure.EXTI_Line = EXTI_Line4;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//ÖжÏʼþ
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //ϽµÑØ´¥·¢
EXTI_InitStructure.EXTI_LineCmd = ENABLE;//ÖжÏÏßʹÄÜ
EXTI_Init(&EXTI_InitStructure);//ÅäÖÃ
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;//ÍⲿÖжÏ4
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;//ÇÀÕ¼ÓÅÏȼ¶1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//×ÓÓÅÏȼ¶2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//ʹÄÜÍⲿÖжÏͨµÀ
NVIC_Init(&NVIC_InitStructure);//ÅäÖÃ
}
通用定时器
通用定时器的时钟来源有很多,支持定时器级联等,但是最基础的就是直接使用外设总线的时钟,以TIM5为例,时钟来源是PCLK1(APB1总线时钟)=HCLK/4=42Mhz;然后,通过psc预分频器处理后得到定时器最终的计数频率。
采用APB1作为时钟来源时,有一句非常绕人的话:如果APB1的分频系数是1,也就是说APB1等于AHB时,通用定时器的时钟来源就是APB1,如果不是,比如说最常见的是APB1=AHB/4=42Mhz,分频系数是4,所以此时通用定时器的时钟来源是42*2=84Mhz。注意!!!!!!!这里并不是通用定时器最终的频率,还需经过psc预分频器处理才能得到。
实现定时器指定的周期触发中断,背景是向上/向下计数模式。需要参考如下的计算方法:
假设需要实现xms中断一次:首先可以知道的是,定时器的时钟源频率是APB1*2=84Mhz,然后经过psc的分频得到的定时器计数频率是:APB1*2/(PSC+1)。所以(PSC+1)/APB1*2就是指定时器计数跳动一次的耗时,所以溢出事件发生的总间隔是
(ARR+1)*(PSC+1)/APB1*2。这里最重要的是分频系数和重装载值在计算的时候都需要加1。我的理解是:重装载的时候本身也需要消耗一次,所以是ARR+1。
前言
硬件方面有很多东西长时间不接触容易忘记,复习的时候记录一下自己的想法,这样便于快速恢复
时钟系统
SPI的初始化中需要配置波特率分频系数,需要了解stm32的时钟和总线等基础结构知识才能搞清楚SPI的实际可配置的工作频率。此外,在网上查找有关SPI的峰值工作频率出现了很多超频会跑飞的情况,大部分的说法是虽然STM32的SPI支持很多不同的工作频率,但最终的选择会受到硬件等诸多因素的影响。
STM32之所以会有相比51单片机复杂很多的时钟系统,是因为stm32f4的结构和外设非常的丰富和复杂,不同的外设所需要的时钟频率是不一样的,为了降低功耗需要因地制宜……STM32的时钟系统有很多功能如果不是使用到的话很难记忆,所以暂时先只熟悉一下默认情况下的STMf4的时钟配置信息。
这张图应该是学习STM32必定会看到一张图,其实里面很多东西不需要记忆,就算记住了长时间不接触也会忘掉。。所以只能能大概看懂我觉得就可以了。。
虽然STM32提供了很多不同的时钟源选择,但是最常见的是 采用HSE外部高速时钟,外接一个8Mhz的晶振-》经过PLL后产生168Mhz提供给系统时钟 。SYSCLK:系统时钟,总线、外设等所有的时钟均是以它为基础的,它是整个芯片系统的脉搏。在STM32执行main函数之前会先执行SysTickInit:选择HSE并等待它稳定-》设定AHB、APB1/2分频系数-》设定PLL的参数(PLL的时钟源以及分频系数等)并且等待它稳定-》选择PLL倍频为系统的时钟源。这些步骤结束后,整个系统的时钟就完全确定了。
PLL=SYSCLK(系统时钟)=HCLK(AHB总线时钟)=168Mhz;
PCLK1(APB1总线时钟)=HCLK/4=42Mhz;
PCLK2(APB2总线时钟)=HCLK/2=84Mhz;
以上是对默认情况下STM32的时钟系统的简单回顾,其实是学习STM32时的一个很基础的内容。
SPI时钟频率问题
stm32f4系列的SPI1外设挂载在SPB2总线上,所以SPI1的基础时钟频率是84Mhz。在对SPI1的初始化中,需要设置SPI波特率发生器的预分频系数,如下图:SPI1的波特率理论上最高可达PCLK2/2即42Mhz。
该楼主发现在STM32F4的datasheet上SPI的器件特性写明最高支持到37.5Mhz:
所以对于主频仍采用168Mhz的情况下,采用4分频即21Mhz会比较稳妥。如果仍采用2分频即42Mhz就相当于是计算机的超频使用,偶尔一两次没问题,但不适合作为产品设计或者是长时间高稳定性的使用场景。所以采用21Mhz相当于是为了稳定和安全的一种妥协吧。
顺便搜索了一下为什么大部分串口的波特率都采用115200或者9600:串口的波特率越高,有效传输距离就越短,对于115200大概只有5M左右,而且波特率越大越容易出错,所以和SPI是同一个道理。串口通信的种种限制是导致它们带宽有限的主要原因吧……
SYSTICK滴答定时器
systick是一个非常简单的定时器,arm官方要求芯片中必须有这样一个定时器。它占用资源很少,一旦使能后就会一直工作,即使在睡眠模式下也正常运转。
本质上就是一个递减计数器,当递减到0时从重装载寄存器中读取新的数值继续递减;
如果使能了中断就会在每次递减到0时触发systick中断;
时钟源可以是HCLK/8或者HCLK,对于F4芯片,HCLK=168Mhz;
正点原子的延时函数没有使用systick中断,它的原理是:
初始化systick的时钟源为HCLK/8;
获取到用户想要延时的时长并根据systick的时钟源确定一个装载值;
将val当前值寄存器清空,使能systick定时器;
systick定时器加载一个值后进行递减;
程序始终监视CTRL寄存器中的countflag标志位是否为1;
如果为1就立即关闭systick定时器
外部中断
STM32F405ZGT6有7组GPIO(A-G),每组有16个GPIO口(0-15),一共144个GPIO口。外部中断先EXTI有16个(0-15)
GPIOX.0映射到EXTI0以此类推。其实用多了以后很好理解,外部可以是板上外设例如按键,也可以是网络模块,它们在某一时刻会触发电平的变化,而外部中断就可以捕获它们并执行相应的中断函数。
比较重要的一个概念是,对于EXTI0,GPIOA0-GPIOG0中同时只能有一个映射到EXTI0。
中断服务函数和中断向量表有关,我的理解是,当触发中断时,会到中断向量表中找到指定的函数地址,进而去执行相应的处理函数。因为中断向量表中只分配了7个,所以只有7个中断服务函数,如下:
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
EXTI9_5_IRQHandler
EXTI15_10_IRQHandler
void EXTIX_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//ʹÄÜSYSCFGʱÖÓ
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource4);//PE4 Á¬½Óµ½ÖжÏÏß4
/* ÅäÖÃEXTI_Line2,3,4 */
EXTI_InitStructure.EXTI_Line = EXTI_Line4;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//ÖжÏʼþ
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //ϽµÑØ´¥·¢
EXTI_InitStructure.EXTI_LineCmd = ENABLE;//ÖжÏÏßʹÄÜ
EXTI_Init(&EXTI_InitStructure);//ÅäÖÃ
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;//ÍⲿÖжÏ4
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;//ÇÀÕ¼ÓÅÏȼ¶1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//×ÓÓÅÏȼ¶2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//ʹÄÜÍⲿÖжÏͨµÀ
NVIC_Init(&NVIC_InitStructure);//ÅäÖÃ
}
通用定时器
通用定时器的时钟来源有很多,支持定时器级联等,但是最基础的就是直接使用外设总线的时钟,以TIM5为例,时钟来源是PCLK1(APB1总线时钟)=HCLK/4=42Mhz;然后,通过psc预分频器处理后得到定时器最终的计数频率。
采用APB1作为时钟来源时,有一句非常绕人的话:如果APB1的分频系数是1,也就是说APB1等于AHB时,通用定时器的时钟来源就是APB1,如果不是,比如说最常见的是APB1=AHB/4=42Mhz,分频系数是4,所以此时通用定时器的时钟来源是42*2=84Mhz。注意!!!!!!!这里并不是通用定时器最终的频率,还需经过psc预分频器处理才能得到。
实现定时器指定的周期触发中断,背景是向上/向下计数模式。需要参考如下的计算方法:
假设需要实现xms中断一次:首先可以知道的是,定时器的时钟源频率是APB1*2=84Mhz,然后经过psc的分频得到的定时器计数频率是:APB1*2/(PSC+1)。所以(PSC+1)/APB1*2就是指定时器计数跳动一次的耗时,所以溢出事件发生的总间隔是
(ARR+1)*(PSC+1)/APB1*2。这里最重要的是分频系数和重装载值在计算的时候都需要加1。我的理解是:重装载的时候本身也需要消耗一次,所以是ARR+1。
举报