Post

嵌入式TM4C1294开发

时钟信号与GPIO工作原理

时钟树

PIOSC(Precision Internal Oscillator)

内部精密时钟,16MHz,未校正时精度有限,容易受温度与供电电压影响

MOSC(Main Oscillator)

主时钟,实验版上焊接了一个25MHz的石英晶体振荡器,可作为高精度时钟源使用

LFIOSC(Low Frequency Internal Osc)

内部低频率时钟,通常接入33kHz振荡源,用于节能模式

RTCOSC(Hibernation Module RTC Osc)

冬眠模块实时时钟,接入32.768kHz振荡器

SYSCLK

系统时钟,可以来源于上述4种时钟源之一

微控制器

内部有一个TLL模块,主要作用是对时钟倍频,然后把时钟输出到各个功能部件,PIOSC和MOSC可以通过PLL模块将PLL频率配置为320MHz或480MHz,从此PLL频率经过分频(1-1024)到想要的时钟频率。微控制器可以工作在RUN(默认情况), SLEEP, DEEP SLEEP模式下

系统时钟配置

1
uint32_t SysCtlClockFreqSet(uint32_t ui32Config, uint32_t ui32SysClock);

ui32Config : 配置字

ui32Config为以下四组值集的“或” 外部时钟频率: SYSCTL_XTAL_25MHZ 振荡器源:SYSCTL_OSC_MAIN_INT30_EXT32 系统时钟源:SYSCTL_USE_PLL/OSC PLL VCO频率:SYSCTL_CFG_VCO_480/320

ui32SysClock: 希望设置的系统时钟频率

例如使用16MHz的PIOSC作为时钟源,产生8MHz的SYSCLK

1
ui32SysClock = SysCtlClockFreqSet((SYSCTL_OSC_INT|SYSCTL_USE_OSC), 8000000);

通用输入输出端口

15个物理GPIO模块,每个模块对应一个端口

Port A~H, J~N, P, Q 共90个输入输出引脚

GPIO函数

1
void SysCtlPeripheralEnable(uint32_t ui32Peripheral);

ui32Peripheral: 外设名称

1
bool SysCtlPeripheralReady(uint32_t ui32Peripheral);

如果指定的外设被使能成功,返回true,否则返回false

1
void GPIOPinTypeGPIOOutput(uint32_t ui32Port, uint8_t ui8Pins)

配置GPIO端口引脚为输出引脚

ui32Port: GPIO端口基地址

ui8Pins: 端口引脚位组合表示

void GPIOPinTypeGPIOInput(uint32_t ui32Port, uint8_t ui8Pins)

配置GPIO端口引脚为输入引脚

1
void GPIOPadConfigSet(uint32_t ui32Port, uint8_t ui8Pins, uint32_t ui32Strength, uint32_t ui32PinType)

配置GPIO端口引脚

ui32Strength: 端口的输出驱动能力,对输入设置无效

ui32PinType: 引脚类型

1
void GPIOPinWrite(uint32_t ui32Port, uint8_t ui8Pins, uint8_t ui8Val)

ui8Val: 设置ui8Pins对应引脚的值

MCU的UART串口通讯编程

常见通讯协议的比较

通讯协议同步/异步半双工/全双工信号线的个数传输速度
\(\text{UART}\)异步全双工2
\(\text{I}^2\text{C}\)同步半双工2
\(\text{SPI}\)同步全双工3/4

UART概述

  • UART(Universal Asynchronous Receiver Transmitter)一般称为串口
  • 有两根数据线:发送(TXD)和接收(RXD),两根线可以实现同时收/发数据,这称为全双工
  • UART通信一般采用一对一的通信方式

TM4C1294NCPDT的UART模块

8个UART模块,每个模块具有以下特点:

  • 可编程的波特率发生器,在常规模式(16分频)下最高可达7.5Mbps,在高速模式(8分频)下最高可达15Mbps
  • 相互独立的16×8发送(TX)FIFO 和接收(RX)FIFO,可降低中断服务对CPU的占用
  • FIFO 触发深度有如下级别可选:1/8、1/4、1/2、3/4或7/8
  • 标准的异步通讯位:起始位、停止位、奇偶校验位
  • 完全可编程的串行接口特性

UART数据帧格式

起始位(1位)+数据位(5~8位)+奇偶校验位(1位)+停止位(1~2位)

UART数据发送方式

阻塞式

数据发送

将数据发送到TxFIFO,如果TxFIFO没有空位,一直等待到TxFIFO有空位

1
UARTCharPut(UART0_BASE, 'A') //发送A字符
数据接收

检查RxFIFO,直到接收到一个数据才返回

1
receive_data = UARTCharGet(UART0_BASE); //从UART0读取一个字符

非阻塞式

数据发送

将数据发送到TxFIFO,如果TxFIFO没有空位,返回FALSE,否则返回TRUE

1
trans_status = UARTCharPutNoonBlocking(UART0_BASE, 'A');
数据接收

如果RxFIFO有数据,则返回数据,如果无则返回-1

1
receive_data = UARTCharGetNonBlocking(UART0_BASE);

通常采用如下形式配合使用

1
2
if(UARTCharsAvail(UART0_BASE)) //判断RxFIFO中是否有数据
  receive_data = UARTCharGetNonBlocking(UART0_BASE);

FIFO操作

  • 在进行UART通信时,中断方式比轮询方式要简便且效率高

  • 两个16字节收发FIFO的引入主要是解决UART收发中断过于频繁而导致CPU效率不高的问题

  • 两个FIFO可以配置不同深度的触发中断,可选择的配置包括:1/8、1/4、1/2、3/4和7/8深度。复位时,两个FIFO的中断触发深度为1/2

