STM32学习记录(2021.1)

STM32学习

在这里插入图片描述

基础知识

在这里插入图片描述

对于具体开发过程中IO口的使用查阅硬件资料里的数据手册即可!(标注FT的都可以5V)

小结

和51单片机相比,操作寄存器这种方法的劣势是你需要去掌握每个寄存器的用法,你才能正确使用
STM32,而对于 STM32 这种级别的 MCU,数百个寄存器记起来又是谈何容易。此时调用函数就成了首选,固件库就是函数的集合,固件库函数的作用是向下负责与寄存器直接打交道,向上提供用户函数调用的接口(API)。直接吃饭的感觉真爽!!
 
 

FlyMcu下载程序

还有一种ST-LINK下载
在这里插入图片描述

在这里插入图片描述

MDK仿真

在这里插入图片描述

小结

32的下载真的没什么花里胡哨的,按部就班的来就行了啊。
 
 

STM32时钟系统

STM32 的时钟系统比较复杂,不像简单的 51 单片机一个系统时钟就可以解决一切。
在这里插入图片描述

实战

STM32 的 IO 口相比 51 而言要复杂得多,所以使用起来也困难很多。首先 STM32 的 IO 口
可以由软件配置成如下 8 种模式:

  • 1、输入浮空
  • 2、输入上拉
  • 3、输入下拉
  • 4、模拟输入
  • 5、开漏输出
  • 6、推挽输出
  • 7、推挽式复用功能
  • 8、开漏复用功能

IO口的使用

在固件库开发中,操作寄存器 CRH 和 CRL 来配置 IO 口的模式和速度是通过 GPIO 初始化
函数完成:
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
这个函数有两个参数,
第一个参数是用来指定 GPIO,取值范围为 GPIOA~GPIOG。
第二个参数为初始化参数结构体指针,结构体类型为 GPIO_InitTypeDef

初始化单个IO口
通过初始化结构体初始化 GPIO 的常用格式是:
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;  //LED0-->PB.5 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度 50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure);//根据设定参数配置 GPIO
初始化多个IO口
初始化多个 IO 口的方式可以是如下:
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5| GPIO_Pin_6| GPIO_Pin_7; //指定端口
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //端口模式:推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化

这里需要注意的是:在配置 STM32 外设的时候,任何时候都要先使能该
外设的时钟。GPIO 是挂载在 APB2 总线上的外设,在固件库中对挂载在 APB2 总线上的外设时
钟使能是通过函数 RCC_APB2PeriphClockCmd()来实现的。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|
RCC_APB2Periph_GPIOE, ENABLE);  //使能 GPIOB,GPIOE 端口时钟
这行代码的作用是使能 APB2 总线上的 GPIOB 和 GPIOE 的时钟

LED实验

  1. 首先,找到之前 3.3 节新建的 Template 工程,在该文件夹下面新建一个 HARDWARE 的文
    件夹,用来存储以后与硬件相关的代码,然后在 HARDWARE 文件夹下新建一个 LED 文件夹,
    用来存放与 LED 相关的代码。
    2.然后我们打开 USER 文件夹下的 LED.uvprojx 工程(如果是使用的上面新建的工程模板,那
    么就是 Template. uvprojx,大家可以将其重命名为 LED. uvprojx),按 按钮新建一个文件,然
    后保存在 HARDWARE->LED 文件夹下面,保存为 led.c。在该文件中输入如下代码:
#include "led.h"
//初始化 PB5 和 PE5 为输出口.并使能这两个口的时钟
//LED IO 初始化
void LED_Init(void)
{
    
    
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|
RCC_APB2Periph_GPIOE, ENABLE); //使能 PB,PE 端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PB.5 推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_5);  //PB.5 输出高
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED1-->PE.5 推挽输出
GPIO_Init(GPIOE, &GPIO_InitStructure); 
GPIO_SetBits(GPIOE,GPIO_Pin_5); /PE.5 输出高
}

3.保存 led.c 代码,然后我们按同样的方法,新建一个 led.h 文件,也保存在 LED 文件夹下面。在 led.h 中输入如下代码:

#ifndef __LED_H
#define __LED_H
#include "sys.h"
//LED 端口定义
#define LED0 PBout(5)// DS0
#define LED1 PEout(5)// DS1 
void LED_Init(void);//初始化 
#endif
这段代码里面最关键就是 2 个宏定义:
#define LED0 PBout(5)// DS0
#define LED1 PEout(5)// DS1 

4.接下来我们将 led.h 也保存一下。接着,我们在 Manage Components 管理里面新建一个
HARDWARE 的组,并把 led.c 加入到这个组里面,

