中断其实就是机器收到信号后,放下正在处理的任务,来处理你设定好的中断函数。
用我们生活中的事情来举个例子。假如你正在吃饭(当前程序),突然接到了快递员的电话让你现在下去拿快递(中断请求),你回答:“好的”(中断响应),然后暂停吃饭下去拿快递(中断处理),拿完快递上来继续吃饭(执行完中断返回执行当前程序)。
而外部中断则是外部信号引起的中断,如高电平、低电平、上升沿、下降沿等。
STM32F4 的每个 IO 都可以作为外部中断的中断输入口。 STM32F407 的中断控制器支持 23个外部中断/事件请求。每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置。
STM32F407 的 23 个外部中断为:
EXTI 线 0~15:对应外部 IO 口的输入中断。
EXTI 线 16:连接到 PVD 输出。
EXTI 线 17:连接到 RTC 闹钟事件。
EXTI 线 18:连接到 USB OTG FS 唤醒事件。
EXTI 线 19:连接到以太网唤醒事件。
EXTI 线 20:连接到 USB OTG HS(在 FS 中配置)唤醒事件。
EXTI 线 21:连接到 RTC 入侵和时间戳事件。
EXTI 线 22:连接到 RTC 唤醒事件。
从上面可以看出, STM32F4 供 IO 口使用的中断线只有 16 个,但是 STM32F4 的 IO 口却远远不止 16 个,那么 STM32F4 是怎么把 16 个中断线和 IO 口一一对应起来的呢?于是 STM32就这样设计, GPIO 的管教GPIOx.0~GPIOx.15(x=A,B,C,D,E, F,G,H,I)分别对应中断线 0~15。这样每个中断线对应了最多 9 个 IO 口,以线 0 为例:它对应了 GPIOA.0、 GPIOB.0、 GPIOC.0、GPIOD.0、 GPIOE.0、 GPIOF.0、 GPIOG.0,GPIOH.0,GPIOI.0。而中断线每次只能连接到 1 个 IO口上,这样就需要通过配置来决定对应的中断线配置到哪个 GPIO 上了。下面我们看看 GPIO
跟中断线的映射关系图:
时钟树的配置不罗嗦了。时钟树的配置
我们要实现当按下按键KEY0时,实现灯的亮灭。将PE4设置为外部中断模式。
1) 设置 IO 口模式,触发条件,设置 IO 口与中断线的映射关系。
2) 配置中断优先级(NVIC),并使能中断。 不要忘记设置NVIC选择卡。
配置完成后,就可以生成工程了。
3) 编写中断处理回调函数 HAL_GPIO_EXTI_Callback
uint16_t A=0;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)==GPIO_PIN_SET)
{
A=2;
HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_SET);
}
else if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)==GPIO_PIN_RESET)
{
A=3;
HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_RESET);
}
}
CubeMx外部中断代码
提取码:tkcq
串口通信是一种通信协议,通信协议就是通信双方在数据传输过程中共同遵守的协定,数据的发送方按照某种协议把数据调制成可以被传输的信号,接收方就可以使用相同的协议,从信号中解析出数据。
本人是按照这个视频配置的串口通信
这里我们选择串口2。并用USB转TTL接口来与电脑进行通信。
连线如下:
USE TO TTL | STM32 |
---|---|
3.3V/5V | 3.3V/5V |
GND | GND |
RX | TX |
TX | RX |
VCC不接,3.3和5V只需接一个即可。
打开串口2,找到RX,TX对应的引脚。
检查GPIO Settiings,是否为对应的引脚。
基础配置完成后,生成代码。
在while中加入如下代码:
HAL_UART_Transmit(&huart2,(uint8_t*)"Hello! ",12,0xFFFF);
//通道句柄,传输信息,传输字长,传输时间
HAL_Delay(1000);
打开串口调试助手,注意波特率要设置一致,我都选择115200bit/s。
重写pintf函数:
int fputc(int c,FILE *stream)
{
uint8_t ch[1]={c};
HAL_UART_Transmit(&huart2,ch,1,0xFFFF);
return c;
}
while (1)
{
printf("Hello %d\r\n",123);
HAL_Delay(1000);
}
重写scanff函数:
int fgetc(FILE *stream)
{
uint8_t ch[1];
HAL_UART_Receive(&huart2,ch,1,0xFFFF);
return ch[0];
}
while (1)
{
int val=0;
scanf("%d",&val);
printf("%d\n",val);
}
关于重写函数时,可能出现的小问题。
如果程序死在了printf上,可以使用 use MicroLIB(微库),在魔术棒 / Targer 选项页中勾选use MicroLIB。
uint8_t Buffer[5];
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
}
HAL_UART_Transmit_IT(&huart2,buf,3);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
HAL_UART_Transmit_IT(&huart2,buf,3);//把接收到的数据发送回去
HAL_UART_Receive_IT(&huart2,buf,3);//再开启下一次的接收
}
HAL_UART_Receive_IT(&huart2,buf,3);
每发送和接收一个字节都会进入中断。
1.调用函数开启一次中断收发的流程。
2.每发送或接收到一个字节会进入中断,由HAL进行处理,用户程序无需参与。
3.传输完指定数量的字节后HAL调用发送或接收完成回调函数。
DirectMemoryAccess(存储器直接访问)是指一种高速的数据传输操作,允许在外部设备和存储器之间直接读写数据,既不通过CPU,也不需要CPU干预。整个数据传输操作在一个称为“DMA控制器”的控制下进行。CPU除了在数据传输开始和结束时做一点处理外,在传输过程中还可以进行其他的工作。这样,在大部分时间里,CPU和输入输出都处于并行操作,因此使整个计算机系统的效率大大提高。
uint8_t Buffer[5];
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
HAL_UART_Transmit_DMA(&huart2,buf,3);
HAL_UART_Receive_DMA(&huart2,buf,3);
}
HAL_UART_Receive_DMA(&huart2,buf,3);
但当我们发送不定长字符时,会遇到数据溢出或者数据长度不够无法返回的错误。因此我们选择不定长输入的空闲中断代码。
/* USER CODE BEGIN 2 */
HAL_UART_Receive_DMA(&huart2, buffer,3);
__HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE);
/* USER CODE END 2 */
并在串口中断函数中,编写空闲中断代码。
extern uint8_t Buffer[5];
void USART1_ IRQHandler(void)
/* USER CODE BEGIN USART1_ IRQn日*/
if(_HAL_UART_GET_FLAG(&huart2,UART_ FLAG_ IDLE) != RESET)//触发空闲中断
{
_HAL_UART_CLEAR_IDLEFLAG(&huart2);//清除空闲中断标志位
HAL_UART_DMAStop(&huart2);//停止发送
uint8_t len=3-_HAL_DMA_GET_COUNTER(huart2.hdmarx);//计算接收到了多少字节
HAL_UART_Transmit_DMA(&huart2,buffer,len);// 把接收到的数据发送出去
HAL_UART_Receive_DMA(&huart2,buffer,3);//开启DMA,准备下次接收
}
/* USER CODE END USART1_ IRQn日*/
串口相关代码
提取码:nj4z