STM32F4学习笔记

部分c语言知识回顾

结构体

声明结构体类型:

结构体成员变量的引用方法:结构体变量名字.成员名

例:要引用 usart1 的成员 BaudRate,方法是:usart1.BaudRate;

结构体指针变量定义也是一样的。
例如:struct U_TYPE *usart3;//定义结构体指针变量 usart1;
结构体指针成员变量引用方法是通过“->”符号实现,比如要访问 usart3 结构体指针指向的结
构体的成员变量 BaudRate,方法是: Usart3->BaudRate;

 结构体的作用:

当初始化一个函数时:

函数的入口参数不断增多,修改更加麻烦,此时引入结构体:

STM32F4 时钟使能和配置,IO 引脚复用

1.

这里主要有5个外设时钟使能函数,使能5个总线挂载的外设时钟,外设在使用之前,必须对时钟进行使能。

如何知道哪个外设挂载在哪个总线下:

比如我们要使能 GPIOA,我们只需要在 stm32f4xx_rcc.h 头文件里面搜索 GPIOA,就可以搜
索到对应的时钟使能函数的第一个入口参数为 RCC_AHB1Periph_GPIOA,从这个宏定义标识
符一眼就可以看出,GPIOA 是挂载在 AHB1 下面。同理,对于串口 1 我们可以搜索 USART1
找到标识符为 RCC_APB2Periph_USART1,那么很容易知道串口 1 是挂载在 APB2 之下。
2.
STM32F4  IO 引脚通过一个复用器连接到内置外设或模块。该复用器一次只允许一个外设的复用功能(AF)连接到对应的 IO 口。这样可以确保共用同一个 IO 引脚的外设之间不会发生冲突。
每个 IO 引脚都有一个复用器,该复用器采用 16 路复用功能输入(AF0 到 AF15),可通过
GPIOx_AFRL(针对引脚 0-7)和 GPIOx_AFRH(针对引脚 8-15)寄存器对这些输入进行配置。
每四位控制一路复用:
1)完成复位后,所有 IO 都会连接到系统的复用功能 0(AF0)。
2)外设的复用功能映射到 AF1 到 AF13。
3)Cortex-M4 EVENTOUT 映射到 AF15。
复用器示意图
如图PC11作为多种复用功能的输出,在F4中可以有复用功能选择,让PC11连接到特定外设。
当需要使用复用功能的时候,我们配置相应的寄存器 GPIOx_AFRL 或者 GPIOx_AFRH,让对应引 脚通过复用器连接到对应的复用功能外设。
下图是GPIOx_AFRL 寄存器的描述
从表中可以看出,32 位寄存器 GPIOx_AFRL 每四个位控制一个 IO 口,所以每个寄存器控制
32/4=8 个 IO 口。寄存器对应四位的值配置决定这个 IO 映射到哪个复用功能 AF。
对于系统复用功能 AF0,我们将 IO 口连接到 AF0 之后,还要根据所用功能进行配置:
1) JTAG/SWD:在器件复位之后,会将这些功能引脚指定为专用引脚。也就是说,这些引脚
在复位后默认就是 JTAG/SWD 功能。如果我们要作为 GPIO 来使用,就需要对对应的 IO
口复用器进行配置。
2) RTC_REFIN:此引脚在系统复位之后要使用的话要配置为浮空输入模式。
3) MCO1 和 MCO2:这些引脚在系统复位之后要使用的话要配置为复用功能模式
对于外设复用功能的配置,除了 ADC 和 DAC 要将 IO 配置为模拟通道之外其他外设功能一律
要配置为复用功能模式,这个配置是在 IO 口对应的 GPIOx_MODER 寄存器中配置的。同时要配
置 GPIOx_AFRH 或者 GPIOx_AFRL 寄存器,将 IO 口通过复用器连接到所需要的复用功能对应的
AFx。
下面表格为部分端口AF的复用
例如,如何将PA9和PA10复用为串口1的功能
//使能外设时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE)
//引脚初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; 
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度 50MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化 PA9,PA10
//配置GPIO_AFRL或AFRH寄存器,将IO口连接到所需AFx,调用GPIO_PinAFConfig
//PA9连接AF7,复用为TX
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
//PA10连接AF7,复用为RX
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);

