TM4C123G_study
圖庫掛了
時鐘
時鐘源
TM4C123內部共有4個時鐘源,見下表
| 時鐘 | 簡介 |
|---|---|
| 內部高精度振蕩器(PIOSC) | 內部振蕩器,其頻率為16MHz,精度為1%,可以用來驅動PLL |
| 主振蕩器 (MOSC) | 外部高速振蕩器,頻率可在4-25M間選擇,可以驅動PLL(此時頻率在5-25M) |
| 低頻內部振蕩器 (LFIOSC) | 適用于深度睡眠省電模式,它的頻率是會改變的,范圍在10KHz-90KHz之間,標準值30KHz |
| 休眠模塊時鐘源 | 32.768KHz晶振,用于實時時鐘源或睡眠時鐘 |

時鐘樹

- MOSC和PIOSC可以用來驅動PLL
- PLL輸出鎖定在400MHz,它可以在經過二分頻和SYSDIV分頻(這個可以程序配置)后提供系統時鐘。注意TM4C123G的最大主頻為80MHz,因此配置時鐘的時候,若使用的PLL,最小分頻數只能是2.5分頻。
時鐘配置
使用函數 void SysCtlClockSet(uint32_t ui32Config); 進行系統時鐘設置
這個函數參數是4個部分做按位與,包括 時鐘分頻SYSDIV設置,系統時鐘來源(直接用振蕩器,還是用PLL倍頻過的),時鐘源選擇(對應上面表2.4),外接晶體頻率
1. 時鐘分頻SYSDIV設置
#define SYSCTL_SYSDIV_1 0x07800000 // Processor clock is osc/pll /1
#define SYSCTL_SYSDIV_2 0x00C00000 // Processor clock is osc/pll /2
...
#define SYSCTL_SYSDIV_62 0x9EC00000 // Processor clock is osc/pll /62
#define SYSCTL_SYSDIV_63 0x9F400000 // Processor clock is osc/pll /63
#define SYSCTL_SYSDIV_64 0x9FC00000 // Processor clock is osc/pll /64
#define SYSCTL_SYSDIV_2_5 0xC1000000 // Processor clock is pll / 2.5
#define SYSCTL_SYSDIV_3_5 0xC1800000 // Processor clock is pll / 3.5
#define SYSCTL_SYSDIV_4_5 0xC2000000 // Processor clock is pll / 4.5
...
#define SYSCTL_SYSDIV_61_5 0xDE800000 // Processor clock is pll / 61.5
#define SYSCTL_SYSDIV_62_5 0xDF000000 // Processor clock is pll / 62.5
#define SYSCTL_SYSDIV_63_5 0xDF800000 // Processor clock is pll / 63.
2.系統時鐘來源(直接用振蕩器,還是用PLL倍頻過的)
#define SYSCTL_USE_PLL 0x00000000 // System clock is the PLL clock
#define SYSCTL_USE_OSC 0x00003800 // System clock is the osc clock
3.時鐘源選擇(對應上面表2.4)
#define SYSCTL_OSC_MAIN 0x00000000 // Osc source is main osc
#define SYSCTL_OSC_INT 0x00000010 // Osc source is int. osc
#define SYSCTL_OSC_INT4 0x00000020 // Osc source is int. osc /4
#define SYSCTL_OSC_INT30 0x00000030 // Osc source is int. 30 KHz
#define SYSCTL_OSC_EXT32 0x80000038 // Osc source is ext. 32 KHz
4.外接晶體頻率
#define SYSCTL_XTAL_1MHZ 0x00000000 // External crystal is 1MHz
#define SYSCTL_XTAL_1_84MHZ 0x00000040 // External crystal is 1.8432MHz
#define SYSCTL_XTAL_2MHZ 0x00000080 // External crystal is 2MHz
#define SYSCTL_XTAL_2_45MHZ 0x000000C0 // External crystal is 2.4576MHz
#define SYSCTL_XTAL_3_57MHZ 0x00000100 // External crystal is 3.579545MHz
...
#define SYSCTL_XTAL_24MHZ 0x00000640 // External crystal is 24.0 MHz
#define SYSCTL_XTAL_25MHZ 0x00000680 // External crystal is 25.0 MHz
配置示例
/* MOSC頻率16M,SYSDIV5分頻,系統時鐘源自PLL鎖相環倍頻,時鐘源使用MOSC */
/* 系統時鐘頻率400M/2/5=40M */
SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_XTAL_16MHZ|SYSCTL_USE_PLL|SYSCTL_OSC_MAIN);
/* MOSC頻率16M,SYSDIV2.5分頻,系統時鐘源自PLL鎖相環倍頻,時鐘源使用MOSC */
/* 系統時鐘頻率400M/2/2.5=80M , 注意這是上限了*/
SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
/* MOSC頻率16M,SYSDIV不分頻,系統時鐘來自時鐘源,時鐘源使用MOSC */
/* 系統時鐘頻率16M/1=16M */
SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
延時函數
TM4C庫提供了一個延時函數,它利用匯編,提供了跨越工具鏈時恒定的延遲。延時3*ui32Count個時鐘周期
__asm void SysCtlDelay(uint32_t ui32Count);
但若系統時鐘頻率不同,一個時鐘周期的長度也不同,一旦改了系統時鐘頻率,延時就會變化,需要改進
利用以下函數獲取系統時鐘頻率(單位Hz)
uint32_t SysCtlClockGet(void);
假設系統時鐘頻率為nHz,即(n/1000)KHz,設cnt=(n/1000),每秒有1000cnt個周期,每個cnt長1ms。
SysCtlDelay(Count)可以延時3Count個周期,令Count=cnt/3,即可延時1個cnt長(即1ms)
-
//延時n毫秒,不用考慮時鐘頻率 #define delay_ms(n); SysCtlDelay(n*(SysCtlClockGet()/3000));
不管用哪個時鐘源,只要工作頻率高于40MHz,就會導致實際延時時間大于設置值。原因好像是芯片內部Flash的讀取頻率最大只能達到40M,
當工作頻率大于40MHz時,通過預取兩個字的指令來達到80M的運行主頻。但是,當遇到SysCtlDelay函數這種短跳轉時這個特性并不能很好的工作,每次都需要讀取指令,所以時間就延長了
也就是說如果主頻大于40M,SysCtlDelay(n*(SysCtlClockGet()/3000))這個方法也不是很準,可以考慮用ROM_SysCtlDelasy()
GPIO
RGB_LED


