中端编程入门

实验任务

一、用stm32F103核心板的GPIOA端一管脚接一个LED,GPIOB端口一引脚接一个开关(用杜邦线模拟代替)。采用中断模式编程,当开关接高电平时,LED亮灯;接低电平时,LED灭灯。
二、
二、采用串口中断方式重做上周的串口通信作业(一)当stm32接收到字符“s”时,停止持续发送“hello windows!”;当接收到字符“t”时,持续发送“hellowindows!”(提示:采用一个全局标量做信号灯)

实验任务一

中断

认识中断

,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行

中断的优先级

当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源

中断嵌套

当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回

中断执行流程

在这里插入图片描述

stm32的中断设置在这里插入图片描述

在这里插入图片描述

简单总结

1.68个可屏蔽中断通道,包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设
2. 使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级

NVIC

NVIC,又名嵌套向量中断控制器,它的任务就是提前对接入的中断进行优先级排序,然后告诉CPU这些中断应该先后进行哪个。减轻了CPU的工作负担。
在这里插入图片描述

优先级分组

1、NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级

2、抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队
在这里插入图片描述

EXTI在这里插入图片描述

·上升沿触发
数字电平从低电平(数字“0”)变为高电平(数字“1”)的那一瞬间叫作上升沿。 上升沿触发是当信号有上升沿时的开关动作,当电位由低变高而触发输出变化的就叫上升沿触发。也就是当测到的信号电位是从低到高也就是上升时就触发,叫做上升沿触发。

·下降沿触发
数字电路中,数字电平从高电平(数字“1”)变为低电平(数字“0”)的那一瞬间叫作下降沿。 [1] 下降沿触发是当信号有下降沿时的开关动作,当电位由高变低而触发输出变化的就叫下降沿触发。也就是当测到的信号电位是从高到低也就是下降时就触发,叫做下降沿触发。

·双边沿触发
也就是无论是从低电平到高电平还是从高电平到低电平都能触发。

·软件触发
通过软件主动查询信号或仪器当前状态,符合条件则控制系统采集信号。软触发有着更大的柔性,但系统整体速度和测量精度一般不如硬触发,特别是有着复杂信号处理模块的时候。软件触发会占用到一定的计算机资源。使用软件触发的话可以先设计一个查询程序,在规定的时间间隔里定时查询触发信号,如果有触发信号,则输出一个真值的布尔量,执行数据采集的分支,如果没有触发信号,则输出一个假值的布尔量,执行空分支。

基本结构

在这里插入图片描述

AFIO复用IO口

AFIO作为一个数组选择器外设,用来给GPIO外设选择中断的引脚。

①.如果只有一个中断,则GPIO引脚可以随意选择,若选择GPIOA Pin15,则AFIO则选择数字15输出到EXTI 。
②.如果有多个中断,则GPIO引脚则要注意不能同时选择相同标号引脚,例如两个中断,一个设为GPIOA15触发,一个设为GPIOB15触发,因为AFIO是数字选择器,这样就只能识别到一个15。
在这里插入图片描述

创建项目

在这里插入图片描述

代码实现

1.配置RCC,把涉及到的外设的时钟全部打开

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);  //打开GPIO时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);   //打开AFIO时钟
	//EXTI 和 NVIC已经默认开启时钟,不需要再开启

2.配置GPIO,选择端口为输入模式(GPIOB14号引脚)

	GPIO_InitTypeDef GPIO_InitStruct;//定义配置初始化结构体
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;     //对于外部中断来说,要选择浮	空、上拉或者下拉输入其中一个模式,这里选择的是浮空触发。如果不清楚看ST参考手册GPIO外设配置表(113页EXTI输入线)
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; 
	GPIO_Init(GPIOB, &GPIO_InitStruct);

3.配置AFIO,选择我们用的GPIO连接到后面EXTI

GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);//用AFIO配置需要的中断引脚选择

