IOT安全二再探stm32

admin 2024-09-18 08:57:07 0

扫一扫用手机浏览

文章目录 [+]


上一篇文章中我们实现了stm32的gpio操作,此次我们将更进一步,继续完成对标题的复现。

经由过程之前的进修信任年夜家都掌握了进修浏览官方手册的才能,在本篇中为了勤俭篇幅不再进行对存放器的位、存放器代码编写的具体阐明,而是侧重讲授原理和背后的常识。

IOT安全二再探stm32
(图片来源网络,侵删)

简单的时钟树

对付任何的计算机来说,时钟都是其至关紧张的一环,在任何stm32的法式启动前,都必需要进行时钟的初始化操作,好在stm32的时钟较为简单,此次我们的目的便是编写我们本身的时钟初始化函数,来替换失落stm32cube为我们天生的代码。我们先来看一下手册中给出的时钟树图:


可以看到时钟树整体并不繁杂,然则由于stm32提供了包含usb、dma在内的多种功效,以是会用到多种频率分歧的时钟旌旗灯号,是以图中有许多涉及到时钟频率缩放的部件,这是我们要存眷的一个点,此外,可以看到外设部门险些都是有sysclk进行简单变化后获得的,以是sysclk也是我们存眷的重点。

从最左边开端,起首是osc_in、osc_out、osc32_in、osc32_out、mco五部门引脚,此中带osc的我们在上一篇文章中应用stm32cube主动天生过,osc代表是来自外部的晶振,晶振经由过程高频震动来提供有纪律的旌旗灯号,也就形成了时钟旌旗灯号。现实上,在芯片内部也有晶振来提供时钟旌旗灯号,但因为繁杂缘故原由,芯片内部每每难以集成高频率、高精度的晶振,无法施展芯片的全体机能,这就必要我们在外部添加高频高精度的晶振来提供时钟旌旗灯号。

mco是时钟输出引脚,可以将时钟旌旗灯号输出,经由过程设置装备摆设响应的存放器即可实现对分歧时钟旌旗灯号的输出。我们临时用不到这个功效。

从osc接入便是真正的时钟源了,外部晶振带来的时钟有两种:

HSE是高速外部时钟的简写,晶振频率可取规模为4~16MHzLSE是低速外部时钟的简写,一样平常采纳32.768KHz

上面我们也说了,除了外部接入的,还有内部集成的,也是两种:

HSI是高速内部时钟的简写,由内部RC振荡器发生,频率为8MHz,异常异常的不稳固。LSI是低速内部时钟的简写,同样由内部RC振荡器发生,频率年夜约为40KHz

起首来看下半部门,对付LSI来说,它分了两条线

IWDG(自力看门狗)的时钟源。用来作为RTC(即及时时钟)的可选项,在时钟树中,梯形表现可选项,上图中可以看到RTC有三个可选项。

这里简单说一下及时时钟是个啥,它“及时”的意思是它失落电后还继续运行,不受限定,除此之外,他便是个通俗的计时器。功效很简单,但它的电路很巧妙,有兴致的同窗可以查查看看,它常常被用来实现光阴戳、记载本地光阴等功效。

对付LSE来说,它只是作为RTC的一个可选项,除此之外,HSE也作为RTC此中一个选项,不外其频率必要除以128,将本来高频的旌旗灯号,经由过程电气元件,成倍的低落它们的频率,终极将本来的频率低落到我们想要的年夜小,也便是”分频“的意思,这是整个时钟树中最常呈现的部件,图中的prescaler是“预分频”的意思,便是在某个必要的旌旗灯号前对其输入旌旗灯号进行“分频”。


再来看上半部门,起首便是pll这个观点,它的全称是锁相环倍频输出,所谓”锁相“即坚持处置后的旌旗灯号与本来的基准旌旗灯号的相位同等,而”倍频“便是说要进步频率。从图中可以看到,HSE颠末处置后到PLLXTPRE部件,然后和HSI配合构成pllsrc的候选,终极颠末PLLMUL放年夜频率(最高可达16倍),输出pll时钟,在其余材料中每每把pll时钟和上面提到的四种时钟配合作为stm32的时钟源,然则我们可以看到,现实上pll时钟终极照样来自HSE和HSI,pll在整个时钟树中最年夜的作用便是拉高了旌旗灯号的频率,HSE固然名字中带有高速,但对付某些装备(好比USB)来说照样远远不够用的,以是必要pll来倍频。

再今后便是sysclk,即体系时钟,听这名字就知道它的位置,根本上所有的外设的时钟旌旗灯号,都是经由过程sysclk的分频获得的。它同样有三个候选人,分离是HSI、pll时钟、HSE,从图中可以看到它最高可以到达72MHZ。

