TM4C123系列UART串口通信详解
一.实验简介
使用TM4C123的串口通信功能实现单片机与PC端通信。
二.UART介绍
TM4C123有八个串口,其中UART0已经与USB集成,UART0建议只用来和PC端通信,不要与外界通信。
除此之外,与STM32不同,TM4的每个串口还都有两个FIFO,一个用来接收,一个用来发送,所谓FIFO也就是数据缓存区,对于接收的FIFO就是PC端传输数据到FIFO中,FIFO再传给单片机处理;对于发送的FIFO就是单片机先将数据存放在FIFO,然后FIFO再将数据发送给PC端。
通过设置FIFO深度来触发中断,FIFO的数据深度有1/8,1/4,1/2,3/4,7/8五种,比如,设置接收FIFIO深度为1/8,那么就是FIFO中装入了16*1/8=2字节时触发中断,如果不用FIFO,那么每接收一个字节都要触发一次中断,导致频繁的进入中断,而使用FIFO以后,将先把一定量的数据存入FIFO中,到达设置的FIFO深度后再触发中断,然后将这些数据一起处理,减少了进入中断的次数,而且更加高效。
FIFO可以设置以下的模式:
//! - \b UART_INT_9BIT - 9-bit Address Match interrupt
//! - \b UART_INT_OE - Overrun Error interrupt
//! - \b UART_INT_BE - Break Error interrupt
//! - \b UART_INT_PE - Parity Error interrupt
//! - \b UART_INT_FE - Framing Error interrupt
//! - \b UART_INT_RT - Receive Timeout interrupt
//! - \b UART_INT_TX - Transmit interrupt
//! - \b UART_INT_RX - Receive interrupt
//! - \b UART_INT_DSR - DSR interrupt
//! - \b UART_INT_DCD - DCD interrupt
//! - \b UART_INT_CTS - CTS interrupt
//! - \b UART_INT_RI - RI interrupt
经常用的有发送,接收,接收超时这几种,发送和接收不用多说。对于接收超时中断来说,要是没有接收超时中断,接收的字节数如果没有到达设置的FIFO深度,那么中断将不会触发,这些数据将得不到有效处理,但是要是有超时中断,如果接收的字节数未到触发深度但是已经没有别的数据可以接受,那么在不超过三个数据的接收时间就会触发超时中断,数据将会照常处理。
三.原理图
或者也可以在Tiva C Series PinMux中查看,链接在上一篇中有。(11条消息) TM4C123系列(三)————PWM输出_G.883的博客-CSDN博客
四.相关函数
没有注明作用的函数请看我之前的文章有写
1.SysCtlClockSet(uint32_t ui32Config)
2.SysCtlPeripheralEnable(uint32_t ui32Peripheral)
3.GPIOPinConfigure(uint32_t ui32PinConfig)
4.GPIOPinTypeUART(uint32_t ui32Port, uint8_t ui8Pins)
5.UARTClockSourceSet(uint32_t ui32Base, uint32_t ui32Source)
参数:uint32_t ui32Base为串口基地址,uint32_t ui32Source为串口的波特时钟源一般为UART_CLOCK_PIOSC是16MHz
作用:为指定的UART设置波特时钟源
6.UARTStdioConfig(uint32_t ui32PortNum, uint32_t ui32Baud, uint32_t ui32SrcClock)
参数:uint32_t ui32PortNum为串口编号,即0,1,2····,uint32_t ui32Baud为波特率,uint32_t ui32SrcClock为UART的源时钟频率(launchpad上为16MHz,所以这个数为16000000)
7.UARTprintf(const char *pcString, …)
参数:const char *pcString为要显示的字符串, …为显示的变量。
作用:与printf的作用和使用格式一样,例如a=10,UARTprintf(“a=%d”, a)
8.UARTConfigSetExpClk(uint32_t ui32Base, uint32_t ui32UARTClk,uint32_t ui32Baud, uint32_t ui32Config)
参数:uint32_t ui32Base为串口基地址,uint32_t ui32UARTClk为提供给UART的时钟频率,通过SysCtlClockGet()函数可以得到,通过uint32_t ui32Config来配置串口的字长,校验位,停止位
作用:配置串口参数
9.UARTCharPut(uint32_t ui32Base, unsigned char ucData)
参数:uint32_t ui32Base为串口基地址,unsigned char ucData为单个字符
作用:打印输出单个字符
####注意配置串口时,要将5,6,7搭配使用;8,9搭配使用;5,6,7和8,9都可以用来配置一个串口,但是不能同时配置同一个串口,也就是不能混用。并且注意5,6,7搭配使用只能用于串口0到串口2,而8,9搭配使用时可以用于所有串口。
10.UARTFIFOEnable(uint32_t ui32Base)
参数:ui32Base为串口基地址
作用:使能FIFO
11.UARTFIFOLevelSet(uint32_t ui32Base, uint32_t ui32TxLevel,uint32_t ui32RxLevel)
参数:ui32Base为串口基地址,ui32TxLevel发送FIFO的深度,ui32RxLevel为接收FIFO的深度
作用:配置接收与发送的FIFO
12.UARTIntEnable(uint32_t ui32Base, uint32_t ui32IntFlags)
参数:ui32Base为串口基地址,ui32IntFlags为中断模式
作用:设置对应串口中断模式
13.UARTIntRegister(uint32_t ui32Base, void (*pfnHandler)(void))
14.IntPrioritySet(uint32_t ui32Interrupt, uint8_t ui8Priority)
参数:ui32Interrupt为中断外设,ui8Priority为中断优先级从0x0到0xE0,0x0优先级最高
15.IntEnable(uint32_t ui32Interrupt)
16.IntMasterEnable()
这两个不懂什么去别的可以去看我之前的文章
17.UARTIntStatus(uint32_t ui32Base, bool bMasked)
18.UARTIntClear(uint32_t ui32Base, uint32_t ui32IntFlags)
19.UARTCharsAvail(uint32_t ui32Base)
参数:ui32Base为串口基地址
作用:判断缓存区是否有字符存在,如果有的话为true,没有直接返回false
20.UARTCharGetNonBlocking(uint32_t ui32Base)
参数:ui32Base为串口基地址
作用:从指定的串口FIFO接收一个字符,NonBlocking指如果没有字符不等待直接返回-1
21.UARTCharPutNonBlocking(uint32_t ui32Base, unsigned char ucData)
参数:ui32Base为串口基地址,ucData为发送的单个字符
作用:从指定的串口FIFO发送一个字符,如果没有字符发送不等待直接返回-1
对应20与21还有一对函数,UARTCharPut与UARTCharGet,与20和21的区别就是这两个函数如果FIFO中没有数据,他会等待有数据在接收,而NonBlocking不等待
22.UARTEnable(uint32_t ui32Base)
参数:ui32Base为串口基地址
作用:使能串口
五.代码
usart.c
#include "uart.h"
#include "gpio.h"
#include "usart.h"
#include "hw_memmap.h"
#include "pin_map.h"
#include "uartstdio.h"
#include "sysctl.h"
#include "hw_ints.h"
void USART0_IRQHandler(void);
void USART_Config(void)
{
//使能外设
SysCtlPeripheralEnable( SYSCTL_PERIPH_GPIOA);
SysCtlPeripheralEnable( SYSCTL_PERIPH_UART0);
//使能复用
GPIOPinConfigure( GPIO_PA0_U0RX);
GPIOPinConfigure( GPIO_PA1_U0TX);
//分配信号
GPIOPinTypeUART( GPIO_PORTA_BASE, GPIO_PIN_0);
GPIOPinTypeUART( GPIO_PORTA_BASE, GPIO_PIN_1);
/*搭配UARTprintf使用*/
//设置串口0的时钟为UART_CLOCK_PIOSC
// UARTClockSourceSet( UART0_BASE, UART_CLOCK_PIOSC);
//设置串口0波特率与波特时钟
// UARTStdioConfig( 0, 115200,
// 16000000);
/*与UARTCharPut,UARTCharPutNonBlocking等搭配使用*/
//配置串口0波特率与校验位,停止位,字长等
UARTConfigSetExpClk( UART0_BASE, SysCtlClockGet(),
115200, UART_CONFIG_WLEN_8|UART_CONFIG_STOP_ONE|UART_CONFIG_PAR_NONE);
//使能FIFO
UARTFIFOEnable( UART0_BASE);
//接收发送的FIFO都为1/4,也就是16*1/4=4个字节
UARTFIFOLevelSet( UART0_BASE, UART_FIFO_TX2_8,
UART_FIFO_RX2_8);
//使能串口的接收与接收超时中断
UARTIntEnable( UART0_BASE, UART_INT_RX|UART_INT_RT);
//注册中断函数
UARTIntRegister( UART0_BASE, USART0_IRQHandler);
//设置中断优先级
IntPrioritySet( INT_UART0, 0);
//开启中断
IntEnable( INT_UART0);
IntMasterEnable();
//使能串口
UARTEnable( UART0_BASE);
}
void USART0_IRQHandler(void)
{
uint32_t re_buf;
//读取中断状态
uint32_t status=UARTIntStatus( UART0_BASE, true);
//清除中断标志位
UARTIntClear( UART0_BASE, status);
//判断UART0有没有字符未读取
while(UARTCharsAvail( UART0_BASE))
{
//如果有字符为读取就取出,使用UARTCharGetNonBlocking防止等待
re_buf=UARTCharGetNonBlocking( UART0_BASE);
//将读取出的字符再发送
UARTCharPutNonBlocking( UART0_BASE, re_buf);
}
}
usart.h
#ifndef __USART_H
#define __USART_H
void USART_Config(void);
void USART0_IRQHandler(void);
#endif
main.c
#include "tm4c123gh6pm.h"
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_sysctl.h"
#include "inc/hw_gpio.h"
#include "inc/hw_memmap.h"
#include "driverlib/gpio.h"
#include "driverlib/sysctl.h"
#include "uartstdio.h"
void main(void)
{
SysCtlClockSet( SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);
USART_Config();
while(1)
{
}
}
注意:在此代码中我设置的是接收和接收超时中断两个UARTIntEnable( UART0_BASE, UART_INT_RX|UART_INT_RT),并且深度都为4个字节,对于英文正好一个英文是一个字节,因为我设置了接收超时中断,所以不管我怎么样发送,都可以正常接收。
但是如果我关闭了接收超时中断
可以发现接收的时候少发送了一个g,那是因为前面十二个字符四个四个一组触发中断,最后单着一个g在FIFO中无法触发中断,所以发不回来。
可以看到如果我再发送aaaa,加上之前的FIFO缓存区的g够了四个就触发中断,之前的g可以发回来,这就是超时中断的作用。