正点原子STM32F103综合课程操作学习笔记(包含代码)

整理资料来源

词汇解释

pin:引脚

specifies:指定

configure:配置

STM32固件库

解释:类似于C语言编程中调用printf()一样,调用标准外设库的库函数。为开发者访问底层硬件提供一个中间API

STM32F10x_StdPeriph_Lib_V3.5.0

在这里插入图片描述

Libraries:

​ CMSIS:内核库文件

CoreSuport:

Cortex-M3核内外设函数文件夹,Cortcx-M3内核通用源文件core_cm3.c和Cortcx-M3内核通用头文core_cm3.h

STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm:启动代码文件

DeviceSuport:

设备外设支持函数文件夹,STM32F0x头文件stm32f10x.h和系统初始化文件system_stm32f10x.c

​ STM32F10x_StdPeriph_Driver:

存放ST为STM32F10x每个外设而编写的库函数源代码文件和头文件

创建Template模板模板自取

Template:

​ |—Systems/CORE:相关的启动文件以及内核文件,系统初始化文件system_stm32f10x.c

​ |—FWLib/HALLIB:存放ST为STM32F10x每个外设而编写的库函数源代码文件和头文件

​ |—|---Src:.c

​ |—|---Inc:.h

​ |—BSP/OBJ:存放用户自己编写的硬件驱动文件,比如LED.c、LED.h等

​ |—USER:存放main函数和中断程序。复制stm32f10x_conf.h、stm32f10x_it.c、stm32f10x_it.h和main.c

  • 芯片型号Device:STMF103RB

  • 其中 DebugConfig 文件夹用于存储一些调试配置文件,Listings 和 Objects 文件夹用来存储 MDK 编译过程的一些中间文件(可删)

  • 把内核、启动文件、外设等相关的库文件添加进Keil工程

  • 在“Target”选项页中设置晶振为8MHz,并勾选“Use MicroLIB”(使用Micro库)

  • 在“Output”选项页中勾选“Create HEX File”,生成HEX文件

  • 在工程设置窗口的“C/C++”选项页的“Define”框中输入两个宏:
    USE_STDPERIPH_DRIVER , STM32F10X_MD

    目的:为了屏蔽编译器的默认搜索路径,转而使用添加到工程中的STM32F10x标准外设库,即使用STM32F10x标准外设库进行STM32的开发

  • 在工程设置窗口的“C/C++”选项页的“Include Paths”中,将工程头文件.h添加进搜索路径中

  • 单击Settings按钮,在弹出的界面中勾选中“Reset and Run”,选择“Erase Full Chip”,每次烧写都将擦除掉芯片上已有的内容

  • 软件模拟仿真??

错误总结

Error: L6320W: Ignoring --entry command. Cannot find argument ‘Reset_Handler’.
Warning: L6320W: Ignoring --first command. Cannot find argument ‘__Vectors’.

在这里插入图片描述

在这里插入图片描述

通用目的的输入输出口

1. GPIO:General Purpose Input/Output

端口号:端口号通常以大写字母命名,从A开始,依次类推。例如,GPIOA、GPIOB、GPIOC、…等

引脚号:每个端口有16个I/O引脚,分别命名为0-15。例如,STM32F103RB微控制器的GPIOA端口有16个引脚,分别为PA0、PA1、PA2、PA3、…、PA14和PA15。

在这里插入图片描述

IO口8种模式:

​ 1、输入浮空GPIO_Mode_IN_FLOATING:浮空(floating)就是逻辑器件的输入引脚即不接高电平,也不接低电平。由于逻辑器件的内部结构,当它输入引脚悬空时,相当于该引脚接了高电平。一般实际运用时,引脚不建议悬空,易受干扰。通俗讲就是让管脚什么都不接,浮空着。

​ 2、输入上拉GPIO_IPU:上拉就是把电位拉高,比如拉到Vcc。上拉就是将不确定的信号通过一个电阻嵌位在高电平!电阻同时起限流作用!强弱只是上拉电阻的阻值不同,没有什么严格区分。

​ 3、输入下拉GPIO_IPD:就是把电压拉低,拉到GND。与上拉原理相似

​ 4、模拟输入GPIO_AIN:应用ADC模拟输入,或者低功耗下省电

​ 5、开漏输出GPIO_OUT_OD:IO输出0接GND,IO输出1,悬空,需要外接上拉电阻,才能实现输出高电平。当输出为1时,IO口的状态由上拉电阻拉高电平,但由于是开漏输出模式,这样IO口也就可以由外部电路改变为低电平或不变。可以读IO输入电平变化,实现C51的IO双向功能

​ 6、推挽输出GPIO_OUT_PP:高低电平,IO输出0-接GND, IO输出1 -接VCC,读输入值是未知的

​ 7、复用功能的推挽输出GPIO_AF_PP:片内外设功能(I2C的SCL,SDA)

​ 8、复用功能的开漏输出GPIO_AF_OD:片内外设功能(TX1,MOSI,MISO.SCK.SS)

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

在这里插入图片描述

  • 通常有5种方式使用某个引脚功能,它们的配置方式如下:
    1、作为普通GPIO输入:根据需要配置该引脚为浮空输入、带弱上拉输入或带弱下拉输入,同时不要使能该引脚对应的所有复用功能模块。
    2、作为普通GPIO输出:根据需要配置该引脚为推挽输出或开漏输出,同时不要使能该引脚对应的所有复用功能模块。
    3、作为普通模拟输入:配置该引脚为模拟输入模式,同时不要使能该引脚对应的所有复用功能模块。
    4、作为内置外设的输入:根据需要配置该引脚为浮空输入、带弱上拉输入或带弱下拉输入,同时使能该引脚对应的某个复用功能模块。
    5、作为内置外设的输出:根据需要配置该引脚为复用推挽输出或复用开漏输出,同时使能该引脚对应的所有复用功能模块。
    注意如果有多个复用功能模块对应同一个引脚,只能使能其中之一,其它模块保持非使能状态。 比如要使用STM32F103VBT6的47、48脚的USART3功能,则需要配置47脚为复用推挽输出或复用开漏输出,配置48脚为某种输入模式,同时使能USART3并保持I2C2的非使能状态。 如果要使用STM32F103VBT6的47脚作为TIM2_CH3,则需要对TIM2进行重映射,然后再按复用功能的方式配置对应引脚。