而下面的css是体系时钟的监督器,由于体系时钟和外设互相关注,一旦产生问题将导致整个体系的奔溃,以是设置了css,当HSE失效时(HSE究竟是来自外部的晶振,失效的可能性要弘远于HSI),它会主动让体系时钟的起源切换为HSI,从而保证体系的稳固。


再向左看就到了外设部门的,usb很简单,只是颠末了分频就获得了终极的旌旗灯号,别的的主要有两条“线”

AHB,全称是Advanced High performance Bus,是一种总线,有其余部件挂载在上面,它频率高速率快,是以是“高机能”的总线,有点相似计算机的北桥,主要衔接高速装备,好比内存、dma等。它是支撑多主装备的总线,可以有多个主模块,信息由主模块流向从模块。ABP,全称是Advanced Peripheral Bus,它相似计算机的南桥,机能、频率较低,衔接的都是SPI,I2C等装备,可以看到,APB分为了ABP1、ABP2两个装备,这是由于它不像AHB那样支撑多主模块,它的主模块便是ABP,ABP再有两个从模块ABP1、ABP2,这两个从模块分离卖力分歧的装备挂载。

末了来看看mco,可以看到它起源自HSE、HSI、PLL、SYSCLK,我们可以经由过程设置存放器,来让它输出这四种时钟旌旗灯号。

到此为止我们就梳理完了整个stm32的时钟树,万万不要忘了上一篇文章中我们说过的紧张的一点:stm32的统统都离不开存放器,时钟也是如斯,查阅手册我们可以看到时钟相关的存放器。


RCC_CR、RCC_CFGR是此中的症结,它们是所有部件的“头头”,它们来节制诸如HSE、PLL等装备的启动与否,分频、倍频的年夜小,此外,APB、AHB等等都有响应的存放器,我们必要依照前一篇文章的方法,把他们一切写出来。这里由于代码是在是太多了,以是我只放出我写的一小部门

#ifndef __SysInt#define __SysInt#define PERIPHY_BASE ((uint32_t)0x40000000)#define ABP1PERIPHY PERIPHY_BASE#define ABP2PERIPHY (PERIPHY_BASE + 0x10000)#define AHBPERIPHY_BASE (PERIPHY_BASE + 0x20000)#define RCC_BASE (AHBPERIPHY_BASE + 0x1000)#define __IO volatiletypedef unsigned int uint32_t;void SetClockConfig(void);typedef struct { uint32_t HSION :1; uint32_t HSIRDY :1; uint32_t Reserved0 :1; uint32_t HSITRIM :5; uint32_t HSICAL :8; uint32_t HSEON :1; uint32_t HSERDY :1; uint32_t HSEBYP :1; uint32_t CSSON :1; uint32_t Reserved1 :4; uint32_t PLLON :1; uint32_t PLLRDY :1; uint32_t Reserved2 :6;}CR_Bit;typedef struct { uint32_t SW :2; uint32_t SWS :2; uint32_t HPRE :4; uint32_t PPRE1 :3; uint32_t PPRE2 :3; uint32_t ADCPRE :2; uint32_t PLLSRC :1; uint32_t PLLXTPRE :1; uint32_t PLLMUL :4; uint32_t USBPRE :1; uint32_t Reverse0 :1; uint32_t MCO :3; uint32_t Reverse1 :5;}CFGR_Bit;typedef struct { uint32_t LSIRDYF : 1; uint32_t LSERDYF : 1; uint32_t HSIRDYF : 1; uint32_t HSERDYF : 1; uint32_t PLLRDYF : 1; uint32_t Reverse0 : 2; uint32_t CSSF : 1; uint32_t LSIRDYIE : 1; uint32_t LSERDYIE : 1; uint32_t HSIRDYIE : 1; uint32_t HSERDYIE : 1; uint32_t PLLRDYIE : 1; uint32_t Reverse1 : 3; uint32_t LSIRDYC : 1; uint32_t LSERDYC : 1; uint32_t HSIRDYC : 1; uint32_t HSERDYC : 1; uint32_t PLLRDYC : 1; uint32_t Reverse2 : 2; uint32_t CSSC : 1; uint32_t Reverse3 : 8;}CIR_Bit;typedef struct { uint32_t LSION : 1; uint32_t LSIRDY : 1; uint32_t Reverse0 : 14; uint32_t Reverse1 : 8; uint32_t RMVF : 1; uint32_t Reverse2 : 1; uint32_t PINRSTF : 1; uint32_t PORRSTF : 1; uint32_t SFTRSTF : 1; uint32_t IWDGRSTF : 1; uint32_t WWDGRSTF : 1; uint32_t LPWRRSTF : 1;}CSR_Bit;typedef struct{ __IO CR_Bit CR; __IO CFGR_Bit CFGR; __IO CIR_Bit CIR; __IO APB2RSTR_Bit APB2RSTR; __IO APB1RSTR_Bit APB1RSTR; __IO AHBENR_Bit AHBENR; __IO APB2ENR_Bit APB2ENR; __IO APB1ENR_Bit APB1ENR; __IO BDCR_Bit BDCR; __IO CSR_Bit CSR;}RCC_Type;