控制LED是一個三極管開關電路,單片機PF1/PF2/PF3連接到LED_R/LED_B/LED_G,GPIO輸出高電平即可點亮二極管
相關庫函數
1.使能外設時鐘
(1)void SysCtlPeripheralEnable(uint32_t ui32Peripheral)
功能:使能外設時鐘
參數:uint32_t ui32Peripheral 要使能的外設
說明:從寫外設使能操作完成到實際上的外設使能間有5個時鐘周期的延遲,這期間內訪問外設將導致一個總線錯誤。應注 意確保在這段時間內不訪問該外設。
2.引腳配置為輸出模式
(2)void GPIOPinTypeGPIOOutput(uint32_t ui32Port, uint8_t ui8Pins)
功能:引腳配置為輸出模式
參數:
ui32Port GPIO口的基地址
ui8Pins bit-packed格式表示的引腳
說明:要使GPIO引腳做為GPIO輸出,必須正確地配置引腳。本函數提供這些引腳的典型配置。引腳使用bit-packed 字節格式表示,每一位表示一個要訪問的引腳,位0表示引腳0,位1表示引腳1,以此類推。
底層:
void GPIOPadConfigSet(uint32_t ui32Port, uint8_t ui8Pins,uint32_t ui32Strength, uint32_t ui32PinType);//設置輸出類型和強度
void GPIODirModeSet(ui32Port, ui8Pins, GPIO_DIR_MODE_OUT);//設置方向(輸入or輸出)
3.寫值到指定引腳
void GPIOPinWrite(uint32_t ui32Port, uint8_t ui8Pins, uint8_t ui8Val);
功能:寫值到指定引腳.
參數:
ui32Port GPIO口的基地制作.
ui8Pins bit-packed 格式表示的引腳
ui8Val 將要寫入引腳的值.
說明:寫相應位的數值到ui8Pins參數指定的引腳,寫數值時不影響配置為輸入的引腳狀態。引腳用 bit-packed 字節格式表示, 每一個位代表一個引腳,位0表示GPIO口的引腳0,位1表示GPIO口的引腳1,以此類推。
4.不受頻率影響的延時函數
SysCtlDelay(100*(SysCtlClockGet()/3000));
示例代碼
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_types.h" //通用類型和宏
#include "inc/hw_memmap.h" //外設和存儲器的基地址
#include "driverlib/sysctl.h" //API函數中外設、狀態等的標志
#include "driverlib/gpio.h"
int main(void)
{
uint8_t ui8LED = 2; //2 = 0010
//1 = 0001
//8 = 0100
//系統時鐘分頻器系數|選擇外部晶振頻率|使用PLL鎖相環作為系統時鐘源
SysCtlClockSet(SYSCTL_SYSDIV_6|SYSCTL_XTAL_16MHZ|SYSCTL_USE_PLL|SYSCTL_OSC_MAIN);
//使能PF時鐘
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
//配置引腳為GPIO輸出,底層是GPIOPadConfigSet和GPIODirModeSet
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3);
while(1)
{
// Turn on the LED :PF1(R),PF2(B),PF3(G)
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, ui8LED);
// Delay for a bit
SysCtlDelay(100*(SysCtlClockGet()/3000));
// Cycle through Red, Green and Blue LEDs
//寫入2->4->8(0010->0100->1000)(R->B->G)
if (ui8LED == 8)
ui8LED = 2;
else
ui8LED = ui8LED*2;
}
}
EXTI
相關庫函數
1.設置指定引腳的中斷觸發類型.
void GPIOIntTypeSet(uint32_t ui32Port, uint8_t ui8Pins,uint32_t ui32IntType)
功能:設置指定引腳的中斷觸發類型.
參數:
(1)ui32Port: GPIO口的基地址
(2)ui8Pins: 多個bit-packed格式表示的引腳
(3)ui32IntType: 中斷觸發類型(有以下類型)
#define GPIO_FALLING_EDGE 0x00000000 // Interrupt on falling edge
#define GPIO_RISING_EDGE 0x00000004 // Interrupt on rising edge
#define GPIO_BOTH_EDGES 0x00000001 // Interrupt on both edges
#define GPIO_LOW_LEVEL 0x00000002 // Interrupt on low level
#define GPIO_HIGH_LEVEL 0x00000006 // Interrupt on high level
//前5個都可和下面這個或運算一起作為ui32IntType參數,但不是所有引腳都支持離散中斷,需要查手冊
#define GPIO_DISCRETE_INT 0x00010000 // Interrupt for individual pins
說明: 為了避免毛刺引發的中斷,用戶必須確保GPIO口處于穩定狀態時執行本函數
2.注冊GPIO中斷的中斷處理程序
void GPIOIntRegister(uint32_t ui32Port, void (*pfnIntHandler)(void))
功能:注冊GPIO中斷的中斷處理程序
參數:
(1)ui32Port :GPIO口的基地址
(2)pfnIntHandler: 是GPIO中斷服務程序入口地址指針。
說明:
(1)不管是什么外設觸發的中斷,都要先注冊中斷服務函數,告訴程序中斷發生時去哪里,類似的函數有SysCtlIntRegister、ADCIntRegister等
(2)如果不利用這些中斷注冊函數,也可以在啟動文件中修改中斷向量表進行手動注冊
(3)GPIOIntRegister只能以GPIO組為單位注冊,不能精確到判斷哪個引腳發生中斷,因此要在中斷服務函數中判斷觸發中斷的引腳,以下為一個示例
//GPIOF中斷服務函數
void io_interrupt(void)
{
//獲取中斷狀態
uint32_t s = GPIOIntStatus(GPIO_PORTF_BASE, true);
//如果PF4觸發中斷
if((s&GPIO_PIN_4) == GPIO_PIN_4)
{...}
}
3.使能指定引腳的中斷.
void GPIOIntEnable(uint32_t ui32Port, uint32_t ui32IntFlags)
功能:使能指定引腳的中斷.
參數:
(1)ui32Port :GPIO口的基地址
(2)ui32IntFlags: 被禁止的中斷源中斷屏蔽位(指示哪些引腳中斷被開啟,是以下參數的邏輯或)
#define GPIO_INT_PIN_0 0x00000001
#define GPIO_INT_PIN_1 0x00000002
#define GPIO_INT_PIN_2 0x00000004
#define GPIO_INT_PIN_3 0x00000008
#define GPIO_INT_PIN_4 0x00000010
#define GPIO_INT_PIN_5 0x00000020
#define GPIO_INT_PIN_6 0x00000040
#define GPIO_INT_PIN_7 0x00000080
說明:這個函數是中斷源級的中斷使能控制
4.使能一個中斷
void IntEnable(uint32_t ui32Interrupt)
功能:使能一個中斷
參數:
(1)ui32Interrupt 指定的被允許的中斷.
說明:這個函數是中斷控制器級的中斷使能控制
5.使能處理器中斷
bool IntMasterEnable(void)
功能:使能處理器中斷.
參數:無
說明:
(1)這是處理器級的中斷使能控制,它決定處理器要不要處理中斷控制器的請求
(2)以上三個函數,從低級到高級對應了中斷處理通路的三道“開關”,如下圖所示

