STM32学习笔记SYS程序解释原子.docx

上传人:b****5 文档编号:28195377 上传时间:2023-07-09 格式:DOCX 页数:24 大小:752.14KB
下载 相关 举报
STM32学习笔记SYS程序解释原子.docx_第1页
第1页 / 共24页
STM32学习笔记SYS程序解释原子.docx_第2页
第2页 / 共24页
STM32学习笔记SYS程序解释原子.docx_第3页
第3页 / 共24页
STM32学习笔记SYS程序解释原子.docx_第4页
第4页 / 共24页
STM32学习笔记SYS程序解释原子.docx_第5页
第5页 / 共24页
点击查看更多>>
下载资源
资源描述

STM32学习笔记SYS程序解释原子.docx

《STM32学习笔记SYS程序解释原子.docx》由会员分享,可在线阅读,更多相关《STM32学习笔记SYS程序解释原子.docx(24页珍藏版)》请在冰豆网上搜索。

STM32学习笔记SYS程序解释原子.docx

STM32学习笔记SYS程序解释原子

SYS.C程序解释

#include

#include"sys.h"

//设置向量表偏移地址

//NVIC_VectTab:

基址

//Offset:

偏移量

//CHECKOK

//091207

voidMY_NVIC_SetVectorTable(u32NVIC_VectTab,u32Offset)

{

//检查参数合法性

assert_param(IS_NVIC_VECTTAB(NVIC_VectTab));

assert_param(IS_NVIC_OFFSET(Offset));

SCB->VTOR=NVIC_VectTab|(Offset&(u32)0x1FFFFF80);//设置NVIC的向量表偏移寄存器

//用于标识向量表是在CODE区还是在RAM区

}

解释:

前面两行是用来检查参数合法性,这里不作分析。

重点看第三行。

#defineNVIC_VectTab_RAM            ((u32)0x)

#defineNVIC_VectTab_FLASH          ((u32)0x08000000)

typedefstruct

{

vuc32CPUID;

vu32ICSR;

vu32VTOR;

vu32AIRCR;

vu32SCR;

vu32CCR;

vu32SHPR[3];

vu32SHCSR;

vu32CFSR;

vu32HFSR;

vu32DFSR;

vu32MMFAR;

vu32BFAR;

vu32AFSR;

}SCB_TypeDef;

 

在<<权威指南>>第一百零四页,有这么一段话:

    NVIC中有一个寄存器,称为“向量表偏移量寄存器”(在地址0xE000_ED08处),通过修改它的值就能定位向量表。

但必须注意的是:

向量表的起始地址是有要求的:

必须先求出系统中共有多少个向量,再把这个数字向上增大到是2的整次幂,而起始地址必须对齐到后者的边界上。

例如,如果一共有32个中断,则共有32+16(系统异常)=48个向量,向上增大到2的整次幂后值为64,因此地址

地址必须能被64*4=256整除,从而合法的起始地址可以是:

0x0,0x100,0x200等。

Offset:

是偏移量的计算

也就是说STM32自己有60个中断,加上CM3的16个,总共有76个中断,扩大到2的整次幂,那就是128,然后再乘以4,得到512,也就是0X200.根据这样计算,合法的偏移地址应该是0X0,0X200,0X400,0X600,0x800......

29 TBLBASE R/W 0 Table base in Code (0) or RAM 

(1) 

28:

7 TBLOFF R/W 0 Table offset value from Code region or RAM region 

屏蔽前七位0x1FFFFF80和后三位+Offset + VECTTOR就是开始地址,低7位没有用到,所以&0X80。

VTOR设置只有BIT【28:

7】,你把(u32)0x1FFFFF80二进制看看是不是【28:

7】。

 

//设置NVIC分组

//NVIC_Group:

NVIC分组0~4总共5组

//CHECKOK

//091209

voidMY_NVIC_PriorityGroupConfig(u8NVIC_Group)