4.配置EXTI,选择触发方式(例如上升沿,下降沿或双边沿),触发响应方式(中断或者事件触发)


	EXTI_InitTypeDef EXTI_InitStruct;
	EXTI_InitStruct.EXTI_Line = EXTI_Line14;//选择PB14对应的14号线路;
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;//开启中断
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;//选择是中断还是事件触发
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising_Falling;//配置为下降沿触发
	EXTI_Init(&EXTI_InitStruct);

5.配置NVIC,给外设配置合适的优先级,最后通过NVIC中断信号进入CPU

	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//先配置一下指定中断分组(这里选择抢占优先级和响应优先级)
	NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn;//这里参数需要到整个文件搜索,不同类型的芯片终端通道列表不一样,选择MD的芯片找到10-15EXTI的通道
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	//指定优先级,因为只有一个中断源,优先级随意设置
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;  //指定抢占优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; //指定响应优先级
	NVIC_Init(&NVIC_InitStruct);

6.中断函数的写入

//第六步写中断函数(在启动文件startup里面找,找对应之前开启的10-15通道)
void EXTI15_10_IRQHandler(void)//必须无参无返回值
{
	//因为有10-15EXTI都能进来所以一般首先要进行判断EXTI14的中断标志位是不是为1
	if(EXTI_GetITStatus(EXTI_Line14)== SET)
	{
			//这里是用PA5引脚点亮小灯泡
			GPIO_InitTypeDef GPIO_InitStructure;	
			RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//打开GOIO时钟
			GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;
			GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
			GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
			GPIO_Init(GPIOA,&GPIO_InitStructure);
		
		if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==Bit_RESET)//判断现在是低电平还是高电平,对应开关等
		{
			
			
			GPIO_SetBits(GPIOA,GPIO_Pin_5);
			//GPIO_ResetBits(GPIOA,GPIO_Pin_5);
		}
	    else if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==Bit_SET)
		{
		
			//GPIO_SetBits(GPIOA,GPIO_Pin_5);
			GPIO_ResetBits(GPIOA,GPIO_Pin_5);
		}
		
		
		//每次执行中断后都应该清除中断标志位,不然会一直申请中断,程序就卡死在中断里面
	EXTI_ClearITPendingBit(EXTI_Line14);
	}
	
}


7.LED.h头文件引入

#ifndef _LED_H
#define _LED_H
void LED_Init(void);
#endif


LED.c用来存储驱动程序的主体代码LED.h用来存放这个驱动程序可以对外提供的函数或变量声明其他都是F103C8T6对应标准库所需的文件,直接添加就好

8.main.c主函数

#include "stm32f10x.h"                  // Device header
#include "LED.h"
int main(void)
{
	LED_Init();
	while(1)
	{
		
	}
} 


开发板实验结果

在这里插入图片描述

实验任务二

一、当stm32接收到字符“s”时,停止持续发送“hello windows!”;当接收到字符“t”时,持续发送“hellowindows!”(提示:采用一个全局标量做信号灯)
#include "stm32f10x.h"
#include "misc.h"
#include <string.h>  // 包含 string.h 以使用 strlen

volatile uint8_t send_enabled = 0;  // 全局变量,控制发送行为

void USART_Configuration(void) {
    USART_InitTypeDef USART_InitStructure;//外设的初始化变量
    GPIO_InitTypeDef GPIO_InitStructure;//初始化GPIO

    // 打开 GPIO 与 USART 端口的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);

    // 配置 USART1 Tx (PA.09) 为复用推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;//选择引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//设置输出速度
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置 USART1 Rx (PA.10) 为浮空输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//选择引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输出
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置 USART 参数
    USART_InitStructure.USART_BaudRate = 9600;//波特率设置
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字节长为8位
    USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
    USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式
    USART_Init(USART1, &USART_InitStructure);//初始化外设Usart1寄存器

    // 使能 USART
    USART_Cmd(USART1, ENABLE);

    // 使能接收中断
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

    // 配置 NVIC
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//设置串口1中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级0
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//子优先级为0
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能
    NVIC_Init(&NVIC_InitStructure);
}