2. IO口库函数介绍(官方库最终也是操作寄存器)

在这里插入图片描述

在这里插入图片描述

GPIO_Init参数:

​ GPIO_TypeDef* GPIOx:选择哪个GPIO端口

​ GPIO_InitTypeDef* GPIO_InitStruct:相关参数初始化

注意:`assert_param`后面 /* Check the parameters */可以获取其参数可选值。选择要获取的typedef struct信息右击Go To the Definition of "typedef struct"

(要先编译才可以跳转参数)

初始化示例代码

GPIO_InitTypeDef GPIO_InitStructure; //定义一个初始化化结构体,之后在这个结构体中设置参数	

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC , ENABLE); //时钟	

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;//这里初始化了所有引脚,前提是这些IO口的配置方式一样
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; 
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; 

GPIO_Init(GPIOC, &GPIO_InitStructure); //根据参数初始化,GPIOC组

GPIO_InitStructure.GPIO_Mode:

表示命名为GPIO_InitStructure的结构体(typedef struct)里面定义的GPIO_Mode。如下例

typedef struct
{
uint16_t GPIO_Pin; /*!< Specifies the GPIO pins to be configured.
This parameter can be any value of @ref GPIO_pins_define */

GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selected pins.
This parameter can be a value of @ref GPIOSpeed_TypeDef */

GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for the selected pins.
This parameter can be a value of @ref GPIOMode_TypeDef */
}GPIO_InitTypeDef;

跑马灯实验步骤

  1. 创建LED.c LED.h文件

    .h文件要点:

    ​ 写下公共宏定义和函数声明,可以被多个.c文件引用,避免重复代码

    ​ 头文件中使用#ifndef、#define开始,#endif结尾,避免头文件内容被重复编译

    #ifndef _LED_H
    #define _LED_H
    #include "stm32f10x.h"//必须引用,包含所有外设寄存器和结构体定义,这样就可以引用具体可函数就不会报错
    void LEDInit(void);
    void LEDdisplay(void);
    
    #endif
    
  2. 使能IO口时钟

时钟库文件stm32f10x_rcc.c源文件

void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState);
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);
  • AHB总线时钟

  • APB1低速外设:TIM2~7、TIM12、TIM13、TIM14、WWDG、SPI2、SPI3、USART2、USART3、UART4、UART5、12C1、12C2、USB、CAN1、CAN2、BKP、PWR、DAC、CEC

  • APB2高速外设:AFIO、GPIOAG、ADC12、TIM1、SPI1、TIM8、USART1、ADC3、TIM15、TIM16、TIM17、TIM9、TIM10、TIM11

例如使能APB2的GPIOC时钟:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC , ENABLE);
  1. 初始化IO口模式。调用函数GPIO_Init()
  2. 操作IO口,输出高低电平,高GPIO_SetBits()、低GPIO_ResetBits()
#include "Led.h"
#include "stm32f10x.h"
void LEDInit()
{  
	GPIO_InitTypeDef GPIO_InitStructure; 
	SystemInit();
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC , ENABLE); 
	
	//定义一个初始化结构体,因为要传入的参数不止一个
		
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; //所有引脚
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; 
	
	GPIO_Init(GPIOC, &GPIO_InitStructure); //根据参数初始化,GPIOC端口
  GPIO_Write(GPIOC,0x00);
}	

void LEDdisplay()
{ 
	u32 i;
  GPIO_SetBits(GPIOC, GPIO_Pin_All);	
	GPIO_ResetBits(GPIOC, GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_6); 
	for(i=0;i<18000000;i++) ;
	while(1)
	{
		//方法一GPIO_SetBits£¬GPIO_ResetBits
		/* 
		GPIO_ResetBits(GPIOC, GPIO_Pin_1); 
		GPIO_SetBits(GPIOC, GPIO_Pin_0);
		for(i=0;i<12000000;i++);		
		GPIO_SetBits(GPIOC, GPIO_Pin_1); 
		for(i=0;i<12000000;i++);
		GPIO_ResetBits(GPIOC, GPIO_Pin_All); 
		GPIO_SetBits(GPIOC, GPIO_Pin_1);
		for(i=0;i<18000000;i++)
		GPIO_ResetBits(GPIOC, GPIO_Pin_All); 
		GPIO_SetBits(GPIOC, GPIO_Pin_2);
		*/
		
		//方法二GPIO_Write
	GPIO_Write(GPIOC, 0xfe); 
	for(i=0;i<6000000;i++) ;
	GPIO_Write(GPIOC, 0xfd); 
	for(i=0;i<6000000;i++) ;
	GPIO_Write(GPIOC, 0xfb); 
	for(i=0;i<6000000;i++) ;
	GPIO_Write(GPIOC, 0xf7); 
	for(i=0;i<6000000;i++) ;
	GPIO_Write(GPIOC, 0xef); 
	for(i=0;i<6000000;i++) ;
	GPIO_Write(GPIOC, 0xdf); 
	for(i=0;i<6000000;i++) ;
	GPIO_Write(GPIOC, 0xbf); 
	for(i=0;i<6000000;i++) ;
	GPIO_Write(GPIOC, 0x7f); 
	for(i=0;i<6000000;i++) ; 
	}
}

蜂鸣器实验

  1. 蜂鸣器简介

    有源蜂鸣器自带了震荡电路,一通电就会发声;无源蜂鸣器则没有自带震荡电路,必须外部提供 2~5Khz 左右的方波驱动,才能发声。

  2. 硬件设计

