1.串口简介
通用同步异步收发器(USART)提供了一种灵活的方法与使用工业标准NRZ异步串行数据格式的外部设备之间进行全双工数据交换。 USART利用分数波特率发生器提供宽范围的波特率选择。它支持同步单向通信和半双工单线通信,也支持LIN(局部互连网),智能卡协议和IrDA(红外数据组织)SIR ENDEC规范,以及调制解调器(CTS/RTS)操作。它还允许多处理器通信。使用多缓冲器配置的DMA方式,可以实现高速数据通信。
串口是计算机上一种非常通用设备通信的协议。大多数计算机包含两个基于RS232的串口。串口同时也是仪器仪表设备通用的通信协议;很多GPIB兼容的设备也带有RS-232口。同时,串口通信协议也可以用于获取远程采集设备的数据。本次所所使用开发板为mini USB串口,有3个分别为USART1、USART2和USART3。我们将以串口1为例完成开发板与上位机之间通讯。
串口的基本功能(使能串口、中断功能、发送、接收)均在USART1_CRTL1寄存器中完成配置。
状态寄存器主要用于检测各个状态标志是否触发(发送标志、接收标志、空闲帧标志等等)。
串口数据寄存器用包含了发送和接收数据。串口数据收发均使用DAT寄存器装载。
我们这里以串口1为例,因为串口1为开发板调试端口,方便后面效果展示。配置模式为:1个起始位+8个数据位+1个停止位,无校验位。开始长发送功能和空闲帧接收功能,通过DMA配合串口数据收发,实现不定长数据处理。
void USART_Init(u32 buad)
{
RCC->APB2PCLKEN|=1<<14;
RCC->APB2PRST|=1<<14;
RCC->APB2PRST&=~(1<<14);
/*配置GPIO*/
RCC->APB2PCLKEN|=1<<2;//PA
GPIOA->PH_CFG&=0xFFFFF00F;
GPIOA->PH_CFG|=0x000008B0;
/*串口配置*/
USART1->BRCF=72000000/buad;
USART1->CTRL1|=1<<2;
USART1->CTRL1|=1<<3;
USART1->CTRL3|=1<<7;//DMA发送
USART1->CTRL3|=1<<6;//DMA接收
USART1->CTRL1|=1<<4;//IDLE(空闲帧中断)
N32_NVIC_SetPriority(USART1_IRQn,1,1);
USART1->CTRL1|=1<<13;//使能串口
DMA_CH5_Init((u32 )&USART1->DAT,(u32 )usart1_rx_buff);//DMA配合串口1接收数据
DMA_CH4_Init((u32 )&USART1->DAT);
}
修改printf重定向,方便后续功能调试。
printf底层实现接口函数为fputc,我们只需要将该函数重定向到串口调试终端即可。
int fputc(int c,FILE *stream)
{
USART1->DAT=c;
while(!(USART1->STS&1<<7));
return c;
}
直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。
DMA 控制器总共可以访问 8 个 AHB 从机: Flash、 SRAM、 ADC、 SDIO、 QSPI、 ETH、 ABP1 和 APB2。
DMA 控制器由 CPU 控制以执行从源到目的的快速数据移动。配置完成后,无需 CPU 干预即可传输数据。因此,可以释放 CPU 用于其他计算/控制任务或节省整体系统功耗。
芯片有两个 DMA( DMA1、 DMA2)控制器,每个 DMA 控制器有 8 个逻辑通道。每个逻辑通道用于服务来自单个或多个外设的内存访问请求。内部仲裁器控制不同 DMA 通道的优先级。
数据访问:根据传输方向确定源地址( DMA_PADDRx 或 DMA_MADDRx),从源地址读取数据。
数据存储:根据传输方向确定目的地址( DMA_PADDRx 或 DMA_MADDRx),将读取的数据存储到目的地址空间。
计算未完成操作的数量,对 DMA_TXNUMx 寄存器进行减量操作,更新下一个操作的源地址和目的地址。
DMA1 请求映像如下图所示。通过配置对应外设的寄存器,每个外设的 DMA 请求均可以独立的开启或关闭,并根据通道优先级,同一时间只有一个请求有效。
根据DMA请求映像可知,USART1_TX在Channel4上,USART1_RX在Channel5上。
DMA通道配置寄存器用于配置DMA传输方向、数据宽度、优先级等功能。
DMA传输数量寄存器用于设置DMA传输的数量,范围在0~65535之间。
DMA1_CH4配置,设置模式为存储器到外设,配合串口1数据发送。
/******DMA配合串口1发送数据************
**形参:u32 cpar -- 外设地址
**
**例:DMA_CH4_Init(&USART1->DR,buff)
**外设地址:USART1->DR的地址
** buff的地址
**作者:IT_阿水
**************************************/
void DMA_CH4_Init(u32 cpar)
{
RCC->AHBPCLKEN|=1<<0;//dma1时钟使能
DMA1_CH4->CHCFG&=~(1<<14);//非存储器到存储器模式
DMA1_CH4->CHCFG|=0x3<<12;//设置CH4优先级为最高
DMA1_CH4->CHCFG&=~(0x3<<10);//存储器数据宽度8位
DMA1_CH4->CHCFG&=~(0x3<<8);//外设数据宽度8位
DMA1_CH4->CHCFG|=1<<7;//存储器地址增量
DMA1_CH4->CHCFG&=~(1<<6);//外设地址不增量
DMA1_CH4->CHCFG&=~(1<<5);//不执行循环操作
DMA1_CH4->CHCFG|=1<<4;//从存储器读
DMA1->CHMAPEN|=1<<0;//使能MAP通道
DMA1_CH4->CHSEL|=16<<0;//开启USART1_TX
DMA1_CH4->CHCFG&=~(1<<0);//关闭通道传输
DMA1_CH4->PADDR=cpar;//外设地址
}
/**********开启DMA1_CH4数据传输*************
***
***形参:u16 data_len -- DMA要传输数目
***********************************************/
void DMA_CH4_Start(u8 *buff,u16 data_len)
{
DMA1_CH4->CHCFG&=~(1<<0);//关闭通道传输
DMA1_CH4->MADDR=(u32)buff;//存储器地址
DMA1_CH4->TXNUM=data_len;//设置传输数量
DMA1_CH4->CHCFG|=1<<0;//开启通道传输
}
DMA1_CH5配置,设置模式为外设到存储器,配合串口1数据接收。
/******DMA配合串口1接收数据************
**形参:u32 cpar -- 外设地址
** u32 cmar -- 存储器地址
**
**例:DMA_CH5_Init(&USART1->DR,buff)
**外设地址:USART1->DR的地址
** buff的地址
**作者:IT_阿水
**************************************/
void DMA_CH5_Init(u32 cpar,u32 cmar)
{
RCC->AHBPCLKEN|=1<<0;//dma1时钟使能
DMA1_CH5->CHCFG&=~(1<<14);//非存储器到存储器模式
DMA1_CH5->CHCFG|=0x3<<12;//设置CH4优先级为最高
DMA1_CH5->CHCFG&=~(0x3<<10);//存储器数据宽度8位
DMA1_CH5->CHCFG&=~(0x3<<8);//外设数据宽度8位
DMA1_CH5->CHCFG|=1<<7;//存储器地址增量
DMA1_CH5->CHCFG&=~(1<<6);//外设地址不增量
DMA1_CH5->CHCFG&=~(1<<5);//执行循环操作
DMA1_CH5->CHCFG&=~(1<<4);//从外设读
DMA1->CHMAPEN|=1<<0;//使能MAP通道
DMA1_CH5->CHSEL=23;//开启USART1_RX
DMA1_CH5->CHCFG&=~(1<<0);//关闭通道传输
DMA1_CH5->PADDR=cpar;//外设地址
DMA1_CH5->MADDR=cmar;//存储器地址
DMA1_CH5->TXNUM=1024;//设置传输数量
DMA1_CH5->CHCFG|=1<<0;//开启通道传输
}
/**********开启DMA1_CH5数据传输*************
***
***形参:u16 data_len -- DMA要传输数目
***********************************************/
void DMA_CH5_Start(u16 data_len)
{
DMA1_CH5->CHCFG&=~(1<<0);//关闭通道传输
DMA1_CH5->TXNUM=data_len;//设置传输数量
DMA1_CH5->CHCFG|=1<<0;//开启通道传输
}
编写串口以中断服务函数,通过触发空闲帧中断,配置DMA实现串口不定长数据接收处理。
当一空闲帧被检测到时, USART_STS.IDLEF 置 1。此时如果 USART_CTRL1.IDLEIEN 已置 1,将产生一个中断。 USART_STS.IDLEF 可通过以下软件操作清零:先读 USART_STS 寄存器,再读 USART_DAT 寄存器。
/*************************串口接收数据函数********************/
void Usart1_Receive_Data(void)
{
DMA1_CH5->CHCFG&=~(1<<0);//关闭通道传输
usart1_cnt=1024-DMA1_CH5->TXNUM;//获取接收到是字符长度
if(usart1_cnt>1024)usart1_cnt=0;
DMA1->INTSTS|=1<<17;//清除标志位
DMA1_CH5->TXNUM=1024;//从新赋值
DMA1_CH5->CHCFG|=1<<0;//开启闭通道传输
usart1_flag=1;
}
/*串口中断服务函数*/
void USART1_IRQHandler(void)
{
u8 c;
//清除标志:先读USART_STS,再读USART_DAT;
c=USART1->STS&1<<4;
if(c)//空闲帧
{
c=USART1->DAT;
Usart1_Receive_Data();//接收数据处理函数
}
USART1->STS=0;//清除标志位
}
通过串口调试终端配合,利用串口数据接收功能,实现串口通讯方式对开发板LED亮灭控制。
#include "n32g45x.h"
#include
#include
#include "led.h"
#include "key.h"
#include "usart.h"
#include "delay.h"
int main()
{
u8 time=0;
u8 key_val;
LED_Init();
KEY_Init();
USART_Init(115200);
printf("串口初始化完成\r\n");
while(1)
{
key_val=Key_Scan();
if(key_val)
{
printf("串口+DMA数据发送测试示例!\r\n");
}
if(usart1_flag)
{
usart1_rx_buff[usart1_cnt]='\0';
printf("%s,%d\r\n",usart1_rx_buff,usart1_cnt);
if(strcmp((char *)usart1_rx_buff,"LED1_ON")==0)LED_D1=1;
else if(strcmp((char *)usart1_rx_buff,"LED1_OFF")==0)LED_D1=0;
else if(strcmp((char *)usart1_rx_buff,"LED2_ON")==0)LED_D2=1;
else if(strcmp((char *)usart1_rx_buff,"LED2_OFF")==0)LED_D2=0;
else if(strcmp((char *)usart1_rx_buff,"LED3_ON")==0)LED_D3=1;
else if(strcmp((char *)usart1_rx_buff,"LED3_OFF")==0)LED_D3=0;
usart1_flag=0;
}
time++;
Delay_Ms(10);
if(time>=50)
{
time=0;
LED_D1=!LED_D1;
}
}
}