当前位置:首页 > 单片机 > 单片机
[导读]这次讲讲利用串口收发中断来进行串口通讯。STM32 上为每个串口分配了一个中断。也就是说无论是发送完成还是收到数据或是数据溢出都产生同一个中断。程序需在中断处理函数中读取状态寄存器(USART_SR)来判断当前的是什

这次讲讲利用串口收发中断来进行串口通讯。STM32 上为每个串口分配了一个中断。也就是说无论是发送完成还是收到数据或是数据溢出都产生同一个中断。程序需在中断处理函数中读取状态寄存器(USART_SR)来判断当前的是什么中断。下面的中断映像图给出了这些中断源是如何汇合成最终的中断信号的。图中也给出了如何控制每一个单独的中断源是否起作用。

另外,Cortex-M3内核中还有个NVIC,可以控制这里的中断信号是否触发中断处理函数的执行,还有这些外部中断的级别。关于NVIC可以参考《ARM CortexM3权威指南》,里面讲解的非常详细。

简单的说,为了开启中断,我们需要如下的代码:


NVIC_InitTypeDef NVIC_InitStructure;

NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

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

USART_ITConfig(USART1, USART_IT_TXE, ENABLE);// 开启发送中断

这里多说一句,串口的发送中断有两个,分别是:

l发送数据寄存器空中断(TXE)

l发送完成中断(TC)

一般来说我们会使用发送数据寄存器空中断,用这个中断发送的效率会高一些。

中断处理函数的框架如下,如果检测到错误就清除错误,收到数了就处理。发完当前数据了就发下一个。


voidUSART1_IRQHandler(void)

{

unsignedintdata;

if(USART1->SR & 0x0F)

{

// See if we have some kind of error, Clear interrupt

data = USART1->DR;

}

elseif(USART1->SR & USART_FLAG_RXNE)//Receive Data Reg Full Flag

{

data = USART1->DR;

// 对收到的数据进行处理,或者干些其他的事

}

elseif(USART1->SR & USART_FLAG_TXE)

{

{// 可以发送数据了,如果没有数据需要发送,就在这里关闭发送中断

USART1->DR = something;// Yes, Send character

}

}

}


下面给一个利用环形缓冲区的串口驱动程序。


#ifndef _COM_BUFFERED_H_

#define _COM_BUFFERED_H_

#define COM1 0

#define COM2 1

#define COM_RX_BUF_SIZE 64

#define COM_TX_BUF_SIZE 64

#define COM_NO_ERR 0

#define COM_BAD_CH 1

#define COM_RX_EMPTY 2

#define COM_TX_FULL 3

#define COM_TX_EMPTY 4

unsignedcharCOMGetCharB (unsignedcharch, unsignedchar*err);

unsignedcharCOMPutCharB (unsignedcharport, unsignedcharc);

voidCOMBufferInit (void);

unsignedcharCOMBufferIsEmpty (unsignedcharport);

unsignedcharCOMBufferIsFull (unsignedcharport);

#endif



#include "stm32f10x_usart.h"

#include "com_buffered.h"

#define OS_ENTER_CRITICAL() __set_PRIMASK(1)

#define OS_EXIT_CRITICAL() __set_PRIMASK(0)

staticvoidCOMEnableTxInt(unsignedcharport)

{

staticUSART_TypeDef* map[2] = {USART1, USART2};

USART_ITConfig(map[port], USART_IT_TXE, ENABLE);

}

typedefstruct{

shortRingBufRxCtr;

unsignedchar*RingBufRxInPtr;

unsignedchar*RingBufRxOutPtr;

unsignedcharRingBufRx[COM_RX_BUF_SIZE];

shortRingBufTxCtr;

unsignedchar*RingBufTxInPtr;

unsignedchar*RingBufTxOutPtr;

unsignedcharRingBufTx[COM_TX_BUF_SIZE];

} COM_RING_BUF;

COM_RING_BUF COM1Buf;

COM_RING_BUF COM2Buf;