在这里插入图片描述

蜂鸣器的驱动信号连接在 STM32 的 PB8 上。图中我们用到一个 PNP 三极管(S8550)放大电流来驱动蜂鸣器,R25 主要用于控制 PNP 管饱和导通作用。当 PB.8 输出低电平的时候,蜂鸣器将发声,当 PB.8 输出高电平的时候,蜂鸣器停止发声。

  1. 软件设计

    beep.h

#ifndef _BEEP_H
#define _BEEP_H
#include "stm32f10x.h"
#include "sys.h"

void BEEP_Init(void);

#endif

beep.c

#include "beep.h"
//蜂鸣器IO口初始化
void BEEP_Init(void){
GPIO_InitTypeDef GPIO_Initure;
	//开启GPIOB时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	GPIO_Initure.GPIO_Pin=GPIO_Pin_8;//开启蜂鸣器连接的PB8引脚 
	GPIO_Initure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
	GPIO_Initure.GPIO_Speed=GPIO_Speed_50MHz;//高速
	GPIO_Init(GPIOB,&GPIO_Initure);
	//BEEP引脚输入高电平,三级管导通,蜂鸣器发声。反之,截止,蜂鸣器关闭。
	GPIO_SetBits(GPIOB,GPIO_Pin_8);//输出1,关闭蜂鸣器输出
}

main.c

#include "stm32f10x.h"
#include "Led.h"
#include "beep.h"
//灯闪蜂鸣器响
int main(void){
	u32 i;
	LEDInit();
	GPIO_SetBits(GPIOC, GPIO_Pin_All);	
	//LEDdisplay();
    BEEP_Init();
	while(1){		
		GPIO_ResetBits(GPIOC, GPIO_Pin_0);//低电平亮
		GPIO_ResetBits(GPIOB,GPIO_Pin_8);//低电平蜂鸣器启动
		for(i=0;i<1000000;i++);
		
		GPIO_SetBits(GPIOC, GPIO_Pin_0);
		GPIO_SetBits(GPIOB,GPIO_Pin_8); 
		for(i=0;i<1000000;i++);
	}
}		

按键“输入”实验

  1. 硬件设计

在这里插入图片描述

按键 KEY0 连接在 PC8 上、KEY1 连接在 PC9 上、KEY2 连接在 PD2 上、KEY_UP连接在 PA0 上。

要注意的是:KEY0、KEY1 和 KEY2 是低电平有效的,而 KEY_UP 是高电平有效的,并且外部都没有上下拉电阻,所以,需要在 STM32 内部设置上下拉

按键扫描两种思路:

1 不连续按,只检测下降沿有效。如先松开再按下

2 连续按,只要检测到低电平都有效。

  1. 软件设计

3种GPIO读取输入操作方式

  • 读取IO口输入电平调用库函数

    uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);

  • 读取IO口输入电平操作寄存器为

    GPIOx_IDR:端口输入寄存器

  • 使用位带操作读取IO口输入电平

    PEin(4) -读取GPIOE.4口电平

    PEin(n) -读取GPIOE.n口电平

按键输入实验步骤

在这里插入图片描述

main.c

#include "stm32f10x.h"
#include "Led.h"
#include "beep.h"
#include "key.h"
int main(void){
	u8 key;
	LEDInit();
	KEY_Init();
	
	while(1){;
  key=KEY_Scan(0);//得到键值,不支持连续按
	if(key){
		switch(key){
			case WK_UP_PRES://控制LED0翻转
				GPIO_ResetBits(GPIOC, GPIO_Pin_0);
				break;
			case KEY0_PRES://控制LED1翻转
				GPIO_ResetBits(GPIOC, GPIO_Pin_1);
				break;
		}
	}
	}
}	

key.h

#ifndef _KEY_H
#define _KEY_H
#include "stm32f10x.h"


#define KEY0       GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_8);//读取按键0
#define KEY1       GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_9);//读取按键1
#define KEY2       GPIO_ReadInputDataBit(GPIOD,GPIO_Pin_2);//读取按键2
#define WK_UP      GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0);//读取按键3 WK_UP


#define KEY0_PRES 1 //KEY0按下
#define KEY1_PRES 2 //KEY0按下
#define KEY2_PRES 3 //KEY0按下
#define WK_UP_PRES 4 //KEY0按下

void KEY_Init(void);
u8 KEY_Scan(u8 mode); //按键扫描函数

#endif

key.c

#include "key.h"

#define KEY0       GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_8);//读取按键0
#define KEY1       GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_9);//读取按键1
#define KEY2       GPIO_ReadInputDataBit(GPIOD,GPIO_Pin_2);//读取按键2
#define WK_UP      GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0);//读取按键3 WK_UP


//IO口初始化
void KEY_Init(void){
GPIO_InitTypeDef GPIO_Initure;
	//开启GPIOA\C\D时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD,ENABLE);

	GPIO_Initure.GPIO_Pin=GPIO_Pin_8|GPIO_Pin_9;//KEY0/1
	GPIO_Initure.GPIO_Mode=GPIO_Mode_IPU;//设置上拉输入
	GPIO_Initure.GPIO_Speed=GPIO_Speed_50MHz;//高速
	GPIO_Init(GPIOC,&GPIO_Initure);//初始化GPIO0/1
	
	//初始化WK_UP
	GPIO_Initure.GPIO_Pin=GPIO_Pin_0;
	GPIO_Initure.GPIO_Mode=GPIO_Mode_IPD;//设置下拉输入
	GPIO_Initure.GPIO_Speed=GPIO_Speed_50MHz;//高速
	GPIO_Init(GPIOA,&GPIO_Initure);

}
//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//返回值:
//0,没有任何按键按下
//KEY0_PRES,KEY0按下
//KEY1_PRES,KEY1按下
//KEY2_PRES,KEY2按下
//WKUP_PRES,WK_UP按下 
//注意此函数有响应优先级,KEY0>KEY1>KEY2>WK_UP!!
u8 KEY_Scan(u8 mode)
{	 u32 i;
	static u8 key_up=1;//按键按松开标志
	if(mode) key_up=1;  //支持连按		
  
	if(key_up && (KEY0==0||KEY1==0||KEY2==0||WK_UP==1))
	{
		for(i=0;i<18000000;i++) ;//去抖动 
		key_up=0;
		if(KEY0==0)return KEY0_PRES;
		else if(KEY1==0)return KEY1_PRES;
		else if(KEY2==0)return KEY2_PRES;
		else if(WK_UP==1)return WKUP_PRES; 
	}else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)key_up=1; 	     
	return 0;// 无按键按下
}

