文章目录
一、STM32之NVIC中断系统
(一)、中断处理机制:
NVIC的定义:NVIC又叫嵌套向量中断控制器,属于CM4内核。它控制着整个芯片中断相关的功能,是内核的一个外设,与内核紧密联系。
NVIC的作用:STM32G431总共有111个中断源,所以经常会出现两个及以上的中断同时进行或者一个中断正在执行,另一个中断突然来临的情况,因此微控制器都有一个处理中断的机制。而STM32G431引用的是NVIC。
NVIC寄存器:定义在core_cm4.h文件中,CM4内核支持256个中断,包含16个系统异常和240个外部中断,并具有256个可编程中断设置。而STM32芯片只有111个中断,包含9个系统中断和102个可屏蔽中断,具有16级可编程的中断优先级。
(二)、Cortex-M4优先级设定:
- 每一个中断都要被指定为抢占优先级或响应优先级(子优先级)。
- 每个中断的优先级有一个8位的寄存器来设定,分成高低两个位段。高位段表示抢占优先级,低位段表示响应优先级(子优先级)。而STM32通常只使用Bit7–Bit4四个比特位。
(三)、优先级分组设置:
组 | SCB_AIRCR | NVIC_IPQx寄存器 | 描 | 述 |
---|---|---|---|---|
抢占优先级 | 响应优先级 | |||
0 | 111 | 0 : 4 | 0位:0 | 4位:0~15 |
1 | 110 | 1 : 3 | 1位:1 | 3位:0~7 |
2 | 101 | 2 : 2 | 2位:0~3 | 2位:0~3 |
3 | 100 | 3 : 1 | 3位:0~7 | 1位:1 |
4 | 011 | 4 : 0 | 4位:0~15 | 0位:0 |
SCB_AIRCR:应用程序中断及复位控制器
- 中断嵌套:具有高抢占优先级的中断可以在具有低抢占优先级的中断处理过程中被响应。(即高抢占优先级中断可以嵌套在低抢占优先级的中断中)。
- 当两个中断源的抢占优先级相同时,这两个中断没有嵌套关系。
(1). 当一个中断来临时,如果另一个中断正在处理中,则这个后到的中断就要等到前一个中断处理结束后才能开始处理。
(2). 如果两个中断同时到达,中断控制器则根据它们的响应优先级(子优先级)高低来决定先处理哪一个。
(3). 如果两个中断抢占优先级和响应优先级(子优先级)都相同,则根据它们在中断向量表中的排位顺寻来判断先处理哪一个。
(四)、NVIC相关函数——设置中断优先级组
HAL_NVIC_SetPriority(IRQn_Type, uint32_t, uint32_t):第一个形参指定中断源,第二个形参指定抢占优先级,第三个形参指定响应优先级。
HAL_NVIC_EnableIRQ(IRQn_Type):用于在NVIC控制器中使能指定中断。
HAL_NVIC_DisableIRQ(IRQn_Type):用于在NVIC控制器中禁用指定中断。
二、外中断引脚测试NVIC中断系统功能
(一)、理论知识:
EXTI——外部中断/事件控制器
STM32芯片集成了一个外部中断/事件控制器,由23个能产生事件/中断请求的边沿检测器组成。每个输入线可以独立配置输入类型(事件/中断)和对应的触发事件(上升沿,下降沿或双边沿都触发),并且都可以独立的被屏蔽。
23个中断/事件请求包括:
引脚IO可以作为EXTI线(0-15)
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唤醒事件
挂起寄存器:保持状态线的中断请求。
外部中断/事件产生过程:
首先,EXTI线输入一个外中断/事件请求到输入线中,经输入线到边沿检测电路中判断触发的类型(上升沿、下降沿或双边沿),然后传到软件中断/事件寄存器中判断是中断还是事件。
a. 中断:请求传输到挂起请求寄存器中,再有挂起请求寄存器保存起来,传输到NVIC中断控制器中来判断中断类型激发中断服务函数。(若被中断屏蔽寄存器屏蔽则没有上述过程)
b. 事件:请求传输到脉波发生器中,再由脉波发生器产生事件。(若被事件屏蔽寄存器屏蔽则不产生事件)
(二)、程序设计:
步骤:
- 设计中断分组;
- 初始化引脚为中断模式;
- 配置中断优先级;
- 开启中断;
- 中断出发作用下,自动执行中断函数,中断函数都指向同一个函数;
- 再次判断是否某个GPIO是否产生中断标志?如果产生的话,中断回调。
以下为步骤为1-4步的STM32CubeMX设计展示:
本程序设计主要是将PA0作为外中断传输到NVIC中断控制器中激发EXTI0_IRQHandler函数。
设置优先组:抢占优先组和响应优先组各占2个比特位。
其中滴答计时器抢占优先级为0,响应优先级为1;EXT0的抢占优先级为1,响应优先级为1。
激活EXTI0,为其使能。
以下为函数设计展示
三、STN32外设之USART
-
串口:STM32G431系列的单片机包含四个串口(3个USART,1个UART)。如下图,我们采用的是Link引脚(PA9,PA10),因此,在使用过程中,我们通常只会用到串口一。
-
串口内部结构图:
发送(TX):TDR寄存器——TXFIFO——移位寄存器(TX)——引脚
接收(RX):引脚——移位寄存器(RX)——RXFIFO——RDR寄存器 -
发送步骤:
a. 向CR1的UE位置1,使串口使能。
b.在CR1的M位定义字长。
c.在CR2对停止位数量进行编程(STOP位)。
d.对DMA进行配置。
e.配置BRR寄存器的波特率。
f.发送器对TE位置1,发送一个空闲帧作为第一次数据发送。
g.把要发送的数据写进USART_DR寄存器中,硬件自动发送,TXE清零。
h.再写入最后一个数据字后,等待TC=1,表示最后一个数据帧的传输结束。 -
接收步骤
a.向CR1的UE位置1,使串口使能。
b.在CR1的M位定义字长。
c.在CR2对停止位数量进行编程(STOP位)。
d.对DMA进行配置。
e.配置BRR寄存器的波特率。
f.将CR1的RE位置1,检测序列,当检测到数据字从1→0后开始接收(因为起始位为低电压)。
g.接收到字符,RXNE置1,移位寄存器的内容传送到RDR寄存器中。
h.如果RXNEIE置1,则产生中断。
i.软件读取DR寄存器,RXNE自动清零(有些情况需要手动写入0来清零)。
*波特率:控制串口的接收和发送频率。
四、USART程序设计&总结详解
(一)、串口编程结构体介绍:
-
句柄结构体:与软件相关。
-
串口初始化结构体:针对硬件串口的相关属性和硬件寄存器配置密切相关。
(二)、串口编程的实现——串口初始化
初始化运行过程:
首先定义一个句柄结构体变量huart1;
然后定义串口号和串口参数数值;
同时在HAL_UART_Init()函数中调用HAL_UART_MspInit()对GPIOA进行初始化配置;
最后进行串口中断的配置。
(三)、串口编程的实现——串口发送函数【HAL_UART_Transmit()】
如上为串口发送的函数形式,具体过程在上文中已经讲到就不重复赘述,总之在程序设计时,我们只需要直接调用HAL_UART_Transmit()函数输入串口、数据、字节数和限定时间就即可。
(四)、串口编程的实现——串口接收函数(中断形式)
串口接收函数采用了中断形式,首先在stm32g4xx_it.c中进行串口中断处理调用HAL_UART_IRQHandler()函数,如果没有错误发生则调用中断指针函数huart->RxISR()跳转到HAL_UART_Receive_IT()中指向UART_RxISR_16BIT_FIFOEN或UART_RxISR_8BIT_FIFOEN,最后调用执行回调函数HAL_UART_RxCpltCallback()到main。
(五)、代码部分
1.main.c
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "led\bsp_led.h"
#include "key\bsp_key.h"
#include "lcd\bsp_lcd.h"
#include "usart\bsp_usart.h"
//变量创建区
__IO uint32_t uwTick_Key_Set_Point = 0;//控制Key_Proc的执行速度
__IO uint32_t uwTick_Lcd_Set_Point = 0;//控制Lcd_Proc的执行速度
__IO uint32_t uwTick_Usart_Set_Point = 0;//控制Usart_Proc的执行速度
//按键扫描专用变量
unsigned char ucKey_Val, ucKey_Down, ucKey_Up, ucKey_Old;
//LCD显示专用变量
unsigned char Lcd_Disp_String[25];
//串口专用变量
int counter = 0;
char str[40];
unsigned char rx_buffer;
//子函数声明区
void SystemClock_Config(void);
void Key_Proc(void);
void Lcd_Proc(void);
void Usart_Proc(void);
//主函数
int main(void)
{
//内核和时钟的初始化
HAL_Init();
SystemClock_Config();
//外设函数的初始化
LED_Init();
KEY_Init();
LCD_Init();
USART1_Init();
HAL_UART_Receive_IT(&huart1, &rx_buffer, 1);
while (1)
{
Key_Proc();
Lcd_Proc();
Usart_Proc();
}
}
void Key_Proc()
{
if((uwTick - uwTick_Key_Set_Point) < 100) return;//减速函数
uwTick_Key_Set_Point = uwTick;
ucKey_Val = Key_Scan();
ucKey_Down = ucKey_Val & (ucKey_Old ^ ucKey_Val);
ucKey_Up = ~ucKey_Val & (ucKey_Old ^ ucKey_Val);
ucKey_Old = ucKey_Val;
if(ucKey_Down == 4)
{
LED_Disp(0xFF);
}
if(ucKey_Down == 3)
{
LED_Disp(0x00);
}
}
void Lcd_Proc(void)
{
if((uwTick - uwTick_Lcd_Set_Point) < 1000) return;//减速函数
uwTick_Lcd_Set_Point = uwTick;
}
void Usart_Proc(void)
{
if((uwTick - uwTick_Usart_Set_Point) < 500) return;//减速函数
uwTick_Usart_Set_Point = uwTick;
sprintf(str, "%04d:Hello,world.\r\n", counter);
HAL_UART_Transmit(&huart1,(unsigned char *)str, strlen(str), 50);
HAL_Delay(500);
if(++counter == 10000){
counter = 0;
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
LED_Disp(0xFF);
HAL_Delay(300);
LED_Disp(0X00);
HAL_UART_Receive_IT(&huart1, &rx_buffer, 1);
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV3;
RCC_OscInitStruct.PLL.PLLN = 20;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
void Error_Handler(void)
{
__disable_irq();
while (1)
{
}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif
- bsp_usart.c
#include "usart\bsp_usart.h"
UART_HandleTypeDef huart1;
void USART1_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 9600;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart1.Init.ClockPrescaler = UART_PRESCALER_DIV1;
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetTxFifoThreshold(&huart1, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetRxFifoThreshold(&huart1, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_DisableFifoMode(&huart1) != HAL_OK)
{
Error_Handler();
}
}
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(uartHandle->Instance==USART1)
{
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_NVIC_SetPriority(USART1_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
}
}
void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{
if(uartHandle->Instance==USART1)
{
__HAL_RCC_USART1_CLK_DISABLE();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);
HAL_NVIC_DisableIRQ(USART1_IRQn);
}
}
- bsp_usart.h
#include "main.h"
extern UART_HandleTypeDef huart1;
void USART1_Init(void);
标签:HAL,USART,引脚,中断,void,UART,优先级,RCC
From: https://blog.csdn.net/2301_81433986/article/details/144539639