完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
刚开始看STM32的库函数,会有很多疑惑,例如指针怎么用,结构体跟指针怎么配合,例如函数的参数有什么要求,如何实时更新IO口的数据等。如果重新进行C语言的学习,那么要学很久才能够系统地认识。这里将比较容易想不起来的知识点进行简单的整理。
1、#ifdef 和 #ifndef #ifdef 标识符A// 如果标识符A定义了,就编译程序段1,否则编译程序段2 程序段1 #else 程序段2 #endif #ifndef 的功能则与 #ifdef相反,是没有定义标识符A的时候编译程序段1。 2、全局define 在软件的选项中,有如此一栏,在上面填写的变量则表示在所有的文件中,上述的标识均被定义过。1#ifdef STM32F10X_HD 大容量芯片需要的一些变量定义 #end 3、extern变量申明 C语言中extern可以置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。这里面要注意,对于extern申明变量可以多次,但定义只有一次。 extern u16 USART_RX_STA; 这个语句是申明USART_RX_STA变量在其他文件中已经定义了,在这里要使用到。 下面通过一个例子说明一下使用方法。 在Main.c定义的全局变量id,id的初始化都是在Main.c里面进行的。 Main.c文件 u8 id; //定义只允许一次 main() { id=1; printf("d%",id); //id=1 test(); printf("d%",id);//id=2 } 123456但是我们希望在test.c的 changeId(void)函数中使用变量id,这个时候我们就需要在test.c里面去申明变量id是外部定义的了,因为如果不申明,变量id的作用域是到不了test.c文件中。 看下面test.c中的代码: extern u8 id;//申明变量id是在外部定义的,申明可以在很多个文件中进行void test(void){ id=2; } 12在test.c中申明变量id在外部定义,然后在test.c中就可以使用变量id了。 4、typedef类型别名 typedef用于为现有类型创建一个新的名字,或称为类型别名,用来简化变量的定义。typedef在MDK用得最多的就是定义结构体的类型别名和枚举类型了。 struct _GPIO { __IO uint32_t CRL; __IO uint32_t CRH; … }; 1 定义了一个结构体GPIO,这样我们定义变量的方式为:1 struct _GPIO GPIOA;//定义结构体变量GPIOA 1但是这样很繁琐。这里我们可以为结体定义一个别名GPIO_TypeDef,这样我们就可以在其他地方通过别名GPIO_TypeDef来定义结构体变量了。 方法如下: 1 typedef struct { __IO uint32_t CRL; __IO uint32_t CRH; … } GPIO_TypeDef; 123Typedef为结构体定义一个别名GPIO_TypeDef, 这样我们可以通过GPIO_TypeDef来定义结构体变量: GPIO_TypeDef _GPIOA,_GPIOB; 这里的GPIO_TypeDef就跟struct _GPIO是等同的作用了。 5、结构体 声明结构体类型: Struct 结构体名 { 成员列表; }变量名列表; 例如 Struct U_TYPE { Int BaudRate Int WordLength; }usart1,usart2; 1在结构体申明的时候可以定义变量,也可以申明之后定义,方法是: Struct 结构体名字 结构体变量列表 ; 例如:struct U_TYPE usart1,usart2; 结构体成员变量的引用方法是: 结构体变量名字.成员名 比如要引用usart1的成员BaudRate,方法是:usart1.BaudRate; 结构体指针变量定义也是一样的,跟其他变量没有啥区别。 例如:struct U_TYPE *usart3;//定义结构体指针变量usart1; 结构体指针成员变量引用方法是通过“->”符号实现,比如要访问usart3结构体指针指向的结构体的成员变量BaudRate,方法是: Usart3->BaudRate;1在我们单片机程序开发过程中,经常会遇到要初始化一个外设比如串口,它的初始化状态是由几个属性来决定的,比如串口号,波特率,极性,以及模式。对于这种情况,在我们没有学习结构体的时候,我们一般的方法是: void USART_Init(u8 usartx,u32 u32 BaudRate,u8 parity,u8 mode); 1这种方式是有效的同时在一定场合是可取的。但是试想,如果有一天,我们希望往这个函数里面再传入一个参数,那么势必我们需要修改这个函数的定义,重新加入字长这个入口参数。但是如果我们这个函数的入口参数是随着开发不段的增多,那么是不是我们就要不断的修改函数的定义呢?这是不是给我们开发带来很多的麻烦呢?那又怎样解决这种情况呢? 这样如果我们使用到结构体就能解决这个问题了。我们可以在不改变入口参数的情况下,只需要改变结构体的成员变量,就可以达到上面改变入口参数的目的。 我们可以将他们通过定义一个结构体来组合在一个。MDK中是这样定义的: typedef struct { uint32_t USART_BaudRate; uint16_t USART_WordLength; uint16_t USART_StopBits; uint16_t USART_Parity; uint16_t USART_Mode; uint16_t USART_HardwareFlowControl; } USART_InitTypeDef; 123456于是,我们在初始化串口的时候入口参数就可以是USART_InitTypeDef类型的变量或者指针变量了,MDK中是这样做的: void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct); 1这样,任何时候,我们只需要修改结构体成员变量,往结构体中间加入新的成员变量,而不需要修改函数定义就可以达到修改入口参数同样的目的了。 6、关于函数中结构体的参数传递 在ST的库函数中,有许多结构体的用法,就像第5点中讲到的一样,用结构体封装有利于函数的传递。 下面是摘抄的一些解读,具有一定的典型性。 在ST的结构体参数传递中,有指针式,也有结构体地址式。 (1)用结构体变量名作为参数。 #include (2)用指向结构体变量的指针作为函数参数 #include (3)用结构体变量的引用变量作函数参数 #include 7、IMPORT 伪指令 IMPORT伪指令用于通知编译器要使用的标号在其他的源文件中定义,但要在当前源文件中引用,而且无论当前源文件是否引用该标号,该标号均会被加入到当前源文件的符号表中。 在ST的工程建立当中,会有两种方式,一种是寄存器版本,一种是固件库版本。 寄存器版本在新建的过程中就有一些功能和文件不需要添加到。 在寄存器版本新建工程后,添加启动文件startup_stm32f10x_hd.s (堆栈、PC初始化,向量异常地址入口初始化、调用MAIN函数),其中,教程里要求注释掉下面几行: Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT __main ; 建议注释部分:{ **寄存器版本代码,因为没有用到 SystemInit 函数,所以注释掉 ;库函数版本代码,建议加上这里(外部必须实现 SystemInit 函数),以初始化 stm32 时钟等。 ;IMPORT SystemInit 调用SystemInit这个函数;LDR R0, =SystemInit ;BLX R0** } LDR R0, =__main BX R0 ENDP 123456789101112131415当报找不到 SystemInit 函数时,解决的办法有下面三个: ①在外部(其他任何.c文件里面)定义SystemInit这个函数,空函数也可以。 ②把 IMPORT SystemInit LDR R0, =SystemInit BLX R0123这两句话注释掉或者去掉。 ③可以添加system_stm32f10x.c这个库文件,到工程里面,也可以解决。 但是第三种方法比较麻烦,因为如果你自己定义了一些函数,也许和system_stm32f10x.c有冲突。 8、文件的包含问题 #include操作是,若后面带的是<>,则文件在安装路径中找; 若后面带的是“”,则文件在源目录中找。 9、Volatile 语句 变量前若有加volatile 这个关键字,则每当系统用到这个变量时,则必须重新读取这个变量的值。 这种语句被大量用来描述一个对应于内存映射的输入输出端口,或者寄存器,如IO口的寄存器等。 如下: int flag = 0; void car_action () {while(1){if (flag) car_go( );}} void car_stop( ){flag = 1;}123456789101112在上述例子中,car_action 没有更改flag 的操作,所以可能只有第一次执行car_action 才会读取flag的值。后续都直接采用第一次读取的值。而实际上在car_stop中,flag的值已经变化。 在这种情况下,car_action函数的执行结果就可能出错。 但若在定义中采用 volatile int flag的写法,则每次要识别flag时,就会追溯到源地址中存储的数据去取数据,程序就能正常执行。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1771 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1619 浏览 1 评论
1070 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
724 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1673 浏览 2 评论
1935浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
728浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
567浏览 3评论
593浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
551浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-22 10:27 , Processed in 0.971724 second(s), Total 76, Slave 60 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号