经典跑马灯

STM32F4的IO口介绍

stm32F4每组IO有10个32位寄存器控制,包括4个配置寄存器+2个数据寄存器+2个复用功能选择寄存器。

 IO口可配置成8种模式:

1.推挽输出:灵活输出高低电平,推挽结构两个三极管,总是一个导通一个截止,高低电平由IC电源决定。

2.开漏输出:输出端相当于三极管的集电极. 要得到高电平状态需要上拉电阻才行,利用外部电路的驱动能力,减少IC内部的驱动,开漏引脚不连接外部的上拉电阻时,只能输出低电平,如果需要同时具备输出高电平的功能,则需要接上拉电阻,很好的一个优点是通过改变上拉电源的电压,便可以改变传输电平。

3.浮空输入:浮空输入状态下,IO的电平状态是不确定的,完全由外部输入决定,如果在该引脚悬空的情况下,读取该端口的电平是不确定的。

4/5/6.上拉输入,下拉输入,模拟输入。

7/8.复用开漏输出、复用推挽输出:可以理解为GPIO口被用作第二功能时的配置情况(即并非作为通用IO口使用)。

GPIO的配置

F4相比于F1的GPIO寄存器更加丰富灵活,下面为解释示例

配置输出模式,有更加独立的函数

对于成员变量GPIO_Mode

GPIO_OType

PP为推挽输出,OD为开漏输出

GPIO_PuPd用于设置上下拉

设置寄存器ODR的值来控制IO口输出状态,是通过GPIO_Write来实现的

IDR寄存器,读取GPIOx的输入,读取某个IO口电平函数

32位的置位/复位(IO口电平)寄存器BSRR

寄存器操作

对应库函数操作

代码部分

硬件连接

led.c

void LED_Init(void)
{
    GPIO_InitType GPIO_InitStructure;
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9|GPIO_Pin_10;
    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_SetBits(GPIOF,GPIO_Pin_9|GPIO_Pin_10)//设置高,灯灭
}

main.c

int main(void)
{
    delay_Init(168);
    LED_Init();

    while(1)
    {
        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
        
    }
}

蜂鸣器实验

硬件

软件

Beep.c

void BEEP_Init(void)
{
    GPIO_InitTyDef GPIO_InitStructure;
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_DOWN;
    GPIO_Init(GPIOF,&GPIO_InitStructure);

    GPIO_ResetBits(GPIOF,GPIO_Pin_8);
}

main.c

int main(void)
{ 
    delay_init(168); //初始化延时函数
    LED_Init(); //初始化 LED 端口
    BEEP_Init(); //初始化蜂鸣器端口
    while(1)
    { 
    GPIO_ResetBits(GPIOF,GPIO_Pin_9); // DS0 拉低,亮 等同 LED0=0;
    GPIO_ResetBits(GPIOF,GPIO_Pin_8); //BEEP 引脚拉低, 等同 BEEP=0;
    delay_ms(300); //延时 300ms
    GPIO_SetBits(GPIOF,GPIO_Pin_9); // DS0 拉高,灭 等同 LED0=1;
    GPIO_SetBits(GPIOF,GPIO_Pin_8); //BEEP 引脚拉高, 等同 BEEP=1;
    delay_ms(300); //延时 300ms
    }
}

按键输入实验

硬件

共4个按键(key_up,key0,key1,key2)                                                                                           key_up控制蜂鸣器,按一次响,再按一次停;                                                                                   key2控制DS0(LED),按一次亮,一次灭;                                                                                   key1控制DS1,按一次亮,一次灭;                                                                                           key0控制两个LED按一次反转状态。

这里需要注意的是:KEY0KEY1 KEY2 是低电平有效的,而 KEY_UP 是高电平有效
的,并且外部都没有上下拉电阻,所以,需要在 STM32F4 内部设置上下拉。

软件

key.c