6.讀取指定GPIO口的中斷狀態
說明:這個函數是中斷源級的中斷使能控制
(4)void IntEnable(uint32_t ui32Interrupt)
功能:使能一個中斷
參數:
(1)ui32Interrupt 指定的被允許的中斷.
說明:這個函數是中斷控制器級的中斷使能控制
(5)bool IntMasterEnable(void)
功能:使能處理器中斷.
參數:無
說明:
(1)這是處理器級的中斷使能控制,它決定處理器要不要處理中斷控制器的請求
(2)以上三個函數,從低級到高級對應了中斷處理通路的三道“開關”,如下圖所示
(3)void GPIOIntEnable(uint32_t ui32Port, uint32_t ui32IntFlags)
功能:使能指定引腳的中斷.
參數:
(1)ui32Port :GPIO口的基地址
(2)ui32IntFlags: 被禁止的中斷源中斷屏蔽位(指示哪些引腳中斷被開啟,是以下參數的邏輯或)
#define GPIO_INT_PIN_0 0x00000001
#define GPIO_INT_PIN_1 0x00000002
#define GPIO_INT_PIN_2 0x00000004
#define GPIO_INT_PIN_3 0x00000008
#define GPIO_INT_PIN_4 0x00000010
#define GPIO_INT_PIN_5 0x00000020
#define GPIO_INT_PIN_6 0x00000040
#define GPIO_INT_PIN_7 0x00000080
1
2
3
4
5
6
7
8
說明:這個函數是中斷源級的中斷使能控制
(4)void IntEnable(uint32_t ui32Interrupt)
功能:使能一個中斷
參數:
(1)ui32Interrupt 指定的被允許的中斷.
說明:這個函數是中斷控制器級的中斷使能控制
(5)bool IntMasterEnable(void)
功能:使能處理器中斷.
參數:無
說明:
(1)這是處理器級的中斷使能控制,它決定處理器要不要處理中斷控制器的請求
(2)以上三個函數,從低級到高級對應了中斷處理通路的三道“開關”,如下圖所示
uint32_t GPIOIntStatus(uint32_t ui32Port, bool bMasked)
功能:讀取指定GPIO口的中斷狀態
參數:
(1)ui32Port: GPIO口的基地址.
(2)bMasked: 指定返回屏蔽的中斷狀態還是原始的中斷狀態
說明: 如果bMasked被設置為真,則函數返回被屏蔽的中斷狀態,否則返回原始的中斷狀態。解釋一下所謂“被屏蔽的中斷狀態”。在GPIOIntEnable這個函數中,沒有寫在第二個參數ui32IntFlags中的引腳是被屏蔽的(即不處理它們的中斷事件)。當bMasked為真時,返回GPIOMIS寄存器值,所有被屏蔽的位都是0,否則返回GPIORIS寄存器值,被屏蔽的位也可能是1(因為雖然不處理這些引腳的中斷事件,但它們的輸入也可能符合中斷特征)
返回值:返回指定GPIO口當前的中斷狀態,返回值是當前有效的GPIO_INT_?values的邏輯或. 
7.清除指定中斷源標志
void GPIOIntClear(uint32_t ui32Port, uint32_t ui32IntFlags)
功能:清除指定中斷源標志
參數:
(1)ui32Port :GPIO口的基地址
(2)ui32IntFlags :被清除的中斷源中斷屏蔽位
發生中斷后,對應的中斷標志位置1,進入中斷服務函數,在服務函數中務必清除中斷標志,否則程序將不停地進入中斷服務函數
示例代碼
#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 "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 ButtonsInit(void);
void io_interrupt(void);
int main()
{
//使能時鐘
SysCtlClockSet(SYSCTL_SYSDIV_4|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
//配置PF1/PF3為輸出,點亮綠燈(PF3)
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE,GPIO_PIN_3|GPIO_PIN_1);
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_3|GPIO_PIN_1,1<<3);
//按鍵使能
ButtonsInit();
while(1)
{
;
}
}
/******************************************************************************************************************
*函數名: ButtonsInit
*描 述:按鍵初始化函數
*輸 入:無
*線 路:PF0<->SW2
PF4<->SW1
******************************************************************************************************************/
void ButtonsInit(void)
{
//使能PF時鐘
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
//設置PF4為輸入,上拉(沒按就是高電平,按下就是低電平)
GPIODirModeSet(GPIO_PORTF_BASE, GPIO_PIN_4, GPIO_DIR_MODE_IN);
//方向為輸入
GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_4, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);//推挽上拉
//PF4配置為下降沿中斷
GPIOIntTypeSet(GPIO_PORTF_BASE, GPIO_PIN_4, GPIO_FALLING_EDGE);//下降沿
//給PF組注冊一個中斷函數
GPIOIntRegister(GPIO_PORTF_BASE, io_interrupt);
//開啟PF4的中斷
GPIOIntEnable(GPIO_PORTF_BASE, GPIO_INT_PIN_4);
IntEnable(INT_GPIOF);
IntMasterEnable();
}
/******************************************************************************************************************
*函數名: io_interrupt
*描 述:GPIO外部中斷處理函數
*輸 入:無
*線 路:PF4<->SW1
******************************************************************************************************************/
void io_interrupt(void)
{
static uint8_t trigger=0;
//獲取中斷狀態
uint32_t s = GPIOIntStatus(GPIO_PORTF_BASE, true);
//清除發生的中斷標志
GPIOIntClear(GPIO_PORTF_BASE, s);
if((s&GPIO_PIN_4) == GPIO_PIN_4)
{
while(!GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_4));//等待按鍵松開
if(!trigger)
trigger=1,GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_3|GPIO_PIN_1,1<<1);//點亮紅燈(PF1)
else
trigger=0,GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_3|GPIO_PIN_1,1<<3);//點亮綠燈(PF3)
}
}
UART
1.Tiva控制器的UART特征

2.UART結構圖

UART和引腳的復用映射表
這個表非常重要,編程時要根據這個參考
(TM4C123GXL板子:串口0的接收端不能用,串口1正常,串口2也不正常,涉及到芯片部分IO解鎖的東西)

3.FIFO操作
看來許多人還沒有真正理解FIFO的作用和優點,仍然停留在每收發一個字符就要中斷處理一次的老思路上。UART收發FIFO主要是為了解決收發中斷過于頻繁而導致的CPU效率不高的問題。
FIFO的必要性。在進行UART通信時,中斷方式比輪詢方式要簡便且效率高。但是,如果沒有收發FIFO,則每傳輸一個數據(5~8位)都要中斷處理一次,效率仍然不高。如果有了收發FIFO,則可以在連續收發若干個數據(可多至14個)后才產生一次中斷,然后一起處理。這就大大提高了收發效率。
接收超時問題。如果沒有接收超時功能,則在對方已經發送完畢而接收FIFO未填滿時并不會觸發中斷(FIFO滿才會觸發中斷),結果造成最后接收的有效數據得不到處理的問題。有了接收超時功能后,如果接收FIFO未填滿而對方發送已經停,則在不超過3個數據的接收時間內就會觸發超時中斷,因此數據會照常得到處理。
總之,FIFO的設計是優秀而合理的,它已經幫你想到了收發過程中存在的任何問題,只要初始化配置UART后,就可以放心收發了,FIFO和中斷例程會自動搞定一切!
完全不必要擔心FIFO大大減少了中斷產生的次數而“可能”造成數據丟失的問題!
發送時,只要發送FIFO不滿,數據只管往里連續放,放完后就直接退出發送子程序。隨后,FIFO真正發送完成后會自動產生中斷,通知主程序說:我已經完成真正的發送。
接收時,如果對方是連續不間斷發送,則填滿FIFO后會以中斷的方式通知主程序說:現在有一批數據來了,請處理。
如果對方是間斷性發送,也不要緊,當間隔時間過長時(2~3個字符傳輸時間),也會產生中斷,這次是超時中斷,通知主程序說:對方可能已經發送完畢,但FIFO未滿,也請處理。
每個UART有兩個16x8的緩沖區,一個用來發送,一個用來接收
FIFO狀態通過UART標志寄存器UARTFR和UART接收狀態寄存器(UARTRSR)顯示。硬件監視空、滿和溢出情況。
FIFO產生中斷的觸發條件由 UART中斷FIFO深度選擇(UARTIFLS)控制。兩個FIFO可以單獨配置為不同的電平情況下觸發中斷??梢赃x擇如下配置: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手冊

相關庫函數
\1. UARTConfigSet()
配置UART,例如:
// 配置UART2:波特率9600,數據位8,停止位1,無校驗
UARTConfigSet(UART2_BASE, 9600, UART_CONFIG_WLEN_8 |
UART_CONFIG_STOP_ONE |
UART_CONFIG_PAR_NONE);
\2. UARTFIFOLevelSet()
設置UART收發FIFO的深度,可以設置的深度有2、4、8、12、14
\3. UARTSpaceAvail()
確認在發送FIFO里是否有可利用的空間。
\4. UARTCharsAvail()
確認在接收FIFO里是否存在字符。
\5. UARTCharPutNonBlocking()
該函數要與UARTSpaceAvail()配合使用,如果已確認發送FIFO里有可用空間,則將字符直接放入發送FIFO,不等待。
\6. UARTCharGetNonBlocking()
該函數要與UARTCharsAvail()配合使用,如果已確認接收FIFO里有字符,則直接從接收FIFO里讀取字符,不等待。
\7. UARTCharPut()
將字符放到發送FIFO里,如果沒有可用空間則一直等待。
\8. UARTCharGet()
從接收FIFO里讀取字符,如果沒有字符則一直等待。
\9. UARTIntEnable()
使能一個或多個UART中斷,例如:
// 同時使能接收中斷(接收FIFO溢出)和接收超時中斷
UARTIntEnable(UART2_BASE, UART_INT_RX | UART_INT_RT);
4.中斷
如下介紹,建議結合相關的寄存器看,包括UARTCTL寄存器和UARTIFLS寄存器