void USART1_IRQHandler(void) {
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
        char data = USART_ReceiveData(USART1);
        if(data == 's') {  // 接收到 's' 停止发送
            send_enabled = 0;
        } else if (data == 't') {  // 接收到 't' 开始发送
            send_enabled = 1;
        }
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);
    }
}

void Delay(__IO uint32_t nCount) {//延迟函数
    for(; nCount != 0; nCount--);
}

int main(void) {
    SystemInit();//系统时钟初始化,保证系统时钟配置正确
    USART_Configuration();

    char *str = "hello windows!\r\n";
    while(1) {
        if(send_enabled) {
            for(uint32_t i = 0; i < strlen(str); i++) {
                while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
                USART_SendData(USART1, str[i]);
            }
        }
        Delay(5000000);
    }
}

在这里插入图片描述

1.初始化配置:
系统时钟初始化 (SystemInit()): 在main函数开始调用,保证系统时钟配置正确。SystemInit()函数申明位于system_stm32f10x.h头文件中,内容在system_stm32f10x.c文件中。
串口配置 (USART_Configuration()): 配置USART1的发送端口(PA.09)和接收端口(PA.10),设定波特率、字长、停止位、奇偶校验位和硬件流控。同时,使能USART1的接收中断。
2.中断服务程序 (USART1_IRQHandler()): 该函数作为USART1接收中断的处理程序,当接收到数据时,会检查中断标志位。根据接收到的字符更新全局控制变量 send_enabled。如果收到字符为’s’,则设置 send_enabled 为0,停止发送操作;如果收到字符为’t’,则设置 send_enabled 为1,开始或继续发送。
3.信息发送控制: 在main循环中,根据send_enabled的值决定是否发送字符串 “hello windows!\r\n”。通过strlen()函数获取待发送字符串的长度,并在一个for循环中通过USART_SendData()函数逐字符发送。
4.延时功能: 在发送一次字符串后,使用Delay()函数进行延时,延时函数通过简单的循环实现,以减缓发送速度,便于观察结果。

一些定义的整理

使能
单片机中的使能通常指的是控制某个器件或模块的开关信号。例如,单片机中的GPIO口可以用来控制LED灯的亮灭,其中需要用到一个使能信号,即一个控制LED灯开关的信号。在程序中,通过设置GPIO口的状态(高电平或低电平)来控制LED灯的亮灭,从而实现使能的功能。除了LED灯,单片机中的其他器件或模块,如LCD显示屏、声音模块等,也需要通过使能信号来控制其开关状态
GPIO与PIN 的区别
STM32的GPIO和PIN都是指引脚,但它们的含义和用法略有不同。 GPIO是General Purpose Input/Output的缩写,通用输入输出引脚,是STM32芯片上的一个模块,它可以控制和读取多个引脚的状态,可以通过寄存器配置来实现不同的功能,例如输入、输出、上拉、下拉、中断等。 PIN是指引脚,是GPIO模块中的单个引脚,每个引脚都有一个唯一的编号,例如PA0、PB1等。可以通过寄存器配置来控制和读取单个引脚的状态,例如设置引脚为输入或输出、设置引脚电平等。 因此,GPIO是一个功能模块,而PIN是GPIO模块中的一个单个引脚。
RCC
STM32单片机的RCC(Reset and Clock Control)时钟模块主要负责系统时钟的控制和管理,包括以下作用: 1. 确定CPU的工作频率:RCC模块可以设置CPU的时钟频率,从而控制单片机的工作速度。 2. 控制外设时钟:RCC模块可以为外设提供时钟信号,使外设能够正常工作。 3. 确定系统时钟源:RCC模块可以选择外部晶振或内部RC振荡器作为系统时钟源,从而控制整个系统的时钟。 4. 确定时钟分频系数:RCC模块可以设置时钟分频系数,从而控制时钟信号的频率。 5. 控制低功耗模式:RCC模块可以控制单片机的低功耗模式,从而延长电池寿命。RCC时钟模块是STM32单片机系统时钟控制和管理的核心模块

