【stm32的中断和串口通信】

stm32的中断和串口通信

本文主要介绍stm32最小开发板外部中断、内部中断方式的串口通信,以及DMA通信


一、外部中断

stm32的外部中断就是指,stm32的引脚接收到外部信号后,暂停当前工作,跳转到中断函数的工作模式。
我们可以自己定义外部信号的形式,也可以自己定义中断函数的功能。
下面通过cubeMX配置引脚

1.1配置引脚

引脚的配置如下
A4输出控制灯的亮灭,设置为GPIO_Output
A1持续输出高电平,设置同上
A7持续输出低电平,设置同上
C13个人习惯开启做测试用,设置同上
B5模拟开关,设置为GPIO_EXTI5

打开cubemx
在这里插入图片描述
选择芯片stm32f103c8,双击下方
在这里插入图片描述
1.配置引脚
在这里插入图片描述
右键,选择signal unping取消锁定
在这里插入图片描述
配置后如下
在这里插入图片描述
2.配置外部中断

在这里插入图片描述
3.配置sys
在这里插入图片描述
4.配置GPIO,把PA4引脚的label 改成LED_A4,方便理解,PB5同理
在这里插入图片描述在这里插入图片描述
5.创建项目
在这里插入图片描述

1.2代码部分

1.打开生成的项目,找到stm32f1xx_it.c
在这里插入图片描述
2.找到EXTI9_5_IRQHandler这个函数,选中HAL_GPIO_EXTI_IRQHandler这个语句按F12跳到该函数
在这里插入图片描述
3.往下找到HAL_GPIO_EXTI_Callback这个函数,我们把中断后需要执行的操作写在这个函数中,根据上面定义的引脚,我们设定的外部条件位输入PB5引脚的电平,并且根据不同的电平控制PA4所接的灯的亮灭
在这里插入图片描述
代码如下

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
      if(GPIO_Pin == SWITCH_Pin){
        //获取B5的电位
        GPIO_PinState pinState = HAL_GPIO_ReadPin(SWITCH_GPIO_Port,SWITCH_Pin);

        //低电位
        if(pinState==GPIO_PIN_RESET)
        HAL_GPIO_WritePin(LED_A4_GPIO_Port,LED_A4_Pin,GPIO_PIN_RESET);//把A4变为低电位
        //高电位
        else
            HAL_GPIO_WritePin(LED_A4_GPIO_Port,LED_A4_Pin,GPIO_PIN_SET);//把A4变为高电位
        }
    }

注意,由于我们使用了自定义的label ,这些定义是在main.h头文件下的,所以我们要在HAL_GPIO_EXTI_Callback函数的文件头部加上引用声明。

1.3结果展示

代码编译后生成.hex文件,我们把它烧录到单片机内,如下图所示,烧录的具体步骤可以参考前几篇博客
在这里插入图片描述
结果如下

外部中断

二、内部中断

内部中断指的是中断的来源来自单片机内部。这里我们主要介绍串口中断。
所谓串口中断,就是当串口接受端接到数据时,会引发中断,执行中断函数的特定值。
下面我们将通过hal库实现一个相关程序。

2.1 配置cubeMX

我们使用的任然时stm32f103c8芯片,打开cubeMX找到该芯片后双击。
1.设置时钟,选择晶振
在这里插入图片描述

2.设置sys
在这里插入图片描述
3.设置串口
在这里插入图片描述
4.设置NVIC
在这里插入图片描述
5.创建项目
在这里插入图片描述

2.2相关代码

思路
代码的原理十分简单,我们把用于交互的字符串先储存在数组内,在上位机接收信号产生中断后,在中断函数中加上字符比较,若是内容符合定义,这改变参量flag(哨兵位),中断完成后,由于哨兵位的改变,while循环中可以执行新的输出。以上便是交互的主要原理了。下面介绍一下代码。

char c;//指令 0:停止  1:开始
char message[]="hello Windows\n";//输出信息
char tips[]="CommandError\n";//提示1
char tips1[]="Start.....\n";//提示2
char tips2[]="Stop......\n";//提示3
int flag=0;//标志 0:停止发送 1.开始发送

传输函数

if(flag==1){
			//发送信息
			HAL_UART_Transmit(&huart1, (uint8_t *)&message, strlen(message),0xFFFF); 
			
			//延时
			HAL_Delay(1000);
		}

中断处理函数,即根据不同的接收字符发送不同字符串

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	
	//当输入的指令为0时,发送提示并改变flag
	if(c=='0'){
		flag=0;
		HAL_UART_Transmit(&huart1, (uint8_t *)&tips2, strlen(tips2),0xFFFF); 
	}
	
	//当输入的指令为1时,发送提示并改变flag
	else if(c=='1'){
		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);  
}

下面给出整改main函数的代码参考

#include "main.h"
#include "usart.h"
#include "gpio.h"
#include <string.h>

void SystemClock_Config(void);

