STM32入门学习 第三天

提示:今天是STM32入门学习的第三天,重点学习了优先级,串口等


 

目录

第一讲 C语言的复习和寄存器地址名称映射 

1.C语言复习

2.MDK中寄存器地址名称映射分析

第二讲 STM32 时钟系统 

第四讲 systeminit时钟初始化函数解析

第五讲 21讲 Systick滴答定时器-延时函数讲解

第六讲 端口复用及重映射

1.端口复用功能

2.端口重映射 

第七讲 NVIC中断优先级

1.优先级的了解 

2.库函数设置

第八讲 串口通信基本原理 

1.串行通信接口背景知识

2.STM32F1串口框图讲解

3.STM32串口常用寄存器和库函数 

        4.串口配置一般步骤(手把手写串口实例)

        5.串口通信实验讲解


第一讲 C语言的复习和寄存器地址名称映射 

1.C语言复习

1.1 位操作

1) 不改变其他位的值的状况下,对某几个位进行设值。

GPIOA->CRL&=0XFFFFFF0F; //将第 4-7 位清 0
GPIOA->CRL|=0X00000040; //设置相应位的值,不改变其他位的值

2) 移位操作提高代码的可读性。

3) ~取反操作使用技巧

1.2 define 宏定义

define 是 C 语言中的预处理命令,它用于宏定义,可以提高源代码的可读性,为编程提供 方便。常见的格式: #define 标识符 字符串 “标识符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等。

例如: #define SYSCLK_FREQ_72MHz 72000000   //定义标识符 SYSCLK_FREQ_72MHz 的值为 72000000。

1.3 extern 变量申明

C 语言中 extern 可以置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。这里面要注意,对于 extern 申明变量可以多 次,但定义只有一次。

在我们的代码中你会看到看到这样的语句: extern u16 USART_RX_STA; 这个语句是申明 USART_RX_STA 变量在其他文件中已经定义了,在这里要使用到。所以,你肯定 可以找到在某个地方有变量定义的语句: u16 USART_RX_STA; 的出现。下面通过一个例子说明一下使用方法。 在 Main.c 定义的全局变量 id,id 的初始化都是在 Main.c 里面进行的。

1.4 typedef 类型别名

typedef 用于为现有类型创建一个新的名字,或称为类型别名,用来简化变量的定义。 typedef 在 MDK 用得最多的就是定义结构体的类型别名和枚举类型了。 

1.5 ifdef条件编译 

单片机程序开发过程中,经常会遇到一种情况,当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。条件编译命令最常见的形式为:

#ifdef 标识符 
程序段1 
#else 
程序段2 
#endif 

1.6 C语言关键字 :static 

Static申明的局部变量,存储在静态存储区。它在函数调用结束之后,不会被释放。它的值会一直保留下来。所以可以说static申明的局部变量,具有记忆功能。
MCU ,一切底层配置,最终都是配置寄存器。

2.MDK中寄存器地址名称映射分析

2.1 51中映射方法:

sfr P0 =0x80;//P0映射到地址0x80

P0=0x00//忘寄存器地址0x80赋值0x00

2.2 STM32中操作:
GPIOA->ODR=0x00000000;

0x00000000是赋值给了GPIOA的ODR寄存器地址也就是说GPIOA->ODR这种写法。

#define PERIPH_BASE           ((uint32_t)0x40000000)//先找外设地址

#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)//再找APS2

#define GPIOA_BASE            (APB2PERIPH_BASE + 0x0800)//然后找那个IO口

#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)//控制寄存器

 

第二讲 STM32 时钟系统 

时钟系统是 CPU 的脉搏,就像人的心跳一样。 STM32 的时钟系统比较复杂,不像简单的 51 单片机一个系统时钟就可以解决一切。 因为首先 STM32 本身非常复杂,外设非常的多,但是并不是所有外设都需要系统时钟这么高的频率,比如看门狗以及 RTC 只需要几十 k 的时钟即可。同一个电路,时钟越快功耗越大,同时抗电磁干扰能力也会越弱,所以对于较为复杂的 MCU 一般都是采取多时钟源的方法来解决这些问题。 

1. 时钟系统框图讲解

其中蓝色为时钟源、灰色为选择器、而PLL为锁相环主要是倍频、绿色为分屏器

在 STM32 中,有五个时钟源,为 HSI、HSE、LSI、LSE、PLL。从时钟频率来分可以分为 高速时钟源和低速时钟源,在这 5 个中 HIS,HSE 以及 PLL 是高速时钟,LSI 和 LSE 是低速时 钟。从来源可分为外部时钟源和内部时钟源,外部时钟源就是从外部通过接晶振的方式获取时 钟源,其中 HSE 和 LSE 是外部时钟源,其他的是内部时钟源。下面我们看看 STM32 的 5 个时 钟源,我们讲解顺序是按图中红圈标示的顺序:

①HSI 是高速内部时钟,RC 振荡器,频率为 8MHz。

②HSE 是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。我们的开发板接的是 8M 的晶振。

③LSI 是低速内部时钟,RC 振荡器,频率为 40kHz。独立看门狗的时钟源只能是 LSI,同 时 LSI 还可以作为 RTC 的时钟源。

④LSE 是低速外部时钟,接频率为 32.768kHz 的石英晶体。这个主要是 RTC 的时钟源。

⑤PLL 为锁相环倍频输出,其时钟输入源可选择为 HSI/2、HSE 或者 HSE/2。倍频可选择为2~16 倍,但是其输出频率最大不得超过 72MHz。

2. 系统时钟SYSCLK可来源于三个时钟源:
①HSI振荡器时钟

②HSE振荡器时钟

③PLL时钟

3.STM32可以选择一个时钟信号输出到MCO脚(PA8)上,可以选择为PLL输出的2分频、HSI、HSE、或者系统时钟。

4.任何一个外设在使用之前,必须首先使能其相应的时钟。

几个重要的时钟: SYSCLK( 系统时钟 ) 、AHB总线时钟 、APB1总线时钟 ( 低速 ): 速度最高 36MHz、APB2总线时钟 ( 高速 ): 速度最高 72MHz、PLL时钟

 

RCC相关的寄存器

typedef struct

{

  __IO uint32_t CR;                //HSI,HSE,CSS,PLL等的使能和就绪标志位

  __IO uint32_t CFGR;           //PLL等的时钟源选择,分频系数设定

  __IO uint32_t CIR;               // 清除/使能 时钟就绪中断

  __IO uint32_t APB2RSTR;  //APB2线上外设复位寄存器

  __IO uint32_t APB1RSTR;   //APB1线上外设复位寄存器

  __IO uint32_t AHBENR;    //DMASDIO等时钟使能

  __IO uint32_t APB2ENR;   //APB2线上外设时钟使能

  __IO uint32_t APB1ENR;   //APB1线上外设时钟使能

  __IO uint32_t BDCR;        //备份域控制寄存器

  __IO uint32_t CSR;           //控制状态寄存器

} RCC_TypeDef;

2.时钟配置相关函数讲解

 

第四讲 systeminit时钟初始化函数解析

初始化之前首先通过宏定义定义系统时钟频率:

     #define SYSCLK_FREQ_72MHz  72000000

初始化之后的状态:

     SYSCLK         72MHz

     AHB                72MHz

     PCLK1           36MHz

     PCLK2           72MHz

     PLL                72MHz

初始化之后可以通过变量 SystemCoreClock 获取系统变量。如果 SYSCLK=72MHz ,那么变量 SystemCoreClock =72000000

 

第五讲 21讲 Systick滴答定时器-延时函数讲解

第六讲 端口复用及重映射

1.端口复用功能

STM32 有很多的内置外设,这些外设的外部引脚都是与 GPIO 复用的。也就是说,一个 GPIO 如果可以复用为内置外设的功能引脚,那么当这个 GPIO 作为内置外设使用的时候,就叫做复用。 这部分知识在《STM32 中文参考手册 V10》的 P109,P116~P121 有详细的讲解哪些 GPIO 管脚是 可以复用为哪些内置外设的。大家都知道,MCU 都有串口,STM32 有好几个串口。比如说 STM32F103ZET6 有 5 个串口,我 们可以查手册知道,串口 1 的引脚对应的 IO 为 PA9,PA10.PA9,PA10 默认功能是 GPIO,所以当 PA9,PA10 引脚作为串口 1 的 TX,RX 引脚使用的时候,那就是端口复用。 

复用端口初始化有几个步骤:

1) GPIO 端口时钟使能。要使用到端口复用,当然要使能端口的时钟了。 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

2) 复用的外设时钟使能。比如你要将端口 PA9,PA10 复用为串口,所以要使能串口时钟。 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

3) 端口模式配置。

在 IO 复用位内置外设功能引脚的时候,必须设置 GPIO 端口的模式,至于 在复用功能下 GPIO 的模式是怎么对应的,这个可以查看手册《STM32 中文参考手册 V10》 P110 的表格“8.1.11 外设的 GPIO 配置”。这里我们拿 Usart1 举例:

