部分c语言知识回顾
结构体
声明结构体类型:

结构体成员变量的引用方法:结构体变量名字.成员名
例:要引用 usart1 的成员 BaudRate,方法是:usart1.BaudRate;
结构体的作用:
当初始化一个函数时:

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

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

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




//使能外设时钟
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按一次反转状态。

软件
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);
}
}
串口通信
相应库函数
串口挂载在APB2下面的外设,使能函数
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能 GPIOA 时钟
设置引脚复用器映射

GPIO端口模式设置

串口参数设置

//发送数据
void USART_SendData(USART_TypeDef*USARTx,uint16_t Data)
//读取串口接收的数据
uint16_t USART_ReceiveData(USART_TypeDef*USARTx)
串口的状态

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引脚处于高电平。






代码
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;//接收数据错误,重新开始接收
}
}
}
}
}
外部中断

1160

被折叠的 条评论
为什么被折叠?