unsignedcharCOMGetCharB (unsignedcharport, unsignedchar*err)

{

// unsigned char cpu_sr;

unsignedcharc;

COM_RING_BUF *pbuf;

switch(port)

{

caseCOM1:

pbuf = &COM1Buf;

break;

caseCOM2:

pbuf = &COM2Buf;

break;

default:

*err = COM_BAD_CH;

return(0);

}

OS_ENTER_CRITICAL();

if(pbuf->RingBufRxCtr > 0)

{

pbuf->RingBufRxCtr--;

c = *pbuf->RingBufRxOutPtr++;

if(pbuf->RingBufRxOutPtr == &pbuf->RingBufRx[COM_RX_BUF_SIZE])

{

pbuf->RingBufRxOutPtr = &pbuf->RingBufRx[0];

}

OS_EXIT_CRITICAL();

*err = COM_NO_ERR;

return(c);

}else{

OS_EXIT_CRITICAL();

*err = COM_RX_EMPTY;

c = 0;

return(c);

}

}

unsignedcharCOMPutCharB (unsignedcharport, unsignedcharc)

{

// unsigned char cpu_sr;

COM_RING_BUF *pbuf;

switch(port)

{

caseCOM1:

pbuf = &COM1Buf;

break;

caseCOM2:

pbuf = &COM2Buf;

break;

default:

return(COM_BAD_CH);

}

OS_ENTER_CRITICAL();

if(pbuf->RingBufTxCtr < COM_TX_BUF_SIZE) {

pbuf->RingBufTxCtr++;

*pbuf->RingBufTxInPtr++ = c;

if(pbuf->RingBufTxInPtr == &pbuf->RingBufTx[COM_TX_BUF_SIZE]) {

pbuf->RingBufTxInPtr = &pbuf->RingBufTx[0];

}

if(pbuf->RingBufTxCtr == 1) {

COMEnableTxInt(port);

OS_EXIT_CRITICAL();

}else{

OS_EXIT_CRITICAL();

}

return(COM_NO_ERR);

}else{

OS_EXIT_CRITICAL();

return(COM_TX_FULL);

}

}

voidCOMBufferInit (void)

{

COM_RING_BUF *pbuf;

pbuf = &COM1Buf;

pbuf->RingBufRxCtr = 0;

pbuf->RingBufRxInPtr = &pbuf->RingBufRx[0];

pbuf->RingBufRxOutPtr = &pbuf->RingBufRx[0];

pbuf->RingBufTxCtr = 0;

pbuf->RingBufTxInPtr = &pbuf->RingBufTx[0];

pbuf->RingBufTxOutPtr = &pbuf->RingBufTx[0];

pbuf = &COM2Buf;

pbuf->RingBufRxCtr = 0;

pbuf->RingBufRxInPtr = &pbuf->RingBufRx[0];

pbuf->RingBufRxOutPtr = &pbuf->RingBufRx[0];

pbuf->RingBufTxCtr = 0;

pbuf->RingBufTxInPtr = &pbuf->RingBufTx[0];

pbuf->RingBufTxOutPtr = &pbuf->RingBufTx[0];

}

unsignedcharCOMBufferIsEmpty (unsignedcharport)

{

// unsigned char cpu_sr;

unsignedcharempty;

COM_RING_BUF *pbuf;

switch(port)

{

caseCOM1:

pbuf = &COM1Buf;

break;

caseCOM2:

pbuf = &COM2Buf;

break;

default:

return(1);

}

OS_ENTER_CRITICAL();

if(pbuf->RingBufRxCtr > 0)

{

empty = 0;

}

else

{

empty = 1;

}

OS_EXIT_CRITICAL();

return(empty);

}

unsignedcharCOMBufferIsFull (unsignedcharport)