- 接收超時中斷:當FIFO不是空的,并且在32位期間沒有接收到進一步的數據時觸發
- 接收中斷:如果開啟FIFO,當收數據>=FIFO深度時觸發;否則每次接收到數據都觸發
- 發送中斷:如果開啟FIFO,當FIFO中數據<=FIFO深度時觸發;否則在FIFO中沒有數據時觸發
5.示例代碼
/*官方例程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)
{
//時鐘分頻 400/2/4=50
SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
//使能GPIOA,UART0外設
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
//GPIO模式配置 PA0--RX PA1--TX
GPIOPinConfigure(GPIO_PA0_U0RX);
GPIOPinConfigure(GPIO_PA1_U0TX);
//GPIO的UART模式
GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
//UART協議配置 波特率115200 8位 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)) //判斷FIFO內是否有數據
UARTCharPut(UART0_BASE, UARTCharGet(UART0_BASE));
}
//UARTCharPut:等待從指定端口發送字符
//UARTCharGet:等待來自端口的字符(FIFO)
}
static void Uart1_Init(void){
//使能GPIOB,UART1外設
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);
//GPIO模式配置 PA0--RX PA1--TX
GPIOPinConfigure(GPIO_PB0_U1RX);
GPIOPinConfigure(GPIO_PB1_U1TX);
//GPIO的UART模式
GPIOPinTypeUART(GPIO_PORTB_BASE, GPIO_PIN_0 | GPIO_PIN_1);
//UART協議配置 波特率115200 8位 1停止位 無校驗位
UARTConfigSetExpClk(UART1_BASE, SysCtlClockGet(), 115200,(UART_CONFIG_WLEN_8|UART_CONFIG_STOP_ONE|UART_CONFIG_PAR_NONE));
//禁用FIFO,默認FIFO Level為4/8 寄存器滿8字節后產生中斷,禁用后接收1位就產生中斷
UARTFIFODisable(UART1_BASE);
//使能UART接收中斷
UARTIntEnable(UART1_BASE,UART_INT_RX);
//UART中斷地址注冊
UARTIntRegister(UART1_BASE,UART1_IRQHandler);
//優先級分組
IntPrioritySet(INT_UART1, USER_INT5);
}
void UART1_IRQHandler(void)//UART中斷函數
{
//獲取中斷標志 原始中斷標志 不屏蔽中斷標志
uint32_t flag = UARTIntStatus(UART1_BASE,1);
//清除中斷標志
UARTIntClear(UART1_BASE,flag);
//判斷FIFO是否還有數據
while(UARTCharsAvail(UART1_BASE))
{
char ch = UARTCharGet(UART1_BASE);
// UARTCharPut(UART1_BASE, 0x05);
}
}
PWM
TM4C123GH6PM控制器包含兩個pwm模塊,每個模塊由4個pwm發生器和一個控制模塊組成,每個發生器可以產生2個pwm信號,一共可以輸出16個pwm信號(同一發生器產生的兩個信號的周期是一致的,但占空比可以設為不同的)
PWM發生器特點

PWM結構圖

其中一個發生器的細節

PWM引腳映射情況
它記錄了在硬件層面上,哪些pwm信號輸出連接到哪些引腳,編程時需要對照查看

PWM模塊時鐘來源

查看原理圖,可見pwm模塊時鐘來源于經過USEPWMDIV分頻的系統時鐘,所有pwm信號的時鐘頻率都是這個。
PWM信號產生過程
(1)類似stm32,TM4C的pwm利用定時器實現(不過TM4C的pwm模塊中有自帶的定時器,不需要想stm32那樣使用timer外設),可以選擇三種計數模式
向上計數(pwm信號右對齊)
向下計數(pwm信號左對齊)
上下計數(pwm信號中間對齊)
(2)每個pwm信號發生器,可以配置兩個pwm比較器(類似stm32中的ccrx),比較器根據設定的比較值和當前計數值輸出高電平脈沖
(3)所有計數器、比較器的信息會被pwm信號發生器檢測,并生成對應的pwm波
PWM配置過程及示例代碼
配置一個PWM發生器,頻率25KHz,信號0(MnPWM0)占空比25%,信號1(MnPWM1)占空比75%,假定系統時鐘頻率為20M
使能PWM時鐘 SysCtlPeripheralEnable()
使能被復用引腳的時鐘 SysCtlPeripheralEnable()
使能引腳復用PWM功能 GPIOPinTypePWM()
將PWM信號分配到合適的引腳上 GPIOPinConfigure()
使能PWM時鐘,設置PWM分頻器為2分頻(PWM時鐘源為10M) SysCtlPWMClockSet();
配置為向下計數,參數立即更新模式 PWMGenConfigure()
設置周期時間(定時器計數范圍),目標頻率25K,PWM頻率10M,則每一個信號周期有400個PWM周期,故裝載值設為400-1(0到399共400個值) PWMGenPeriodSet()
設置信號0占空比25%,信號1占空比75% PWMPulseWidthSet()
啟動PWM發生器的定時器 PWMGenEnable()
使能PWM輸出 PWMOutputState()
#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 "driverlib/interrupt.h"
#include "driverlib/gpio.h"
#include "driverlib/uart.h"
#include "uartstdio.h"
#include "driverlib/systick.h"
#include "driverlib/pin_map.h"
#include "driverlib/pwm.h"
#define delay_ms(n); SysCtlDelay(n*(SysCtlClockGet()/3000));
/******************************************************************************************************************
*函數名: PWMInit()
*描 述:PWM初始化函數
*輸 入:無
*線 路:PF2<->M1PWM6
PF4<->M1PWM7
******************************************************************************************************************/
void PWMInit(void)
{
//配置PWM時鐘(設置USEPWMDIV分頻器)
SysCtlPWMClockSet(SYSCTL_PWMDIV_1); //PWM時鐘 16M
//使能時鐘
SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1); //使能PWM模塊1時鐘
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); //使能GPIOF時鐘
//使能引腳復用PWM功能
GPIOPinTypePWM(GPIO_PORTF_BASE,GPIO_PIN_2);
GPIOPinTypePWM(GPIO_PORTF_BASE,GPIO_PIN_3);
//PWM信號分配
GPIOPinConfigure(GPIO_PF2_M1PWM6); //PF2->PWM模塊1信號6
GPIOPinConfigure(GPIO_PF3_M1PWM7); //PF3->PWM模塊1信號7
//配置PWM發生器
//模塊1->發生器3->上下計數,不同步
PWMGenConfigure(PWM1_BASE,PWM_GEN_3,PWM_GEN_MODE_UP_DOWN|PWM_GEN_MODE_NO_SYNC);
//配置PWM周期
PWMGenPeriodSet(PWM1_BASE,PWM_GEN_3,64000); //64*10^3/16/10^6=4ms
//配置PWM占空比
PWMPulseWidthSet(PWM1_BASE,PWM_OUT_6,PWMGenPeriodGet(PWM1_BASE, PWM_GEN_3)*0.01); //比較值為四分之一總計數值,25%
PWMPulseWidthSet(PWM1_BASE,PWM_OUT_7,PWMGenPeriodGet(PWM1_BASE, PWM_GEN_3)*0.01); //比較值為四分之三總計數值,75%
//使能PWM模塊1輸出
PWMOutputState(PWM1_BASE,PWM_OUT_6_BIT,true);
PWMOutputState(PWM1_BASE,PWM_OUT_7_BIT,true);
//使能PWM發生器
PWMGenEnable(PWM1_BASE,PWM_GEN_3);
}
/******************************************************************************************************************
*函數名: SetDuty(uint32_t ui32Base,uint32_t ui32PWMOut,float duty)
*描 述:PWM初始化函數
*輸 入:PWM模塊編號、信號編號、占空比
******************************************************************************************************************/
void SetDuty(uint32_t ui32Base,uint32_t ui32PWMOut,float duty)
{
uint32_t ui32Gen;
uint32_t ui32OutBits;
switch(ui32PWMOut)
{
case PWM_OUT_0: ui32Gen=PWM_GEN_0,ui32OutBits=PWM_OUT_0_BIT; break;
case PWM_OUT_1: ui32Gen=PWM_GEN_0,ui32OutBits=PWM_OUT_1_BIT; break;
case PWM_OUT_2: ui32Gen=PWM_GEN_1,ui32OutBits=PWM_OUT_2_BIT; break;
case PWM_OUT_3: ui32Gen=PWM_GEN_1,ui32OutBits=PWM_OUT_3_BIT; break;
case PWM_OUT_4: ui32Gen=PWM_GEN_2,ui32OutBits=PWM_OUT_4_BIT; break;
case PWM_OUT_5: ui32Gen=PWM_GEN_2,ui32OutBits=PWM_OUT_5_BIT; break;
case PWM_OUT_6: ui32Gen=PWM_GEN_3,ui32OutBits=PWM_OUT_6_BIT; break;
case PWM_OUT_7: ui32Gen=PWM_GEN_3,ui32OutBits=PWM_OUT_7_BIT; break;
}
//配置占空比
PWMPulseWidthSet(ui32Base, ui32PWMOut, PWMGenPeriodGet(ui32Base, ui32Gen)*duty);
PWMOutputState(ui32Base, ui32OutBits, true);
//使能發生器模塊
PWMGenEnable(ui32Base, ui32Gen);
}
int main()
{
SysCtlClockSet(SYSCTL_SYSDIV_1|SYSCTL_USE_OSC|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN); //系統時鐘16M
PWMInit();
float duty1=0.1,duty2=0.9;
float d=0.01;
while(1)
{
SetDuty(PWM1_BASE,PWM_OUT_6,duty1);
SetDuty(PWM1_BASE,PWM_OUT_7,duty2);
delay_ms(10);
duty1+=d;
duty2-=d;
if(duty1>=0.95 && duty2<=0.05)
d=-0.01;
else if(duty2>=0.95 && duty1<=0.05)
d=0.01;
}
}
PWM_Init
void PWM_Init(void)
{
SysCtlPWMClockSet(SYSCTL_PWMDIV_8); // Set divider to 80M/8=10M 1/10M=0.1us 設置PWM時鐘配置
SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1); //啟用外設(在上電時,所有外設被設置為禁用),此處啟用外設PWM1
SysCtlDelay(2); //啟用外設后插入幾個周期,使得時鐘完全激活
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); // 此處使能外設GPIOF
SysCtlDelay(2);//啟用外設后插入幾個周期,使得時鐘完全激活
// 使用備用功能
GPIOPinConfigure(GPIO_PF2_M1PWM6);//此處將M1PWM6復用到PF2引腳
GPIOPinConfigure(GPIO_PF3_M1PWM7);//此處將M1PWM7復用到PF3引腳
// 使用引腳與PWM外設
GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_2);//M1PWM6 PF2
GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_3);//M1PWM7 PF3
// 配置PWM發生器倒計時模式與即時更新的參數
PWMGenConfigure(PWM1_BASE, PWM_GEN_2, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC);
PWMGenConfigure(PWM1_BASE, PWM_GEN_3, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC);
// 配置PWM發生器的周期
PWMGenPeriodSet(PWM1_BASE, PWM_GEN_2, 1000); // Set the period
PWMGenPeriodSet(PWM1_BASE, PWM_GEN_3, 1000);
//使能PWM發生器的定時/計數器
PWMGenEnable(PWM1_BASE, PWM_GEN_2);
PWMGenEnable(PWM1_BASE, PWM_GEN_3);
// 使能PWM輸出
PWMOutputState(PWM1_BASE, PWM_OUT_6_BIT | PWM_OUT_7_BIT , true);
}
SysCtlPWMClockSet(SYSCTL_PWMDIV_8);
// 設置PWM時鐘配置
SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1);
//啟用外設(在上電時,所有外設被設置為禁用),此處啟用外設PWM1
SysCtlDelay(2);
//啟用外設后插入幾個周期,使得時鐘完全激活 (該延時沒有精確的時間劃分)
GPIOPinConfigure(GPIO_PF2_M1PWM6);
//函數配置引腳復用器 此處將M1PWM復用到PF2引腳
GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_2);
//此功能不能將任何引腳轉換為PWM引腳,它只配置PWM引腳以正常工作。還需要GPIOPINConfigure()函數調用來正確配置PWM函數的引腳
PWMGenConfigure(PWM1_BASE, PWM_GEN_2, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC);
//參數:PWM模塊基址,PWM發生器(PWM_GEN_0,PWM_GEN_1,PWM_GEN_2,PWM_GEN_3),PWM發生器配置
//將PWM發生器配置為倒計時模式,并立即更新參數
PWMGenPeriodSet(PWM1_BASE, PWM_GEN_2, 1000); //配置PWM發生器的周期 參數:PWM模塊基址,要修改的PWM發生器,指定PWM發生器輸出的周期
PWMGenEnable(PWM1_BASE, PWM_GEN_2);//使能PWM發生器的定時/計數器
PWMOutputState(PWM1_BASE, PWM_OUT_6_BIT | PWM_OUT_7_BIT , true);//使能或者禁用PWM輸出
PWM_Set
void PWM_Set(uint16_t motor1,uint16_t motor2)
{
PWMPulseWidthSet(PWM1_BASE,PWM_OUT_6,motor1); //PF2
PWMPulseWidthSet(PWM1_BASE,PWM_OUT_7,motor2); //PF3
}
PWMPulseWidthSet(PWM1_BASE,PWM_OUT_6,motor1);
//設置指定PPWM輸出的脈沖寬度
輸入捕獲
輸入捕獲引腳映射關系