char c;//指令 0:停止  1:开始
char message[]="hello Windows\n";//输出信息
char tips[]="CommandError\n";//提示1
char tips1[]="Start.....\n";//提示2
char tips2[]="Stop......\n";//提示3
int flag=0;//标志 0:停止发送 1.开始发送


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)
{
	
	//当输入的指令为0时,发送提示并改变flag
	if(c=='0'){
		flag=0;
		HAL_UART_Transmit(&huart1, (uint8_t *)&tips2, strlen(tips2),0xFFFF); 
	}
	
	//当输入的指令为1时,发送提示并改变flag
	else if(c=='1'){
		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.3结果展示

经过编译生成hex文件后,结果如下

串口字符

三、串口发送字符串

对于上个代码,想必已经有人发现了不足。那就是,这个代码只可以传字符,如果我发的是字符串,那么就无法正常识别了。所以,我们需要进行改进。

3.改进思路

我们意识到,我们每次对比接收到的信号和命令的部分总数在

HAL_UART_RxCpltCallback函数中,根据中断规则,每次接收到数据,就会进行中断,从而执行中断函数。所以说,接收一个字符串时,会发生多个中断,这是我们不愿意看到的,我们希望改变对比字符串的位置,比如把它放进main函数中
下面

这里我们从新定义一个中断处理函数

void DEBUG_USART_IRQHandler(void)
{
	uint8_t temp;
	//接收中断
	if(USART_GetFlagStatus(USART1, USART_IT_RXNE) != RESET)
	{
		// 读取接收的数据
		temp = USART_ReceiveData(USART1);
		//接收未完成
		if((USART_RX_FLAG & 0x8000)==0)
		{
			//接收到了0x0d
			if(USART_RX_FLAG & 0x4000)
			{
				// 接收错误,重新开始
				if(temp != 0x0a) USART_RX_FLAG=0;
				// 接收完成
				else USART_RX_FLAG |= 0x8000;
			}
			// 还未接收到0x0d
			else
			{
				if(temp == 0x0d)
				{
					USART_RX_FLAG |= 0x4000;
				}
				else
				{
					USART_RX_BUF[USART_RX_FLAG & 0x3FFF]=temp;
					USART_RX_FLAG++;
					//接收数据错误,重新开始接收
					if(USART_RX_FLAG > 99) USART_RX_FLAG=0;
				}
			}
		}
	}
}

以上函数仅用于接收数据,不难看出,如果接收多位数据,中断函数会继续中断,执行中断函数,直到接收完成,我们定义的接收数组一定要是全局变量,不然会被刷新。

void clear_USART()
{
	uint8_t len=0;
	uint8_t i=0;
 len = USART_RX_FLAG & 0x3FFF;
			USART_SendString(USART1, "发送消息:\n");
			for(i=0; i<len;i++)
			{
				// 向串口发送数据
				USART_SendData(USART1, USART_RX_BUF[i]);
				//等待发送结束
				while(USART_GetFlagStatus(USART1, USART_FLAG_TC)!=SET);
			}
			USART_SendString(USART1, "\n\n");
			
			USART_RX_FLAG=0;
			memset(USART_RX_BUF,0,sizeof(USART_RX_BUF));
		}

当接收字符串时,我们需要知道串口接收的数据时什么情况,所以我们需要一个调试函数,来帮助我们了解串口接收的情况,有没有按照我们想要的进度工作。

int main(void)
{
	uint8_t len=0;
	uint8_t i=0;
	uint8_t flag=0;
	// USART初始化
	USART_Config();
	while(1)
	{
		clear_USART();
		if(strcmp((char *)USART_RX_BUF,"stop stm32!")==0)
			{	
				USART_SendString(USART1, "stm32已停止发送!");
				flag=0;
			break;
			}
		if(strcmp((char *)USART_RX_BUF,"go stm32!")==0)
		{
			flag=1;
		}
		clear_USART();
//		"hello windows!\n\r"
		if(flag==1)
		{
		USART_SendString(USART1, "hello windows!\r\n");
			delay_ms(800);
			if(strcmp((char *)USART_RX_BUF,"go stm32!stop stm32!")==0)
			{	
				USART_SendString(USART1, "stm32已停止发送!\r\n");
				flag=0;
			break;
			}
		}
		
	}
}

以上是main函数部分

补充部分引脚定义

//串口1-USART1
#define DEBUG_UARTx										USART1;
#define DEBUG_UART_CLK      					RCC_APB2Periph_USART1
#define DEBUG_UART_APBxClkCmd					RCC_APB2PeriphClockCmd
#define DEBUG_UART_BAUDRATE						115200

//USART GPIO 引脚宏定义
#define DEBUG_USART_GPIO_CLK				  (RCC_APB2Periph_GPIOA)
#define DEBUG_USART_GPIO_APBxClkCmd		RCC_APB2PeriphClockCmd
#define DEBUG_USART_TX_GPIO_PORT			GPIOA
#define DEBUG_USART_TX_GPIO_PIN       GPIO_Pin_9
#define DEBUG_USART_RX_GPIO_PORT			GPIOA
#define DEBUG_USART_RX_GPIO_PIN				GPIO_Pin_10

#define DEBUG_USART_IRQ								USART1_IRQn
#define DEBUG_USART_IRQHandler				USART1_IRQHandler
void DEBUG_UART_Config(void);
void Usart_SendByte(USART_TypeDef * pUSARTx,uint8_t ch);
void USART_SendString(USART_TypeDef * pUSARTx,char *str);
void delay_ms(uint16_t delay_ms);

如果我们用的是hal库,这些在stm32f1xx_hal.h文件中都可以找到映射。

3.2结果展示

将上述代码编译生成后,结果如下

串口字符串

四、总结

本次学习的难度比较大,耗时较长。客观上说,中断本身对初学者就比较复杂,尤其在刚接触hal库后,对自动生成的诸多函数不甚明解,不明所以。个人认为,从hal库出发学习stm32也入门还是比较难的。继续努力吧。

五、参考

1.https://www.bilibili.com/video/BV1th411z7sn/?spm_id_from=333.337.search-card.all.click
2.https://blog.csdn.net/qq_43279579/article/details/110138564
3.https://blog.csdn.net/weixin_51102592/article/details/121361597
4.https://blog.csdn.net/weixin_43793181/article/details/109134705

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值