采用中断模式编程,当开关接高电平时,LED亮灯;接低电平时,LED灭灯。
1.本部分主要解释如何用stm32F103核心板的GPIOA端一管脚接一个LED,GPIOB端口一引脚接一个开关;并采用中断模式编程,当开关接高电平时,LED亮灯;接低电平时,LED灭灯。
1.1 中断技术详解
1.1.1什么是中断?
● 在单片机系统中,如果遇到需要紧急处理的突发事件时,CPU需要迅速的作出反应,暂停正在运行的程序来处理突发事件,这时就需要中断
● 中断是指单片机正在执行程序的时,发生突发事件从而打断当前程序,转而去处理这一事件,当处理完成后再回到原来被打断出继续执行原程序的过程。
● 意义:中断能提高CPU的效率,同事能够对突发事件做出实时处理。实现程序的并行化,实现嵌入式系统进程之间的切换。
中断分为同步中断和异步中断。
● 同步中断——同步中断是当指令执行时由 控制单元产生的,之所以称为同步,是因为只有在一条指令终止执行后 CPU 才会发出中断。
● 异步中断——异步中断是由其他硬件设备依照 CPU 时钟信号随机 产生的。
※ 通常我们所说的中断指的是异步中断,我们将同步中断称为异常。(异常是由程序的错误产生的,或者是由内核必须处理的异常条件产生的)
1.1.2中断源
● 任何能够引发中断的事件都称为中断源。
● 分类:中断源可分为硬件中断源和软件中断源。
硬件中断源:外设,数据通道,时钟电路和故障源(如电源掉电)。
软件中断源: 为调试程序设置的中断,中断指令,执行过程出错。
硬件中断源:外设,数据通道,时钟电路和故障源(如电源掉电)。
软件中断源: 为调试程序设置的中断,中断指令,执行过程出错。
1.1.3中断处理的基本过程
● 中断处理分为如下几个步骤:中断请求,中断响应,保护断点,处理中断,中断返回。
中断请求阶段
● 发生在CPU内部的中断(内部中断),不需要中断请求,CPU内部的中断控制逻辑直接接收处理。
● 外部中断请求由中断源提出。外部中断源利用CPU的中断输入引脚 输入中断请求信号。一般CPU设有两个中断请求输入引脚:可屏蔽中断请求输入引脚和不可屏蔽中断请求输入引脚。
中断请求触发器
● 每个中断源发中断请求信号的时间是不确定的,而CPU在何时响应中断也 是不确定的。所以,每个中断源都有一个中断请求触发器,锁存自己的中断请求信号,并保持到CPU响应这个中断请求之后才将其清除。
中断允许触发器
● 在CPU内部有一个中断允许触发器,当其为“1”时,允许CPU响应中断, 称为开中断。若其为“0”,不允许CPU响应中断,中断被屏蔽,称为关中断。
● 通常,当CPU复位时,中断允许触发器也复位为“0”,即关中断。当 CPU中断响应时,CPU自动关闭中断,禁止接受另一个新的中断。
● 中断允许触发器的状态可以用开中断或关中断指令来设置。
中断判优阶段
● CPU一次只能接受一个中断源的请求,当多个中断源同时向CPU提出中断请求时,CPU必须找出中断优先级最高的中断源,这一过程称为中断判优。
● 中断判优可以采用硬件方法,也可采用软件方法。
软件判优
● CPU检测到中断请求后,首先读取中断请求寄存器的内容,逐位检测它们的状态,检测到某一位为1,就确定对应的中断源有中断请求,转去执行它的中断服务程序。
● 先检测哪一个,哪一个的优先级就高,后检测哪一个,哪一个优先级就低,检测的顺序就是各中断源的优先级顺序。(软件判优耗时较长。如果中断源很多,中断的实时性就很差,但是软件判优优先权安排灵活。)
中断响应阶段
● 经过中断判优,中断处理就进入中断响应阶段。中断响应时,CPU向中断源发出中断响应信号,同时:
① 保护硬件现场;
② 关中断;
③ 保护断点;
④ 获得中断服务程序的入口地址。
中断服务阶段
中断服务程序的一般结构为:
1)保护现场。 在中断服务程序的起始部分安排若干条入栈指令,将各寄存器的内容压入堆栈保存。
2)开中断。 在中断服务程序执行期间允许级别更高的中断请求中断现 行的中断服务程序,实现中断嵌套。
3)中断服务。 完成中断源的具体要求。
4)恢复现场。 中断服务程序结束前,必须恢复主程序的中断现场。通常是将保存在堆栈中的现场信息弹出到原来的寄存器中。
5)中断返回。 返回到原程序的断点处,继续执行原程序。
中断返回阶段
返回到原程序的断点处,恢复硬件现场,继续执行原程序。
中断返回操作是中断响应操作的逆过程。
1.1.4中断优先级
8086/8088中断优先级:
软件中断(除单步中断)>非屏蔽中断NMI>可屏蔽中断INTR>单步中断
不同优先级中断的处理原则:
不同优先级的多个中断源同时发出中断请求,按照优先级高低依次处理。
低优先级中断正在处理,出现高优先级请求,应转去执行高优先级请求,服务结束后返回原低优先级中断服务程序继续执行。
高优先级中断正在处理,出现低优先级请求,可咋不响应。 中断处理时,出现同级别请求,应在当前中断处理结束后再处理新的请求。
1.2工程新建
1.2.1硬件准备
1.2.2中断工程新创
● 选择STM32F103C8T6。
1.2.3外设设置
● 设置指示灯LED引脚PB5,设置引脚模式为输出模式GPIO_Output
● 设置按键引脚PA1,设置引脚为外部中断功能,PA1与外部中断线EXIT1连接GPIO_EXIT1
1.2.4GPIO设置
● 对于开关对应管脚PA1,设置其触发方式为上升沿触发。
● 使能对应的外部中断线,点击Enabled。
1.2.5配置中断优先级
● 大多数情况下不必设置中断优先级,直接使用中断编号设置的默认中断优先级。
1.2.6时钟设置
● 设置32MHZ。
1.2.7工程生成
● 命名与选择MDK-ARM。
1.3中断控LED亮灭
1.3.1代码编写
● 在Keil文件中的gpio.c文件可以找到中断服务函数(void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin))。
● 当捕获到上升沿,触发中断,就会进入到这个函数里面。
● 在main.c文件中找个地方重新写一下,因为前面的 __weak 表示此函数为虚函数,需要用户重写的。
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if( GPIO_Pin == A1_EXTI_Pin)//判断外部中断源
{
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);//翻转LED状态
}
}
1.3.2代码编译
编译发现无错误
1.3.3烧录实现
1.3.4最终实现
采用中断模式编程,当中断接高电平时,LED亮灯;接低电平时,LED灭灯。(因为无开关,只能使用插入不同电压实现模拟开关,当中断接口不接高电平或低电平时,LED频闪。)
采用串口中断方式完成单字符接受与字符串接受
2.本部分主要介绍通过STM32F103C8T6接收到字符“s”时,停止持续发送“hello windows!”; 当接收到字符“t”时,持续发送“hello windows!”;接收到字符“stop stm32!”时,停止持续发送“hello windows!”; 当接收到字符“go stm32!”时,持续发送“hello windows!”。
2.1单字符收发
2.1.1工程新创
● 点击ACCESS TO MCU SELECTOR
● 选择STMF103C8T6芯片。
2.1.2.设置RCC
2.1.3设置SYS
●Debug选择 Serial Wire
2.1.4设置USART
●Mode选择 Asynchronous
2.1.5设置NVIC
● 点击USART1 global interrupt。
2.1.6生成项目
2.2代码编译与收发实现
2.2.1在KEIL中进行代码编写
● 点击在main函数前定义全局变量。
char c;//指令 s:停止 t:开始
char message[]="hello Windows\n";//输出信息
char tips[]="CommandError\n";//提示1
char tips1[]="Start.....\n";//提示2
char tips2[]="Stop......\n";//提示3
int flag=0;//标志 s:停止发送 t:开始发送
2.2.2.main函数中的while循环里面添加传输代码
if(flag==1){
//发送信息
HAL_UART_Transmit(&huart1, (uint8_t *)&message, strlen(message),0xFFFF);
//延时
HAL_Delay(1000);
}
2.2.3在main函数下面重写中断处理函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//当输入的指令为s时,发送提示并改变flag
if(c=='s'){
flag=0;
HAL_UART_Transmit(&huart1, (uint8_t *)&tips2, strlen(tips2),0xFFFF);
}
//当输入的指令为t时,发送提示并改变flag
else if(c=='t'){
flag=1;
HAL_UART_Transmit(&huart1, (uint8_t *)&tips1, strlen(tips1),0xFFFF);
}
//当输入不存在指令时,发送提示并改变flag
else {
flag=0;
HAL_UART_Transmit(&huart1, (uint8_t *)&tips, strlen(tips),0xFFFF);
}
//重新设置中断
HAL_UART_Receive_IT(&huart1, (uint8_t *)&c, 1);
}
2.2.4 代码汇总
#include "main.h"
#include "usart.h"
#include "gpio.h"
#include <string.h>
void SystemClock_Config(void);
char c;//指令 s:停止 t:开始
char message[]="hello Windows\n";//输出信息
char tips[]="CommandError\n";//提示1
char tips1[]="Start.....\n";//提示2
char tips2[]="Stop......\n";//提示3
int flag=0;//标志 s:停止发送 t:开始发送
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
//设置接受中断
HAL_UART_Receive_IT(&huart1, (uint8_t *)&c, 1);
//当flag为1时,每秒发送一次信息
//当flag为0时,停止
while (1)
{
if(flag==1){
//发送信息
HAL_UART_Transmit(&huart1, (uint8_t *)&message, strlen(message),0xFFFF);
//延时
HAL_Delay(1000);
}
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//当输入的指令为s时,发送提示并改变flag
if(c=='s'){
flag=0;
HAL_UART_Transmit(&huart1, (uint8_t *)&tips2, strlen(tips2),0xFFFF);
}
//当输入的指令为t时,发送提示并改变flag
else if(c=='t'){
flag=1;
HAL_UART_Transmit(&huart1, (uint8_t *)&tips1, strlen(tips1),0xFFFF);
}
//当输入不存在指令时,发送提示并改变flag
else {
flag=0;
HAL_UART_Transmit(&huart1, (uint8_t *)&tips, strlen(tips),0xFFFF);
}
//重新设置中断
HAL_UART_Receive_IT(&huart1, (uint8_t *)&c, 1);
}
/* USER CODE END 4 */
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
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_0) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
2.2.5 编译
2.2.6 打开串口助手
实现效果:
2.3字符串发收
2.3.1新建工程
● 生成工程的过程如上
2.3.2代码编写
● main.c文件代码如下:
#include "main.h"
#include "usart.h"
#include "gpio.h"
#include "stdio.h"
#include "string.h"
uint8_t aRxBuffer;
uint8_t Uart1_RxBuff[256];
uint8_t str1[20] = "stop stm32";
uint8_t str2[20] = "go stm32";
uint8_t Uart1_Rx_Cnt = 0;
uint8_t cAlmStr[] = "Êý¾ÝÒç³ö(´óÓÚ256)\r\n";
unsigned int flag = 1;
void SystemClock_Config(void);
int main(void)
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);
while (1)
{
if(flag == 1)
{
printf("ppm Hello windows!\r\n");
}
else
{
//printf("stop stm32 NO!\r\n");
}
HAL_Delay(500);
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
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_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
UNUSED(huart);
if (strcmp(Uart1_RxBuff, str1) == 0) flag = 0;
if (strcmp(Uart1_RxBuff, str2) == 0) flag = 1;
if(Uart1_Rx_Cnt >= 255)
{
Uart1_Rx_Cnt = 0;
memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff));
HAL_UART_Transmit(&huart1, (uint8_t *)&cAlmStr, sizeof(cAlmStr),0xFFFF);
}
else
{
Uart1_RxBuff[Uart1_Rx_Cnt++] = aRxBuffer;
if((Uart1_RxBuff[Uart1_Rx_Cnt-1] == 0x0A)&&(Uart1_RxBuff[Uart1_Rx_Cnt-2] == 0x0D))
{
HAL_UART_Transmit(&huart1, (uint8_t *)&Uart1_RxBuff, Uart1_Rx_Cnt,0xFFFF);
Uart1_Rx_Cnt = 0;
memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff));
}
}
HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);
}
void Error_Handler(void)
{
__disable_irq();
while (1)
{
}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif
2.3.3烧录打开串口助手
●打开串口助手,实现效果如下:
STM32采用串口DMA方式,用115200bps或更高速率向上位机连续发送数据。
3.1DMA
3.1.1 DMA简介
DMA的出现大大减轻了CPU的工作量。在硬件系统中,主要由CPU(内核)、外设、内存(SRAM)、总线等结构组成,数据经常要在内存和外设之间,外设和外设之间转移。例如:CPU需要处理从外设采集回来的数据,CPU需要先将数据从ADC外设的寄存器读取到内存中(变量)去,然后进行运算处理,这是一般的解决方法。CPU的资源是非常宝贵的,我们可以设法把转移的工作交给其他部件来完成,CPU把更多的资源用于数据运算和中断响应上,如此DMA便登场了。DMA正是为CPU分担数据转移工作,因为DMA的存在,CPU才被解放出来,它可以在数据转移的同时进行数据运算,相应中断,大大提高了效率。
3.1.2 MDA的主要特性
3.1.3 DMA中断特性
3.2串口通信DMA传输完成中断
3.2.1 步骤
打开CUBRMX新建项目,芯片选择是F103C8,点击后创建项目;
把PA9和PA10选择为USART1_RX和USART1_TX,然后再点击左边的USART1,将mode置为异步通信模式,我们这里只需要接收,不需要发送,后面再点击add键,添加引脚
3.2.2 代码
Open Project 打开keil5,进入到main.c文件,在main.c文件中的while循环那块的代码如下:
while (1)
{
uint8_t send_char[]="hello world\n";//发送的字符串
HAL_UART_Transmit_DMA(&huart1,(uint8_t *)send_char,0xc);//DMA发送
HAL_Delay(500);//延时
}
3.2.3 实验结果
烧录
串口调试