年夜概300多行布局体界说完毕后,我们就可以开端写时钟的初始化代码了,现实上,只要明确了逻辑,这部门代码并不难写。

起首我们必要明白的第一件事:谁才是最先启动的时钟。显然外部晶振是不克不及担此重担的,你总不克不及外部晶振不启动你就不干活了对吧。以是启动的必然是HSI和LSI,只有他们干活了我们能力继续事情,经由过程RCC存放器,我们起首进行HSI使能,然后要写个while轮回,一直期待直到HSI预备停当能力开端下一步,代码如下

//使能HSI RCC->CR.HSION = 1; //期待HSI停当 while(!RCC->CR.HSIRDY);

接着便是启动外部晶振了,和上面的操作相似,我们这里同样while轮回写死。

//使能HSE RCC->CR.HSEON = 1; //期待HSE停当 while(!RCC->CR.HSERDY);

接下来便是对各个装备进行简单的分频、倍频设置,我们依据本身的现实环境进行调整即可,我写了具体的注释,代码如下:

//调整低速APB预分频(APB1)为2分频 //调整高速APB预分频(APB2)为不分频 //调整AHB预分频为不分频 //调整ADC预分频为2分频 RCC->CFGR.PPRE1 = 4; RCC->CFGR.PPRE2 = 0; RCC->CFGR.HPRE = 0; RCC->CFGR.ADCPRE = 0; //调整PLL输入时钟源为HSE RCC->CFGR.PLLSRC = 1; //调整PLL倍频系数为9 RCC->CFGR.PLLMUL = 7; //使能PLL时钟 RCC->CR.PLLON = 1; //期待PLL时钟停当 while(!RCC->CR.PLLRDY); //调整SYSCLK为PLL RCC->CFGR.SW = 2; //期待SYSCLK为PLL while(RCC->CFGR.SWS!=2);

到此,我们就简单了实现了我们本身的时钟初始化,对应如下stm32cube主动天生的函数,替换即可。

RCC_DeInit(); //初始化RCCRCC_HSEConfig(RCC_HSE_ON); //设置HSEHSEStartUpStatus = RCC_WaitForHSEStartUp(); //期待HSE停当RCC_HCLKConfig(); //设置AHB时钟RCC_PCLK2Config(); //设置高速AHB时钟RCC_PCLK1Config(); //设置低速AHB时钟RCC_PLLConfig(); //设置PLLRCC_PLLCmd(ENABLE); //启用pllwhile(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) //期待PLL事情RCC_SYSCLKConfig(); //设置体系时钟while(RCC_GetSYSCLKSource() != 0x08) //断定是否体系时钟源是否为PLLRCC_APB2PeriphClockCmd()/RCC_APB1PeriphClockCmd() //启动外设时钟

有些繁杂的中止

在标题中使用usart传输flag,但usart作为一种通讯手腕,它终极照样依附于中止机制,以是我们必需得先研讨明确stm32的中止机制,能力更好的进修usart的使用。对付stm32来说,因为它依附于ARM的内核,以是它的中止机制与ARM内核互相关注,以M3内核为例:

m3支撑256个中止,而stm32在根基长进行了删减,使用了84个中止;m3支撑256级的中止优先级,可以做到每一个中止都有一个优先级,而stm32只保存了16级

我们在上一篇文章中,应用stm32cube主动将此中的一个GPIO引脚“进化”为了中止exti1,年夜概分成了三步

初始化exti初始化NVIC编写中止服务函数

第一步中的exti是外部中止的意思,在stm32的84个中止中,有64个属于外部中止,严厉来说,stm32每一个GPIO引脚都可以“进化”为外部中止,然则它划定中止以组为单元,好比PA1、PB1、PC1、PD1、PE1、PF1、PG1为一个组,图中的exti1就表现这是第一组中止组,每一个组统一光阴只能有一个代表进场,一旦选择了PA1,那么剩下的组员就不克不及再“进化”了。

所谓“进化”,现实上便是将响应的端口映射到响应的外部变乱,端口呈现变化就会触发对应的外部变乱,进而到外部变乱的中止服务法式。可以参考下图中exit的衔接方式,每一组的引脚衔接到一个选择器上,然后交由对应的EXTI处置