从表格中可以看出,我们要配置全双工的串口 1,那么 TX 管脚需要配置为推挽复用输出, RX 管脚配置为浮空输入或者带上拉输入。

我们在使用复用功能的是时候,最少要使能 2 个时钟:

1) GPIO 时钟使能                 2) 复用的外设时钟使能 

同时要初始化 GPIO 以及复用外设功能

2.端口重映射 

为了使不同器件封装的外设 IO 功能数量达到最优,可以把一些复用功能重新映射到其他一 些引脚上。STM32 中有很多内置外设的输入输出引脚都具有重映射(remap)的功能。我们知道每 个内置外设都有若干个输入输出引脚,一般这些引脚的输出端口都是固定不变的,为了让设计 工程师可以更好地安排引脚的走向和功能,在 STM32 中引入了外设引脚重映射的概念,即一个 外设的引脚除了具有默认的端口外,还可以通过设置重映射寄存器的方式,把这个外设的引脚 映射到其它的端口。 简单的讲就是把管脚的外设功能映射到另一个管脚,但不是可以随便映射的。

具体对应关 系《STM32 中文参考手册 V10》的 P116 页“8.3 复用功能和调试配置”有讲解。这里我们同样 拿串口 1 为例来讲解。 

 从表中可以看出,默认情况下,串口 1 复用的时候 的引脚位 PA9,PA10,同时我们可以将 TX 和 RX 重新映射到管脚 PB6 和 PB7 上面去。

所以重映射我们同样要使能复用功能的时候讲解的 2 个时钟外,还要使能 AFIO 功能时钟,然后 要调用重映射函数。详细步骤为:

1)使能 GPIOB 时钟: RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

2)使能串口 1 时钟: RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

3)使能 AFIO 时钟: RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

4)开启重映射: GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);根据第一个参数,来确定是部分重映射还是全部重映射

这样就将串口的 TX 和 RX 重映射到管脚 PB6 和 PB7 上面了。

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

AFIO_MAPR :配置复用功能重映射
AFIO_EXTICRX: 配置外部中断线映射
AFIO_EVCR:   配置 EVENTOUT 事件输出

其他重映射,大家 除了查看中文参考手册之外,还可以从 GPIO_PinRemapConfig 函数入手查看第一个入口参数的 取值范围可以得知。

USART1 只有一种重映射,而对于 USART3,存在部分重映射和完全重映射。所谓部分重映射就是部分管脚和默认的是一样的,而部分管脚是重新映射到其他管脚。而完全重 映射就是所有管脚都重新映射到其他管脚。看看手册中的 USART3 重映射表: 

部分重映射就是 PB10,PB11,PB12 重映射到 PC10,PC11,PC12 上。而 PB13 和 PB14 和没有重映射 情况是一样的,都是 USART3_CTS 和 USART3_RTS 对应管脚。完全重映射就是将这两个脚重新映 射到 PD11 和 PD12 上去。我们要使用 USART3 的部分重映射,我们调用函数方法为: GPIO_PinRemapConfig(GPIO_PartialRemap_USART3, ENABLE);

第七讲 NVIC中断优先级

1.优先级的了解 

中断优先级设置的步骤:

1. 系统运行开始的时候设置中断分组。确定组号,也就是确定抢占优先级和子优先级的 分配位数。调用函数为 NVIC_PriorityGroupConfig();

2. 设置所用到的中断的中断优先级别。对每个中断调用函数为 NVIC_Init();

们总结一下 中断优先级设置的步骤: 1. 系统运行开始的时候设置中断分组。确定组号,也就是确定抢占优先级和子优先级的 分配位数。调用函数为 NVIC_PriorityGroupConfig(); 2. 设置所用到的中断的中断优先级别。对每个中断调用函数为 NVIC_Init();

CM3 内核支持 256 个中断,其中包含了 16 个内核中断和 240 个外部中断,并且具有 256 级的可编程中断设置。STM32并没有使用 CM3 内核的全部东西,而是只用了它的一部分。STM32有 84 个中断,包括 16 个内核中断和 68 个可屏蔽中断,具有 16 级可编程的中断优先级。STM32F103系列上面,又只有 60 可屏蔽中断 (在 107 系列才有 68 个)

终端管理:首先,对STM32中断进行分组,组0~4。同时,对每个中断设置一个抢占优先级和一个响应优先级值。

分组配置是在寄存器SCB->AIRCR中配置:

 通过这个表,我们就可以清楚的看到组 0~4 对应的配置关系,例如组设置为 3,每个中断的中断优先寄存器的高四位中的最高 3 位是抢占优先级,低 1 位是 响应优先级。每个中断,你可以设置抢占优先级为 0~7,响应优先级为 1 或 0。抢占优先级的 级别高于响应优先级。而数值越小所代表的优先级就越高。

