文章目录
在之前的串口通信的实验(输入字符,打印到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);
}
}
}