端口复用和重映射

  • 什么是端口复用(重要:可以用到丰富的外设资源)

在这里插入图片描述

  • 设置端口复用怎么配置

在这里插入图片描述

在这里插入图片描述

  • 什么是端口重映射

在这里插入图片描述

  • 重映射配置过程

在这里插入图片描述

查表来查看哪些端口可以重映射

在这里插入图片描述

  • 哪些情况需要开启AFIO辅助功能时钟

在这里插入图片描述

​ AFIO复用功能寄存器:

在这里插入图片描述

中断系统

1 中断优先级分组

  • 中断管理方法

在这里插入图片描述

  • 响应和抢占的区别

    1) 响应和抢占都是值越高优先级别越小

    2 )抢占可以打断抢占优先级低的,响应不可以。

    3 )在抢占优先级相同的情况下,响应优先级高的先执行

    4 )如果抢占和响应优先级都一样哪个先发生,哪个中断就先执行

2 外部中断概述

  • STM32 的每个 IO 都可以作为外部中断

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

  • STM32F103 的19 个外部中断为:

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

    线 16:连接到 PVD 输出。

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

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

STM32 是怎么把 16 个中断线和 GPIO 口一一对应起来的

在这里插入图片描述

STM32的内部中断处理机制

在这里插入图片描述

中断服务操作函数

STM32将中断服务程序统一放在标准外设库stm32f10x_it.c文件中,其中的每个中断服务函数都只有函数名,函数体都是空的,需要用户自己编写相应的函数体,但中断服务程序的函数名是不能更改的。

下面自己编写的中断服务函数举例:

//中断服务函数
void EXTI8_IRQHandler(void)//外部中断
{
    GPIO_EXTI_IRQHandler(GPIO_PIN_0);		//调用中断处理公用函数
}

void EXTI9_5_IRQHandler(void)
{
    GPIO_EXTI_IRQHandler(GPIO_PIN_8);		//调用中断处理公用函数
	GPIO_EXTI_IRQHandler(GPIO_PIN_9);		//调用中断处理公用函数
}

void EXTI2_IRQHandler(void)
{
    GPIO_EXTI_IRQHandler(GPIO_PIN_2);		//调用中断处理公用函数
}

NVIC相关库函数

NVIC(Nested vectoredinterrupt controller)嵌套向量控制器;用于中断分组,从而分配抢占优先级和响应优先级;misc.c文件里

  • 中断优先级设置步骤
	NVIC_InitTypeDef NVIC_InitStructure;
	//先NVIC中断优先级分组设置
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

	//针对每个中断设置对应的优先级
	//选择中断通道
	NVIC_InitStructure.NVIC_IRQChannel=EXTI9_5_IRQn;
	//设置抢占式优先级
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
	//设置响应式优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
	//使能中断
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	//NVIC初始化
	NVIC_Init(&NVIC_InitStructure);
  • STM32外部中断与各通道对应关系(在stm32f10x.h的typedef enum IRQn中)

    NVIC_IRQChannel:定义初始化的是哪一个中断,这个可以在stm32f10x.h文件中查到每个中断对应的名字,如USART1_IRQn;

  • 中断向量表(STM32F10x系列)

外部中断/事件控制器EXTI

stm32f10x_exti.c文件里

	EXTI_InitTypeDef EXTI_InitStructure;
	//清空中断线上的中断标志位
	EXTI_ClearITPendingBit(EXTI_Line9);
	
	//设置IO口与中断线的映射关系
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource9);
	
	//选择中断线路
	EXTI_InitStructure.EXTI_Line=EXTI_Line9;
	//设置为中断请求(事件or中断)
	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
	//设置中断触发方式为上升沿触发
	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;
	//外部中断使能
	EXTI_InitStructure.EXTI_LineCmd=ENABLE;
	//EXTI初始化中断线:触发方式
	EXTI_Init(&EXTI_InitStructure);

按键控制中断实验步骤

在这里插入图片描述

EXIT_KEY.c

#include "key.h"
//IO口初始化
void KEY_Init(void){
    GPIO_InitTypeDef GPIO_Initure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD,ENABLE);//开启GPIOA\C\D时钟	

	GPIO_Initure.GPIO_Pin=GPIO_Pin_8|GPIO_Pin_9;//KEY0/1
	GPIO_Initure.GPIO_Mode=GPIO_Mode_IPU;//设置上拉输入
	GPIO_Initure.GPIO_Speed=GPIO_Speed_50MHz;//高速
	GPIO_Init(GPIOC,&GPIO_Initure);//初始化GPIO0/1
	
	//初始化WK_UP
	GPIO_Initure.GPIO_Pin=GPIO_Pin_0;
	GPIO_Initure.GPIO_Mode=GPIO_Mode_IPD;//设置下拉输入
	GPIO_Initure.GPIO_Speed=GPIO_Speed_50MHz;//高速
	GPIO_Init(GPIOA,&GPIO_Initure);
}

LED.c

#include "LED.H"

