stm32f4开发板07 探索者开发板 怎么单步调试

21ic官方微信-->
后使用快捷导航没有帐号?
查看: 1363|回复: 12
【正点原子探索者STM32F407开发板连载】第9章 串口通信实验
&&已结帖(0)
主题帖子积分
中级工程师, 积分 3198, 距离下一级还需 1802 积分
中级工程师, 积分 3198, 距离下一级还需 1802 积分
主题帖子积分
专家等级:结帖率:1%打赏:0.00受赏:0.10
主题帖子积分
中级工程师, 积分 3198, 距离下一级还需 1802 积分
中级工程师, 积分 3198, 距离下一级还需 1802 积分
第九章 串口通信实验
1.硬件平台:正点原子探索者STM32F407开发板
2.软件平台:MDK5.1
3.固件库版本:V1.4.0
复制代码
前面三章介绍了STM32F4的IO口操作。这一章我们将学习STM32F4的串口,教大家如何使用STM32F4的串口来发送和接收数据。本章将实现如下功能:STM32F4通过串口和上位机的对话,STM32F4在收到上位机发过来的字符串后,原原本本的返回给上位机。本章分为如下几个小节:9.1 STM32F4串口简介9.2 硬件设计9.3 软件设计9.4 下载验证 9.1 STM32F4串口简介& && & 串口作为MCU的重要外部接口,同时也是软件开发重要的调试手段,其重要性不言而喻。现在基本上所有的MCU都会带有串口,STM32自然也不例外。STM32F4的串口资源相当丰富的,功能也相当强劲。ALIENTEK探索者STM32F4开发板所使用的STM32F407ZGT6最多可提供6路串口,有分数波特率发生器、支持同步单线通信和半双工单线通讯、支持LIN、支持调制解调器操作、智能卡协议和IrDA SIR ENDEC规范、具有DMA等。5.3节对串口有过简单的介绍,大家看这个实验的时候记得翻过去看看。接下来我们将主要从库函数操作层面结合寄存器的描述,告诉你如何设置串口,以达到我们最基本的通信功能。本章,我们将实现利用串口1不停的打印信息到电脑上,同时接收从串口发过来的数据,把发送过来的数据直接送回给电脑。探索者STM32F4开发板板载了1个USB串口和2个RS232串口,我们本章介绍的是通过USB串口和电脑通信。在4.4.章节端口复用功能已经讲解过,对于复用功能的IO,我们首先要使能GPIO时钟,然后使能相应的外设时钟,同时要把GPIO模式设置为复用。这些准备工作做完之后,剩下的当然是串口参数的初始化设置,包括波特率,停止位等等参数。在设置完成只能接下来就是使能串口,这很容易理解。同时,如果我们开启了串口的中断,当然要初始化NVIC设置中断优先级别,最后编写中断服务函数。串口设置的一般步骤可以总结为如下几个步骤:1)& && &串口时钟使能,GPIO时钟使能。2)& && &设置引脚复用器映射:调用GPIO_PinAFConfig函数。3)& && &GPIO初始化设置:要设置模式为复用功能。4)& && &串口参数初始化:设置波特率,字长,奇偶校验等参数。5)& && &开启中断并且初始化NVIC,使能中断(如果需要开启中断才需要这个步骤)。6)& && &使能串口。7)& && &编写中断处理函数:函数名格式为USARTxIRQHandler(x对应串口号)。下面,我们就简单介绍下这几个与串口基本配置直接相关的几个固件库函数。这些函数和定义主要分布在stm32f4xx_usart.h和stm32f4xx_usart.c文件中。1)& && &串口时钟和GPIO时钟使能。串口是挂载在APB2下面的外设,所以使能函数为:RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟GPIO时钟使能,就非常简单,因为我们使用的是串口1,串口1对应着芯片引脚PA9,PA10,所以这里我们只需要使能GPIOA时钟即可:RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟2)& && &设置引脚复用器映射引脚复用器映射配置方法在我们4.4小节讲解非常清晰,调用函数为:GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //PA9复用为USART1GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);//PA10复用为USART1因为串口使用到PA9,PA10,所以我们要把PA9和PA10都映射到串口1。所以这里我们要调用两次函数。& && & 对于GPIO_PinAFConfig函数的第一个和第二个参数很好理解,就是设置对应的IO口,如果是PA9那么第一个参数是GPIOA,第二个参数就是GPIO_PinSource9。第二个参数,实际我们不需要去记忆,只需要根据我们4.7小节讲解的快速组织代码技巧里面,去相应的配置文件找到外设对应的AF配置宏定义标识符即可,串口1为GPIO_AF_USART1。3)& && &GPIO端口模式设置:PA9和PA10要设置为复用功能。GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHzGPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA104)& && &串口参数初始化:设置波特率,字长,奇偶校验等参数串口初始化是调用函数USART_Init来实现的,具体设置方法如下:USART_InitStructure.USART_BaudRate =//一般设置为9600;USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_NUSART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式USART_Init(USART1, &USART_InitStructure); //初始化串口5)& && &使能串口使能串口调用函数USART_Cmd来实现,具体使能串口1方法如下:USART_Cmd(USART1, ENABLE);&&//使能串口6)& && &串口数据发送与接收。STM32F4的发送与接收是通过数据寄存器USART_DR来实现的,这是一个双寄存器,包含了TDR和RDR。当向该寄存器写数据的时候,串口就会自动发送,当收到数据的时候,也是存在该寄存器内。STM32库函数操作USART_DR寄存器发送数据的函数是:void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);通过该函数向串口寄存器USART_DR写入一个数据。STM32库函数操作USART_DR寄存器读取串口接收到的数据的函数是:uint16_t USART_ReceiveData(USART_TypeDef* USARTx);通过该函数可以读取串口接受到的数据。7)& && &串口状态串口的状态可以通过状态寄存器USART_SR读取。USART_SR的各位描述如图9.1.1所示:
图9.1.1 USART_SR寄存器各位描述这里我们关注一下两个位,第5、6位RXNE和TC。RXNE(读数据寄存器非空),当该位被置1的时候,就是提示已经有数据被接收到了,并且可以读出来了。这时候我们要做的就是尽快去读取USART_DR,通过读USART_DR可以将该位清零,也可以向该位写0,直接清除。TC(发送完成),当该位被置位的时候,表示USART_DR内的数据已经被发送完成了。如果设置了这个位的中断,则会产生中断。该位也有两种清零方式:1)读USART_SR,写USART_DR。2)直接向该位写0。& & 状态寄存器的其他位我们这里就不做过多讲解,大家需要可以查看中文参考手册。在我们固件库函数里面,读取串口状态的函数是:FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);这个函数的第二个入口参数非常关键,它是标示我们要查看串口的哪种状态,比如上面讲解的RXNE(读数据寄存器非空)以及TC(发送完成)。例如我们要判断读寄存器是否非空(RXNE),操作库函数的方法是:USART_GetFlagStatus(USART1, USART_FLAG_RXNE);我们要判断发送是否完成(TC),操作库函数的方法是:USART_GetFlagStatus(USART1, USART_FLAG_TC);这些标识号在MDK里面是通过宏定义定义的:#define USART_IT_PE& && && && && && && && &&&((uint16_t)0x0028)#define USART_IT_TC& && && && && && && && &&&((uint16_t)0x0626)#define USART_IT_RXNE& && && && && && && && &((uint16_t)0x0525)……//(省略部分代码)#define USART_IT_NE& && && && && && && && &&&((uint16_t)0x0260)#define USART_IT_FE& && && && && && && && &&&((uint16_t)0x0160)8)& && &开启中断并且初始化NVIC,使能相应中断& && & 这一步如果我们要开启串口中断才需要配置NVIC中断优先级分组。通过调用函数NVIC_Init来设置。 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;& && && && &//响应优先级3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;& && && && && & //IRQ通道使能NVIC_Init(&NVIC_InitStructure);& && &//根据指定的参数初始化VIC寄存器、同时,我们还需要使能相应中断,使能串口中断的函数是:void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT,FunctionalState NewState)这个函数的第二个入口参数是标示使能串口的类型,也就是使能哪种中断,因为串口的中断类型有很多种。比如在接收到数据的时候(RXNE读数据寄存器非空),我们要产生中断,那么我们开启中断的方法是:USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断,接收到数据中断我们在发送数据结束的时候(TC,发送完成)要产生中断,那么方法是:USART_ITConfig(USART1,USART_IT_TC,ENABLE);这里还要特别提醒,因为我们实验开启了串口中断,所以我们在系统初始化的时候需要先设置系统的中断优先级分组,我们是在我们main函数开头设置的,代码如下:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2我们设置分组为2,也就是2位抢占优先级,2位响应优先级。9)& && &获取相应中断状态当我们使能了某个中断的时候,当该中断发生了,就会设置状态寄存器中的某个标志位。经常我们在中断处理函数中,要判断该中断是哪种中断,使用的函数是:ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)比如我们使能了串口发送完成中断,那么当中断发生了, 我们便可以在中断处理函数中调用这个函数来判断到底是否是串口发送完成中断,方法是:USART_GetITStatus(USART1, USART_IT_TC)返回值是SET,说明是串口发送完成中断发生。10)& &中断服务函数& && & 串口1中断服务函数为:void USART1_IRQHandler(void) ;当发生中断的时候,程序就会执行中断服务函数。然后我们在中断服务函数中编写我们相应的逻辑代码即可。通过以上一些寄存器的操作外加一下IO口的配置,我们就可以达到串口最基本的配置了,关于串口更详细的介绍,请参考《STM32F4XX中文参考手册》第676页至720页,通用同步异步收发器这一章节。9.2 硬件设计本实验需要用到的硬件资源有:1)&&指示灯DS02)&&串口1串口1之前还没有介绍过,本实验用到的串口1与USB串口并没有在PCB上连接在一起,需要通过跳线帽来连接一下。这里我们把P6的RXD和TXD用跳线帽与PA9和PA10连接起来。如图9.2.1所示:图9.2.1 硬件连接图示意图连接上这里之后,我们在硬件上就设置完成了,可以开始软件设计了。9.3 软件设计本章的代码设计,比前两章简单很多,因为我们的串口初始化代码和接收代码就是用我们之前介绍的SYSTEM文件夹下的串口部分的内容。这里我们对代码部分稍作讲解。打开串口实验工程,然后在SYSTEM组下双击usart.c,我们就可以看到该文件里面的代码,先介绍uart_init函数,该函数代码如下:void uart_init(u32 bound){& &GPIO_InitTypeDef GPIO_InitSUSART_InitTypeDef USART_InitSNVIC_InitTypeDef NVIC_InitS //GPIOA和USART1时钟使能①RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟& && &//USART_DeInit(USART1);&&//复位串口1 ②GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //PA9复用为USART1GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //PA10复用为USART1& && &//USART1_TX& &PA.9 PA.10 ③GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHzGPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10 //USART 初始化设置 ④USART_InitStructure.USART_BaudRate =//一般设置为9600;USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位USART_InitStructure.USART_HardwareFlowControl =USART_HardwareFlowControl_NUSART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;& && &//收发模式USART_Init(USART1, &USART_InitStructure); //初始化串口
由于篇幅限制,请下载附件和pdf学习。
实验详细手册和源码下载地址:
正点原子探索者STM32F407开发板购买地址:&&
本帖子中包含更多资源
才可以下载或查看,没有帐号?
正点原子-专业的STM32开发平台供应商。
主题帖子积分
技术达人, 积分 8539, 距离下一级还需 1461 积分
技术达人, 积分 8539, 距离下一级还需 1461 积分
主题帖子积分
专家等级:结帖率:100%
主题帖子积分
技术达人, 积分 8539, 距离下一级还需 1461 积分
技术达人, 积分 8539, 距离下一级还需 1461 积分
这个板子不错。
为江山踏坏了乌骓马,为社稷拉断了宝雕弓。
主题帖子积分
资深技术员, 积分 439, 距离下一级还需 61 积分
资深技术员, 积分 439, 距离下一级还需 61 积分
主题帖子积分
专家等级:结帖率:100%
主题帖子积分
资深技术员, 积分 439, 距离下一级还需 61 积分
资深技术员, 积分 439, 距离下一级还需 61 积分
太及时了,正想搞这485通信呢
主题帖子积分
主题帖子积分
专家等级:结帖率:100%打赏:0.00受赏:37.00
主题帖子积分
485还是相对比较简单的;
主题帖子积分
资深技术员, 积分 439, 距离下一级还需 61 积分
资深技术员, 积分 439, 距离下一级还需 61 积分
主题帖子积分
专家等级:结帖率:100%
主题帖子积分
资深技术员, 积分 439, 距离下一级还需 61 积分
资深技术员, 积分 439, 距离下一级还需 61 积分
mmuuss586 发表于
485还是相对比较简单的;
是呀,但还是参考一下,效率高点,:$
主题帖子积分
助理工程师, 积分 1792, 距离下一级还需 208 积分
助理工程师, 积分 1792, 距离下一级还需 208 积分
主题帖子积分
专家等级:结帖率:100%
主题帖子积分
助理工程师, 积分 1792, 距离下一级还需 208 积分
助理工程师, 积分 1792, 距离下一级还需 208 积分
电力监控仪表、STM32、物联网
主题帖子积分
主题帖子积分
专家等级:结帖率:100%打赏:0.00受赏:37.00
主题帖子积分
hqtdzgs 发表于
是呀,但还是参考一下,效率高点,
恩,谢谢分享;
技术奇才奖章
人才类勋章
时间类勋章
奔腾之江水
发帖类勋章
精英会员奖章
等级类勋章
技术高手奖章
人才类勋章
时间类勋章
涓涓之细流
发帖类勋章
技术领袖奖章
人才类勋章
坚毅之洋流
发帖类勋章
荣誉元老奖章
等级类勋章
欢快之小溪
发帖类勋章
社区建设奖章
等级类勋章查看: 3792|回复: 1
【正点原子探索者STM32F407开发板例程连载+教学】第6章 跑马灯实验-GPIO
主题帖子精华
在线时间32 小时
第六章 跑马灯实验
[C] 纯文本查看 复制代码
硬件平台:正点原子探索者STM32F407开发板
软件平台:MDK5.1
固件库版本:V1.4.0
任何一个单片机,最简单的外设莫过于IO口的高低电平控制了,本章将通过一个经典的跑马灯程序,带大家开启STM32F4之旅,通过本章的学习,你将了解到STM32F4的IO口作为输出使用的方法。在本章中,我们将通过代码控制ALIENTEK探索者STM32F4开发板上的两个LED:DS0和DS1交替闪烁,实现类似跑马灯的效果。
本章分为如下四个小节:
&&&&&& 6.1, STM32F4 IO口简介
&&&&&& 6.2, 硬件设计
&&&&&& 6.3, 软件设计
&&&&&& 6.4, 下载验证
6.1 STM32F4 IO简介
本章将要实现的是控制ALIENTEK探索者STM32F4开发板上的两个LED实现一个类似跑马灯的效果,该实验的关键在于如何控制STM32F4的IO口输出。了解了STM32F4的IO口如何输出的,就可以实现跑马灯了。通过这一章的学习,你将初步掌握STM32F4基本IO口的使用,而这是迈向STM32F4的第一步。
这一章节因为是第一个实验章节,所以我们在这一章将讲解一些知识为后面的实验做铺垫。为了小节标号与后面实验章节一样,这里我们不另起一节来讲。
&&&&&& 在讲解STM32F4的GPIO之前,首先打开我们光盘的第一个固件库版本实验工程跑马灯实验工程(光盘目录为:“4,程序源码\标准例程-库函数版本\实验1跑马灯/USER/
LED.uvproj”),可以看到我们的实验工程目录:
&&&&&&&&&&&&&&&&
f3f09bbf6e750ee9f860_445.jpg (0 Bytes, 下载次数: 0)
22:54 上传
图6.1.1 跑马灯实验目录结构
&&& 接下来我们逐一讲解一下我们的工程目录下面的组以及重要文件。
①&&&&&& 组FWLib下面存放的是ST官方提供的固件库函数,每一个源文件stm32f4xx_ppp.c都对应一个头文件stm32f4xx_ppp.h。分组内的文件我们可以根据工程需要添加和删除,但是一定要注意如果你引入了某个源文件,一定要在头文件stm32f4xx_conf.h文件中确保对应的头文件也已经添加。比如我们跑马灯实验,我们只添加了5个源文件,那么对应的头文件我们必须确保在stm32f4xx_conf.h内也包含进来,否则工程会报错。
②&&&&&& 组CORE下面存放的是固件库必须的核心文件和启动文件。这里面的文件用户不需要修改。大家可以根据自己的芯片型号选择对应的启动文件。
③&&&&&& 组SYSTEM是ALIENTEK提供的共用代码,这些代码的作用和讲解在第五章都有讲解,大家可以翻过去看下。
④&&&&&& 组HARDWARE下面存放的是每个实验的外设驱动代码,他的实现是通过调用FWLib下面的固件库文件实现的,比如led.c里面调用stm32f4xx_gpio.c内定义的函数对led进行初始化,这里面的函数是讲解的重点。后面的实验中可以看到会引入多个源文件。
⑤&&&&&& 组USER下面存放的主要是用户代码。但是system_stm32f4xx.c文件用户不需要修改,同时stm32f4xx_it.c里面存放的是中断服务函数,这两个文件的作用在3.1节有讲解,大家可以翻过去看看。Main.c函数主要存放的是主函数了,这个大家应该很清楚。
工程分组情况我们就讲解到这里,接下来我们就要进入我们跑马灯实验的讲解部分了。这里需要说明一下,我们在讲解固件库之前会首先对重要寄存器进行一个讲解,这样是为了大家对寄存器有个初步的了解。大家学习固件库,并不需要记住每个寄存器的作用,而只是通过了解寄存器来对外设一些功能有个大致的了解,这样对以后的学习也很有帮助。
首先要提一下,在固件库中,GPIO端口操作对应的库函数函数以及相关定义在文件stm32f4xx_gpio.h和stm32f4xx_gpio.c中。
相对于STM32F1来说,STM32F4的GPIO设置显得更为复杂,也更加灵活,尤其是复用功能部分,比STM32F1改进了很多,使用起来更加方便。
&&&&&& STM32F4每组通用 I/O 端口包括 4 个 32 位配置寄存器(MODER、OTYPER、OSPEEDR 和 PUPDR)、2 个 32 位数据寄存器(IDR 和 ODR)、1 个 32 位置位/复位寄存器 (BSRR)、1 个 32 位锁定寄存器 (LCKR) 和 2 个 32 位复用功能选择寄存器(AFRH 和 AFRL)等。
这样,STM32F4每组IO有10个32位寄存器控制,其中常用的有4个配置寄存器+2个数据寄存器+2个复用功能选择寄存器,共8个,如果在使用的时候,每次都直接操作寄存器配置IO,代码会比较多,也不容易记住,所以我们在讲解寄存器的同时会讲解是用库函数配置IO的方法。
同STM32F1一样,STM32F4的IO可以由软件配置成如下8种模式中的任何一种:
1、输入浮空
2、输入上拉
3、输入下拉
4、模拟输入
5、开漏输出
6、推挽输出
7、推挽式复用功能
8、开漏式复用功能
关于这些模式的介绍及应用场景,我们这里就不详细介绍了,感兴趣的朋友,可以看看这个帖子了解下: 。接下来我们详细介绍IO配置常用的8个寄存器: MODER、OTYPER、OSPEEDR、PUPDR、ODR、IDR 、AFRH和AFRL。同时讲解对应的库函数配置方法。
首先看MODER寄存器,该寄存器是GPIO端口模式控制寄存器,用于控制GPIOx(STM32F4最多有9组IO,分别用大写字母表示,即x=A/B/C/D/E/F/G/H/I,下同)的工作模式,该寄存器各位描述如表5.2.5.1所示:
6a8a5e0d3a9d39fda8b46_872.jpg (0 Bytes, 下载次数: 0)
22:54 上传
表5.2.5.1 GPIOx MODER寄存器各位描述
&&&&&& 该寄存器各位在复位后,一般都是0(个别不是0,比如JTAG占用的几个IO口),也就是默认条件下一般是输入状态的。每组IO下有16个IO口,该寄存器共32位,每2个位控制1个IO,不同设置所对应的模式见表5.2.5.1描述。
&&&&&& 然后看OTYPER寄存器,该寄存器用于控制GPIOx的输出类型,该寄存器各位描述见表5.2.5.2所示:
2b4a003dcbcfc9ff6d82c09_175.jpg (0 Bytes, 下载次数: 0)
22:54 上传
表5.2.5.2 GPIOx OTYPER寄存器各位描述
&&&&&& 该寄存器仅用于输出模式,在输入模式(MODER[1:0]=00/11时)下不起作用。该寄存器低16位有效,每一个位控制一个IO口,复位后,该寄存器值均为0。
&&&&&& 然后看OSPEEDR寄存器,该寄存器用于控制GPIOx的输出速度,该寄存器各位描述见表5.2.5.3所示:
a510dc7f9d8818aced67_889.jpg (0 Bytes, 下载次数: 0)
22:54 上传
表5.2.5.3 GPIOx OSPEEDR寄存器各位描述
&&&&&& 该寄存器也仅用于输出模式,在输入模式(MODER[1:0]=00/11时)下不起作用。该寄存器每2个位控制一个IO口,复位后,该寄存器值一般为0。
然后看PUPDR寄存器,该寄存器用于控制GPIOx的上拉/下拉,该寄存器各位描述见表5.2.5.4所示:
7a0bb9a96b72c4.jpg (0 Bytes, 下载次数: 0)
22:54 上传
表5.2.5.4 GPIOx PUPDR寄存器各位描述
该寄存器每2个位控制一个IO口,用于设置上下拉,这里提醒大家,STM32F1是通过ODR寄存器控制上下拉的,而STM32F4则由单独的寄存器PUPDR控制上下拉,使用起来更加灵活。复位后,该寄存器值一般为0。
前面,我们讲解了4个重要的配置寄存器。顾名思义,配置寄存器就是用来配置GPIO的相关模式和状态,接下来我们讲解怎么在库函数初始化GPIO的配置。
GPIO相关的函数和定义分布在固件库文件stm32f4xx_gpio.c和头文件stm32f4xx_gpio.h文件中。
在固件库开发中,操作四个配置寄存器初始化GPIO是通过GPIO初始化函数完成:
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef*
GPIO_InitStruct)
这个函数有两个参数,第一个参数是用来指定需要初始化的GPIO对应的GPIO组,取值范围为GPIOA~GPIOK。第二个参数为初始化参数结构体指针,结构体类型为GPIO_InitTypeDef。下面我们看看这个结构体的定义。首先我们打开我们光盘的跑马灯实验,然后找到FWLib组下面的stm32f4xx_gpio.c文件,定位到GPIO_Init函数体处,双击入口参数类型GPIO_InitTypeDef后右键选择“Go to definition of …”可以查看结构体的定义:
typedef struct
& uint32_t
GPIO_P&&&&
GPIOMode_TypeDef GPIO_M&&&&
GPIOSpeed_TypeDef GPIO_S&
GPIOOType_TypeDef GPIO_OT&&
GPIOPuPd_TypeDef GPIO_PuPd;&&&
}GPIO_InitTypeD
下面我们通过一个GPIO初始化实例来讲解这个结构体的成员变量的含义。
通过初始化结构体初始化GPIO的常用格式是:
GPIO_InitTypeDef&
GPIO_InitS
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9//GPIOF9
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化GPIO
上面代码的意思是设置GPIOF的第9个端口为推挽输出模式,同时速度为100M,上拉。
从上面初始化代码可以看出,结构体GPIO_InitStructure的第一个成员变量GPIO_Pin用来设置是要初始化哪个或者哪些IO口,这个很好理解;第二个成员变量GPIO_Mode是用来设置对应IO端口的输出输入端口模式,这个值实际就是配置我们前面讲解的GPIOx的MODER寄存器的值。在MDK中是通过一个枚举类型定义的,我们只需要选择对应的值即可:
typedef enum
GPIO_Mode_IN&& = 0x00, /*!&
GPIO Input Mode */
GPIO_Mode_OUT& = 0x01, /*!&
GPIO Output Mode */
GPIO_Mode_AF&& = 0x02, /*!&
GPIO Alternate function Mode */
GPIO_Mode_AN&& = 0x03& /*!& GPIO Analog Mode */
}GPIOMode_TypeD
GPIO_Mode_IN是用来设置为复位状态的输入,GPIO_Mode_OUT是通用输出模式,GPIO_Mode_AF是复用功能模式,GPIO_Mode_AN是模拟输入模式。
第三个参数GPIO_Speed是IO口输出速度设置,有四个可选值。实际上这就是配置的GPIO对应的OSPEEDR寄存器的值。在MDK中同样是通过枚举类型定义:
typedef enum
GPIO_Low_Speed&&&& = 0x00, /*!&
Low speed&&& */
GPIO_Medium_Speed& = 0x01, /*!&
Medium speed */
GPIO_Fast_Speed&&& = 0x02, /*!&
Fast speed&& */
GPIO_High_Speed&&& = 0x03& /*!& High speed&& */
}GPIOSpeed_TypeD
/* Add legacy definition */
GPIO_Speed_2MHz&&&
GPIO_Low_Speed&&&
GPIO_Speed_25MHz&&
GPIO_Medium_Speed
GPIO_Speed_50MHz&& GPIO_Fast_Speed
GPIO_Speed_100MHz&
GPIO_High_Speed&
这里需要说明一下,实际我们的输入可以是GPIOSpeed_TypeDef枚举类型中GPIO_High_Speed枚举类型值,也可以是GPIO_Speed_100MHz这样的值,实际上GPIO_Speed_100MHz就是通过define宏定义标识符定义出来的,它跟GPIO_High_Speed是等同的。
第四个参数GPIO_OType是GPIO的输出类型设置,实际上是配置的GPIO的OTYPER寄存器的值。在MDK中同样是通过枚举类型定义:
typedef enum
GPIO_OType_PP = 0x00,
GPIO_OType_OD = 0x01
}GPIOOType_TypeD
如果需要设置为输出推挽模式,那么选择值GPIO_OType_PP,如果需要设置为输出开漏模式,那么设置值为GPIO_OType_OD。
&&&&&& 第五个参数GPIO_PuPd用来设置IO口的上下拉,实际上就是设置GPIO的PUPDR寄存器的值。同样通过一个枚举类型列出:
typedef enum
GPIO_PuPd_NOPULL = 0x00,
GPIO_PuPd_UP&&&& = 0x01,
GPIO_PuPd_DOWN&& = 0x02
}GPIOPuPd_TypeD
这三个值的意思很好理解,GPIO_PuPd_NOPULL为不使用上下拉,GPIO_PuPd_UP为上拉,GPIO_PuPd_DOWN为下拉。我们根据我们 需要设置相应的值即可。
这些入口参数的取值范围怎么定位,怎么快速定位到这些入口参数取值范围的枚举类型,在我们上面章节4.7的“快速组织代码”章节有讲解,不明白的朋友可以翻回去看一下,这里我们就不重复讲解,在后面的实验中,我们也不再去重复讲解怎么定位每个参数的取值范围的方法。
看完了GPIO的参数配置寄存器,接下来我们看看GPIO输入输出电平控制相关的寄存器。
首先我们看ODR寄存器,该寄存器用于控制GPIOx的输出,该寄存器各位描述见表5.2.5.5所示:
62a88ed6b9ba6c3a6efb_175.jpg (0 Bytes, 下载次数: 0)
22:54 上传
表5.2.5.5 GPIOx ODR寄存器各位描述
该寄存器用于设置某个IO输出低电平(ODRy=0)还是高电平(ODRy=1),该寄存器也仅在输出模式下有效,在输入模式(MODER[1:0]=00/11时)下不起作用。
在固件库中设置ODR寄存器的值来控制IO口的输出状态是通过函数GPIO_Write来实现的:
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t
PortVal);&&
该函数一般用来往一次性一个GPIO的多个端口设值。
使用实例如下:
GPIO_Write(GPIOA,0x0000);
大部分情况下,设置IO口我们都不用这个函数,后面我们会讲解我们常用的设置IO口电平的函数。
同时读ODR寄存器还可以读出IO口的输出状态,库函数为:
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef*
GPIOx, uint16_t GPIO_Pin);
这两个函数功能类似,只不过前面是用来一次读取一组IO口所有IO口输出状态,后面的函数用来一次读取一组IO口中一个或者几个IO口的输出状态。
接下来我们看看IDR寄存器,该寄存器用于读取GPIOx的输入,该寄存器各位描述见表5.2.5.6所示:
e5a7fff24af178d01cb19a9_658.jpg (0 Bytes, 下载次数: 0)
22:54 上传
表5.2.5.6 GPIOx IDR寄存器各位描述
该寄存器用于读取某个IO的电平,如果对应的位为0(IDRy=0),则说明该IO输入的是低电平,如果是1(IDRy=1),则表示输入的是高电平。库函数相关函数为:
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx,
uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
前面的函数是用来读取一组IO口的一个或者几个IO口输入电平,后面的函数用来一次读取一组IO口所有IO口的输入电平。比如我们要读取GPIOF.5的输入电平,方法为:
GPIO_ReadInputDataBit(GPIOF, GPIO_Pin_5);
&&&&&& 接下来我们看看32 位置位/复位寄存器 (BSRR),顾名思义,这个寄存器是用来置位或者复位IO口,该寄存器和ODR寄存器具有类似的作用,都可以用来设置GPIO端口的输出位是1还是0。寄存器描述如下:
a0fd08a353f442caf0c067a_76.jpg (0 Bytes, 下载次数: 0)
22:54 上传
表5.2.5.7 BSRR寄存器各位描述
对于低16位(0-15),我们往相应的位写1,那么对应的IO口会输出高电平,往相应的位写0,对IO口没有任何影响。高16位(16-31)作用刚好相反,对相应的位写1会输出低电平,写0没有任何影响。也就是说,对于BSRR寄存器,你写0的话,对IO口电平是没有任何影响的。我们要设置某个IO口电平,只需要为相关位设置为1即可。而ODR寄存器,我们要设置某个IO口电平,我们首先需要读出来ODR寄存器的值,然后对整个ODR寄存器重新赋值来打到设置某个或者某些IO口的目的,而BSRR寄存器,我们就不需要先读,而是直接设置。
BSRR寄存器使用方法如下:
GPIOA-&BSRR=1&&1; //设置GPIOA.1为高电平
GPIOA-&BSRR=1&&(16+1)//设置GPIOA.1为低电平;
库函数操作BSRR寄存器来设置IO电平的函数为:
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t
GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t
GPIO_Pin);
函数GPIO_SetBits用来设置一组IO口中的一个或者多个IO口为高电平。GPIO_ResetBits用来设置一组IO口中一个或者多个IO口为低电平。比如我们要设置GPIOB.5输出高,方法为:
&&&&&& GPIO_SetBits(GPIOB,GPIO_Pin_5);//GPIOB.5输出高&&&
设置GPIOB.5输出低电平,方法为:
GPIO_ResetBits(GPIOB,GPIO_Pin_5);//GPIOB.5输出低
&&&&&& 最后我们来看看2 个 32 位复用功能选择寄存器(AFRH 和 AFRL),这两个寄存器是用来设置IO口的复用功能的。关于这两个寄存器的配置以及相关库函数的使用,在我们前面章节4.4 IO引脚复用和映射有详细讲解,这里我们就不做过多的说明。
GPIO相关的函数我们先讲解到这里。虽然IO操作步骤很简单,这里我们还是做个概括性的总结,操作步骤为:
1)& 使能IO口时钟。调用函数为RCC_AHB1PeriphClockCmd ()。
2)& 初始化IO参数。调用函数GPIO_Init();
3)& 操作IO。操作IO的方法就是上面我们讲解的方法。
&&&&&& 上面我们讲解了STM32F4 IO口的基本知识以及固件库操作GPIO的一些函数方法,下面我们来讲解我们的跑马灯实验的硬件和软件设计。
6.2 硬件设计
本章用到的硬件只有LED(DS0和DS1)。其电路在ALIENTEK探索者STM32F4开发板上默认是已经连接好了的。DS0接PF9,DS1接PF10。所以在硬件上不需要动任何东西。其连接原理图如图6.2.1下:
85a7b656e68c092d39b3fc_59.jpg (0 Bytes, 下载次数: 0)
22:54 上传
图6.2.1 LED与STM32F4连接原理图
6.3 软件设计
这是我们的第一个实验,所以我教大家怎么从我们前面讲解的Template工程一步一步加入我们的固件库以及我们的led相关的驱动函数到我们工程,使之跟我们光盘的跑马灯实验工程一模一样。首先大家打开我们3.3.2小节新建的库函数版本工程模板。如果您还没有新建,也可以直接打开我们光盘已经新建好了的工程模板,路径为:“\4,程序源码\标准例程-库函数版本\实验0
Template工程模板”。注意,是直接点击工程下面的USER目录下面的Template.uvproj。
大家可以看到,我们模板里面的FWLIB分组下面,我们引入了所有的固件库源文件和对应的头文件,如下图6.3.1:
aecc6918fef_526.jpg (0 Bytes, 下载次数: 0)
22:54 上传
图6.3.1& Template模板工程结构
实际上,这些大家可以根据工程需要添加,比如我们跑马灯实验,我们并没有用到ADC,自然我们可以去掉stm32f4xx_adc.c,这样可以减少工程编译时间。
跑马灯实验我们主要用到的固件库文件是:
stm32f4xx_gpio.c /stm32f4xx_gpio.h
stm32f4xx_rcc.c/stm32f4xx_rcc.h
misc.c/ misc.h
stm32f4xx_usart .c/stm32f4xx_usart.h
stm32f4xx_syscfg.c/stm32f4xx_syscfg.h
其中stm32f4xx_rcc.h头文件在每个实验中都要引入,因为系统时钟配置函数以及相关的外设时钟使能函数都在这个其源文件stm32f4xx_rcc.c中。stm32f4xx_usart.h和misc.h头文件和对应的源文件在我们SYSTEM文件夹中都需要使用到,所以每个实验都会引用。stm32f4xx_syscfg.h和对应的源文件虽然本实验也没有用到,但是后面很多实验都要使用到,所以我们不妨也添加进来。
在stm32f4xx_conf.h文件里面,这些头文件默认都是打开的,实际我们可以不用理。当然我们也可以注释掉其他不用的头文件,但是如果你引入了某个源文件,一定不能不包含对应的头文件:
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_usart.h"
#include “stm32f4xx_syscfg.h”
#include "misc.h"
接下来,我们讲解怎样去掉多余的其他的源文件,方法如下图,右击Template,选择“Manage
project Items”,进入这个选项卡:
d7798e3daf40a5cd70c86ae_294.jpg (0 Bytes, 下载次数: 0)
22:54 上传
我们选中“FWLIB”分组,然后选中不需要的源文件点击删除按钮删掉,留下下图中我们使用到的五个源文件,然后点击OK:
6b0efc459c54aa.jpg (0 Bytes, 下载次数: 0)
22:54 上传
这样我们的工程FWLIB下面只剩下五个源文件:
ea67e53fa1dcf_22.jpg (0 Bytes, 下载次数: 0)
22:54 上传
然后我们进入我们工程的目录,在工程根目录文件夹下面新建一个HARDWARE的文件夹,用来存储以后与硬件相关的代码。然后在HARDWARE文件夹下新建一个LED文件夹,用来存放与LED相关的代码。如图6.3.5所示:
85ec9b7e_437.jpg (0 Bytes, 下载次数: 0)
22:54 上传
图6.3.5新建HARDWARE文件夹
接下来,我们回到我们的工程(如果是使用的上面新建的工程模板,那么就是Template.uvproj,大家可以将其重命名为LED.uvproj),按按钮新建一个文件,然后按保存在HARDWARE-&LED文件夹下面,保存为led.c,操作步骤如下图:
021dcdfd10a92c8f.jpg (0 Bytes, 下载次数: 0)
22:54 上传
图6.3.6 新建文件
5c49fb28c18daadad.jpg (0 Bytes, 下载次数: 0)
22:54 上传
图6.3.7 保存led.c
然后在lcd.c文件中输入如下代码(代码大家可以直接打开我们光盘的跑马灯实验,从相应的文件中间复制过来),输入后保存即可:
#include "led.h"
//初始化PF9和PF10为输出口.并使能这两个口的时钟&&&&&&&& &&&
//LED IO初始化
void LED_Init(void)
{&&& &&&& &
GPIO_InitTypeDef&
GPIO_InitS
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能GPIOF时钟
//GPIOF9,F10初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;//LED0和LED1对应IO口
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化GPIO
GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);//GPIOF9,F10设置高,灯灭
该代码里面就包含了一个函数void LED_Init(void),该函数的功能就是用来实现配置PF9和PF10为推挽输出。这里需要注意的是:在配置STM32外设的时候,任何时候都要先使能该外设的时钟!GPIO是挂载在AHB1总线上的外设,在固件库中对挂载在AHB1总线上的外设时钟使能是通过函数RCC_AHB1PeriphClockCmd ()来实现的。对于这个入口参数设置,在我们前面的“4.7快速组织代码”章节已经讲解很清楚了。看看我们的代码:
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能GPIOF时钟
这行代码的作用是使能AHB1总线上的GPIOF时钟。
在设置完时钟之后,LED_Init调用GPIO_Init函数完成对PF9和PF10的初始化配置,然后调用函数GPIO_SetBits控制LED0和LED1输出1(LED灭)。至此,两个LED的初始化完毕。这样就完成了对这两个IO口的初始化。这段代码的具体含义,大家可以看前面一小节,我们有详细的讲解。初始化函数代码如下:
//GPIOF9,F10初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;//LED0和LED1对应IO口
& GPIO_InitStructure.GPIO_Mode
= GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化GPIO
GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);//GPIOF9,F10设置高,灯灭
保存led.c代码,然后我们按同样的方法,新建一个led.h文件,也保存在LED文件夹下面。在led.h中输入如下代码:
#ifndef __LED_H
#define __LED_H
#include "sys.h"
//LED端口定义
#define LED0 PFout(9) // DS0
#define LED1 PFout(10)&&&&&& // DS1&&&&
void LED_Init(void);//初始化&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&&&& &&&&
这段代码里面最关键就是2个宏定义:
#define LED0 PFout(9) // DS0 PF9
#define LED1 PFout(10)&&&&&& // DS1 PF10
这里使用的是位带操作来实现操作某个IO口的1个位的,关于位带操作前面第五章5.2.1已经有介绍,这里不再多说。需要说明的是,这里同样可以使用固件库操作来实现IO口操作。如下:
GPIO_SetBits(GPIOF, GPIO_Pin_9);&&&&& &&//设置GPIOF.9输出1,等同LED0=1;
GPIO_ResetBits (GPIOF, GPIO_Pin_9);&&&&& //设置GPIOF.9输出0,等同LED0=0;
有兴趣的朋友不妨修改我们的位带操作为库函数直接操作,这样也有利于学习。
将led.h也保存一下。接着,我们在Manage Components管理里面新建一个HARDWARE的组,并把led.c加入到这个组里面,如图6.3.8所示:
e0e9ff1cc5c863ad72dfcc9_356.jpg (0 Bytes, 下载次数: 0)
22:54 上传
图6.3.8 给工程新增HARDWARE组
单击OK,回到工程,然后你会发现在Project Workspace里面多了一个HARDWARE的组,在该组下面有一个led.c的文件。如图6.3.9所示:
765a1fec60cdb98d6ee91df04f.jpg (0 Bytes, 下载次数: 0)
22:54 上传
图6.3.9新增HARDWARE组
然后用之前介绍的方法(在3.3.2节介绍的)将led.h头文件的路径加入到工程里面,然后点击OK回到主界面。
dba8ec045eccd047e05e78cc_929.jpg (0 Bytes, 下载次数: 0)
22:54 上传
图6.3.10 添加LED目录到PATH
回到主界面后,在main函数里面编写如下代码:
main(void)
delay_init(168);&&&&&&&&&&& & //初始化延时函数
LED_Init();&&&&&&&&&& &&&& &&//初始化LED端口
&/**下面是通过直接操作库函数的方式实现IO控制**/&
GPIO_ResetBits(GPIOF,GPIO_Pin_9);& //LED0对应引脚GPIOF.9拉低,亮& 等同LED0=0;
GPIO_SetBits(GPIOF,GPIO_Pin_10);&& //LED1对应引脚GPIOF.10拉高,灭 等同LED1=1;
delay_ms(500);&
&&&&&&&&& &&&//延时500ms
GPIO_SetBits(GPIOF,GPIO_Pin_9);&& &&
//LED0对应引脚GPIOF.0拉高,灭& 等同LED0=1;
GPIO_ResetBits(GPIOF,GPIO_Pin_10); //LED1对应引脚GPIOF.10拉低,亮 等同LED1=0;
delay_ms(500);&&&&&&&&&&&&&&&&&&&& //延时500ms
代码包含了#include
"led.h"这句,使得LED0、LED1、LED_Init等能在main()函数里被调用。这里我们需要重申的是,在固件库中,系统在启动的时候会调用system_stm32f4xx.c中的函数SystemInit()对系统时钟进行初始化,在时钟初始化完毕之后会调用main()函数。 所以我们不需要再在main()函数中调用SystemInit()函数。当然如果有需要重新设置时钟系统,可以写自己的时钟设置代码,SystemInit()只是将时钟系统初始化为默认状态。
main()函数非常简单,先调用delay_init()初始化延时,接着就是调用LED_Init()来初始化GPIOF.9和GPIOF.10为输出。最后在死循环里面实现LED0和LED1交替闪烁,间隔为500ms。
上面是通过库函数来实现的IO操作,我们也可以修改main()函数,直接通过位带操作达到同样的效果,大家不妨试试。位带操作的代码如下:
int main(void)
delay_init(168);&&&&&&&& & //初始化延时函数
LED_Init();&&&&&&& &&&&&&& //初始化LED端口
& while(1)
&&& &&&&&LED0=0;&&&&&&&&&&&&&&&&&&& & //LED0亮
&&&&&& & &&&LED1=1;&&&&&&&&&&&&&&&&&&&&&&&&&& //LED1灭
&&&&&&&&&&&&& &delay_ms(500);
&&&&&&&&&&&&& &LED0=1;&&&&&&&&&&&&&&&&&&&&&&&&&& //LED0灭
&&&&&&&&&&&&& &LED1=0;&&&&&&&&&&&&&&&&&&&&&&&&&& //LED1亮
&&&&&&&&&&&&& &delay_ms(500);
当然我们也可以通过直接操作相关寄存器的方法来设置IO,我们只需要将主函数修改为如下内容:
int main(void)
&&&&&& delay_init(168);&&&&&&&&&&& &
//初始化延时函数
&&&&&& LED_Init();&&&&&&&&&& &&&&&&& //初始化LED端口
&&&&&& while(1)
GPIOF-&BSRRH=GPIO_Pin_9;//LED0亮
&&&&&& &GPIOF-&BSRRL=GPIO_Pin_10;//LED1灭
&&&&&& &delay_ms(500);
GPIOF-&BSRRL=GPIO_Pin_9;//LED0灭
&&&&&& &GPIOF-&BSRRH=GPIO_Pin_10;//LED1亮
&&&&&& & &delay_ms(500);
将主函数替换为上面代码,然后重新执行,可以看到,结果跟库函数操作和位带操作一样的效果。大家可以对比一下。这个代码在我们跑马灯实验的main.c文件中有注释掉,大家可以替换试试。
然后按,编译工程,得到结果如图6.3.11所示:
0d27eec2bdce_335.jpg (0 Bytes, 下载次数: 0)
22:54 上传
&&&&&& &&&&&&&&&&&&&&&&&&&&&&&&& 图6.3.11编译结果
可以看到没有错误,也没有警告。从编译信息可以看出,我们的代码占用FLASH大小为:5678字节(),所用的SRAM大小为:1880个字节(1832+48)。
这里我们解释一下,编译结果里面的几个数据的意义:
&&&&&& Code:表示程序所占用FLASH的大小(FLASH)。
&&&&&& RO-data:即Read Only-data,表示程序定义的常量(FLASH)。
&&&&&& RW-data:即Read Write-data,表示已被初始化的变量(SRAM)
&&&&&& ZI-data:即Zero Init-data,表示未被初始化的变量(SRAM)
有了这个就可以知道你当前使用的flash和sram大小了,所以,一定要注意的是程序的大小不是.hex文件的大小,而是编译后的Code和RO-data之和。
接下来,大家就可以下载验证了。如果有JTAG,则可以用jtag进行在线调试(需要先下载代码),单步查看代码的运行,STM32F4的在线调试方法介绍,参见:3.4.2节。
6.4 下载验证
这里我们使用flymcu下载(也可以通过JTAG等仿真器下载,具体方法请参考3.4.2小节),如图6.4.1所示:
cba2eaafa2d6b790b.jpg (0 Bytes, 下载次数: 0)
22:54 上传
6.4.1 利用flymcu下载代码
下载完之后,运行结果如图6.4.2所示,LED0和LED1循环闪烁:
2ac2036bf7bceb0bf08cce68a0e0bb7d_774.jpg (0 Bytes, 下载次数: 0)
22:54 上传
&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&& 图6.4.2 程序运行结果
至此,我们的第一章的学习就结束了,本章作为STM32F4的入门第一个例子,介绍了STM32F4的IO口的使用及注意事项,同时巩固了前面的学习,希望大家好好理解一下。
实验详细手册和源码下载地址:
正点原子探索者STM32F407开发板购买地址:
21:43 上传
点击文件名下载附件
1.7 MB, 下载次数: 1572
21:43 上传
点击文件名下载附件
465.75 KB, 下载次数: 339
我是开源电子网?网站管理员,对网站有任何问题,请与我联系!
主题帖子精华
新手上路, 积分 30, 距离下一级还需 20 积分
在线时间0 小时
uint8_t&GPIO_ReadOutputDataBit(GPIO_TypeDef*&GPIOx,&uint16_t&GPIO_Pin);&
这两个函数功能类似,只不过前面是用来一次读取一组IO口所有IO口输出状态,后面的函数用来一次读取一组IO口中一个或者几个IO口的输出状态。
这个函数不能用来一次读取几个IO口输出状态吧。
库函数定义:
if&(((GPIOx-&ODR)&&&GPIO_Pin)&!=&(uint32_t)Bit_RESET)
&&{
&&&&bitstatus&=&(uint8_t)Bit_SET;
&&}
&&else
&&{
&&&&bitstatus&=&(uint8_t)Bit_RESET;
&&}
&&return&
如果几个PIN传进去,只要有一个PIN是高电平,返回就是1
Powered by}

我要回帖

更多关于 stm32f469开发板 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信