#include "key.h"
#include "delay.h"
//按键初始化
void key_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOE,ENABLE);
    //初始化key0,1,2
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN;
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;//上拉输入
    GPIO_Init(GPIOE,&GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
    GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_DOWN;//下拉输入
    GPIO_Init(GPIOA,&GPIO_InitStructure);
}
//按键处理函数
//注意此函数有响应优先级,KEY0>KEY1>KEY2>WK_UP
u8 KEY_Scan(u8 mode)
{
    static u8 key_up=1; //表示按键的状态,1表示未被按下
    if(mode) //模式1表示检测按键可以被连续按的模式
    {
     key_up=1;
    }
    if(key_up&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1)) //有按键按下返回相应的值
    {
     delay_ms(10);
     key_up=0;
     if(KEY0==0) return 1;
     else if(KEY1==0) return 2;
     else if(KEY2==0) return 3;
     else if(WK_UP==1) return 4;
    }
    else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)
    {
     key_up=1;
     return 0;
    }
}

key.h

#ifndef __KEY_H
#define __KEY_H
#include "sys.h" 
/*下面的方式是通过直接操作库函数方式读取 IO*/
#define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) //PE4
#define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3) //PE3 
#define KEY2 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2) //PE2
#define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) //PA0
#define KEY0_PRES 1
#define KEY1_PRES 2
#define KEY2_PRES 3
#define WKUP_PRES 4
void KEY_Init(void); //IO 初始化
u8 KEY_Scan(u8); //按键扫描函数
#endif

main.c

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "beep.h"
#include "key.h"
int main(void)
{ 
 u8 key; //保存键值
 delay_init(168); //初始化延时函数
 LED_Init(); //初始化 LED 端口
 BEEP_Init(); //初始化蜂鸣器端口
 KEY_Init(); //初始化与按键连接的硬件接口
 LED0=0; //先点亮红灯
while(1)
{ 
 key=KEY_Scan(0); //得到键值
 if(key)
 { 
  switch(key)
  { 
   case WKUP_PRES: //控制蜂鸣器
   BEEP=!BEEP;
   break;
   case KEY0_PRES: //控制 LED0 翻转
   LED0=!LED0;
   break;
   case KEY1_PRES: //控制 LED1 翻转
   LED1=!LED1;
   break;
   case KEY2_PRES: //同时控制 LED0,LED1 翻转
   LED0=!LED0;
   LED1=!LED1;
   break;
  }
 }
 else delay_ms(10);
}
}

串口通信

探索者 STM32F4 开发板板载了 1 USB 串口和 2 RS232 串口
串口设置一般有如下设置:
1.串口时钟使能
2.设置引脚复用器映射:调用GPIO_PinAFConfig函数。
3.GPIO初始化设置
4.串口参数设置
5.开启中断并且初始化NVIC,使能中断(若要开启中断)
6.使能串口
7.编写中断函数:一般格式USARTxIRQHandker(x为串口号)

相应库函数

串口挂载在APB2下面的外设,使能函数

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
串口1对应的引脚为PA10,PA9;再使能GPIOA
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能 GPIOA 时钟

设置引脚复用器映射

GPIO端口模式设置

串口参数设置

使能串口
串口数据的发送和接收
发送与接收是通过寄存器USART_DR来实现的,这是一个双寄存器,包含了TDR和RDR,向该寄存器写数据,串口会自动发送,当收到数据时,也是存在该寄存器中。
操作USART_DR发送数据的库函数和读取串口接收数据的库函数为
//发送数据
void USART_SendData(USART_TypeDef*USARTx,uint16_t Data)
//读取串口接收的数据
uint16_t USART_ReceiveData(USART_TypeDef*USARTx)

串口的状态