void LEDInit()
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(GPIOC,&GPIO_InitStructure);
	GPIO_Write(GPIOC,0xff);
}

exit.c

#include  "EXTI_KEY.H"
void EXTI_Key_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	EXTI_InitTypeDef EXTI_InitStructure;	
	//打开GPIOC口和AFIO时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO,ENABLE);	
	//初始化PC9引脚为上拉输入模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(GPIOC,&GPIO_InitStructure);//GPIO初始化    
	
	EXTI_ClearITPendingBit(EXTI_Line9);//清空中断标志	
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource9);//选择中断引脚
	EXTI_InitStructure.EXTI_Line=EXTI_Line9;//选择中断线路
	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;//设置为中断请求
	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;//设置中断触发方式为上升沿触发
	EXTI_InitStructure.EXTI_LineCmd=ENABLE;//外部中断使能
	EXTI_Init(&EXTI_InitStructure);//EXTI初始化
		
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);//NVIC中断优先级分组设置
	NVIC_InitStructure.NVIC_IRQChannel=EXTI9_5_IRQn;//选择中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//设置抢占式优先级	
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;//设置响应式优先级
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//使能中断
	NVIC_Init(&NVIC_InitStructure);//NVIC初始化
}
#include "EXTI.h"
#include "key.h"

void EXTIInit(void){
	//初始化IO口为输入
		KEY_Init();
	//打开GPIOC口和AFIO时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO,ENABLE);
	//8
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource8);
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line=EXTI_Line8;
	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
	EXTI_InitStructure.EXTI_LineCmd=ENABLE;	
	EXTI_Init(&EXTI_InitStructure);
	//9
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource9);
	EXTI_InitStructure.EXTI_Line=EXTI_Line9;
	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
	EXTI_InitStructure.EXTI_LineCmd=ENABLE;	
	EXTI_Init(&EXTI_InitStructure);
    //key0
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitStructure.NVIC_IRQChannel=EXTI9_5_IRQn;//对应5-9中断线
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStructure);	
}

main.c

#include "beep.h"
#include "key.h"
#include "EXTI.h"

int main(void){
	u32 i;
	LEDInit();
	KEY_Init();
	EXTIInit();//
	while(1){
    for(i=0;i<20000;i++);
	}
}

中断服务函数

//添加中断处理程序
void EXTI9_5_IRQHandler(void)//按键中断
{ u32 i;	 
  if(EXTI_GetITStatus(EXTI_Line8) != RESET)//得到中断线8的状态
    {			//控制LED0翻转
       //GPIO_WriteBit(GPIOC,GPIO_Pin_0,(BitAction)((1-GPIO_ReadOutputDataBit(GPIOC,GPIO_Pin_0))));
			for(i=0;i<5;i++){		
			GPIO_ResetBits(GPIOC, GPIO_Pin_0);
			for(i=0;i<500000;i++); 
			GPIO_SetBits(GPIOC, GPIO_Pin_0); 
			for(i=0;i<500000;i++); 
			}
			EXTI_ClearITPendingBit(EXTI_Line8);	
	}			
		if(EXTI_GetITStatus(EXTI_Line9) != RESET)//得到中断线9的状态
    {			//控制LED1翻转
      
			GPIO_WriteBit(GPIOC,GPIO_Pin_1,(BitAction)((1-GPIO_ReadOutputDataBit(GPIOC,GPIO_Pin_1))));
					      				
	}
		EXTI_ClearITPendingBit(EXTI_Line9);	
}

Systick定时器

基础知识

简单定时器,24位倒计数,计到0,对于CM3、CM4内核芯片都有

通常用来做延时或实时系统的心跳时钟,这样可以节省MCU资源

用于实现延迟有两种方式——查询法、中断法

在这里插入图片描述

延时函数

基于操作寄存器的

#include    "systick.h"
//毫秒级别
void Delay_ms(uint16_t  nCount)
{	uint32_t  temp;	
	SysTick->LOAD = 9000 * nCount;//时间加载 
	SysTick->CTRL = 0x01;//Enable Systick,开始倒数 
	SysTick->VAL = 0;//清空计数器	
	do
	{
		temp = SysTick ->CTRL;
	}while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达 
	SysTick->CTRL = 0; //Disable Systick
	SysTick->VAL = 0;//清空计数器 
}
//微秒级别
void Delay_us(uint16_t  nCount)
{	uint32_t  temp;	
	SysTick->LOAD = 9 * nCount;
	SysTick->CTRL = 0x01;
	SysTick->VAL = 0;
	do
	{
		temp = SysTick ->CTRL;
	}while((temp&0x01)&&(!(temp&(1<<16))));
	SysTick->CTRL = 0;
	SysTick->VAL = 0;
}

如果基于标准外设库则只能采用中断法

 不考虑RCC及NVIC复杂设置,初始化时RCC和NVIC都作缺省设置(RCC_DeInit();NVIC_DeInit();)1、设置延时值
       SysTick_SetReload(延时时间数据);
2、使能中断
       SysTick_ITConfig(ENABLE);
3、启动SysTick
      SysTick_CounterCmd(SysTick_Counter_Enable);
4、中断服务程序为 
      void SysTick_Handler()
     {   }:  a)延时时间数据1,000,000接近1秒(174秒左右误差1秒)

STM32通用定时器

三种定时器的区别

在这里插入图片描述

通用定时器的概述

第一部分是时钟发生器。第二部分为时基单元。第三部分为输入捕获。第四部分为输出比较。第五部分包含了几个捕获/比较寄存器。

在这里插入图片描述

​ 通用定时器工作过程

STM32F1 的通用定时器是一个通过可编程预分频器(PSC)驱动的 16 位自动装载计数器(CNT)构成。STM32 的通用定时器可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)等。 使用定时器预分频器和RCC时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。STM32 的每个通用定时器都是完全独立的,没有互相共享的任何资源。