{

u32temp,temp1;

temp1=(~NVIC_Group)&0x07;//取后三位

temp1<<=8;

temp=SCB->AIRCR;//读取先前的设置

temp&=0X0000F8FF;//清空先前分组

temp|=0X05FA0000;//写入钥匙

temp|=temp1;

SCB->AIRCR=temp;//设置分组

}

解释:

CM3内核支持256个中断,其中包含了16个内核中断和240个外部中断,并且具有256级的可编程中断设置。

但STM32并没有使用CM3内核的全部东西,而是只用了它的一部分。

STM32有76个中断,包括16个内核中断和60个可屏蔽中断,具有16级可编程的中断优先级。

而我们常用的就是这60个可屏蔽中断,所以我们就只针对这60个可屏蔽中断进行介绍。

在MDK内,与NVIC相关的寄存器,MDK为其定义了如下的结构体:

typedefstruct

{

vu32ISER[2];

u32RESERVED0[30];

vu32ICER[2];

u32RSERVED1[30];

vu32ISPR[2];

u32RESERVED2[30];

vu32ICPR[2];

u32RESERVED3[30];

vu32IABR[2];

u32RESERVED4[62];

vu32IPR[15];

}NVIC_TypeDef;

STM32的中断在这些寄存器的控制下有序的执行的。

了解这些中断寄存器,你才能方便的

使用STM32的中断。

下面重点介绍这几个寄存器:

ISER[2]:

ISER全称是:

InterruptSet-EnableRegisters,这是一个中断使能寄存器组。

上面说了STM32的可屏蔽中断只有60个,这里用了2个32位的寄存器,总共可以表示64个中断。

而STM32只用了其中的前60位。

ISER[0]的bit0~bit31分别对应中断0~31。

ISER[1]的bit0~27对应中断32~59;这样总共60个中断就分别对应上了。

你要使能某个中断,必须设置相应的ISER位为1,使该中断被使能(这里仅仅是使能,还要配合中断分组、屏蔽、IO口映射等设置才算是一个完整的中断设置)。

具体每一位对应哪个中断,请参考stm32f10x_nvic..h里面的第36行处。

ICER[2]:

全称是:

InterruptClear-EnableRegisters,是一个中断除能寄存器组。

该寄存器组与ISER的作用恰好相反,是用来清除某个中断的使能的。

其对应位的功能,也和ICER一样。

这里要专门设置一个ICER来清除中断位,而不是向ISER写0来清除,是因为NVIC的这些寄存器都是写1有效的,写0是无效的。

具体为什么这么设计,请看《CM3权威指南》第125页,NVIC概览一章。

ISPR[2]:

全称是:

InterruptSet-PendingRegisters,是一个中断挂起控制寄存器组。

每个位对应的中断和ISER是一样的。

通过置1,可以将正在进行的中断挂起,而执行同级或更高级别的中断。

写0是无效的。

ICPR[2]:

全称是:

InterruptClear-PendingRegisters,是一个中断解挂控制寄存器组。

其作用与ISPR相反,对应位也和ISER是一样的。

通过设置1,可以将挂起的中断接挂。

写0无效。

IABR[2]:

全称是:

ActiveBitRegisters,是一个中断激活标志位寄存器组。

对应位所代表的中断和ISER一样,如果为1,则表示该位所对应的中断正在被执行。

这是一个只读寄存器,通过它可以知道当前在执行的中断是哪一个。

在中断执行完了由硬件自动清零。

IPR[15]:

全称是:

InterruptPriorityRegisters,是一个中断优先级控制的寄存器组。

这个寄存器组相当重要!

STM32的中断分组与这个寄存器组密切相关。

IPR寄存器组由15个32bit的寄存器组成,每个可屏蔽中断占用8bit,这样总共可以表示15*4=60个可屏蔽中断。

刚好和STM32的可屏蔽中断数相等。

IPR[0]的[31~24],[23~16],[15~8],[7~0]分别对应中中断3~0,依次类推,总共对应60个外部中断。

而每个可屏蔽中断占用的8bit并没有全部使用,而是只用了高4位。