邊沿計數模式
在該模式中,TimerA或TimerB被配置為能夠捕獲外部輸入脈沖邊沿事件的遞增/減計數器。共有3種邊沿事件類型:正邊沿、負邊沿、雙邊沿
工作過程是:
(1)減計數:設置裝載值Preload,并預設一個匹配值Match(應當小于裝載值);計數使能后,在特定的CCP管腳每輸入1個脈沖(正邊沿、負邊沿或雙邊沿有效),計數值就減1;當計數值與匹配值Match相等時停止運行(若使能中斷,這時會被觸發)。定時器自動重裝載Preload值,但如果需要再次捕獲外部脈沖,則要重新進行配置。
(2)加計數:設置匹配值Match;計數使能后,在特定的CCP管腳每輸入1個脈沖(正邊沿、負邊沿或雙邊沿有效),計數值就加1;當計數值與匹配值Match相等時停止運行(若使能中斷,這時會被觸發)。定時器自動清0并自動開始捕獲外部脈沖計數,不需要重新進行配置

注意:
配置為邊沿計數模式時,定時器必須配置為拆分模式,64-bit未拆分模式下不可以用Capture
此模式下,8bit的定時器預分頻寄存器(Prescaler)不再作為分頻器使用,定時器頻率和系統時鐘頻率相同, 預分頻寄存器的8bit空間作為計數范圍的擴展,增加到定時器計數器的高位。也就是說32/64位定時器的輸入捕獲計數范圍為24/48bit
配置過程:
GPIO設置:
1.GPIOPinConfigure 進行引腳到TnCCPm的信號映射
2.GPIOPinTypeTimer 配置引腳到定時器模式
3.GPIOPadConfigSet 配置其他引腳參數
配置定時器模塊為捕捉-邊沿計數模式:
- TimerConfigure 配置定時器模式。注意第二個參數一定是TIMER_CFG_SPLIT_PAIR和一下之一相或
(1)TIMER_CFG_A_CAP_COUNT 模塊A捕捉-邊沿減計數模式
(2)TIMER_CFG_A_CAP_COUNT_UP 模塊A捕捉-邊沿加計數模式
(3)TIMER_CFG_B_CAP_COUNT 模塊B捕捉-邊沿減計數模式
(4)TIMER_CFG_B_CAP_COUNT_UP 模塊B捕捉-邊沿加計數模式
設置要捕捉的邊沿:
- TimerControlEvent
設置計數范圍:
- TimerMatchSet設置加/減計數結束值
- TimerLoadSet設置減計數起始值
中斷設置:
- TimerIntRegister 注冊中斷服務函數
- TimerIntEnable 源級中斷使能,這里注意配置中斷類型
(1)TIMER_CAPA_MATCH – 模塊A計數到達預設值
(2)TIMER_CAPB_MATCH – 模塊B計數到達預設值 - IntEnable 中斷控制器級中斷使能
- IntMasterEnable處理器級中斷使能
啟動定時器模塊:
- TimerEnable
邊沿計時模式
簡介:在該模式中,TimerA/B被配置為自由運行的16位遞減計數器,允許在輸入信號的上升沿或下降沿捕獲事件。
工作過程是:設置裝載值(默認為0xFFFF)、捕獲邊沿類型;計數器被使能后開始自由運行,從裝載值開始遞減計數(或從0開始遞增計數),計數到0(或裝載值)時重裝(或清零),繼續計數;如果從CCP管腳上出現有效的輸入脈沖邊沿事件,則當前計數值被自動復制到一個特定的寄存器里,該值會一直保存不變,直至遇到下一個有效輸入邊沿時被刷新。為了能夠及時讀取捕獲到的計數值,應當使能邊沿事件捕獲中斷,并在中斷服務函數里讀取。