STM32F1 的通用 TIMx (TIM2、TIM3、TIM4)定时器功能包括:

1)16 位向上、向下、向上/向下自动装载计数器(TIMx_CNT)。

2)16 位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数为 1~65535 之间的任意数值。

3)4 个独立通道(TIMx_CH1~4),这些通道可以用来作为:

​ A.输入捕获

​ B.输出比较

​ C.PWM 生成(边缘或中间对齐模式)

​ D.单脉冲模式输出

4)可使用外部信号(TIMx_ETR)控制定时器和定时器互连(可以用 1 个定时器控制另外一个定时器)的同步电路。

5)如下事件发生时产生中断/DMA:

​ A.更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)

​ B.触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)

​ C.输入捕获

​ D.输出比较

​ E.支持针对定位的增量(正交)编码器和霍尔传感器电路

​ F.触发输入作为外部时钟或者按周期的电流管理

定时器库函数

stm32f10x_tim.c

//定时器参数初始化
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
typedef struct
{
uint32_t Prescaler;//预分频系数
uint32_t CounterMode; //计数方式
uint32_t Period;//自动装载值 ARR
uint32_t ClockDivision;//时钟分频因子
uint32_t RepetitionCounter;
uint32_t AutoReloadPreload;
}TIM_Base_InitTypeDef;;

该函数的 2 个参数用来设置 TIM3 的溢出时间。因为我们在 Stm32_Clock_Init 函数里面已经初始化 APB1 的时钟为 2分频,所以 APB1 的时钟为 36M,而从 STM32 的内部时钟树图得知:当 APB1 的时钟分频数为1的时候,TIM27的时钟为APB1的时钟,而如果APB1的时钟分频数不为1,那么TIM27的时钟频率将为 APB1 时钟的两倍。因此,TIM3 的时钟为 72M,再根据我们设计的 arr自动装载值 和 psc分频系数的值,就可以计算中断时间了。计算公式如下:

​ Tout= ((arr+1)*(psc+1))/Tclk;

其中:

Tclk:TIM3 的输入时钟频率(单位为 Mhz)。

Tout:TIM3 溢出时间(单位为 us)。

例:

//定时器时钟为72MHz,分频系数为7199,内部时钟源经过定时器预分频后得到72M/7200=10kHz。自动重装载为 4999,那么由公式计算得定时器周期就是500ms
TIM_TimeBaseStructure.TIM_Period=5000-1;
TIM_TimeBaseStructure.TIM_Prescaler=7200-1;//因为计数是从0开始的,所以要减1

定时器中断实现步骤

在这里插入图片描述

定时0.5s翻转IO口电平例子:

//1 定时器时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
//2 初始化定时器,配置TIM_Period,TIM_Prescaler		
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period=5000-1;//设置自动重载计数周期值
TIM_TimeBaseStructure.TIM_Prescaler=7200-1;//设置分频系数
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//定时器模式,向上计数,从0开始产生中断
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;//时间分割值
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);
//3 开启定时器中断,配置NVIC
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);//使能更新中断

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//先NVIC中断优先级分组设置
NVIC_InitTypeDef NVIC_InitStructure;//中断优先级设置
NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn;//TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;//从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//IRQ通道使能
NVIC_Init(&NVIC_InitStructure);//初始化NVIC寄存器
//4 使能定时器
TIM_Cmd(TIM2,ENABLE);
//5 中断服务函数stm32f10x_it.c里
void TIM3_IRQHandler(void){
    if(TIM_GetITStatus(TIM3,TIM_IT_Update)!=RESET){//获取定时器3中断状态(set表示1,reset表示0)
GPIO_WriteBit(GPIOC,GPIO_Pin_3,(BitAction)((1-GPIO_ReadOutputDataBit(GPIOC,GPIO_Pin_3))));//翻转PC3引脚
    TIM_ClearFlag(TIM2,TIM_IT_Update);
    }
}

PWM输出实验

PWM 简介

脉冲宽度调制(PWM)是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽度的控制,PWM 原理如图

在这里插入图片描述

改变 CCRx 的值,就可以改变 PWM 输出的占空比,改变 ARR 的值,就可以改变 PWM 输出的

频率

在这里插入图片描述

在这里插入图片描述

怎么理解定时器中的TIM通道

首先先说一下TIM可以实现的功能如下:

(1) **基础计时。**计时器依次加一不停计数,在计数到最大值自动重载(比如16位定时器计数到65535后)重新从0开始。这个功能就是我们常常说的基础定时器功能。这个功能是TIM1、TIM2、TIM3、TIM4都具有的基础功能。

(2)输入捕获。例如当外部一个PWM信号发送给单片机,单片机管脚检测到下降沿信号后开始计时,再次检测到下个下降沿后停止计数,这样就可以测量这个信号两个下降沿所需时间。这个功能叫输入捕获功能。那这个功能是不是TIM1/TIM2/TIM3/TIM4 都具有呢?答案不一定。有的TIM不均有这种功能。需要结合芯片手册进行分析

(3)输出比较。例如我们设定一个寄存器的值为500,计数器依此加一,当计数器的值小于500时输出低电平,当计数器的值大于等于500时输出高电平。这个功能同样也不是所有TIM都有的。是一些所谓的高级定时器才具有。需要结合芯片手册进行分析

(4) **PWM输出。**设置定时器中对应寄存器,设置PWM的周期,占空比等等。这个功能同样也不是所有定时器均有的,需要结合芯片进行分析(这个不均有不是说普通定时器不能产生PWM,而是不能直接通过配置PWM寄存器实现)

(5)**为电机等等设置的特殊计数器。**在电机的控制中,需要设置死区时间等等,有的TIM为了适应电机等等开发环境,具有更加高级的电机控制等等专门的定时器功能,毫无疑问,这个肯定也不是所有TIM都具有的。