5.回到工程,然后你会发现在 Project Workspace 里面多了一个 HARDWARE 的组,
在改组下面有一个 led.c 的文件。

蜂鸣器实验

.c代码

#include "beep.h"
//初始化 PB8 为输出口.并使能这个口的时钟
//LED IO 初始化
void BEEP_Init(void)
{
    
    
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
//使能 GPIOB 端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;  //BEEP-->GPIOB.8 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度为 50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure);  //根据参数初始化 GPIOB.8
GPIO_ResetBits(GPIOB,GPIO_Pin_8); //输出 0,关闭蜂鸣器输出
}

.h代码

#ifndef __BEEP_H
#define __BEEP_H
#include "sys.h"
//蜂鸣器端口定义
#define BEEP PBout(8) // BEEP,蜂鸣器接口 
void BEEP_Init(void); //初始化
#endif

mian.c代码

#include "sys.h" 
#include "delay.h"
#include "led.h"
#include "beep.h"
//ALIENTEK 精英 STM32 开发板实验 2
//蜂鸣器实验
int main(void)
{
    
    
delay_init(); //延时函数初始化
LED_Init();  //初始化与 LED 连接的硬件接口
BEEP_Init(); //初始化蜂鸣器端口
while(1)
{
    
     LED0=0;
BEEP=0;
delay_ms(300);
LED0=1;
BEEP=1;
delay_ms(300);
}
}

按键实验

.c代码

#include "key.h"
#include "sys.h"
#include "delay.h"
//按键初始化函数
void KEY_Init(void) //IO 初始化
{
    
    
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|
RCC_APB2Periph_GPIOE,ENABLE); //使能 PORTA,PORTE 时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4;//GPIOE.3~4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
GPIO_Init(GPIOE, &GPIO_InitStructure); //初始化 GPIOE3,4
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //初始化 WK_UP-->GPIOA.0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 设置成输入,下拉
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.0
}
//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//0,没有任何按键按下
//1,KEY0 按下
//2,KEY1 按下
//3,KEY3 按下 WK_UP
//注意此函数有响应优先级,KEY0>KEY1>KEY_UP!!
u8 KEY_Scan(u8 mode)
{
    
     
static u8 key_up=1;//按键按松开标志
if(mode)key_up=1; //支持连按
if(key_up&&(KEY0==0||KEY1==0||WK_UP==1))
{
    
    
delay_ms(10);//去抖动
key_up=0;
if(KEY0==0)return KEY0_PRES;
else if(KEY1==0)return KEY1_PRES;
else if(WK_UP==1)return WKUP_PRES;
}else if(KEY0==1&&KEY1==1&&WK_UP==0)key_up=1;
return 0;// 无按键按下
}

.h代码

#ifndef __KEY_H
#define __KEY_H
#include "sys.h"
#define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)//读取按键 0
#define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)//读取按键 1
#define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)//读取按键 WK_UP)
#define KEY0_PRES 1  //KEY0 按下
#define KEY1_PRES  2  //KEY1 按下
#define WKUP_PRES 3  //WK_UP 按下(
void KEY_Init(void); //IO 初始化
u8 KEY_Scan(u8); //按键扫描函数
#endif

main.c代码

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

串口实验

串口设置的一般步骤可以总结为如下几个步骤:

  1. 串口时钟使能,GPIO 时钟使能
  2. 串口复位
  3. GPIO 端口模式设置
  4. 串口参数初始化
  5. 开启中断并且初始化 NVIC(如果需要开启中断才需要这个步骤)
  6. 使能串口
  7. 编写中断处理函数

.c代码

//初始化 IO 串口 1
//bound:波特率
void uart_init(u32 bound)
{
    
    
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//①串口时钟使能,GPIO 时钟使能,复用时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|
RCC_APB2Periph_GPIOA, ENABLE); //使能 USART1,GPIOA 时钟
//②串口复位
USART_DeInit(USART1); //复位串口 1
//③GPIO 端口模式设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //ISART1_TX PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //USART1_RX PA.10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.10
//④串口参数初始化
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); //初始化串口
#if EN_USART1_RX //如果使能了接收
//⑤初始化 NVIC
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ; //抢占优先级 3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级 3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能
NVIC_Init(&NVIC_InitStructure);  //中断优先级初始化
//⑤开启中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //开启中断
#endif
//⑥使能串口
USART_Cmd(USART1, ENABLE); //使能串口
}