第二步是初始化NVIC,NVIC是嵌套向量中止节制器的意思,他是所有中止的归属,不管是上面的exti照样usart、usb等等终极都是NVIC,然后由NVIC通报给cpu处置,末了经由过程flash中止向量表肯定对应中止对应的函数地址,跳转到对应的中止处置函数。NVIC提供了 43个中止通道(通道以提前分派好,好比exti1便是第7个通道,查阅手册即可),个中止通道都具备本身的中止优先级节制字节PRI_n,每4个通道的8位中止优先级节制字组成一个32位的优先级存放器,经由过程设置存放器即可转变对应的中止优先级,当有两个以上的中止到来时,NVIC会依据中止的优先级进行抉择。

但现实上,stm32固然每个通道提供了8位中止优先级,但因为它自己只支撑16种优先级,以是只有高4位是有用的,而高4位也颇有讲求,分为了抢占式优先级和相应优先级,高位为抢占式优先级,低位为相应式优先级(可以随意率性位数,好比1和3、2和2、4和0),他们的关系有点繁杂:

具有高抢占式优先级的中止可以在具有低抢占式优先级的中止处置进程中被相应,也便是实现了中止嵌套。同样的抢占优先级的中止假如同时到来,就依据相应优先级断定,实现了中止的判优

主动天生的代码如下:

NVIC_InitTypeDefNVIC_InitStructure;NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //选择中止分组2NVIC_InitStructure.NVIC_IRQChannel= EXTI1_IRQChannel; //选择exti1NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 0; //抢占式中止优先级设置为0NVIC_InitStructure.NVIC_IRQChannelSubPriority= 0; //相应式中止优先级设置为0NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //使能中止

第三步是编写中止服务函数,这一步就没啥好说的了,写就完事了,独一要注意的是,因为中止通道中EXTI0 – EXTI4这5个外部中止在分歧的通道中,以是有着本身的零丁的中止相应函数,EXTI5-9共用一个通道,也就只有一个中止相应函数,EXTI10-15也是共用一个中止相应函数。

1 void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource) 2 { 3 uint32_t tmp = 0x00; 4 /* Check the parameters */ 5 assert_param(IS_GPIO_EXTI_PORT_SOURCE(GPIO_PortSource)); //assert_param是对参数进行有用性反省 6 assert_param(IS_GPIO_PIN_SOURCE(GPIO_PinSource)); 7 8 tmp = ((uint32_t)0x0F) << (0x04 * (GPIO_PinSource & (uint8_t)0x03)); 9 AFIO->EXTICR[GPIO_PinSource >> 0x02] &= ~tmp;10 AFIO->EXTICR[GPIO_PinSource >> 0x02] |= (((uint32_t)GPIO_PortSource) << (0x04 * (GPIO_PinSource & (uint8_t)0x03)));11 }

上述主动天生的代码是将PD11,PD12映射到外部变乱上,9、10行是症结,经由过程与或操作,将存放器的值改为对应的值,进而实现目标,假如你已经按上一篇文章中所说的将GPIO及中止的存放器全体写作布局体的话,只必要查看手册将响应的布局体赋值即可。

上述中止都是exti的内容,除了exti,还有usart等一堆的中止,这些中止不必要和外部变乱映射即可使用,也便是改写了第一步,起首打开对应中止的,再交由NVIC,终极跳转至对应的处置函数。


usart原理或许有些繁杂,但单纯去写一个带有usart的代码却异常容易,这里我们就不再使用存放器做操作了,我们直接采纳中止的库函数来进行操作。我们在stm32cube中开启usart功效,天生的代码中会添加usart库,里面提供了数据发送、数据接管等功效,这些不是我们本节的重点,主要先来看看中止部门

HAL_UART_TxHalfCpltCallback(); //一折半据发送完成后的回调函数。HAL_UART_TxCpltCallback(); //全体数据发送完成后的回调函数HAL_UART_RxHalfCpltCallback(); //一折半据接管完成后的回调函数。HAL_UART_RxCpltCallback(); //全体数据接管完成后的回调函数HAL_UART_ErrorCallback(); //传输进程中呈现差错时的回调函数。

我们只必要编写这些回调函数即可实现对应的功效,其他的诸如usb、dma等等也是相似,用起来相称便利。

小结

到此我们已经实现了标题中年夜部门内容,末了便是usart的数据传输了,下一篇文章中我们将重点讨论usart的原理与使用,并以usart为动身点,再来看看其他stm32有趣的处所。

相关文章

清苑新能源车,引领绿色出行新潮流

随着全球气候变化和能源危机的日益严峻,绿色出行已成为全球共识。作为我国新能源产业的佼佼者,清苑新能源车凭借其卓越的性能和环保理念,...

家电资讯 2024-12-29 阅读0 评论0