<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      TM4C123G_study

      圖庫掛了

      時鐘

      時鐘源

      TM4C123內部共有4個時鐘源,見下表

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

      img

      時鐘樹

      img

      1. MOSC和PIOSC可以用來驅動PLL
      2. 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)可以延時3
      Count個周期,令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 配置其他引腳參數

      配置定時器模塊為捕捉-邊沿計數模式:

      1. 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捕捉-邊沿加計數模式

      設置要捕捉的邊沿:

      1. TimerControlEvent

      設置計數范圍:

      1. TimerMatchSet設置加/減計數結束值
      2. TimerLoadSet設置減計數起始值

      中斷設置:

      1. TimerIntRegister 注冊中斷服務函數
      2. TimerIntEnable 源級中斷使能,這里注意配置中斷類型
        (1)TIMER_CAPA_MATCH – 模塊A計數到達預設值
        (2)TIMER_CAPB_MATCH – 模塊B計數到達預設值
      3. IntEnable 中斷控制器級中斷使能
      4. IntMasterEnable處理器級中斷使能

      啟動定時器模塊:

      1. 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捕捉-邊沿加計時模式
      設置計時范圍

      1. TimerLoadSet無論加計時還是減計時,都用這個函數設置Preload值

      中斷設置

      1. 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 位尋址模式

      ![1690613571472](C:\Users\86177\AppData\Roaming\Typora ypora-user-images\1690613571472.png)

      二、結構圖:

      img

      三、 初始化與配置

      以下例子給出如何配置 I2C 模塊用于主機傳輸一個字節。這里假定系統時鐘為 20 MHz 。

      1. 在系統控制模塊使用 RCGCI2C 寄存器使能 I2C 時鐘。
      2. 通過在系統控制模塊的 RCGCGPIO 寄存器為相應的 GPIO 模塊使能時鐘。要了解使能哪些GPIO 端口。
      3. 在 GPIO 模塊,通過 GPIOAFSEL 寄存器位它們的復用功能使能相應的引腳。請參看表 23-4,以確定配置那個 GPOI。
      4. 使能 I2CSDA 引腳來配置開漏操作。
      5. 在 GPIOPCTL 寄存器配置 PMCn 位組為相應的引腳配置 I2C 信號。
      6. 向 I2CMCR 寄存器寫入 0x00000010 值來初始化 I2C 主機。
      7. 通過寫入 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.
      8. 規定主機的從機地址,下一個操作是一個發送,該發送通過向 I2CMSA 寄存器值寫入0x00000076 實現。這設置了從機地址為 0x3B。
      9. 通過向 I2CMDR 寄存器寫入所需數據將數據(位)傳輸到數據寄存器。
      10. 啟動從主機到從機的數據單字節的傳輸是通過向 I2CMCS 寄存器寫入 0x00000007 (STOP,START, RUN)實現。
      11. 等待直到傳輸完成,通過輪詢 I2CMCS 寄存器的 BUSBSTY 位,直到該位被清除。
      12. 檢測 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;
      }
      
      posted @ 2024-12-26 13:47  燊楽  閱讀(128)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产欧美va欧美va在线| 在线a久青草视频在线观看| 亚洲最大日韩精品一区| 国产成人综合亚洲第一区| 美女爽到高潮嗷嗷嗷叫免费网站| 欧美一区二区| 精品国产粉嫩一区二区三区| 亚洲AVAV天堂AV在线网阿V| 午夜成人性爽爽免费视频| 黄男女激情一区二区三区| 亚洲国产综合性亚洲综合性| 国产日产欧产精品精品| 麻豆亚洲精品一区二区| 日韩精品理论片一区二区| 好吊妞视频这里有精品| 一区二区三区精品视频免费播放 | 4hu四虎永久免费地址ww416| 欧美亚洲日本国产其他| 熟妇啊轻点灬大JI巴太粗| 婷婷久久综合九色综合88| 久播影院无码中文字幕| 91福利一区二区三区| 国产高清在线男人的天堂| 久久国产精品色av免费看| 久久久久国产精品人妻| 成人综合婷婷国产精品久久蜜臀| 虎白女粉嫩尤物福利视频| 日韩有码av中文字幕| 人妻少妇88久久中文字幕 | 日韩精品卡一卡二卡三卡四| 国产精品自拍中文字幕| 青川县| 国产人妻人伦精品婷婷| 国产成人高清亚洲综合| 铜山县| 国产精品入口麻豆| 午夜福利在线观看成人| 激情五月开心婷婷深爱| 日韩一区二区三区精彩视频| 国产一区国产精品自拍| 久久久无码精品亚洲日韩蜜臀浪潮|