说完了功能下面来说说通道。

通道是什么,我们换个说法,普通=路,这样是不是好理解一点,也就是说

TIM1有4路定时器。TIM2也有4路,TIM3也有4路,TIM4也有4路。为什么要这么多路呢?

比如我们要产生8路周期,占空比都不同的PWM信号输出,那我们可以选TIM2 CH1/CH2/CH3/CH4 还有TIM3 的CH1,CH2,CH3,CH4 这8路进行输出**,需要这么多路,就是为了**

可以输出/输入 更多的信号。输出的信号分别是哪些管脚呢。很明显就是 PA0 /PA1/PA2/PA3/PA6/PA7/PB0/PB1 这8个管脚。

在这里插入图片描述

TIMX有四个独立通道,每个独立通道分别对应着一根管脚;当我们需要实现某个功能的时候(比如输入捕获),需要找TIMX对应输入捕获的那根引脚进行配置;

PWM输出配置

通过重映射 TIM3_CH1到 PC6 上,由 TIM3_CH1 输出 PWM 改变占空比(低电平越长越亮)来控制 DS6 的亮度

在这里插入图片描述

PWM没有自己的引脚,只能PB5重映射,端口复用

void TIM3_PWMInit()
{  
	GPIO_InitTypeDef GPIO_InitStructure;   //声明一个结构体变量,用来初始化GPIO

	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//声明一个结构体变量,用来初始化定时器

	TIM_OCInitTypeDef TIM_OCInitStructure;//根据TIM_OCInitStruct中指定的参数初始化外设TIMx

	/* 1 使能定时器3和相关IO口时钟 */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	

	/* 2 配置GPIO的模式和IO口 */
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出
	GPIO_Init(GPIOC,&GPIO_InitStructure);

	/* 3开启AFIO时和钟设置完全重映射 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
    GPIO_PinRemapConfig(GPIO_FullRemap_TIM3,ENABLE);//改变指定管脚的映射	//PC7
    
    /* 4 TIM3定时器初始化 */
	TIM_TimeBaseInitStructure.TIM_Period = 900-1; //不分频,PWM 频率=72000/900=8Khz//设置自动重装载寄存器周期的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 1-1;//设置用来作为TIMx时钟频率预分频值,此处分频系数为1, 即不分频
	TIM_TimeBaseInitStructure.TIM_ClockDivision = 0;//设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//TIM向上计数模式
	TIM_TimeBaseInit(TIM3, & TIM_TimeBaseInitStructure);

	/* 5 初始化输出比较参数 */
	//PWM初始化	  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
	TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//PWM1模式
	TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//PWM输出使能
	TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;//极性低
	TIM_OC1Init(TIM3,&TIM_OCInitStructure);
	TIM_OC2Init(TIM3,&TIM_OCInitStructure);
	//注意此处初始化时TIM_OC2Init而不是TIM_OCInit,否则会出错。因为固件库的版本不一样。
	/* 6 使能预装载寄存器 */
	TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);//使能或者失能TIMx在CCR1上的预装载寄存器
	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);//使能或者失能TIMx在CCR2上的预装载寄存器
	/* 7 使能定时器 */
	TIM_Cmd(TIM3,ENABLE);//使能或者失能TIMx外设
}

main.c

#include "public.h"
u8 hour,minute,second;
int main()
{
	LEDInit();
	//TIM2Init();
	//while(1);
	u8 dir=1;//方向
	u32 Duty=0;	  	  
	TIM3_PWMInit();	 //PWM初始化
	while(1)
	{
		Delay_ms(10);
		if(dir==1)		
		{
			Duty++;
			if(Duty>100)	dir=0;				
		}
		else
		{
			Duty--;
			if(Duty==0)   dir=1;			
		}
        /* 8 不断改变比较值,达到不同占空比 */
		TIM_SetCompare1(TIM3, Duty);//设置TIMx捕获比较1寄存器值	
		TIM_SetCompare2(TIM3, Duty);//设置TIMx捕获比较2寄存器值		
	}
}

定时器输入捕获实验

输入捕获简介

在这里插入图片描述

串行通信

通信接口的背景知识

  • 处理器与外部设备通信的两种方式:并行和串行

  • 串行通信

    • 数据传输方向:单工、半双工、全双工
    • 通信方式:同步通信(带时钟同步信号传输,SPI\IIC通信接口);异步通信(不带时钟同步信号,UART通用异步收发器、单总线)

在这里插入图片描述

STM32串口通信基础

  • STM32串口通信接口

    • UART:通用异步收发器(2个)
    • USART:通用同步异步收发器(3个)
  • USART特点

在这里插入图片描述

寄存器和库函数配置

在这里插入图片描述

  • 串口波特率设置,STM32 的每个串口都一个自己独立的波特率寄存器 USART_BRR,通过设置该寄存器就可以达到配置不停波特率的目的。STM32 的串口波特率计算公式如下:

在这里插入图片描述

上式中,f 是给串口的时钟(PCLK1 用于 USART2、3、4、5,PCLK2 用于 USART1);USARTDIV 是一个无符号定点数。通过 USARTDIV 得到串口 USART_BRR 寄存器的值。**假设我们的串口 1要设置为 115200 的波特率,而 PCLK2 的时钟为 72Mhz。**这样根据上面的公式有:USARTDIV=72000000/(115200*16)=39.0625

那么得到:

DIV_Fraction=16*0.625=1=0X01;(USARTDIV的4位小数部分)

DIV_Mantissa=39=0X27;(USARTDIV的12位整数部分)

这样,我们就得到了 USART1->BRR 的值为 0X0271。只要设置串口 1 的 BRR 寄存器值为0X0271 就可以得到 115200 的波特率。

  • 常用库函数stm32f10x_usart.c

在这里插入图片描述

  • 串口配置一般步骤

在这里插入图片描述

