0
点赞
收藏
分享

微信扫一扫

TM4C123G学习记录(6)--UART


文章目录

  • ​​一、实验简介​​
  • ​​二、TM4C的UART资源​​
  • ​​(1)Tiva控制器的UART特征​​
  • ​​(2)UART结构图​​
  • ​​(3)UART和引脚的复用映射表​​
  • ​​(4)FIFO操作​​
  • ​​(5)中断、​​
  • ​​三、编程实践​​
  • ​​(1)UART配置和使用的基本流程​​
  • ​​(2)使用类似C语言中stdio.h的方法​​
  • ​​(3)UART中断​​
  • ​​(4)FIFO测试​​
  • ​​四、部分库函数小结​​
  • ​​(1)void UARTprintf(const char *pcString, ...)​​
  • ​​(2)void UARTCharPut(uint32_t ui32Base, unsigned char ucData)​​
  • ​​(3)int32_t UARTCharGet(uint32_t ui32Base)​​
  • ​​(4)bool UARTCharPutNonBlocking(uint32_t ui32Base, unsigned char ucData)​​
  • ​​(5)int32_t UARTCharGetNonBlocking(uint32_t ui32Base)​​
  • ​​(6)void UARTFIFOLevelSet(uint32_t ui32Base, uint32_t ui32TxLevel,uint32_t ui32RxLevel)​​
  • ​​(7)void UARTFIFOEnable(uint32_t ui32Base)​​

一、实验简介

通过TM4C123G的板载UART串口和电脑通信,观察各种中断发生情况

二、TM4C的UART资源

简单介绍TM4C123G的UART资源,关于波特率发生原理、数据传输流程、ISR、调制解调器握手、9位UART模式、环回模式、DMA等内容省略

(1)Tiva控制器的UART特征

TM4C123G学习记录(6)--UART_串口

(2)UART结构图

TM4C123G学习记录(6)--UART_#include_02

(3)UART和引脚的复用映射表

这个表非常重要,编程时要根据这个参考

TM4C123G学习记录(6)--UART_串口_03

(4)FIFO操作

关于FIFO的作用可以看看这篇文章,讲的很明白:​​uart FIFO​​

  • 每个UART有两个16x8的缓冲区,一个用来发送,一个用来接收
  • FIFO状态通过​​UART标志寄存器UARTFR​​和​​UART接收状态寄存器(UARTRSR)​​显示。硬件监视空、满和溢出情况。
  • FIFO产生中断的触发条件由​​ UART中断FIFO深度选择(UARTIFLS)​​控制。两个FIFO可以单独配置为不同的电平情况下触发中断。可以选择如下配置:1/8、1/4、1/2、3/4、7/8。(例如:1/4代表连续FIFO装入16*1/4=4个字节产生一个接受中断
  • 复位后FIFO都是禁用的并作为1字节的保留寄存器,FIFO中断默认为两个都是1/2选项。通过​​UARTLCRH​​的​​FEN​​位启用FIFO。

注意:关于复位后FIFO是默认使能还是禁用,似乎TI手册之间有矛盾,总之不要用默认设置,自己手动设置一下吧

以下内容来自ti的getting start手册

TM4C123G学习记录(6)--UART_数据_04

(5)中断、

如下介绍,建议结合相关的寄存器看,包括​​UARTCTL寄存器​​​和​​UARTIFLS寄存器​

TM4C123G学习记录(6)--UART_TM4C_05

  • 接收超时中断:当FIFO不是空的,并且在32位期间没有接收到进一步的数据时触发
  • 接收中断:如果开启FIFO,当收数据>=FIFO深度时触发;否则每次接收到数据都触发
  • 发送中断:如果开启FIFO,当FIFO中数据<=FIFO深度时触发;否则在FIFO中没有数据时触发

三、编程实践

下面就按TI​​getting start​​ 手册的顺序进行UART实验吧

(1)UART配置和使用的基本流程

TM4C123G学习记录(6)--UART_数据_06


下面看一个官方例程,UART0实现echo

/*官方例程1*/
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
int main(void)
{
SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
GPIOPinConfigure(GPIO_PA0_U0RX);
GPIOPinConfigure(GPIO_PA1_U0TX);
GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
UARTConfigSetExpClk(UART0_BASE, SysCtlClockGet(), 115200,
(UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));

UARTCharPut(UART0_BASE, 'E');
UARTCharPut(UART0_BASE, 'n');
UARTCharPut(UART0_BASE, 't');
UARTCharPut(UART0_BASE, 'e');
UARTCharPut(UART0_BASE, 'r');
UARTCharPut(UART0_BASE, ' ');
UARTCharPut(UART0_BASE, 'T');
UARTCharPut(UART0_BASE, 'e');
UARTCharPut(UART0_BASE, 'x');
UARTCharPut(UART0_BASE, 't');
UARTCharPut(UART0_BASE, ':');
UARTCharPut(UART0_BASE, ' ');
while (1)
{
if (UARTCharsAvail(UART0_BASE))
UARTCharPut(UART0_BASE, UARTCharGet(UART0_BASE));
}
}

官方对于程序的讲解如下

TM4C123G学习记录(6)--UART_TM4C_07

(2)使用类似C语言中stdio.h的方法

包含TivaWare提供的​​uartstdio.h​​​和​​uartstdio.c​​​文件,即可使用​​类似printf()​​等C函数

  • UART时钟、波特率配置函数需要从​​UARTConfigSetExpClk​​​变为​​UARTStdioConfig​
  • ​UARTprintf();​​​的使用方法和C中​​printf()​​​完全类似,支持​​%d​​等标准输入输出格式符

TM4C123G学习记录(6)--UART_TM4C_08

  • 特别注意一下,同一时间,只有一个串口可以用UARTprintf​​,​​uartstdio.c​​中对printf重定向,默认是定向到UART0​​,我这里修改成​​UART1​​,如下

利用uartstdio改写上面的官方例程实现一样的功能,大家可以对比一下(我用的UART1,官方是UART0)

/*官方例程1改写,使用uartstdio*/
#include <stdint.h>
#include <stdbool.h>
#include "inc/tm4c123gh6pm.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/sysctl.h"
#include "driverlib/interrupt.h"
#include "driverlib/gpio.h"
#include "driverlib/uart.h"
#include "uartstdio.h"
#include "driverlib/systick.h"
#include "driverlib/pin_map.h"

int main(void)
{
SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
//使能外设
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);

//配置复用功能
GPIOPinConfigure(GPIO_PB0_U1RX);
GPIOPinConfigure(GPIO_PB1_U1TX);

//分配UART信号
GPIOPinTypeUART(GPIO_PORTB_BASE,GPIO_PIN_0|GPIO_PIN_1);

//配置UART参数(这样配置可以用UARTprintf)
UARTClockSourceSet(UART1_BASE, UART_CLOCK_PIOSC); //使用16MHz内部高精度振荡器(PIOSC)作为UART模块时钟
UARTStdioConfig(1,115200, 16000000); //UART编号、波特率、UART时钟频率(频率要和上一行设的一致)

UARTprintf("Enter Text: \n");

uint8_t getChar;
while (1)
{
getChar=UARTCharGet(UART1_BASE);
UARTCharPut(UART1_BASE,getChar);
}
}