{

// unsigned char cpu_sr;

charfull;

COM_RING_BUF *pbuf;

switch(port)

{

caseCOM1:

pbuf = &COM1Buf;

break;

caseCOM2:

pbuf = &COM2Buf;

break;

default:

return(1);

}

OS_ENTER_CRITICAL();

if(pbuf->RingBufTxCtr < COM_TX_BUF_SIZE) {

full = 0;

}else{

full = 1;

}

OS_EXIT_CRITICAL();

return(full);

}

// This function is called by the Rx ISR to insert a character into the receive ring buffer.

staticvoidCOMPutRxChar (unsignedcharport, unsignedcharc)

{

COM_RING_BUF *pbuf;

switch(port)

{

caseCOM1:

pbuf = &COM1Buf;

break;

caseCOM2:

pbuf = &COM2Buf;

break;

default:

return;

}

if(pbuf->RingBufRxCtr < COM_RX_BUF_SIZE) {

pbuf->RingBufRxCtr++;

*pbuf->RingBufRxInPtr++ = c;

if(pbuf->RingBufRxInPtr == &pbuf->RingBufRx[COM_RX_BUF_SIZE]) {

pbuf->RingBufRxInPtr = &pbuf->RingBufRx[0];

}

}

}

// This function is called by the Tx ISR to extract the next character from the Tx buffer.

// The function returns FALSE if the buffer is empty after the character is extracted from

// the buffer. This is done to signal the Tx ISR to disable interrupts because this is the

// last character to send.

staticunsignedcharCOMGetTxChar (unsignedcharport, unsignedchar*err)

{

unsignedcharc;

COM_RING_BUF *pbuf;

switch(port)

{

caseCOM1:

pbuf = &COM1Buf;

break;

caseCOM2:

pbuf = &COM2Buf;

break;

default:

*err = COM_BAD_CH;

return(0);

}

if(pbuf->RingBufTxCtr > 0) {

pbuf->RingBufTxCtr--;

c = *pbuf->RingBufTxOutPtr++;

if(pbuf->RingBufTxOutPtr == &pbuf->RingBufTx[COM_TX_BUF_SIZE])

{

pbuf->RingBufTxOutPtr = &pbuf->RingBufTx[0];

}

*err = COM_NO_ERR;

return(c);

}else{

*err = COM_TX_EMPTY;

return(0);

}

}

voidUSART1_IRQHandler(void)

{

unsignedintdata;

unsignedcharerr;

if(USART1->SR & 0x0F)

{

// See if we have some kind of error

// Clear interrupt (do nothing about it!)

data = USART1->DR;

}

elseif(USART1->SR & USART_FLAG_RXNE)//Receive Data Reg Full Flag

{

data = USART1->DR;

COMPutRxChar(COM1, data);// Insert received character into buffer

}

elseif(USART1->SR & USART_FLAG_TXE)

{

data = COMGetTxChar(COM1, &err);// Get next character to send.

if(err == COM_TX_EMPTY)

{// Do we have anymore characters to send ?

// No, Disable Tx interrupts

//USART_ITConfig(USART1, USART_IT_TXE| USART_IT_TC, ENABLE);

USART1->CR1 &= ~USART_FLAG_TXE | USART_FLAG_TC;

}

else

{

USART1->DR = data;// Yes, Send character

}

}

}

voidUSART2_IRQHandler(void)

{

unsignedintdata;

unsignedcharerr;

if(USART2->SR & 0x0F)

{

// See if we have some kind of error

// Clear interrupt (do nothing about it!)

data = USART2->DR;

}

elseif(USART2->SR & USART_FLAG_RXNE)//Receive Data Reg Full Flag

{

data = USART2->DR;

COMPutRxChar(COM2, data);// Insert received character into buffer

}

elseif(USART2->SR & USART_FLAG_TXE)

{

data = COMGetTxChar(COM2, &err);// Get next character to send.

if(err == COM_TX_EMPTY)

{// Do we have anymore characters to send ?

// No, Disable Tx interrupts

//USART_ITConfig(USART2, USART_IT_TXE| USART_IT_TC, ENABLE);

USART2->CR1 &= ~USART_FLAG_TXE | USART_FLAG_TC;

}

else

{

USART2->DR = data;// Yes, Send character

}

}

}