#include <stdio.h>
#include "USART_Init_Config.h"
void USART_Init_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
   	USART_InitTypeDef USART_InitStructure;
    /* 1 串口时钟、GPIO时钟使能 */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);  
    USART_DeInit(USART1);//串口复位 
    /* 3 GPIO端口设置 */
    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); 
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA.10
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure); 
    /* 3 串口初始化 */
	USART_InitStructure.USART_BaudRate = 115200;//波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//数据位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位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);
    
	USART_Cmd(USART1, ENABLE); //使能串口
}
/* 8 串口数据收发 */
int fputc(int ch, FILE *f)
{
	USART_ClearFlag(USART1,USART_FLAG_TC);
    USART_SendData(USART1, (uint8_t) ch);
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
          return (ch);
}
int fgetc(FILE *f)
{    USART_ClearFlag(USART1,USART_FLAG_TC);
     while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
          return (int)USART_ReceiveData(USART1);
}

main.c

#include "public.h"
//实现USART1串口通过串口调试器接收字符“A”, LED1灯闪烁1次;接收字符“B”,LED2灯闪烁2次;接收字符“C”,LED3灯闪烁3次。
int main(void)
{  
	 LEDInit();
	 USART_Init_Config();
while(1){
	 u32 i; char ch;
		ch = getchar();
    switch(ch)
    {
     case 'A':
			 printf("   接收的指令是:%c",ch);
			 for(i=0;i<1;i++){
				GPIO_ResetBits(GPIOC,GPIO_Pin_1);
				Delay_ms(1000);
				GPIO_SetBits(GPIOC,GPIO_Pin_1);
				Delay_ms(1000);
			 }
				printf("   LED1闪烁一次");
				break;
     case 'B':
			 printf("   接收的指令是:%c",ch);
			 for(i=0;i<2;i++){
				GPIO_ResetBits(GPIOC,GPIO_Pin_2);
				Delay_ms(1000);
				GPIO_SetBits(GPIOC,GPIO_Pin_2);
				Delay_ms(1000);
			 }
				printf("   LED2闪烁两次");
			  break;
		 case 'C':
			 printf("   接收的指令是:%c",ch);
				for(i=0;i<3;i++){
				GPIO_ResetBits(GPIOC,GPIO_Pin_3);
				Delay_ms(1000);
				GPIO_SetBits(GPIOC,GPIO_Pin_3);
				Delay_ms(1000);
			 }
				printf("   LED3闪烁三次");
			  break;
 }
}
    //使USART1串口发送字符串“Hello World!”,将字符串显示在串口调试器中
//     char Temp;
//   while(1)
//   {
//      if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE))
//      {  USART_ClearFlag(USART1,USART_FLAG_TC);
//          Temp = USART_ReceiveData(USART1);//发送数据到串口,这里要用到XCOM串口调试软件
//          USART_SendData(USART1,Temp);//接受数据,从DR读取接受到的数据
//          USART_ClearFlag(USART1,USART_FLAG_RXNE); 
//      }	
//  }
}

DMA的原理与配置

STM32 DMA 简介——作用:为CPU减负

DMA,全称为:Direct Memory Access,即直接存储器访问,DMA 传输将数据从一个地址空间复制到另外一个地址空间。当 CPU 初始化这个传输动作,传输动作本身是由DMA 控制器来实行和完成。**DMA 传输方式无需 CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路,能使 CPU 的效率大为提高。**STM32 最多有 2 个 DMA 控制器(DMA2 仅存在大容量产品中,中容量只有 DMA1),DMA1 有 7个通道。DMA2 有 5 个通道。每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁起来协调各个 DMA 请求的优先权。

STM32 的 DMA 有以下一些特性:

●每个通道都直接连接专用的硬件 DMA 请求,每个通道都同样支持软件触发。这些功能通过软件来配置。

●在七个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),假如在相等优先权时由硬件决定(请求 0 优先于请求 1,依此类推) 。

●独立的源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐。

●支持循环的缓冲器管理

●每个通道都有 3 个事件标志(DMA 半传输,DMA 传输完成和 DMA 传输出错),这 3 个事件标志逻辑或成为一个单独的中断请求。

●存储器和存储器间的传输

●外设和存储器,存储器和外设的传输

●闪存、SRAM、外设的 SRAM、APB1 APB2 和 AHB 外设均可作为访问的源和目标。

●可编程的数据传输数目:最大为 65536

STM32F103RBT6 有一个 DMA 控制器,DMA1,本章,我们仅针对 DMA1 进行介绍。从外设(TIMx、ADC、SPIx、I2Cx 和 USARTx)产生的 DMA 请求,通过逻辑或输入到DMA 控制器,这就意味着同时只能有一个请求有效。外设的 DMA 请求,可以通过设置相应的外设寄存器中的控制位,被独立地开启或关闭

版权声明:本文为CSDN博主「Inifite_v胜」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_47681071/article/details/118404776

生成海报
点赞 0

Inifite_v胜

我还没有学会写个人说明!

暂无评论

相关推荐

基于STM32的室内环境监测系统

设计简介: 本设计是基于单片机的室内环境监测器,主要实现以下功能: 可实现LCD1602显示环境数据实时监测一氧化碳、甲烷、烟雾值空气质量大于各自限值报警,并通过TTS报警 标签&#x

基于stm32f407的示波器

一.设计要求 二.整体思路 硬件部分主要负责电压的缩放以及垂直灵敏度的控制,因为stm32的大部分引脚最高输入电压为3.3v,而要求的电压需要50v,需要进行电压缩放。 软件部分主要负责方波的实现&#x

实验一 stm32F407VETx点亮流水灯

二、设计指标 使电路板上的8个LED轮流点亮,并按键控制点亮速度。 三、操作 1、CubeMX操作 1.1依据开发板LED引脚设置CubeMX中8个LED的引脚为GPIO_Output模式, 2、按键设置