STM32单片机学习(20) —— 利用中断实现串口通信(填前面的坑)

文章目录

在之前的串口通信的实验(输入字符,打印到OLED屏幕,以及控制LED)中,我们总是会因为函数阻塞,导致不能正常的运行程序,只能通过一些其他的办法来缓解,但终归没有真正的解决。而在我们学习中断之后就可以解决了。所以本文将继续从头开始分析如何完成该项目。

实验要求

通过串口通信实现以下功能:

1.PC端发送0点亮LED, 发送1熄灭LED

2.PC端发送一行字符串, 以'\n'作为结束, 展示在OLED显示屏上

接线图

在这这里需要注意USART串口通信接线时交叉的Rx-Tx, Tx-Rx。

代码框架

c 复制代码
// OLED和LED操作以及初始化直接调用函数模块中现成的,故不做考虑
#include "../tools/OLED.h"
#include "../tools/LED.h"
// 使用USART1外设,故先初始化引脚
void init_RxTx();
// 初始化USART1外设
void init_USART1();
// 中断就需要NVIC
// 初始化NVIC, 但是注意,在初始化NVIC之前,要先进行分组
void init_NVIC();
// 设置RCNE为中端源
void USART1_RXNEConfig();
// 设置ISR,注意ISR名称要从启动文件中寻找
void USART1_IRQHandler();
// 需要保证在做中断处理的时候可以与主程序正常交换数据,需要设置一个全局变量
volatile uint8_t new_char;

完整代码

c 复制代码
#include "stm32f10x.h"
#include "../tools/Delay.h"
#include "../tools/OLED.h"
#include "../tools/LED.h"



typedef enum {
    LED_ON,	// 点亮LED
    LED_OFF,	// 熄灭LED
    LED_BLINK,	// LED闪烁
    OUTPUT_OLED	// 输出到OLED
} CONTROL_STATE;
// 需要保证在做中断处理的时候可以与主程序正常交换数据,需要设置一个全局变量
volatile CONTROL_STATE state;
// 设置一个字符缓冲区,用于接收和发送字符串
volatile char Buffer[100];
// 设置字符串下标
volatile uint8_t Index = 0;
// 设置USART输入模式
typedef enum{
	CONTROL,
	INPUT
}CMD_MODE;
volatile CMD_MODE flag = CONTROL;

// 使用USART1外设,故先初始化引脚
void init_RxTx() {
    // 开时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    // 初始化Tx
    GPIO_InitTypeDef GPIO_InitStruct;
    // 采用复用推挽输出模式
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    // Tx为PA9
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    GPIO_SetBits(GPIOA, GPIO_Pin_9);
    //初始化Rx
    // 采用浮空输入模式
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    // Rx为PA10
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
}

// 初始化USART1外设
void init_USART1() {
    // 开时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    // 初始化USART1
    USART_InitTypeDef USART_InitStruct;
    // 设置波特率
    USART_InitStruct.USART_BaudRate = 115200;
    // 无硬件控制流
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    // 读写模式
    USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx ;
    // 无校验位
    USART_InitStruct.USART_Parity = USART_Parity_No ;
    // 停止位为1
    USART_InitStruct.USART_StopBits = USART_StopBits_1;
    // 数据长度为8
    USART_InitStruct.USART_WordLength = USART_WordLength_8b ;
    // 初始化
    USART_Init(USART1, &USART_InitStruct);
    // 使能开启USART1
    USART_Cmd(USART1, ENABLE);
}

// 中断就需要NVIC
// 初始化NVIC, 但是注意,在初始化NVIC之前,要先进行分组
void init_NVIC() {
    // NVIC不需要开时钟, 但是在初始化之前需要进行分组
    // 分组,全设置为抢占有优先级
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

    NVIC_InitTypeDef NVIC_InitStruct;
    // 中断类型
    NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
    // 开中断
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    // 设置抢占优先级
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    // 无需设置子优先级
    // 初始化
    NVIC_Init(&NVIC_InitStruct);
}

// 设置RXNE为中端源
void USART1_RXNEConfig() {
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
}

void clear_output(){
	OLED_Clear();
	OLED_ShowString(1, 2, "waiting:");
}


// 设置ISR,注意ISR名称要从启动文件中寻找
void USART1_IRQHandler() {
    // 当数据寄存器非空时触发中断
    if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) {
    uint8_t receive_char = USART_ReceiveData(USART1);
	if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE)) flag = INPUT;
	if(flag == CONTROL){
		switch (receive_char) {
			case '0':
				state = LED_ON;
				break;

			case '1':
				state = LED_OFF;
				break;

			case '2':
				state = LED_BLINK;
				break;

			default:
				// 切换输入模式
				flag = INPUT;
				Buffer[Index++] = receive_char;
				break;
			}
		}
		else{
			if (receive_char == '\n'){
				clear_output();
				state = OUTPUT_OLED;
				Buffer[Index] = '\0';
				Index = 0;
				// 切换命令模式
				flag = CONTROL;
			}
			else Buffer[Index++] = receive_char;
		}

    }
}

int main() {
	// 初始化USART1
    init_RxTx();
    init_USART1();
	// 初始化中断
    init_NVIC();
    USART1_RXNEConfig();
	// 初始化LED和OLED
    LED_AllInit();
    OLED_Init();
	
    OLED_ShowString(1, 2, "waiting:");
 
    while (1) {


        switch (state) {
        case LED_ON:
            LED_AllOn();
            break;

        case LED_OFF:
            LED_AllOff();
            break;

        case LED_BLINK:
            Delay_Ms(200);
            LED_AllOn();
            Delay_Ms(200);
            LED_AllOff();
            break;

        case OUTPUT_OLED:
		OLED_ShowString(2, 2, Buffer);

        }
    }

}
相关推荐
一路往蓝-Anbo3 小时前
第三篇:ADC 与模拟前端
stm32·嵌入式硬件·嵌入式·硬件设计
努力小周6 小时前
STM32智能安防系统
c语言·stm32·单片机·嵌入式硬件·物联网·计算机网络·pcb工艺
袁小皮皮不皮7 小时前
1.HCIP BFD 学习笔记(优化版)
服务器·网络·笔记·网络协议·学习·智能路由器·ip
装不满的克莱因瓶7 小时前
【自动驾驶领域】学习 Cityscapes 数据集——城市街景语义理解的标准基准
人工智能·pytorch·python·深度学习·学习·机器学习·自动驾驶
清辞8538 小时前
产品经理需求推进流程
大数据·深度学习·学习·产品经理
华科大胡子8 小时前
在STM32上跑通TinyML
stm32·单片机·嵌入式硬件
YM52e9 小时前
鸿蒙PC ArkTS 声明合并问题深度解析与最佳实践
学习·华为·harmonyos·鸿蒙·鸿蒙系统
海兰9 小时前
【实用程序】电商销售分析仪表盘 — 从零搭建一个AI参与的全栈数据洞察系统
人工智能·学习·算法
iCxhust10 小时前
C#进程管理程序
开发语言·汇编·stm32·单片机·c#·微机原理
ken223210 小时前
在 Libreoffice Calc中输入自定义表情字符时,需要保存之后,才能正常显示
学习