(3)UART中断

进一步改进刚才的例程,使用接收中断代替主函数中的轮询检测,同时我们增加一个LED来指示数据接收和发送的情况

  1. 增加头文件​​inc/hw_ints.h​​​和​​driverlib/interrupt.h​​(具体路径根据你的工程来)
  2. 进行中断配置,开启接受中断和超时中断`
  3. 配置指示用的LED
  4. 编写中断服务函数:这里首先要读中断状态寄存器,分析发生中断的种类;然后用读取的来清除中断标志(TM4C库函数中,中断标志返回值和清除中断标志函数的参数都是bit-packed格式,可以直接用,挺巧妙的);接下来读取并发送收到的数据;最后让led闪烁一下
  5. 别忘了注册中断服务函数,可以用函数注册,也可以手动修改中断像量表

#include <stdint.h>
#include <stdbool.h>
#include "inc/tm4c123gh6pm.h" //Register Definitions
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/sysctl.h"
//#include "inc/hw_ints.h"
#include "driverlib/interrupt.h"
#include "driverlib/gpio.h"
#include "driverlib/uart.h"
#include "uartstdio.h"
#include "driverlib/systick.h"
#include "driverlib/pin_map.h"


#define delay_ms(n); SysCtlDelay(n*(SysCtlClockGet()/3000));


void UARTIntHandler(void)
{
uint32_t ui32Status;
ui32Status = UARTIntStatus(UART1_BASE, true); //get interrupt status
UARTIntClear(UART1_BASE, ui32Status); //clear the asserted interrupts
while(UARTCharsAvail(UART1_BASE)) //loop while there are chars
{
UARTCharPutNonBlocking(UART1_BASE, UARTCharGetNonBlocking(UART1_BASE)); //echo character
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, GPIO_PIN_2); //blink LED
delay_ms(1);
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0); //turn off LED
}
}

int main(void)
{
SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);

//使能外设
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);

//配置复用功能
GPIOPinConfigure(GPIO_PB0_U1RX);
GPIOPinConfigure(GPIO_PB1_U1TX);

//分配UART信号
GPIOPinTypeUART(GPIO_PORTB_BASE,GPIO_PIN_0|GPIO_PIN_1);

//LED配置
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); //enable GPIO port for LED
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_2); //enable pin for LED PF2

//串口参数设置
UARTClockSourceSet(UART1_BASE, UART_CLOCK_PIOSC); //使用16MHz内部高精度振荡器(PIOSC)作为UART模块时钟
UARTStdioConfig(1,115200, 16000000); //UART编号、波特率、UART时钟频率(频率要和上一行设的一致)

//中断使能
IntMasterEnable(); //enable processor interrupts
IntEnable(INT_UART1); //enable the UART interrupt
UARTIntEnable(UART1_BASE, UART_INT_RX | UART_INT_RT); //only enable RX and TX interrupts

//注册中断服务函数
UARTIntRegister(UART1_BASE,UARTIntHandler);

UARTprintf("Enter Text: \n");

while (1) //let interrupt handler do the UART echo function
{
;
}
}

(4)FIFO测试

修改上述代码,测试FIFO的功能。
配置接收中断,当接收FIFO半满(8byte)时触发。利用串口调试助手一个char一个char发送。
观察到发送8个char后进入中断,FIFO测试成功

#include <stdint.h>
#include <stdbool.h>
#include "inc/tm4c123gh6pm.h" //Register Definitions
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/sysctl.h"
//#include "inc/hw_ints.h"
#include "driverlib/interrupt.h"
#include "driverlib/gpio.h"
#include "driverlib/uart.h"
#include "uartstdio.h"
#include "driverlib/systick.h"
#include "driverlib/pin_map.h"


#define delay_ms(n); SysCtlDelay(n*(SysCtlClockGet()/3000));


void UARTIntHandler(void)
{
uint32_t ui32Status;
ui32Status = UARTIntStatus(UART1_BASE, true); //get interrupt status
UARTIntClear(UART1_BASE, ui32Status); //clear the asserted interrupts
while(UARTCharsAvail(UART1_BASE)) //loop while there are chars
{
UARTCharPutNonBlocking(UART1_BASE, UARTCharGetNonBlocking(UART1_BASE)); //echo character
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, GPIO_PIN_2); //blink LED
delay_ms(1);
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0); //turn off LED
}
}

int main(void)
{
SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);

//使能外设
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);

//配置复用功能
GPIOPinConfigure(GPIO_PB0_U1RX);
GPIOPinConfigure(GPIO_PB1_U1TX);

//分配UART信号
GPIOPinTypeUART(GPIO_PORTB_BASE,GPIO_PIN_0|GPIO_PIN_1);

//LED配置
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); //enable GPIO port for LED
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_2); //enable pin for LED PF2

//串口参数设置
UARTClockSourceSet(UART1_BASE, UART_CLOCK_PIOSC); //使用16MHz内部高精度振荡器(PIOSC)作为UART模块时钟
UARTStdioConfig(1,115200, 16000000); //UART编号、波特率、UART时钟频率(频率要和上一行设的一致)

//FIFO配置,
UARTFIFOLevelSet(UART1_BASE,UART_FIFO_TX4_8,UART_FIFO_RX4_8); //FIFO填入半满(8byte)时触发中断
UARTFIFOEnable(UART1_BASE);

//中断使能
IntMasterEnable(); //enable processor interrupts
IntEnable(INT_UART1); //enable the UART interrupt
UARTIntEnable(UART1_BASE, UART_INT_RX);

//注册中断服务函数
UARTIntRegister(UART1_BASE,UARTIntHandler);

UARTprintf("Enter Text: \n");

while (1) //let interrupt handler do the UART echo function
{
;
}
}

进一步进行其他FIFO中断测试,如下:

  • 打开FIFO的情况下,打开​​UART_INT_OE​​(FIFO溢出中断),此时FIFO深度无作用,FIFO接收17个(FIFO溢出)后进入中断,第17个byte丢失,会输出FIFO中存储的所有16个字符,特别注意FIFO溢出是一个错误引发的中断,在中断服务函数中,除了用UARTIntClear清除标志,还要写一句​HWREG(UART1_BASE+UART_O_ECR)=0;​清除错误标志,否则中断只能进一次。
  • 关闭FIFO的情况下(相当于FIFO深度为1),打开​​UART_INT_OE​​​(FIFO溢出中断),连续收到两个byte溢出,在中断中打印第一个byte;打开​​UART_INT_RX​​(FIFO溢出中断),每收到一个byte都会进中断打印

四、部分库函数小结

(1)void UARTprintf(const char *pcString, …)

  1. 包含​​uartstdio.c​​​和​​uartstdio.h​​后可用
  2. 类似C语言printf()

(2)void UARTCharPut(uint32_t ui32Base, unsigned char ucData)

  1. 串口输出一个char型
  2. 等待FIFO中有数据再发送

(3)int32_t UARTCharGet(uint32_t ui32Base)

  1. 串口接收一个uint32_t型数据
  2. 等待FIFO中有数据再接收

(4)bool UARTCharPutNonBlocking(uint32_t ui32Base, unsigned char ucData)

  1. 串口输出一个char型
  2. 若发送时FIFO已满时,直接返回false而不在那循环等待

(5)int32_t UARTCharGetNonBlocking(uint32_t ui32Base)

  1. 串口接收一个uint32_t型数据
  2. 如果遇到接收时FIFO为空,直接返回false而不在那循环等待

(6)void UARTFIFOLevelSet(uint32_t ui32Base, uint32_t ui32TxLevel,uint32_t ui32RxLevel)

  1. 配置输入输出FIFO的深度

(7)void UARTFIFOEnable(uint32_t ui32Base)

  1. 开启某UART的FIFO功能


举报

相关推荐

0 条评论