这4位,又分为抢占优先级和子优先级。

抢占优先级在前,子优先级在后。

而这两个优先级各占几个位又要根据SCB->AIRCR中中断分组的设置来决定。

这里简单介绍一下STM32的中断分组:

STM32将中断分为5个组,组0~4。

该分组的设置是由SCB->AIRCR寄存器的bit10~8来定义的。

具体的分配关系如下表所示:

优先级具体详解请详见CM3权威指南第108页。

 

通过这个表,我们就可以清楚的看到组0~4对应的配置关系,例如组设置为3,那么此时所有的60个中断,每个中断的中断优先寄存器的高四位中的最高3位是抢占优先级,低1位是响应优先级。

每个中断,你可以设置抢占优先级为0~7,响应优先级为1或0。

抢占优先级的级别高于响应优先级。

而数值越小所代表的优先级就越高。

MY_NVIC_PriorityGroupConfig(u8NVIC_Group):

该函数的参数NVIC_Group为要设置的分组号,可选范围为0~4,总共5组。

如果参数非法,将可能导致不可预料的结果。

temp1=(~NVIC_Group)&0x07;//取后三位

假设分组为第3组,则NVIC_Group=3=00000011,~NVIC_Group=11111100.

~NVIC_Group&0x07=00000100.

赋给temp1=00000000000000000000000000000100(u32)

temp1<<=8;

此时左移8位后temp1=00000000000000000000010000000000

temp=SCB->AIRCR;//读取先前的设置

temp&=0X0000F8FF;//清空先前分组

设先前设置为:

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

与:

00000000000000001111100011111111相与。

=0x0000F8FF=00000000000000001111100011111111

使得SCB->AIRCR[10:

8]为零,从而就达到分组清零的目的。

temp|=0X05FA0000;//写入钥匙

通过上面的介绍我们知道SCB->AIRCR的修改需要通过在高16位写入0X05FA这

个密钥才能修改的,故在设置AIRCR之前,应该把密钥加入到要写入的内容的高16位,以保证能正常的写入AIRCR。

在修改AIRCR的时候,我们一般采用读->改->写的步骤,来实现不改变AIRCR原来的其他设置。

以上就是MY_NVIC_PriorityGroupConfig函数设置中断优先级分组的思路。

temp|=temp1;

把上面左移8位得到的分组赋给temp,即SCB->AIRCR[10:

8]为100,即得到上面的假设第3组。

SCB->AIRCR=temp;//设置分组

最后把这个分组写入AIRCR中,完成分组的设置。

 

//设置NVIC

//NVIC_PreemptionPriority:

抢占优先级

//NVIC_SubPriority:

响应优先级

//NVIC_Channel:

中断编号

//NVIC_Group:

中断分组0~4

//注意优先级不能超过设定的组的范围!

否则会有意想不到的错误

//组划分:

//组0:

0位抢占优先级,4位响应优先级

//组1:

1位抢占优先级,3位响应优先级

//组2:

2位抢占优先级,2位响应优先级

//组3:

3位抢占优先级,1位响应优先级

//组4:

4位抢占优先级,0位响应优先级

//NVIC_SubPriority和NVIC_PreemptionPriority的原则是,数值越小,越优先

//CHECKOK

//100329

voidMY_NVIC_Init(u8NVIC_PreemptionPriority,u8NVIC_SubPriority,u8NVIC_Channel,u8NVIC_Group)

