您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 质量控制/管理 > 第4章STM32-开发基础知识总结
STM32开发基础知识•MDK下C语言基础复习•STM32系统架构•STM32时钟系统•端口复用和重映射•STM32NVIC中断管理•MDK中寄存器地址名称映射分析•MDK固件库快速开发技巧MDK下C语言基础复习位操作位操作在单片机开发中的一些实用技巧1)不改变其他位的值的状况下,对某几个位进行设值。这个场景单片机开发中经常使用,方法就是先对需要设置的位用&操作符进行清零操作,然后用|操作符设值。比如我要改变GPIOA的状态,可以先对寄存器的值进行&清零操作GPIOA-CRL&=0XFFFFFF0F;//将第4-7位清0然后再与需要设置的值进行|或运算GPIOA-CRL|=0X00000040;//设置相应位的值,不改变其他位的值移位操作提高代码的可读性移位操作在单片机开发中也非常重要,下面让我们看看固件库的GPIO初始化的函数里面的一行代码GPIOx-BSRR=(((uint32_t)0x01)pinpos);这个操作就是将BSRR寄存器的第pinpos位设置为1,为什么要通过左移而不是直接设置一个固定的值呢?其实,这是为了提高代码的可读性以及可重用性。这行代码可以很直观明了的知道,是将第pinpos位设置为1。如果你写成GPIOx-BSRR=0x0030;这样的代码就不好看也不好重用了。类似这样的代码很多:GPIOA-ODR|=15;//PA.5输出高,不改变其他位这样我们一目了然,5告诉我们是第5位也就是第6个端口,1告诉我们是设置为1了。uint8_t/uint16_t/uint32_t/uint64_t是什么数据类型这些数据类型是C99中定义的,具体定义在:/usr/include/stdint.hISOC99:7.18Integertypesstdint.h~取反操作使用技巧SR寄存器的每一位都代表一个状态,某个时刻我们希望去设置某一位的值为0,同时其他位都保留为1,简单的作法是直接给寄存器设置一个值:TIMx-SR=0xFFF7;这样的作法设置第3位为0,但是这样的作法同样不好看,并且可读性很差。看看库函数代码中怎样使用的:TIMx-SR=(uint16_t)~TIM_FLAG;而TIM_FLAG是通过宏定义定义的值:#defineTIM_FLAG_Update((uint16_t)0x0001)#defineTIM_FLAG_CC1((uint16_t)0x0002)看这个应该很容易明白,可以直接从宏定义中看出TIM_FLAG_Update就是设置的第0位了,可读性非常强。define宏定义define是C语言中的预处理命令,它用于宏定义,可以提高源代码的可读性,为编程提供方便。常见的格式:#define标识符字符串“标识符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等。例如:#defineSYSCLK_FREQ_72MHz72000000定义标识符SYSCLK_FREQ_72MHz的值为72000000。ifdef条件编译单片机程序开发过程中,经常会遇到一种情况,当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。条件编译命令最常见的形式为:#ifdef标识符程序段1#else程序段2#endif它的作用是:当标识符已经被定义过(一般是用#define命令定义),则对程序段1进行编译,否则编译程序段2。其中#else部分也可以没有,即:#ifdef程序段1#endif这个条件编译在MDK里面是用得很多的,在stm32f10x.h这个头文件中经常会看到这样的语句:#ifdefSTM32F10X_HD大容量芯片需要的一些变量定义#end而STM32F10X_HD则是我们通过#define来定义的。extern变量申明C语言中extern可以置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。这里面要注意,对于extern申明变量可以多次,但定义只有一次。在我们的代码中你会看到看到这样的语句:externu16USART_RX_STA;这个语句是申明USART_RX_STA变量在其他文件中已经定义了,在这里要使用到。所以,你肯定可以找到在某个地方有变量定义的语句:u16USART_RX_STA;的出现。下面通过一个例子说明一下使用方法。引出IO口在Main.c定义的全局变量id,id的初始化都是在Main.c里面进行的。Main.c文件u8id;//定义只允许一次main(){id=1;printf(d%,id);//id=1test();printf(d%,id);//id=2}但是我们希望在test.c的changeId(void)函数中使用变量id,这个时候我们就需要在test.c里面去申明变量id是外部定义的了,因为如果不申明,变量id的作用域是到不了test.c文件中。看下面test.c中的代码:externu8id;//申明变量id是在外部定义的,申明可以在很多个文件中进行voidtest(void){id=2;}在test.c中申明变量id在外部定义,然后在test.c中就可以使用变量id了。对于extern申明函数在外部定义的应用,这里我们就不多讲解了。typedef类型别名typedef用于为现有类型创建一个新的名字,或称为类型别名,用来简化变量的定义。typedef在MDK用得最多的就是定义结构体的类型别名和枚举类型了。struct_GPIO{__IOuint32_tCRL;__IOuint32_tCRH;…};定义了一个结构体GPIO,这样我们定义变量的方式为:struct_GPIOGPIOA;//定义结构体变量GPIOA但是这样很繁琐,MDK中有很多这样的结构体变量需要定义。这里我们可以为结体定义一个别名GPIO_TypeDef,这样我们就可以在其他地方通过别名GPIO_TypeDef来定义结构体变量了。方法如下:typedefstruct{__IOuint32_tCRL;__IOuint32_tCRH;…}GPIO_TypeDef;Typedef为结构体定义一个别名GPIO_TypeDef,这样我们可以通过GPIO_TypeDef来定义结构体变量:GPIO_TypeDef_GPIOA,_GPIOB;这里的GPIO_TypeDef就跟struct_GPIO是等同的作用了。这样是不是方便很多?结构体经常很多用户提到,他们对结构体使用不是很熟悉,但是MDK中太多地方使用结构体以及结构体指针,这让他们一下子摸不着头脑,学习STM32的积极性大大降低,其实结构体并不是那么复杂,这里我们稍微提一下结构体的一些知识,还有一些知识我们会在下一节的“寄存器地址名称映射分析”中讲到一些。声明结构体类型:Struct结构体名{成员列表;}变量名列表;例如:StructU_TYPE{IntBaudRateIntWordLength;}usart1,usart2;在结构体申明的时候可以定义变量,也可以申明之后定义,方法是:Struct结构体名字结构体变量列表;例如:structU_TYPEusart1,usart2;结构体成员变量的引用方法是:结构体变量名字.成员名比如要引用usart1的成员BaudRate,方法是:usart1.BaudRate;结构体指针变量定义也是一样的,跟其他变量没有啥区别。例如:structU_TYPE*usart3;//定义结构体指针变量usart1;结构体指针成员变量引用方法是通过“-”符号实现,比如要访问usart3结构体指针指向的结构体的成员变量BaudRate,方法是:Usart3-BaudRate;上面讲解了结构体和结构体指针的一些知识,其他的什么初始化这里就不多讲解了。讲到这里,有人会问,结构体到底有什么作用呢?为什么要使用结构体呢?下面我们将简单的通过一个实例回答一下这个问题。在我们单片机程序开发过程中,经常会遇到要初始化一个外设比如串口,它的初始化状态是由几个属性来决定的,比如串口号,波特率,极性,以及模式。对于这种情况,在我们没有学习结构体的时候,我们一般的方法是:voidUSART_Init(u8usartx,u32u32BaudRate,u8parity,u8mode);这种方式是有效的同时在一定场合是可取的。但是试想,如果有一天,我们希望往这个函数里面再传入一个参数,那么势必我们需要修改这个函数的定义,重新加入字长这个入口参数。于是我们的定义被修改为:voidUSART_Init(u8usartx,u32BaudRate,u8parity,u8mode,u8wordlength);但是如果我们这个函数的入口参数是随着开发不断的增多,那么是不是我们就要不断的修改函数的定义呢?这是不是给我们开发带来很多的麻烦?那又怎样解决这种情况呢?这样如果我们使用到结构体就能解决这个问题了。我们可以在不改变入口参数的情况下,只需要改变结构体的成员变量,就可以达到上面改变入口参数的目的。结构体就是将多个变量组合为一个有机的整体。上面的函数,BaudRate,wordlength,Parity,mode,wordlength这些参数,他们对于串口而言,是一个有机整体,都是来设置串口参数的,所以我们可以将他们通过定义一个结构体来组合在一个。MDK中是这样定义的:typedefstruct{uint32_tUSART_BaudRate;uint16_tUSART_WordLength;uint16_tUSART_StopBits;uint16_tUSART_Parity;uint16_tUSART_Mode;uint16_tUSART_HardwareFlowControl;}USART_InitTypeDef;于是,我们在初始化串口的时候入口参数就可以是USART_InitTypeDef类型的变量或者指针变量了,MDK中是这样做的:voidUSART_Init(USART_TypeDef*USARTx,USART_InitTypeDef*USART_InitStruct);这样,任何时候,我们只需要修改结构体成员变量,往结构体中间加入新的成员变量,而不需要修改函数定义就可以达到修改入口参数同样的目的了。这样的好处是不用修改任何函数定义就可以达到增加变量的目的。在以后的开发过程中,如果你的变量定义过多,如果某几个变量是用来描述某一个对象,你可以考虑将这些变量定义在结构体中,这样也许可以提高你的代码的可读性。使用结构体组合参数,可以提高代码的可读性,不会觉得变量定义混乱。STM32系统架构STM32主系统主要由四个驱动单元和四个被动单元构成。四个驱动单元是:内核DCode总线;系统总线;通用DMA1;通用DMA2;四被动单元是:AHB到APB的桥:连接所有的APB设备;内部FlASH闪存;内部SRAM;FSMC;①ICode总线:该总线将M3内核指令总线和闪存指令接口相连,指令的预取在该总线上面完成。②DCode总线:该总线将M3内核的DCode总线与闪存存储器的数据接口相连接,常量加载和调试访问在该总线上面完成。③系统总线:该总线连接M3内核的系统总线到总线矩阵,总线矩阵协调内核和DMA间访问。④DMA总线:该总线将DMA的AHB主控接口与总线矩阵相连,总线矩阵协调CPU的DCode和DMA到SRAM,闪存和外设的访问。⑤总线矩阵:总线矩阵协调内核系统总线和DMA主控总线之间的访问仲裁,仲裁利用轮换算法。⑥AHB/APB桥:这两个桥在AHB和2个APB总线间提供同步连接,APB1操作速度限于36MHz,APB2操作速度全速。STM32时钟系统众所周知,时钟系统是CPU的脉搏,就像人的心跳一样。所以时钟系统的重要性就不言而喻了。STM32的时钟系统比较复杂,不像简单的51单片机一个系统时钟就可以解决一切。于是有人要问,采用一个系统时钟不是很简单吗?为什么STM32要有多个时钟源呢?因为首先STM32本身非常复杂,外设非常的多,但是并不是所有外设都需要系统时钟这
本文标题:第4章STM32-开发基础知识总结
链接地址:https://www.777doc.com/doc-7074895 .html