二、当stm32接收到字符“stop stm32!”时,停止持续发送“hello windows!”;当接收到字符“go stm32!”时,持续发送“hello windows!”(提示:要将接收到的连续字符保存到一个字符数组里,进行判别匹配。写一个接收字符串的函数。)
#include "stm32f10x.h"
#include "misc.h"
#include <string.h>

#define BUFFER_SIZE 100
volatile char buffer[BUFFER_SIZE];
volatile int buffer_index = 0;
volatile int send_enabled = 0;

void Delay(__IO uint32_t nCount) {
    for (; nCount != 0; nCount--);
}

void USART_Configuration(void) {
    USART_InitTypeDef USART_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);

    // USART Tx (PA.09) 配置为复用推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // USART Rx (PA.10) 配置为浮空输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    USART_InitStructure.USART_BaudRate = 9600;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStructure);

    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 开启接收中断
    USART_Cmd(USART1, ENABLE);
}

void NVIC_Configuration(void) {
    NVIC_InitTypeDef NVIC_InitStructure;

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

void USART1_IRQHandler(void) {
//串口1的中断响应函数,当串口1 发生了相应的中断后,就会跳到该函数执行
    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
    //接收中断
        char data = (char)USART_ReceiveData(USART1);
        if (buffer_index < BUFFER_SIZE - 1) {
            buffer[buffer_index++] = data;
            buffer[buffer_index] = '\0';  // 保持字符串结尾

            char* temp_buffer = (char*)buffer; // 创建一个非 volatile 指针

            if (strstr(temp_buffer, "stop stm32!") != NULL) {
                send_enabled = 0;
                buffer_index = 0;  // 清空缓冲区
            } else if (strstr(temp_buffer, "go stm32!") != NULL) {
                send_enabled = 1;
                buffer_index = 0;  // 清空缓冲区
            }
        }
    }
}

int main(void) {
    SystemInit();
    USART_Configuration();
    NVIC_Configuration();

    char *str = "hello windows!\r\n";
    while (1) {
        if (send_enabled) {
            for (uint32_t i = 0; i < strlen(str); i++) {
                while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
                USART_SendData(USART1, str[i]);
            }
        }
        Delay(5000000);
    }
}

1.系统初始化
首先是系统的基本初始化,包括系统时钟和串口配置。这一步是为了配置和启动基本的硬件设备,确保STM32可以正常工作并进行数据通信。
2.USART配置
GPIO配置: 配置GPIOA的9号脚(Tx发射端)为复用推挽输出,10号脚(Rx接收端)为浮空输入,以适应串口通信的物理连接需求。
USART设置: 设置串口1的波特率、字长、停止位、奇偶校验位和流控,以及启用其接收和发送功能。
3.中断配置
使能USART接收中断: 这允许设备在接收到数据时自动触发中断服务程序(ISR),处理接收到的数据。
NVIC配置: 配置中断控制器,设置中断优先级和中断通道,确保USART中断可以被有效处理。
4.中断服务程序(ISR)
这里是处理接收数据的核心逻辑:
数据接收: 从USART数据寄存器读取接收到的数据。
缓冲区管理: 将接收到的数据逐字节存储到缓冲区,并在缓冲区末尾维护字符串结束符。
字符串匹配: 检查缓冲区中是否包含特定控制命令(如"stop stm32!“或"go stm32!”),并根据匹配结果更新发送模式的使能状态。
控制逻辑:当接收到"stop stm32!"命令时,停止发送数据;接收到"go stm32!“命令时,开始或继续发送数据。
5.主循环
主循环中包含了数据发送的逻辑:
条件判断: 根据send_enabled变量的状态决定是否发送数据。
数据发送: 若允许发送数据,则循环发送预设的字符串"hello windows!\r\n”。
延时: 在发送完一次数据后进行延迟,以便观察发送数据的效果。

在这里插入图片描述

心得

太难理解了,老天,太抽象了。

参考博客:

第12周–中断编程入门-CSDN博客

STM32以中断的方式点亮LED小灯(标准库)_stm32按键中断控制led灯-CSDN博客

STM32之USART-串口通信(含串口实验详细解析)_stm32 串口11bit-CSDN博客

  • 15
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值