2.库函数设置

通过以上介绍,我们熟悉了 STM32 中断设置的大致过程。接下来我们介绍如何使用库函 数实现以上中断分组设置以及中断优先级管理,使得我们以后的中断设置简单化。NVIC 中断 管理函数主要在 misc.c 文件里面。

首先要讲解的是中断优先级分组函数 NVIC_PriorityGroupConfig,其函数申明如下:

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);

这个函数的作用是对中断的优先级进行分组,这个函数在系统中只能被调用一次,一旦分 组确定就最好不要更改。这个函数我们可以找到其实现:

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
 assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
 SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}

 从函数体可以看出,这个函数唯一目的就是通过设置 SCB->AIRCR 寄存器来设置中断优先级分 组,这在前面寄存器讲解的过程中已经讲到。而其入口参数通过双击选中函数体里面的 “IS_NVIC_PRIORITY_GROUP”然后右键“Go to defition of …”可以查看到为

#define IS_NVIC_PRIORITY_GROUP(GROUP)
(((GROUP) == NVIC_PriorityGroup_0) || 
((GROUP) == NVIC_PriorityGroup_1) || \
((GROUP) == NVIC_PriorityGroup_2) || \
((GROUP) == NVIC_PriorityGroup_3) || \
((GROUP) == NVIC_PriorityGroup_4))

 分组范围为 0-4。比如我们设置整个系统的中断优先级分组值 为 2,那么方法是: NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

这样就确定了一共为“2 位抢占优先级,2 位响应优先级”。

设置好了系统中断分组,那么对于每个中断我们又怎么确定他的抢占优先级和响应优先级 呢?下面我们讲解一个重要的函数为中断初始化函数 NVIC_Init,其函数申明为: void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct) 其中 NVIC_InitTypeDef 是一个结构体,我们可以看看结构体的成员变量:

typedef struct
{
 uint8_t NVIC_IRQChannel; 
 uint8_t NVIC_IRQChannelPreemptionPriority;
 uint8_t NVIC_IRQChannelSubPriority; 
 FunctionalState NVIC_IRQChannelCmd; 
} NVIC_InitTypeDef;

NVIC_InitTypeDef 结构体中间有三个成员变量,这三个成员变量的作用是:

NVIC_IRQChannel:定义初始化的是哪个中断,这个我们可以在 stm32f10x.h 中找到 每个中断对应的名字。例如 USART1_IRQn。

NVIC_IRQChannelPreemptionPriority:定义这个中断的抢占优先级别。

NVIC_IRQChannelSubPriority:定义这个中断的子优先级别。

NVIC_IRQChannelCmd:该中断是否使能。

我们要使能串口 1 的中断,同时设置抢占优先级为 1,子优先级位 2,初始化的方法是:

NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口 1 中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;// 抢占优先级为 1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;// 子优先级位 2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能
NVIC_Init(&NVIC_InitStructure); //根据上面指定的参数初始化 NVIC 寄存器

第八讲 串口通信基本原理 

  • 1.串行通信接口背景知识
  • 2.STM32F1串口框图讲解   
  • 3.STM32串口常用寄存器和库函数   
  • 4.串口配置方法(手把手写简单的通信实例)   
  • 5.串口通信实验讲解

1.串行通信接口背景知识

处理器与外部设备通信的两种方式:

并行通信

  • 传输原理:数据各个位同时传输。
  • 优点:速度快
  • 缺点:占用引脚资源多

串行通信

  • 传输原理:数据按位顺序传输。
  • 优点:占用引脚资源少
  • 缺点:速度相对较慢

a.串行通信:按照数据传送方向,分为:

单工:数据传输只支持数据在一个方向上传输

半双工:允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;

全双工:允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。

b.串行通信的通信方式

同步通信:带时钟同步信号传输。SPI,IIC通信接口

异步通信:不带时钟同步信号。UART(通用异步收发器),单总线。

常见的串行通信接口:

2.STM32F1串口框图讲解

STM32的串口通信接口:

UART:通用异步收发器

USART:通用同步异步收发器

大容量STM32F10x系列芯片,包含3个USART和2个UART

UART异步通信方式引脚连接方法:RXD:数据输入引脚。数据接受。TXD:数据发送引脚。数据发送。

 第二幅图有转换器是因为时钟脉冲可能不同。

UART异步通信方式引脚:

UART异步通信方式特点:

全双工异步通信。
分数波特率发生器系统,提供精确的波特率。发送和接受共用的可编程波特率,最高可达4.5Mbits/s
可编程的数据字长度(8位或者9位);
可配置的停止位(支持1或者2位停止位);
可配置的使用DMA多缓冲器通信。
单独的发送器和接收器使能位。
检测标志:① 接受缓冲器  ②发送缓冲器空 ③传输结束标志
多个带标志的中断源。触发中断。
其他:校验控制,四个错误检测标志。
串口通信过程

STM32串口异步通信需要定义的参数: 
① 起始位
② 数据位(8位或者9位)
③ 奇偶校验位(第9位)
④ 停止位(1,15,2位)
⑤ 波特率设置  

 

 3.STM32串口常用寄存器和库函数 

3.1     STM32 串口常用寄存器和库函数
常用的串口相关寄存器:
USART_SR 状态寄存器
USART_DR 数据寄存器
USART_BRR 波特率寄存器
串口操作相关库函数(省略入口参数):
void USART_Init(); //串口初始化:波特率,数据字长,奇偶校验,硬件流控以及收发使能
void USART_Cmd();//使能串口
void USART_ITConfig();//使能相关中断

void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据

FlagStatus USART_GetFlagStatus();//获取状态标志位
void USART_ClearFlag();//清除状态标志位
ITStatus USART_GetITStatus();//获取中断状态标志位
void USART_ClearITPendingBit();//清除中断状态标志位

 FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);

void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);

uint16_t USART_ReceiveData(USART_TypeDef* USARTx);

 void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);

波特率计算方法

4.串口配置一般步骤(手把手写串口实例)

串口时钟使能, GPIO 时钟使能 : RCC_APB2PeriphClockCmd();
串口复位 : USART_DeInit (); 这一步不是必须的
GPIO 端口模式设置 : GPIO_Init (); 模式设置为 GPIO_Mode_AF_PP
串口参数初始化 USART_Init ();
开启中断并且初始化 NVIC(如果需要开启中断才需要这个步骤)

     USART_ITConfig();

     NVIC_Init();

使能串口:USART_Cmd();

编写中断处理函数USARTx_IRQHandler();

⑧串口数据收发:

void USART_SendData();//发送数据到串口,DR

uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据

⑨串口传输状态获取:

FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);

void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);

#include "stm32f10x.h"
 


void MY_USART_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStrue;
	
	GPIO_InitStrue.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStrue.GPIO_Pin=GPIO_Pin_9;
	GPIO_InitStrue.GPIO_Speed=GPIO_Speed_10MHz;
	GPIO_Init(GPIOA,&GPIO_InitStrue);

	GPIO_InitStrue.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	GPIO_InitStrue.GPIO_Pin=GPIO_Pin_10;
	GPIO_InitStrue.GPIO_Speed=GPIO_Speed_10MHz;
	GPIO_Init(GPIOA,&GPIO_InitStrue);
	
	USART_InitTypeDef USART_InitStrue;
	
	USART_InitStrue.USART_BaudRate=115200;
	USART_InitStrue.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
	USART_InitStrue.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
	USART_InitStrue.USART_Parity=USART_Mode_Tx;
	USART_InitStrue.USART_StopBits=USART_StopBits_1;
	USART_InitStrue.USART_WordLength=USART_WordLength_8b;
	
	USART_Init(USART1,&USART_InitStrue);
	
	USART_Cmd(USART1,ENABLE);
	
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
	
	NVIC_InitTypeDef  NVIC_InitStruct;
	 
	NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
	 
	NVIC_Init(&NVIC_InitStruct);

}

void USART1_IRQHandler(void)
{
	unsigned char res;
	
	if(USART_GetITStatus(USART1,USART_IT_RXNE))
	{
		res=USART_ReceiveData(USART1);
		USART_SendData(USART1,res);
	}

}

int main(void)
 {		
 	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	 MY_USART_Init();
	 while(1);
} 

5.串口通信实验讲解

SYSTEM 组下双击 usart.c,我们就可以看到该文件里面的代码, 先介绍 uart_init 函数,该函数代码如下:

//初始化 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); //使能串口
}

① 串口时钟使能,GPIO 时钟使能 ② 串口复位 ③ GPIO 端口模式设置 ④ 串口参数初始化 ⑤ 初始化 NVIC 并且开启中断 ⑥ 使能串口

在 main.c 里面编写如下代码:

#include "key.h"
#include "sys.h"
#include "usart.h"
int main(void)
{
u8 t;
u8 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); 
}
}
}

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值