下面给个例子主程序,来演示如何使用上面的串口驱动代码。


#include "misc.h"

#include "stm32f10x.h"

#include "com_buffered.h"

voidUART_PutStrB (unsignedcharport, uint8_t *str)

{

while(0 != *str)

{

COMPutCharB(port, *str);

str++;

}

}

voidUSART1_Init(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

USART_InitTypeDef USART_InitStructure;

NVIC_InitTypeDef NVIC_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

GP

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

在嵌入式开发中,STM32的时钟系统因其灵活性和复杂性成为开发者关注的焦点。然而,看似简单的时钟配置背后,隐藏着诸多易被忽视的陷阱,轻则导致系统不稳定,重则引发硬件损坏。本文从时钟源选择、PLL配置、总线时钟分配等关键环...

关键字: STM32 时钟系统

在嵌入式系统开发中,STM32系列微控制器的内部温度传感器因其低成本、高集成度特性,广泛应用于设备自检、环境监测等场景。然而,受芯片工艺差异和电源噪声影响,其原始数据存在±1.5℃的固有误差。本文从硬件配置、校准算法、软...

关键字: STM32 温度传感器

在能源效率与智能化需求双重驱动下,AC-DC转换器的数字控制技术正经历从传统模拟方案向全数字架构的深刻变革。基于STM32微控制器的PFM(脉冲频率调制)+PWM(脉冲宽度调制)混合调制策略,结合动态电压调整(Dynam...

关键字: AC-DC STM32

当前智能家居产品需求不断增长 ,在这一背景下 ,对现有浇花装置缺陷进行了改进 ,设计出基于STM32单片机的全 自动家用浇花机器人。该设计主要由机械结构和控制系统构成 ,机械结构通过麦克纳姆轮底盘与喷洒装置的结合实现机器...

关键字: STM32 麦克纳姆轮 安全可靠 通过性强

用c++编程似乎是让你的Arduino项目起步的障碍吗?您想要一种更直观的微控制器编程方式吗?那你需要了解一下Visuino!这个图形化编程平台将复杂电子项目的创建变成了拖动和连接块的简单任务。在本文中,我们将带您完成使...

关键字: Visuino Arduino ESP32 STM32

基于STM32与LoRa技术的无线传感网络凭借其低功耗、广覆盖、抗干扰等特性,成为环境监测、工业自动化等场景的核心解决方案。然而,如何在复杂电磁环境中实现高效休眠调度与动态信道优化,成为提升网络能效与可靠性的关键挑战。本...

关键字: STM32 LoRa

在实时控制系统、高速通信协议处理及高精度数据采集等对时间敏感的应用场景中,中断响应延迟的优化直接决定了系统的可靠性与性能上限。STM32系列微控制器凭借其灵活的嵌套向量中断控制器(NVIC)、多通道直接内存访问(DMA)...

关键字: STM32 DMA

数字电源技术向高功率密度、高效率与高动态响应方向加速演进,STM32微控制器凭借其基于DSP库的算法加速能力与对LLC谐振变换器的精准控制架构,成为优化电源动态性能的核心平台。相较于传统模拟控制或通用型数字控制器,STM...

关键字: STM32 数字电源

STM32微控制器凭借其针对电机控制场景的深度优化,成为高精度、高可靠性驱动系统的核心选择。相较于通用型MCU,STM32在电机控制领域的核心优势集中体现在FOC(磁场定向控制)算法的硬件加速引擎与PWM死区时间的动态补...

关键字: STM32 电机控制

无线充电技术加速渗透消费电子与汽车电子领域,基于Qi协议的无线充电发射端开发成为智能设备能量补给的核心课题。传统模拟控制方案存在响应滞后、参数调整困难等问题,而基于STM32的数字PID控制结合FOD(Foreign O...

关键字: STM32 无线充电
关闭