目录
一、STM32中断介绍
1、中断概念
中断其实就是当 CPU 执行程序时,由于发生了某种随机的事件(外部或内部),引起 CPU 暂时中断正在运行的程序,转去执行一段特殊的服务程序(中断服务子程序或中断处理程序),以处理该事件,该事件处理完后又返回被中断的程序继续执行,这一过程就称为中断。
中断示意图:
2、中断优先级
处理器根据不同中断的重要程序设置不同的优先等级。不同优先级中断的处理原则是:高级中断可以打断低级中断;低级中断不能打断高级中断,如图所示:
3、中断向量表
如图所示:
除了个别异常的优先级被定死外,其它异常的优先级都是可编程的。这些中断通道已按照不同优先级顺序固定分配给相应的外部设备。从 STM32F10x 中文参考手册的中断向量表可以知道具体分配到那些外设,这里只截取一部分,如需了解更详细可参考《STM32F10x 中文参考手册》
STM32 的 IO 口外部中断函数只有 6 个,分别为:
EXPORT EXTI0_IRQHandler
EXPORT EXTI1_IRQHandler
EXPOR T EXTI2_IRQHandler
EXPORT EXTI3_IRQHandler
EXPORT EXTI4_IRQHandler
EXPORT EXTI9_5_IRQHandler
EXPORT EXTI15_10_IRQHandler
中断线 0-4 每个中断线对应一个中断函数,中断线 5-9 共用中断函数 EXTI9_5_IRQHandler,中断线 10-15 共用中断函数 EXTI15_10_IRQHandler。
二、高低电平控制LED灯亮和灭
进入STMCUBEMX,选择新建项目
选择STM32F103C8,创建新的项目
点击start project。
点击sys,将debug选项改为Serial Wire
然后在Rcc里的HSE选择Crystal/Ceramic Resonator
将PB0选为外部中断触发器,PA8是控制led灯的,将它选择为GPIO_output就行了
选择PLLCLK,然后将后面的晶振频率最大值改为72M赫兹
配置EXIT
点击project manager把toolchain那里改为MDK-ARM。
点击code generator,选择generate...如下图所示:
然后点击上图中的generate code。
选择open project。
进入到keil里,接下来就是在回调函数里写代码了,将回调函数重写一遍就行了,代码如下
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
GPIO_PinState b0_pin = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0); // 读取b0的状态
b0_pin=1-b0_pin;
switch (GPIO_Pin){//判断引脚
case GPIO_PIN_0:
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6,1-b0_pin); // 将a6写入与b0相同的电位
break;
}
}
烧录,然后运行结果为
三、中断实现串口通信
还是使用CUBEMX,新建项目的过程和上一个是一样的,就不多说了设置rcc
SYS里也是选择serial Wire
选择UASRT1,将mode改为异步通信,选择下面的NVIC Setting,将enabled选上。
- project manager里的步骤也和上一个是一样的,接下来就是在keil5里打开项目,进入main.c文件,在里面定义如下数据,注意不是在main函数里,而是在头文件后。
uint8_t aRxBuffer;//接收缓冲中断
uint8_t Uart1_RxBuff[256];//接收缓冲
uint8_t Uart1_Rx_Cnt=0;//接收缓冲计数
uint8_t cAlmStr[]="数据溢出(大于256)";
- 然后将HAL_UART_RxCpltCallback函数重写,和上面的回调函数是一个道理
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(Uart1_Rx_Cnt >= 255) //溢出判断
{
Uart1_Rx_Cnt = 0;
memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff));
HAL_UART_Transmit(&huart1, (uint8_t *)&cAlmStr, sizeof(cAlmStr),0xFFFF);
}
else
{
Uart1_RxBuff[Uart1_Rx_Cnt++] = aRxBuffer; //接收数据转存
if((Uart1_RxBuff[Uart1_Rx_Cnt-1] == 0x0A)||(Uart1_RxBuff[Uart1_Rx_Cnt-2] == 0x0D)) //判断结束位
{
HAL_UART_Transmit(&huart1, (uint8_t *)&Uart1_RxBuff, Uart1_Rx_Cnt,0xFFFF); //将收到的信息发送出去
Uart1_Rx_Cnt = 0;
memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff)); //清空数组
}
}
HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1); //再开启接收中断
}
- 然后在主函数写一个接收中断函数
-
int main(void) { //初始化 HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); //接收中断函数 HAL_UART_Receive_IT(&huart1,(uint8_t*)&aRxBuffer,1); while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }
运行结果
-
四、DMA
-
1、DMA定义:
DMA用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU的干预,通过DMA数据可以快速地移动。这就节省了CPU的资源来做其他操作。
-
2、DMA传输方式
-
DMA的作用就是实现数据的直接传输,而去掉了传统数据传输需要CPU寄存器参与的环节,主要涉及四种情况的数据传输,但本质上是一样的,都是从内存的某一区域传输到内存的另一区域(外设的数据寄存器本质上就是内存的一个存储单元)。四种情况的数据传输如下:
外设到内存
内存到外设
内存到内存
外设到外设 -
3、DMA的主要特征
-
每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过软件来配置;
在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推);
独立数据源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐;
支持循环的缓冲器管理;
每个通道都有3个事件标志(DMA半传输、DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求;
存储器和存储器间的传输、外设和存储器、存储器和外设之间的传输;
闪存、SRAM、外设的SRAM、APB1、APB2和AHB外设均可作为访问的源和目标;
可编程的数据传输数目:最大为65535。
-
五、实现用DMA连续向上位机发送数据
- 打开CUBRMX新建项目,芯片选择是F103C8,点击后创建项目,这两就不写了,先把PA9和PA10选择为USART1_RX和USART1_TX,然后再点击左边的USART1,将mode置为异步通信模式,我们这里只需要接收,不需要发送,后面再点击add键,添加引脚
设置串口
DMA设置
- 点击DMASettings 点击 Add 添加通道
- 选择USART_RX USART_TX 传输速率设置为中速
- DMA传输模式为正常模式
- DMA内存地址自增,每次增加一个Byte(字节)
DMA基础设置
右侧点击System Core 点击DMA
项目文件设置
创建工程文件
然后点击GENERATE CODE 创建工程
进入keil5,
进入到main.c文件,在main.c文件中的while循环那块的代码如下
while (1)
{
uint8_t send_char[]="hello \n";//发送的字符串
HAL_UART_Transmit_DMA(&huart1,(uint8_t *)send_char,0xc);//DMA发送
HAL_Delay(500);//延时
}
结果为:
六、总结
此次作业的内容较多,所以花费了我很多的时间来完成,同时在完成此次作业 的时候也遇到了很多问题,通过查询资料和问同学,一些问题也基本解决,通过此次作业,学到了关于中断以及DMA的一些相关知识,获益良多。
七、参考
【STM32】HAL库 STM32CubeMX教程十一---DMA (串口DMA发送接收)_Z小旋-CSDN博客
stm32hal库串口DMA收发 - Breezy-ye - 博客园
STM32F103C8通过DMA方式向上位机连续发送信息_junseven164的博客-CSDN博客