注意:
配置為邊沿計時模式時,定時器必須配置為拆分模式,64-bit未拆分模式下不可以用Capture
此模式下,8bit的定時器預分頻寄存器(Prescaler)不再作為分頻器使用,定時器頻率和系統時鐘頻率相同, 預分頻寄存器的8bit空間作為計數范圍的擴展,增加到定時器計時器的高位。也就是說32/64位定時器的輸入捕獲計時范圍為24/48bit
配置過程:和計數模式基本一樣,僅有以下區別
需要配置定時器模塊為捕捉-邊沿計時模式
TimerConfigure第二個參數是TIMER_CFG_SPLIT_PAIR和一下之一相或
(1)TIMER_CFG_A_CAP_TIME 模塊A捕捉-邊沿減計時模式
(2)TIMER_CFG_A_CAP_TIME UP 模塊A捕捉-邊沿加計時模式
(3)TIMER_CFG_B_CAP TIME 模塊B捕捉-邊沿減計時模式
(4)TIMER_CFG_B_CAP_ TIME _UP 模塊B捕捉-邊沿加計時模式
設置計時范圍
- TimerLoadSet無論加計時還是減計時,都用這個函數設置Preload值
中斷設置
- TimerIntEnable的中斷類型參數變為以下二者之一
(1) TIMER_CAPA_EVENT 模塊A發生捕獲事件
(2) TIMER_CAPB_EVENT 模塊B發生捕獲事件
示例代碼
//timer0A輸入捕獲,脈沖從PB6輸入(PF0作T0CCP0)
int timer0A_cnt=0;
void Timer0Init()
{
// 啟用Timer0模塊
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
// 啟用GPIO_F作為脈沖捕捉腳
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
// 配置GPIO腳為使用Timer4捕捉模式
GPIOPinConfigure(GPIO_PB6_T0CCP0);
GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_6);
// 為管腳配置弱上拉模式(捕獲下降沿,配置為上拉)
GPIOPadConfigSet(GPIO_PORTB_BASE, GPIO_PIN_6, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);
// 配置使用Timer4的TimerA模塊為邊沿觸發加計數模式
TimerConfigure(TIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_COUNT_UP);
// 使用下降沿觸發
TimerControlEvent(TIMER0_BASE, TIMER_A, TIMER_EVENT_NEG_EDGE);
// 設置計數范圍為0~9
TimerMatchSet(TIMER0_BASE, TIMER_A, 10-1); //理論匹配周期10^-4*10=0.001s
// 注冊中斷處理函數以響應觸發事件
TimerIntRegister(TIMER0_BASE, TIMER_A, Timer0AIntHandler);
// 系統總中斷開
IntMasterEnable();
// 時鐘中斷允許,中斷事件為Capture模式中邊沿觸發,計數到達預設值
TimerIntEnable(TIMER0_BASE, TIMER_CAPA_MATCH);
// NVIC中允許定時器A模塊中斷
IntEnable(INT_TIMER0A);
// 啟動捕捉模塊
TimerEnable(TIMER0_BASE, TIMER_A);
}
//中斷服務函數 理論周期:0.001s
void Timer0AIntHandler(void)
{
TimerIntClear(TIMER0_BASE, TIMER_CAPA_MATCH);
timer0A_cnt++;
}
Timer
單次運行模式和連續運行模式
定時器的基本功能為計數(包括加計數和減計數兩種),Stellaris LM4F中以系統時鐘為計數節拍(當計數器被拆分使用時可以使用預分頻功能,為了簡單起見這里不作討論)。當加計數時,計數器由零開始,逐步加一,直到到達用戶預設值;減計數則由某一用戶預設值開始,逐步減一,直到計數為零。每當計數完成,則會置相應的狀態位(包括中斷),提示計時完成。
單次運行與連續運行工作時沒有區別,不同的是單次運行在完成一次計時后會自動停止,連續模式下定時器會自動從計數起點開始(根據計數方向為零或用戶設定值)繼續計時。
定時器程序設置
1.啟用時鐘模塊
使用SysCtlPeripheralEnable函數啟用相應的定時器模塊。
程序示例:
SysCtlPeripheralEnable(SYSCTL_PERIPH_WTIMER0);
在StellarisWare中,32/16-bit定時器模塊為TIMER,64/32-bit定時器模塊為WTIMER (Wide Timer)。除了名字不同、計數范圍不同外沒有其它區別。
2.設置時鐘模塊工作模式
使用TimerConfigure函數對定時器模塊的工作模式進行設置,將其設置為定時器功能。
程序示例:
TimerConfigure(TIMER0_BASE,TIMER_CFG_ONE_SHOT);
TimerConfigure(WTIMER2_BASE,TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_ONE_SHOT |TIMER_CFG_B_PERIODIC);
在不拆分的情況下,可以用下面參數中的一個將模塊設置成所需的定時器模式:
TIMER_CFG_ONE_SHOT – 單次減計數模式
TIMER_CFG_ONE_SHOT_UP – 單次加計數模式
TIMER_CFG_PERIODIC – 連續減計數模式
TIMER_CFG_PERIODIC_UP – 連續加計數模式
TIMER_CFG_RTC – 實時時鐘模式
如果需要將計時器拆分,則需使用參數TIMER_CFG_SPLIT_PAIR然后用“|”號連接被拆分定時器A、B的設置。如果只使用了一個可以只設置用到的那個。拆分出來的定時器A和B的設置方法是一樣的,只是函數名中各自用A和B:
TIMER_CFG_A_ONE_SHOT – 定時器A單次減計數
TIMER_CFG_A_ONE_SHOT_UP –定時器A單次加計數
TIMER_CFG_A_PERIODIC – 定時器A連續減計數
TIMER_CFG_A_PERIODIC_UP – 定時器A連續加計數
? TIMER_CFG_B_ONE_SHOT – 定時器B單次減計數
? TIMER_CFG_B_ONE_SHOT_UP –定時器B單次加計數
? TIMER_CFG_B_PERIODIC – 定時器B連續減計數
? TIMER_CFG_B_PERIODIC_UP – 定時器B連續加計數
3.設置時鐘的計數范圍
使用TimerLoadSet、TimerLoadSet64函數可以為計數設置范圍。設置未拆分使用的64/32-bit定時器模塊,需要使用TimerLoadSet64函數,對其它模塊、其它狀況的設置使用TimerLoadSet函數。計數范圍為設置值到零(加計數: 0~預設值,減計數: 預設值~0)。
程序示例:
TimerLoadSet64(TIMER3_BASE, 80000);
TimerLoadSet(WTIMER0_BASE, TIMER_B, 10000);
4.啟動時鐘
使用TimerEnable函數啟動定時器??梢杂玫膮涤蠺IMER_A、TIMER_B和TIMER_BOTH??梢苑謩e或同時啟動A、B定時器。如果定時器沒有拆分,直接使用TIMER_A即可。
程序示例:
TimerEnable(WTIMER0_BASE, TIMER_B);
定時器讀取及中斷設置
1.計數值讀取
可以使用TimerValueGet函數和TimerValueGet64函數獲得定時器當前的計數值。需要注意的是TimerValueGet64返回的是64位結果。
程序示例:
long val = TimerValueGet(TIMER1_BASE, TIMER_A);
long long timer_val = TimerValueGet64(WTIMER3_BASE);
2.中斷設置
一般定時器多用中斷響應以滿足時間要求??梢杂肨imerIntRegister向系統注冊中斷處理函數,用TimerIntEnable來允許某個定時器的中斷請求。需要注意的是,在M4中還應該用IntEnable在系統層使能定時器的中斷。當然,系統總中斷開關也必須用IntMasterEnable使能。
TimerIntEnable在該模式下可以支持:
TIMER_TIMA_TIMEOUT
TIMER_TIMB_TIMEOUT
程序示例:
TimerIntRegister(WTIMER0_BASE, TIMER_B, WTimer0BIntHandler);
IntMasterEnable();
TimerIntEnable(WTIMER0_BASE, TIMER_TIMB_TIMEOUT);
IntEnable(INT_WTIMER0B);
在Timer中斷中,需要手工清除中斷標志位,可以使用如下代碼:
unsignedlong ulstatus = TimerIntStatus(TIMER4_BASE, TIMER_TIMA_TIMEOUT |TIMER_TIMB_TIMEOUT);
TimerIntClear(TIMER4_BASE,ulstatus);
示例代碼
// Stellaris硬件定義及StellarisWare驅動定義頭文件
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_timer.h"
#include "inc/hw_ints.h"
#include "driverlib/timer.h"
#include "driverlib/interrupt.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "utils/uartstdio.h"
// 用于記錄進入定時器中斷的次數
unsigned long g_ulCounter = 0;
// 初始化UART的函數
extern void InitConsole(void);
// 定時器的中斷處理函數
Void WTimer0BIntHandler(void)
{
// 清除當前中斷標志
TimerIntClear(WTIMER0_BASE, TIMER_TIMB_TIMEOUT);
// 更新進入中斷次數的計數
g_ulCounter++;
}
// 主程序
int main(void)
{
// 用來記錄上次計數以判斷是否計數改變
unsigned long ulPrevCount = 0;
// 設置LM4F時鐘為50MHz
SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
// 使能64/32-bit的時鐘模塊WTIMER0
SysCtlPeripheralEnable(SYSCTL_PERIPH_WTIMER0);
// 初始化UART
InitConsole();
// 打印程序信息
UARTprintf("32-Bit Timer Interrupt ->");
UARTprintf("
Timer = Wide Timer0B");
UARTprintf("
Mode = Periodic");
UARTprintf("
Rate = 1s
");
// 設置WTimer0-B模塊為連續減計數
TimerConfigure(WTIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_B_PERIODIC);
// 設置定時器的計數值,這里用系統頻率值,即每秒一個中斷
TimerLoadSet(WTIMER0_BASE, TIMER_B, SysCtlClockGet());
// 設置WTimer0-B的中斷處理函數
TimerIntRegister(WTIMER0_BASE, TIMER_B, WTimer0BIntHandler);
// 啟用系統總中斷開關
IntMasterEnable();
// 啟用WTimer0-B超時中斷
TimerIntEnable(WTIMER0_BASE, TIMER_TIMB_TIMEOUT);
// 在系統層面(NVIC)使能WTimer0-B中斷
IntEnable(INT_WTIMER0B);
// 啟動定時器
TimerEnable(WTIMER0_BASE, TIMER_B);
while(1)
{
// 循環等待WTimer0-B中斷更新g_ulCounter計數
// 若計數改變則進行UART輸出
if(ulPrevCount != g_ulCounter)
{
// UART輸出計數值
UARTprintf("Number of interrupts: %d
", g_ulCounter);
ulPrevCount = g_ulCounter;
}
}
}
static void Timer_Init(void){
//定時器0使能
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
//32位周期定時器
TimerConfigure(TIMER0_BASE,TIMER_CFG_PERIODIC);
//設定裝載值,(80M/1000)*1/80M=1ms
TimerLoadSet(TIMER0_BASE,TIMER_A,SysCtlClockGet()/1000);
//總中斷使能
IntEnable(INT_TIMER0A);
//中斷輸出,設置模式
TimerIntEnable(TIMER0_BASE,TIMER_TIMA_TIMEOUT);
//中斷函數注冊
TimerIntRegister(TIMER0_BASE,TIMER_A,TIMER0A_Handler);
//定時器使能開始計數
TimerEnable(TIMER0_BASE,TIMER_A);
//中斷優先級劃分
IntPrioritySet(INT_TIMER0A,USER_INT7);
}
void TIMER0A_Handler(void){
Scheduler_Run();
//清除當前中斷標志
TimerIntClear(TIMER0_BASE,TIMER_TIMA_TIMEOUT);
}
I2C
一、I2C接口的介紹:
內部集成電路(I2C)總線通過一個兩線設計(串行數據線 SDA 和串行時鐘線 SCL)來提供雙向數據傳輸,并且與外部 I2C 器件諸如串行存儲器(RAM 和 ROM),網絡設備,LCD,音頻發生器等聯系。I2C 總線也可用于產品開發和制造的系統測試和診斷的目的。TM4C123GH6PM 微控制器提供與其他 I2C 總線上的設備交互(發送和接收)的能力。TM4C123GH6PM 控制器的 I2C 模塊具有以下特點:
? I2C 總線上的設備可被配置為主機或從機
— 支持一個主機或從機發送和接收數據
— 同時支持主機和從機操作
? 四個 I2C 模式:
— 主機發送模式
— 主機接收模式
— 從機發送模式
— 從機接收模式
? 四個發送速率:
— 標準模式(100 Kbps)
— 快速模式(400 Kbps)
— 超快速模式(1Mbps)
— 高速模式(3.33Mbps)
? 時鐘低超時中斷的
? 雙從地址能力
? 抗干擾
? 主機和從機中斷的產生
— 當主機發送或接收操作完成時(或因錯誤終止時),產生中斷
— 當從機發送數據或主機需要數據或檢測到起始或停止條件時,產生中斷
? 主機由仲裁和時鐘同步,支持多主機,以及 7 位尋址模式

二、結構圖:

三、 初始化與配置
以下例子給出如何配置 I2C 模塊用于主機傳輸一個字節。這里假定系統時鐘為 20 MHz 。
- 在系統控制模塊使用 RCGCI2C 寄存器使能 I2C 時鐘。
- 通過在系統控制模塊的 RCGCGPIO 寄存器為相應的 GPIO 模塊使能時鐘。要了解使能哪些GPIO 端口。
- 在 GPIO 模塊,通過 GPIOAFSEL 寄存器位它們的復用功能使能相應的引腳。請參看表 23-4,以確定配置那個 GPOI。
- 使能 I2CSDA 引腳來配置開漏操作。
- 在 GPIOPCTL 寄存器配置 PMCn 位組為相應的引腳配置 I2C 信號。
- 向 I2CMCR 寄存器寫入 0x00000010 值來初始化 I2C 主機。
- 通過寫入 I2CMTPR 寄存器正確的值來設置所需的 100 Kbps 的 SCL 時鐘速度。寫入I2CMTPR 寄存器的值代表在一個 SCL 時鐘周期中系統時鐘周期數。TPR 值由以下等式確定:
TPR = (System Clock/(2(SCL_LP + SCL_HP)SCL_CLK))-1;
TPR = (20MHz/(2(6+4)100000))-1;
TPR = 9向 I2CMTPR 寄存器寫入 0x00000009. - 規定主機的從機地址,下一個操作是一個發送,該發送通過向 I2CMSA 寄存器值寫入0x00000076 實現。這設置了從機地址為 0x3B。
- 通過向 I2CMDR 寄存器寫入所需數據將數據(位)傳輸到數據寄存器。
- 啟動從主機到從機的數據單字節的傳輸是通過向 I2CMCS 寄存器寫入 0x00000007 (STOP,START, RUN)實現。
- 等待直到傳輸完成,通過輪詢 I2CMCS 寄存器的 BUSBSTY 位,直到該位被清除。
- 檢測 I2CMCS 寄存器的 ERROR 位以確保傳輸被應答。
四、環路操作實驗
? 可以通過設置 I2C 主機配置(I2CMCR)寄存器的 LPBK 位將 I2C 模塊放入一個內部環路模式用于診斷或調試工作。在環回模式下,來自主機的 SDA 和 SCL 信號和從機模塊的 SDA 和SCL 信號綁定在一起,并允許內部的設備測試,而不必去通過 I / O實驗的驗證就是通過主機發送數據-從機接收,從機發數據-主機接收
/********************************************************
*實驗功能:通過環路模式實現單字節的主機/從機數據的發送和接收
*時間:2019-6-30
*作者:MountXing
*******************************************************/
//包含一系列頭文件
#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_i2c.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/gpio.h"
#include "driverlib/i2c.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
#include "utils/uartstdio.h"
//要發送的I2C數據包的數量
#define NUM_I2C_DATA 5
//地址
#define SLAVE_ADDRESS 0x3C
/*****************************************************************************
*初始化配置使用 UART0 外設
*-UART0RX - PA0
*-UART0TX - PA1
*****************************************************************************/
void Init_Console_Uart0(void)
{
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
GPIOPinConfigure(GPIO_PA0_U0RX);
GPIOPinConfigure(GPIO_PA1_U0TX);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC); //內部16M時鐘
GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
UARTStdioConfig(0, 115200, 16000000);
}
/*****************************************************************************
*配置I2C0主從機,并使用環回模式連接它們。
*初始化I2C0外設
*I2C0SCL - PB2
*I2C0SDA - PB3
*****************************************************************************/
int main(void)
{
uint32_t pui32DataTx[NUM_I2C_DATA]; //發送數據緩沖區
uint32_t pui32DataRx[NUM_I2C_DATA]; //接受數據緩沖區
uint32_t ui32Index; //要發送數據到緩沖區的位置
//配置外部時鐘為20M
SysCtlClockSet(SYSCTL_SYSDIV_1|SYSCTL_USE_OSC|SYSCTL_OSC_MAIN|SYSCTL_XTAL_16MHZ);
SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0); //使能I2C0外設
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB); //使能PB端口
//配置引腳的復用功能 PB2 PB3
GPIOPinConfigure(GPIO_PB2_I2C0SCL);
GPIOPinConfigure(GPIO_PB3_I2C0SDA);
//配置I2C引腳
GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_2);
GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3);
//I2CLoopbackEnable(I2C0_BASE); //調用固件庫的環回模式。(I2C.c固件庫里沒有這個函數)
HWREG(I2C0_BASE + I2C_O_MCR) |= 0x01; //調用寄存器的環回模式 (改用寄存器實現)
//初始化并使能主機模式,使用系統時鐘為 I2C0 模塊提供時鐘頻率,
//主機模塊傳輸速//率為 100Kbps
I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), false);
// 使能從機模式
I2CSlaveEnable(I2C0_BASE);
//設置從機地址
I2CSlaveInit(I2C0_BASE, SLAVE_ADDRESS);
//設置主機放在總線上的地址,寫入從機
I2CMasterSlaveAddrSet(I2C0_BASE, SLAVE_ADDRESS, false);
//調用uart初始化函數串口顯示
Init_Console_Uart0();
UARTprintf("I2C 回送例子--->");
UARTprintf("
模式 = I2C0");
UARTprintf("
模式 = 單獨的發送/接收");
UARTprintf("
比率 = 100kbps
");
// 數據初始化發送.
pui32DataTx[0] = 'I';
pui32DataTx[1] = '2';
pui32DataTx[2] = 'C';
pui32DataTx[3] = 'O';
pui32DataTx[4] = 'K';
// 初始化接收緩沖區.
for(ui32Index = 0; ui32Index < NUM_I2C_DATA; ui32Index++)
{
pui32DataRx[ui32Index] = 0;
}
UARTprintf("轉換方式: 主機 -> 從機
");
// 將I2C數據從主機發送到從機
for(ui32Index = 0; ui32Index < NUM_I2C_DATA; ui32Index++)
{
UARTprintf(" 發送: '%c' . . . ", pui32DataTx[ui32Index]);
// 將要發送的數據放在數據寄存器中
I2CMasterDataPut(I2C0_BASE, pui32DataTx[ui32Index]);
//從機回顯發送數據到主機
I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_SEND);
// 等待從機接收應答
while(!(I2CSlaveStatus(I2C0_BASE) & I2C_SLAVE_ACT_RREQ))
{
}
// 從從機寄存器讀取數據
pui32DataRx[ui32Index] = I2CSlaveDataGet(I2C0_BASE);
// 等待主機接收應答
while(I2CMasterBusy(I2C0_BASE))
{
}
UARTprintf("接收: '%c'
", pui32DataRx[ui32Index]);
}
// 重置接收緩沖區
for(ui32Index = 0; ui32Index < NUM_I2C_DATA; ui32Index++)
{
pui32DataRx[ui32Index] = 0;
}
UARTprintf("
轉換方式: 從機 -> 主機
");
//主機從該地址讀取數據
I2CMasterSlaveAddrSet(I2C0_BASE, SLAVE_ADDRESS, true);
// 初始化I2C主機模塊狀態為單端接收
I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
// 等待主機請求從機發送數據
while(!(I2CSlaveStatus(I2C0_BASE) & I2C_SLAVE_ACT_TREQ))
{
}
for(ui32Index = 0; ui32Index < NUM_I2C_DATA; ui32Index++)
{
UARTprintf(" 發送: '%c' . . . ", pui32DataTx[ui32Index]);
//將要發送的數據放到從機數據寄存器中
I2CSlaveDataPut(I2C0_BASE, pui32DataTx[ui32Index]);
// 初始化I2C主機模塊狀態為單端接收
I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
// 等待從機發送完畢
while(!(I2CSlaveStatus(I2C0_BASE) & I2C_SLAVE_ACT_TREQ))
{
}
// 從主機數據寄存器讀取數據
pui32DataRx[ui32Index] = I2CMasterDataGet(I2C0_BASE);
UARTprintf("接收: '%c'
", pui32DataRx[ui32Index]);
}
//顯示實驗結束
UARTprintf("
傳輸實驗結束.
");
return 0;
}

浙公網安備 33010602011771號