串口的状态可以通过状态寄存器 USART_SR 读取。
RXNE(读数据寄存器非空),当该位被置1时,表示已经有数据被接收到了,并且可以读出来,此时要去读取USART_DR寄存器,读取DR后,RXNE将会被清零,也可以向该位写0,来清除。
TC(发送完成),当该位被置位时,表示USART_DR内的数据已经被发送完成了,如果设置了这个位的中断,则会产生中断。该位有两种清零方式:1.读USART_SR,写USART_DR。2.直接写0
读取的该状态寄存器的库函数为
FlagStatus USART_GetFlagStatus(USART_TypeDef*USARTx,uint16_t USART_FLAG);
函数的第二个入口参数非常关键,它是标示我们要查看串口的哪种状态

例如我们要判断读寄存器是否非空(RXNE)操作库函数的方法是:
USART_GetFlagStatus(USART1, USART_FLAG_RXNE);

我们要判断发送是否完成(TC),操作库函数的方法是:
USART_GetFlagStatus(USART1, USART_FLAG_TC);

开中断 初始NVIC 使能中断

NVIC

使能

对于第二个函数参数,标志使能串口的类型,例如:

要注意,中断分组

获取中断状态

当我们使能某个中断后,中断发生了会置状态寄存器中某一标志位,可以通过调用下面这个库函数,获取这个中断是哪种中断。

返回值为SET,说明是串口发送完成中断发生

中断服务函数

USART硬件知识

至少需要两个引脚RX TX

RX:接收数据输入,串行数据输入引脚

TX:发送数据输出引脚。若关闭发送器,该输出引脚模式由其IO口端口配置决定,如果使能了发送器但没有待发送的数据,则TX引脚处于高电平。

注意: 数据发送期间不应复位 TE 位。发送期间复位 TE 位会冻结波特率计数器,从而将损坏 TX 脚上的数据。当前传输的数据将会丢失。
使能 TE 位后,将会发送空闲帧。
连接

代码

usart.c

void uart_init(u32 bound){
   //GPIO端口设置
  GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟
 
	//串口1对应引脚复用映射
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9复用为USART1
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10复用为USART1
	
	//USART1端口配置
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	//速度50MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
	GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10

   //USART1 初始化设置
	USART_InitStructure.USART_BaudRate = bound;//波特率设置
	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_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式
  USART_Init(USART1, &USART_InitStructure); //初始化串口1
	
  USART_Cmd(USART1, ENABLE);  //使能串口1 
	
	//USART_ClearFlag(USART1, USART_FLAG_TC);
		
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断

	//Usart1 NVIC 配置
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器、

	
}
void USART1_IRQHandler(void)                	//串口1中断服务程序
{
	u8 Res;
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
	{
		Res =USART_ReceiveData(USART1);//(USART1->DR);	//读取接收到的数据
		
		if((USART_RX_STA&0x8000)==0)//接收未完成
		{
			if(USART_RX_STA&0x4000)//接收到了0x0d
			{
				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
				else USART_RX_STA|=0x8000;	//接收完成了 
			}
			else //还没收到0X0D
			{	
				if(Res==0x0d)USART_RX_STA|=0x4000;
				else
				{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
				}		 
			}
		}   		 
  } 

} 

外部中断

STM32F407 22 个外部中断为:
EXTI 线 0~15:对应外部 IO 口的输入中断。
EXTI 线 16:连接到 PVD 输出。
EXTI 线 17:连接到 RTC 闹钟事件。
EXTI 线 18:连接到 USB OTG FS 唤醒事件。
EXTI 线 19:连接到以太网唤醒事件。
EXTI 线 20:连接到 USB OTG HS(FS 中配置)唤醒事件。
EXTI 线 21:连接到 RTC 入侵和时间戳事件。
EXTI 线 22:连接到 RTC 唤醒事件
GPIO 的管脚 GPIOx.0~GPIOx.15(x=A,B,C,D,E,F,G,H,I)分别对应中断线 0~15。这
样每个中断线对应了最多 9 IO 口,以线 0 为例:它对应了 GPIOA.0GPIOB.0GPIOC.0
GPIOD.0GPIOE.0GPIOF.0GPIOG.0,GPIOH.0,GPIOI.0。而中断线每次只能连接到 1 IO
口上,这样就需要通过配置来决定对应的中断线配置到哪个 GPIO 上了.

开启外部中断步骤

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值