{

u32temp;

u8IPRADDR=NVIC_Channel/4;//每组只能存4个,得到组地址

u8IPROFFSET=NVIC_Channel%4;//在组内的偏移

IPROFFSET=IPROFFSET*8+4;//得到偏移的确切位置

MY_NVIC_PriorityGroupConfig(NVIC_Group);//设置分组

temp=NVIC_PreemptionPriority<<(4-NVIC_Group);

temp|=NVIC_SubPriority&(0x0f>>NVIC_Group);

temp&=0xf;//取低四位

if(NVIC_Channel<32)NVIC->ISER[0]|=1<

elseNVIC->ISER[1]|=1<<(NVIC_Channel-32);

NVIC->IPR[IPRADDR]|=temp<

解释:

这个函数是NVIC设置函数MY_NVIC_Init,该函数有4个参数,分别为:

NVIC_PreemptionPriority、NVIC_SubPriority、NVIC_Channel、NVIC_Group。

第一个参数NVIC_PreemptionPriority为中断抢占优先级数值,第二个参数NVIC_SubPriority为中断子优先级数值,前两个参数的值必须在规定范围内,否则也可能产生意想不到的错误。

第三个参数NVIC_Channel为中断的编号(范围为0~59),最后一个参数NVIC_Group为中断分组设置(范围为0~4)。

IPR[0]的[31~24],[23~16],[15~8],[7~0]分别对应中中断3~0

IPR[1]的[31~24],[23~16],[15~8],[7~0]分别对应中中断7~4

.

.

.

IPR[14]的[31~24],[23~16],[15~8],[7~0]分别对应中中断59~56

u8IPRADDR=NVIC_Channel/4;//每组只能存4个,得到组地址

假如是中断7,则IPRADDR=1,即我们用的是IPR[1]。

u8IPROFFSET=NVIC_Channel%4;//在组内的偏移

中断7对应的偏移是3。

IPROFFSET=IPROFFSET*8+4;//得到偏移的确切位置

那就是到[31~24]。

注意,每个中断的8位只用了它的高4位,所以是+4.其实是[31~28].

MY_NVIC_PriorityGroupConfig(NVIC_Group);//设置分组

参照前面的分析。

temp=NVIC_PreemptionPriority<<(4-NVIC_Group);

假设设置分组3,假如NVIC_PreemptionPriority=00000011,左移1位,temp=00000000000000000000000000000110.

temp|=NVIC_SubPriority&(0x0f>>NVIC_Group);

00001111右移3位是:

00000001,然后与00000001(1位相应优先级数值设为1),得00000001,然后与temp相或为:

00000111.temp=00000000000000000000000000000111.

temp&=0xf;//取低四位

此时temp=00000000000000000000000000000111.

if(NVIC_Channel<32)NVIC->ISER[0]|=1<

elseNVIC->ISER[1]|=1<<(NVIC_Channel-32);

使能中断位,这个很好理解。

NVIC->IPR[IPRADDR]|=temp<

假如还是中断7,则IPROFFSET=28,则temp左移28位为

01110000000000000000000000000000,此时IPR[1]的[31~28]位是0111.

 

//外部中断配置函数

//只针对GPIOA~G;不包括PVD,RTC和USB唤醒这三个

//参数:

GPIOx:

0~6,代表GPIOA~G;BITx:

需要使能的位;TRIM:

触发模式,1,下降沿;2,上升沿;3,任意电平触发

//该函数一次只能配置1个IO口,多个IO口,需多次调用

//该函数会自动开启对应中断,以及屏蔽线

//待测试...

voidEx_NVIC_Config(u8GPIOx,u8BITx,u8TRIM)

{

u8EXTADDR;

u8EXTOFFSET;

EXTADDR=BITx/4;//得到中断寄存器组的编号

EXTOFFSET=(BITx%4)*4;

RCC->APB2ENR|=0x01;//使能io复用时钟

AFIO->EXTICR[EXTADDR]&=~(0x000F<

AFIO->EXTICR[EXTADDR]|=GPIOx<

//自动设置

EXTI->IMR|=1<

//EXTI->EMR|=1<

if(TRIM&0x01)EXTI->FTSR|=1<

if(TRIM&0x02)EXTI->RTSR|=1<

}

解释:

该函数为Ex_NVIC_Config,该函数有3个参数:

GPIOx为GPIOA~G(0~6),在sys.h里面有定义。

代表要配置的IO口。

BITx则代表这个IO口的第几位。

TRIM为触发方式,低2位有效(0x01代表下降触发;0x02代表上升沿触发;0x03代表任意电平触发)。

STM32的19个外部中断为:

线0~15:

对应外部IO口的输入中断。

因为STM32任何一个IO口都可以配置成中断输入口,但是IO口的数目远大于中断线数(16个)。

于是STM32就这样设计,GPIOA~GPIOG的[15:

0]分别对应中断线15~0。

这样每个中断线对应了最多7个IO口,以线0为例:

它对应了GPIOA.0、PIOB.0、GPIOC.0、GPIOD.0、GPIOE.0、GPIOF.0、GPIOG.0。

而中断线每次只能连接到1个IO口上,这样就需要EXTICR来决定对应的中断线配置到哪个GPIO上了。

EXTICR[0]的[3:

0]只能存放GPIOA.0~G.0--------------线0;

EXTICR[0]的[7:

4]只能存放GPIOA.1~G.1--------------线1;

.

.

.

EXTICR[3]的[15:

12]只能存放GPIOA.15~G.15-----------线15;

EXTADDR=BITx/4;//得到中断寄存器组的编号

假设是GPIOA的第5口,则EXTADDR=1,即选择EXTICR[1]。

EXTOFFSET=(BITx%4)*4;

假设是GPIOA的第5口,则EXTOFFSET=4,即选择EXTICR[1]的[7:

4].

RCC->APB2ENR|=0x01;//使能io复用时钟

这个很容易理解。

AFIO->EXTICR[EXTADDR]&=~(0x000F<

0x000F左移4位是0x00F0,取个反即0XFF0F,正好是[7:

4]清零。

AFIO->EXTICR[EXTADDR]|=GPIOx<

GPIOA=0,则左移4位,还是0,对应[7:

4]=0000,正好选择PA口;

GPIOB=1,左移4位,就是00010000,对应[7:

4]=0001,正好选择PB口。

IMR:

中断屏蔽寄存器。

这是一个32寄存器。

但是只有前19位有效。

当位x设置为1时,则开启这个线上的中断,否则关闭该线上的中断。

EMR:

事件屏蔽寄存器,同IMR,只是该寄存器是针对事件的屏蔽和开启。

EXTI->IMR|=1<

把位和中断线对应起来,这个容易理解。

if(TRIM&0x01)EXTI->FTSR|=1<

如果是下降沿,则把FTSR对应位的值赋1.

if(TRIM&0x02)EXTI->RTSR|=1<

如果是上升沿,则把RTSR对应位的值赋1.

 

//不能在这里执行所有外设复位!

否则至少引起串口不工作.

//把所有时钟寄存器复位

voidMYRCC_DeInit(void)

{

RCC->APB1RSTR=0x00000000;//复位结束

RCC->APB2RSTR=0x00000000;

RCC->AHBENR=0x00000014;//睡眠模式闪存和SRAM时钟使能.其他关闭.

RCC->APB2ENR=0x00000000;//外设时钟关闭.

RCC->APB1ENR=0x00000000;

RCC->CR|=0x00000001;//使能内部高速时钟HSION

RCC->CFGR&=0xF8FF0000;//复位SW[1:

0],HPRE[3:

0],PPRE1[2:

0],PPRE2[2:

0],ADCPRE[1:

0],MCO[2:

0]

RCC->CR&=0xFEF6FFFF;//复位HSEON,CSSON,PLLON

RCC->CR&=0xFFFBFFFF;//复位HSEBYP

RCC->CFGR&=0xFF80FFFF;//复位PLLSRC,PLLXTPRE,PLLMUL[3:

0]andUSBPRE

RCC->CIR=0x00000000;//关闭所有中断

//配置向量表

#ifdefVECT_TAB_RAM

MY_NVIC_SetVectorTable(NVIC_VectTab_RAM,0x0);

#else

MY_NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x0);

#endif

}

解释:

关于复位用到得一系列寄存器参考“免积分-STM32-中文资料参考”的第51页后。

 

/

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 医药卫生 > 中医中药

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1