main.c

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
int main(void)
{
    
    
u8 t,len;
u16 times=0;
delay_init(); //延时函数初始化 
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 设 置 NVIC 中 断 分 组 2
uart_init(115200); //串口初始化波特率为 115200
LED_Init();  //LED 端口初始化
KEY_Init(); //初始化与按键连接的硬件接口
while(1)
{
    
    
if(USART_RX_STA&0x8000)
{
    
     len=USART_RX_STA&0x3f; //得到此次接收到的数据长度
printf("\r\n 您发送的消息为:\r\n\r\n");
for(t=0;t<len;t++)
{
    
     USART_SendData(USART1, USART_RX_BUF[t]); //向串口 1 发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
//等待发送结束
}
printf("\r\n\r\n"); //插入换行
USART_RX_STA=0;
}else
{
    
     times++;
if(times%5000==0)
{
    
     printf("\r\n 精英 STM32 开发板 串口实验\r\n");
printf("正点原子@ALIENTEK\r\n\r\n");
}
if(times%200==0)printf("请输入数据,以回车键结束\n");
if(times%30==0)LED0=!LED0; //闪烁 LED,提示系统正在运行.
delay_ms(10);
}
} 
}

外部中断实验

STM32F103 的中断控制器支持 19 个外部中断事件请求。每个中断设有状态位,每个中断事件都有独立的触发和屏蔽设置。STM32F103 的
19 个外部中断为:

线 0~15:对应外部 IO口的输入中断。

线 16:连接到 PVD 输出。

线 17:连接到 RTC 闹钟事件。

线 18:连接到 USB 唤醒事件。

从上面可以看出,STM32 供 IO 口使用的中断线只有 16 个

STM32 的 IO 口外部中断函数只有 6 个,

分别为:

EXPORT EXTI0_IRQHandler
EXPORT EXTI1_IRQHandler
EXPORT EXTI2_IRQHandler
EXPORT EXTI3_IRQHandler
EXPORT EXTI4_IRQHandler
EXPORT EXTI9_5_IRQHandler
EXPORT EXTI15_10_IRQHandler

中断线 0-4 每个中断线对应一个中断函数,
中断线 5-9 共用中断函数 EXTI9_5_IRQHandler,
中断线 10-15 共用中断函数EXTI15_10_IRQHandler。
在编写中断服务函数的时候会经常使用到两个函数,
第一个函数是判断某个中断线上的中断是否发生(标志位是否置位):

ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
这个函数一般使用在中断服务函数的开头判断中断是否发生。

另一个函数是清除某个中断线上的中断标志位:

void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
这个函数一般应用在中断服务函数结束之前,清除中断标志位。

IO口外部中断的一般步骤
1)初始化 IO 口为输入。
2)开启 AFIO 时钟。
3)设置 IO 口与中断线的映射关系。。
4)初始化线上中断,设置触发条件等。。
5)配置中断分组(NVIC),并使能中断。。
6)编写中断服务函数。。
通过以上几个步骤的设置,我们就可以正常使用外部中断了。

串行通信

在这里插入图片描述
在这里插入图片描述
usart.c

#include "sys.h"
#include "usart.h"	  
#include "delay.h"	  

 
串口1中断服务程序
//u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
接收状态
bit15,	接收完成标志
bit14,	接收到0x0d
bit13~0,	接收到的有效字节数目
//u16 USART_RX_STA=0;       //接收状态标记	  
  
void uart_init(u32 bound){
    
    
  //GPIO端口设置
  GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
//	NVIC_InitTypeDef NVIC_InitStructure;
	 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟
  
	//USART1_TX   GPIOA.9
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
   
  //USART1_RX	  GPIOA.10初始化
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

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

	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_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
  USART_Cmd(USART1, ENABLE);                    //使能串口1 

}

//void USART1_IRQHandler(void)                	//串口1中断服务程序
//{
    
    
//	u8 Res;
//	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  
//	{
    
    
//		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
//		Res =USART_ReceiveData(USART1);	//读取接收到的数据
//		
//		if(Res==0x01)
//			{
    
    
//			GPIO_ResetBits(GPIOB,GPIO_Pin_12);
//			delay_ms(1000);
//			GPIO_SetBits(GPIOB,GPIO_Pin_12);
//			delay_ms(1000);

//			}   		 
//    } 
//} 

usart.h

#ifndef __USART_H
#define __USART_H
#include "stdio.h"	
#include "sys.h" 
#define USART_REC_LEN  			200  	//定义最大接收字节数 200
#define EN_USART1_RX 			1		//使能(1)/禁止(0)串口1接收
	  	
extern u8  USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
extern u16 USART_RX_STA;         		//接收状态标记	
//如果想串口中断接收,请不要注释以下宏定义
void uart_init(u32 bound);
#endif

猜你喜欢

转载自blog.csdn.net/k1507157/article/details/113465963