TxFIFO的基本工作过程

  • 只要有数据填充到TxFIFO里,就会立即启动发送过程
  • 在发送的同时其它待发送的数据还可以继续填充到TxFIFO里
  • 当TxFIFO被填满时就不要在继续填充了,否则会造成数据丢失
  • TxFIFO会按照填入数据的先后顺序把数据一个个发送出去,直到TxFIFO全空为止
  • 已发送出去的数据会被自动清除,在TxFIFO里同时会多出一个空位
  • 当接收器(Receiver)接收到数据时,就会往RxFIFO里填充接收到的数据
  • 程序应当及时取走RxFIFO里的数据,数据被取走同时也会自动从RxFIFO里删除,RxFIFO就会多出一个空位
  • 如果RxFIFO里的数据未被及时取走而造成RxFIFO已满,则以后再接收到的数据因无空位可以填充而造成丢失

TxFIFO的中断处理过程

  • 发送数据时,触发TxFIFO中断的条件是当TxFIFO里剩余的数据减少到预设的深度时触发中断
  • 为了减少中断次数提高发送效率,TxFIFO中断触发深度级别越浅越好,如1/8深度
  • 在需要发送大量数据时,首先要填充TxFIFO以启动发送过程,一定要填充到超过预设的触发深度(最好填满);随着数据的发送,当TxFIFO里剩余的数据减少到预设的触发深度时会自动触发中断
  • 在中断服务函数里,继续填充发送数据,填满时退出。下次中断时继续填充,直到所有待发送数据都填充完毕为止

RxFIFIO的中断处理过程

  • 接收数据时,触发RxFIFO中断的条件是当RxFIFO里累计的数据增加到预设的深度时触发中断
  • 为了减少中断次数提高接收效率,RxFIFO中断触发深度级别越深越好,如7/8深度
  • 在需要接收大量数据时,每次中断产生时都要及时地从RxFIFO里取走已接收到的数据(最好全部取走),以免造成RxFIFO溢出
  • 在使能接收中断(UART_INT_RX)的同时,一般还要使能接收超时中断(UART_INT_RT);如果没有超时中断,则在RxFIFO未填充到预设深度而对方已经发送完毕的情况下并不会触发接收中断(UART_INT_RX),会造成最后接收的有效数据得不到处理的问题

UART中断控制

  • 出现以下情况时,可使UART产生中断:
    • FIFO溢出中断
    • 线路中止错误
    • 奇偶校验错误
    • 帧错误
    • 发送中断
    • 接收中断
    • 接收超时
  • 由于所有中断事件在发送到中断控制器之前会在一起做“或运算”操作,任何时刻UART模块都只能向中断控制器产生一个中断请求。通过查询中断状态函数UartIntStatus(),程序可以在同一个中断服务函数里处理多个事件的中断
  • UART中断要由编程人员根据情况清除中断标志位(使用UartIntClear(函数),如果中断标志位不清除,会导致反复的进出中断

中断及程序架构设计

SysTick定时器

  • 是ARM集成在内核中的一个24位倒计时定时器
  • 从设定值开始减1计数,直到0,然后再次重装,循环往复

SysTick中断编程示例

1
2
3
4
SysTickPeriodSet(ui32SysClock/50); // 心跳节拍设为20ms
SysTickEnable(); // 使能Systick
SysTickIntEnable(); // SysTick中断允许,每20ms进入一次中断
IntMasterEnable(); // 总中断允许

中断优先级

TM4C1294NCPDT的优先级配置寄存器

  • 使用高3位(Bit7~Bit5),最多可配8个优先级
  • 高3位进一步可以设置为“分组(抢占)优先级”和“子优先级”两部分
  • 低5位(Bit4~Bit0)未使用,它们读出总是为0,对这些位的写操作被忽略
  • 可能的优先级为0x00(高优先级)、0×20、0×40、0×60、0×80、0xA0、0xC0、0xE0(最低)

中断设置函数

1
void IntPriorityGroupingSet(uint32_t ui3Bits);

ui32Bits: 指定用于分组(抢占)优先级的位数,可取值0~7;默认值为7;取值3~7均表示设置Bit7、Bit6和Bit5全部为分组优先级位,即有8个分组(抢占)优先级。

例如:

配置中断分组(抢占)优先级占2位(Bit7和Bit6),Bit5为子优先级位,即有4个分组优先级

1
IntPriorityGroupingSet(2);

配置中断分组优先级占0位,Bit7、Bit6和Bit5全部为子优先级位,即只有1个分组优先级

1
IntPriorityGroupingSet(0);
1
void IntPrioritySet(uint32_t ui32Interrupt, uint8_t ui8Priority);

ui32Interrupt: 指定中断源

ui8Priority: 指定优先级

例如:

设置UART0中断优先级为0xE0

1
IntPrioritySet(INT_UART0, 0xE0);

设置SysTick中断优先级为0x20

1
IntPrioritySet(FAULT_SYSTICK, 0x20);
1
void IntPriorityMaskSet(uint32_t ui32PriorityMask);

对优先级数值等于或大于ui32PriorityMask的中断源予以屏蔽。默认值为0,表示不屏蔽任何中断

1
IntPriorityMaskSet(0x80);// 屏藏优先级数值大于或等于Ox80的中断
1
uint32_t IntPriorityGet();

获取当前的优先级屏蔽水平

This post is licensed under CC BY 4.0 by the author.