C6748_UART(1) - 基本知識


  通用異步收發傳輸器(Universal Asynchronous Receiver/Transmitter),通常稱作UART,是一種異步收發傳輸器。該總線可雙向通信,可以實現去全雙工傳輸和接收。將資料由串行通信並行通信間作傳輸轉換,作為並行輸入成為串行輸出的芯片,通常集成於其他通訊接口的連結上。其原理就是,串並轉換和並串轉換。 

1.UART的工作原理 

首先介紹幾個定義:
              波特率:在通信中定義的是單位時間碼元的傳輸速率,而在UART協議下,一個碼元就是一個bit。
              UART數據傳輸格式及定義見下圖所示:
  •  發送數據過程空閑狀態,線路處於高電位;當收到發送數據指令后,拉低線路一個數據位的時間T,接着數據按低位到高位依次發送,數據發送完畢后,接着發送奇偶校驗位和停止位(停止位為高電位),一幀資料發送結束。
  •  接收數據過程空閑狀態,線路處於高電位;當檢測到線路的下降沿(線路電位由高電位變為低電位)時說明線路有數據傳輸,按照約定的波特率從低位到高位接收數據,數據接收完畢后,接着接收並比較奇偶校驗位是否正確,如果正確則通知后續設備准備接收數據或存入緩存。
       由於UART是異步傳輸,沒有傳輸同步時鍾。為了能保證數據傳輸的正確性, UART采用16倍數據波特率的時鍾進行采樣。每個數據有16個時鍾采樣,取中間的采樣值,以保證采樣不會滑碼或誤碼。一般UART一幀的數據位數為8,這樣即使每個數據有一個時鍾的誤差,接收端也能正確地采樣到數據。
       由於UART是異步傳輸,沒有傳輸同步時鍾,為了能夠保證數據傳輸的正確性,采用16倍數據波特率的時鍾進行采樣,也即一個bit采用16個系統時鍾采樣,在系統中采用一個計數器完成這個功能。例如系統時鍾為150MHz,波特率為9600,采樣一次也即計數器的計數周期,150000000/9600/16=976.5625, 取977。那么實際波特率為150000000/977/16=9595.701,存在誤差。也即如下這張圖
   總的來說,數據的誤差與波特率設置有關,按照以下的計算方式,可以計算得到該波特率的因子,該因子相當於分頻器,只能為整數,若計算得到的因子為小數,則需要四舍五入,而此時的UART的輸入時鍾頻率經過因子分頻后得到的實際波特率與設置值會存在誤差,這就是誤差的來源。 

 

2.關於UART的FIFO

       FIFO的必要性。在進行UART通信時,中斷方式比輪詢方式要簡便且效率高。但是,如果沒有收發FIFO,則每傳輸一個數據(5~8位)都要中斷處理一次,效率仍然不高。如果有了收發FIFO,則可以在連續收發若干個數據(可多至14個)后才產生一次中斷,然后一起處理。這就大大提高了收發效率。
       接收超時問題。如果沒有接收超時功能,則在對方已經發送完畢而接收FIFO未填滿時並不會觸發中斷(FIFO滿才會觸發中斷),結果造成最后接收的有效數據得不到處理的問題。 有了接收超時功能后,如果接收FIFO未填滿而對方發送已經停,則在不超過3個數據的接收時間內就會觸發超時中斷,因此數據會照常得到處理。
       發送時,只要發送FIFO不滿,數據只管往里連續放,放完后就直接退出發送子程序。隨后,FIFO真正發送完成后會自動產生中斷,通知主程序說:我已經完成真正的發送。
       接收時,如果對方是連續不間斷發送,則填滿FIFO后會以中斷的方式通知主程序說:現在有一批數據來了,請處理。
       如果對方是間斷性發送,也不要緊,當間隔時間過長時(2~3個字符傳輸時間),也會產生中斷,這次是超時中斷,通知主程序說:對方可能已經發送完畢,但FIFO未滿,也請處理。 

3.UART與COM口的區別

  嵌入式中說的串口,一般是指UART口, 但是我們經常搞不清楚它和COM口的區別,  以及RS232, TTL等關系,  實際上UART,COM指的物理接口形式(硬件), 而TTL、RS-232是指的電平標准(電信號).

  UART有4個pin(VCC, GND, RX, TX), 用的TTL電平,  低電平為0(0V),高電平為1(3.3V或以上)。

COM口是我們台式機上面常用的口(下圖),9個pin, 用的RS232電平,  它是負邏輯電平,它定義+5~+12V為低電平,而-12~-5V為高電平

引腳介紹(COM口比較多pin,但是常用的也是這幾個):

VCC:供電pin,一般是3.3v,在我們的板子上沒有過電保護,這個pin一般不接更安全

GND:接地pin,有的時候rx接受數據有問題,就要接上這個pin,一般也可不接

RX:接收數據pin

TX:發送數據pin,

   接設備的時候,一般只接GND RX TX。不會接Vcc或者+3.3v的電源線,避免與目標設備上的供電沖突。 

 

4.UART工作模式

  TMS320C6748有三個硬件串口,Uart0、Uart1、Uart2.每個串口有輪詢模式(POLL)、中斷模式(INT)、直接數據訪問模式(DMA)。 先來分析下串口三種不同工作模式的實現機制及優缺點:

(1)輪詢模式
  輪詢方式是對I/O設備的程序輪流查詢,是早期的計算機系統對I/O設備的一種管理方式。它定時對各種設備輪流詢問一遍有無處理要求。輪流詢問之后,有要求的,則加以處理。在處理I/O設備的要求之后,處理機返回繼續工作。
       盡管輪詢需要時間,但輪詢要比I/O設備的速度要快得多一遍不會發生不能及時處理的問題。
       當然,再快的處理機,能處理的輸入輸出設備的數量也是有一定限度的。而且,程序輪詢占據了一部分CPU處理時間,是一種效率較低的方式,在DSP程序中很少應用。

(2)中斷方式
  處理器的高速和輸入輸出設備的低速是一種矛盾,是設備管理要解決的一個重要問題。為了提高整體效率,減少在程序直接控制方式中CPU之間的數據傳送,是很必要的。
  中斷方式的優缺點:
  I/O設備中斷方式使處理器的利用率提高,且能支持多通道程序和I/O設備的並行操作。不過,中斷方式仍然存在一些問題。首先,MCU要配置各種各樣的輸入輸出設備。如果這些IO設備都通過中斷處理方式並行操作,那么中斷次數的急劇會造成CPU無法響應中斷和出現數據丟失現象。
      其次,如果IO控制器的數據緩沖區比較小,在緩沖區裝滿數據之后將會發生中斷。那么,在數據傳送過程中,發生中斷的機會增多,這將耗去大量CPU處理時間。

(3)直接內存存儲(DMA)方式
直接內存存儲技術是指,數據在內存與IO設備間直接進行成塊傳輸。
   DMA有兩個技術特征,首先是直接傳送,其次是塊傳送。
  所謂直接傳送,即在內存與IO設備間傳送一個數據塊的過程中,不需要CPU的任何干涉,只需要CPU在過程開始時向設備發出“傳送數據”的命令,然后通過中斷來得知過程是否結束和下次操作是否准備就緒。

 

DMA與中斷的區別
  (1) 中斷方式是數據緩沖區寄存器滿之后發出中斷,要求CPU進行中斷處理,而DMA方式則是在所要求傳送的數據塊全部傳送結束時要求CPU進行中斷處理。這就大大減少了CPU進行中斷處理的次數。
  (2) 中斷方式的數據傳送是在中斷處理時由CPU控制完成的,而DMA方式則是在DMA控制器的控制下,不經過CPU控制。這就排除了因並行設備過多而來不及處理以及因速度不匹配而造成數據丟失等現象。

DMA方式的優缺點
  DMA方式中,由於IO設備直接同內存發生塊的數據交換,因此效率比較高。
  DMA控制器功能的強弱,是決定DMA效率的關鍵因素。DMA控制器需要為每次數據傳送做大量的工作,數據傳送單位的增大意味着傳送次數的減少。另外,DMA方式竊取了時鍾周期,CPU處理效率低了,要想盡量少地竊取時鍾周期,就要設法提高DMA控制器的性能,這樣可以較少地影響CPU處理效率。

 

下面講解TMS320C6748三種工作方式程序:
`1  POLL工作方式

 1  int main(void)
 2        {
 3             
 4             PSCInit();                                        // 外設使能配置           
 5             GPIOBankPinMuxSet();                   // GPIO 管腳復用配置
 6          
 7             UARTInit();                                     // UART 初始化
 8             
 9             unsigned char i;                             // 發送字符串
10             for(i = 0; i < 34; i++)
11                    UARTCharPut(SOC_UART_1_REGS, Send);
12           
13              unsigned char Receive;                // 接收緩存
14              // 主循環
15              for(;;)
16              {
17                   Receive=UARTCharGet(SOC_UART_1_REGS);
18                   UARTCharPut(SOC_UART_1_REGS, Receive+1);
19               }
20        }

 

    從DSP串口 Poll模式程序可以看到,Poll模式使用比較簡單,容易上手。

 

 2 中斷方式

 1  int main(void)
 2         {
 3               // 外設使能配置
 4               PSCInit();
 5         
 6               // GPIO 管腳復用配置
 7               GPIOBankPinMuxSet();
 8 
 9 
10               // DSP 中斷初始化
11               InterruptInit();
12 
13               // UART 初始化
14               UARTInit();
15 
16               // UART 中斷初始化
17               UARTInterruptInit();
18 
19               // 主循環
20               for(;;)
21               {
22               }
23        }
24 
25      void UARTIsr()
26      {
27              static unsigned int length = sizeof(txArray);
28              static unsigned int count = 0;
29              unsigned char rxData = 0;
30              unsigned int int_id = 0;
31 
32              // 確定中斷源
33              int_id = UARTIntStatus(SOC_UART_0_REGS);
34 
35              // 清除 UART2 系統中斷
36              IntEventClear(SYS_INT_UART0_INT);
37 
38              // 發送中斷
39              if(UART_INTID_TX_EMPTY == int_id)
40              {
41                     if(0 < length)
42                     {
43                           // 寫一個字節到 THR
44                           UARTCharPutNonBlocking(SOC_UART_0_REGS, txArray[count]);
45                           length--;
46                           count++;
47                    }
48                    if(0 == length)
49                   {
50                         // 禁用發送中斷
51                         UARTIntDisable(SOC_UART_0_REGS, UART_INT_TX_EMPTY);
52                   }
53             }
54 
55                 // 接收中斷
56                 if(UART_INTID_RX_DATA == int_id)
57                 {
58                        rxData = UARTCharGetNonBlocking(SOC_UART_0_REGS);
59                        UARTCharPutNonBlocking(SOC_UART_0_REGS, rxData);
60                 }
61 
62                 // 接收錯誤
63                 if(UART_INTID_RX_LINE_STAT == int_id)
64                 {
65                        while(UARTRxErrorGet(SOC_UART_0_REGS))
66                        {
67                              // 從 RBR 讀一個字節                           UARTCharGetNonBlocking(SOC_UART_0_REGS);
68                        }
69                 }
70                 return;
71             }

 

3  DMA模式

  1 int main(void)
  2 {
  3         // 外設使能配置
  4         PSCInit();
  5 
  6         // DSP 中斷初始化
  7         InterruptInit();
  8 
  9         // EDMA3 中斷初始化
 10         EDMA3InterruptInit();
 11 
 12         // EDMA3 初始化
 13         EDMA3UARTInit();
 14 
 15     // 初始化串口終端 使用串口2
 16     UARTStdioInit();
 17 
 18     // 申請串口 EDMA3 發送通道
 19     EDMA3RequestChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA,
 20                           EDMA3_CHA_UART2_TX, EDMA3_CHA_UART2_TX,
 21                           EVT_QUEUE_NUM);
 22 
 23     // 注冊回調函數
 24     cb_Fxn[EDMA3_CHA_UART2_TX] = &callback;
 25 
 26     // 申請串口 EDMA3 接收通道
 27     EDMA3RequestChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA,
 28                           EDMA3_CHA_UART2_RX, EDMA3_CHA_UART2_RX,
 29                           EVT_QUEUE_NUM);
 30 
 31     // 注冊回調函數
 32     cb_Fxn[EDMA3_CHA_UART2_RX] = &callback;
 33 
 34     volatile char enter[] = "Tronlong UART2 EDMA3 Application......\n\rPlease Enter 20 bytes from keyboard\r\n";
 35     volatile char buffer[RX_BUFFER_SIZE];
 36     unsigned int buffLength = 0;
 37 
 38     // 發送數據
 39     buffLength = strlen((const char *)enter);
 40     UartTransmitData(EDMA3_CHA_UART2_TX, EDMA3_CHA_UART2_TX, enter, buffLength);
 41 
 42     // 使能串口 DMA 模式
 43     UARTDMAEnable(SOC_UART_2_REGS, UART_RX_TRIG_LEVEL_1 |  \
 44                                      UART_DMAMODE |         \
 45                                      UART_FIFO_MODE );
 46 
 47     // 等待從回調函數返回
 48     while(flag == 0);
 49     flag = 0;
 50 
 51     // 接收數據
 52     UartReceiveData(EDMA3_CHA_UART2_RX, EDMA3_CHA_UART2_RX, buffer);
 53 
 54     // 使能串口 DMA 模式
 55     UARTDMAEnable(SOC_UART_2_REGS, UART_RX_TRIG_LEVEL_1 | \
 56                                      UART_DMAMODE |        \
 57                                      UART_FIFO_MODE );
 58 
 59     // 等待從回調函數返回
 60     while(flag == 0);
 61     flag = 0;
 62 
 63     // 發送數據
 64     UartTransmitData(EDMA3_CHA_UART2_TX, EDMA3_CHA_UART2_TX, buffer, RX_BUFFER_SIZE);
 65 
 66     // 使能串口 DMA 模式
 67     UARTDMAEnable(SOC_UART_2_REGS, UART_RX_TRIG_LEVEL_1 |  \
 68                                      UART_DMAMODE |         \
 69                                      UART_FIFO_MODE );
 70 
 71     // 等待從回調函數返回
 72     while(flag == 0);
 73     flag = 0;
 74 
 75     // 釋放 EDMA3 通道
 76     EDMA3FreeChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA,
 77                        EDMA3_CHA_UART2_TX, EDMA3_TRIG_MODE_EVENT,
 78                        EDMA3_CHA_UART2_TX, EVT_QUEUE_NUM);
 79 
 80     EDMA3FreeChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA,
 81                        EDMA3_CHA_UART2_RX, EDMA3_TRIG_MODE_EVENT,
 82                        EDMA3_CHA_UART2_RX, EVT_QUEUE_NUM);
 83 
 84         // 主循環
 85         for(;;)
 86         {
 87 
 88         }
 89 }
 90 
 91 
 92 void Edma3ComplHandlerIsr(void)
 93 {
 94     volatile unsigned int pendingIrqs;
 95     volatile unsigned int isIPR = 0;
 96 
 97     unsigned int indexl;
 98     unsigned int Cnt = 0;
 99 
100     indexl = 1;
101 
102     IntEventClear(SYS_INT_EDMA3_0_CC0_INT1);
103 
104     isIPR = HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_S_IPR(1));
105     if(isIPR)
106     {
107         while((Cnt < EDMA3CC_COMPL_HANDLER_RETRY_COUNT)&& (indexl != 0u))
108         {
109             indexl = 0u;
110             pendingIrqs = HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_S_IPR(1));
111 
112             while(pendingIrqs)
113             {
114                 if((pendingIrqs & 1u) == TRUE)
115                 {
116                     HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_S_ICR(1)) = (1u << indexl);
117 
118                     (*cb_Fxn[indexl])(indexl, EDMA3_XFER_COMPLETE);
119                 }
120                 ++indexl;
121                 pendingIrqs >>= 1u;
122             }
123             Cnt++;
124         }
125     }
126 }
127 
128 void Edma3CCErrHandlerIsr()
129 {
130     volatile unsigned int pendingIrqs = 0;
131     unsigned int regionNum = 0;
132     unsigned int evtqueNum = 0;
133     unsigned int index = 1;
134     unsigned int Cnt = 0;
135 
136     IntEventClear(SYS_INT_EDMA3_0_CC0_ERRINT);
137 
138     if((HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_EMR) != 0 ) || \
139        (HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_QEMR) != 0) || \
140        (HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_CCERR) != 0))
141     {
142         while((Cnt < EDMA3CC_ERR_HANDLER_RETRY_COUNT) && (index != 0u))
143         {
144             index = 0u;
145             pendingIrqs = HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_EMR);
146             while(pendingIrqs)
147             {
148                 if((pendingIrqs & 1u) == TRUE)
149                 {
150                     HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_EMCR) = (1u<<index);
151                     HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_S_SECR(regionNum)) = (1u<<index);
152                 }
153 
154                 ++index;
155                 pendingIrqs >>= 1u;
156             }
157 
158             index = 0u;
159             pendingIrqs = HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_QEMR);
160 
161             while(pendingIrqs)
162             {
163                 if((pendingIrqs & 1u)==TRUE)
164                 {
165                     HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_QEMCR) = (1u<<index);
166                     HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_S_QSECR(0)) = (1u<<index);
167                 }
168 
169                 ++index;
170                 pendingIrqs >>= 1u;
171             }
172 
173             index = 0u;
174             pendingIrqs = HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_CCERR);
175 
176                         if(pendingIrqs != 0u)
177                         {
178                                 for(evtqueNum = 0u; evtqueNum < EDMA3_0_NUM_EVTQUE; evtqueNum++)
179                                 {
180                                         if((pendingIrqs & (1u << evtqueNum)) != 0u)
181                                         {
182                                                 HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_CCERRCLR) = (1u << evtqueNum);
183                                         }
184                                  }
185 
186                                  if ((pendingIrqs & (1 << EDMA3CC_CCERR_TCCERR_SHIFT)) != 0u)
187                                  {
188                                          HWREG(SOC_EDMA30CC_0_REGS + EDMA3CC_CCERRCLR) = \
189                                                   (0x01u << EDMA3CC_CCERR_TCCERR_SHIFT);
190                                  }
191 
192                                  ++index;
193                         }
194 
195                         Cnt++;
196         }
197     }
198 }
199 
200 /****************************************************************************/
201 /*                                                                          */
202 /*              發送數據                                                    */
203 /*                                                                          */
204 /****************************************************************************/
205 void UartTransmitData(unsigned int tccNum, unsigned int chNum,
206                          volatile char *buffer, unsigned int buffLength)
207 {
208     EDMA3CCPaRAMEntry paramSet;
209 
210     // 配置參數 RAM
211     paramSet.srcAddr = (unsigned int)buffer;
212     // 接收緩存寄存器 / 發送保持寄存器 地址
213     paramSet.destAddr = SOC_UART_2_REGS + 0;
214     paramSet.aCnt = MAX_ACNT;
215     paramSet.bCnt = (unsigned short)buffLength;
216     paramSet.cCnt = MAX_CCNT;
217 
218     // 源索引自增系數 1 即一個字節
219     paramSet.srcBIdx = (short)1u;
220 
221     // 目標索引自增系數
222     paramSet.destBIdx = (short)0u;
223 
224     // 異步傳輸模式
225     paramSet.srcCIdx = (short)0u;
226     paramSet.destCIdx = (short)0u;
227     paramSet.linkAddr = (unsigned short)0xFFFFu;
228     paramSet.bCntReload = (unsigned short)0u;
229     paramSet.opt = 0x00000000u;
230     paramSet.opt |= (EDMA3CC_OPT_DAM );
231     paramSet.opt |= ((tccNum << EDMA3CC_OPT_TCC_SHIFT) & EDMA3CC_OPT_TCC);
232     paramSet.opt |= (1 << EDMA3CC_OPT_TCINTEN_SHIFT);
233 
234     // 寫參數 RAM
235     EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, chNum, &paramSet);
236 
237     // 使能 EDMA3 通道
238     EDMA3EnableTransfer(SOC_EDMA30CC_0_REGS, chNum, EDMA3_TRIG_MODE_EVENT);
239 }
240 
241 /****************************************************************************/
242 /*                                                                          */
243 /*              接收數據                                                    */
244 /*                                                                          */
245 /****************************************************************************/
246 void UartReceiveData(unsigned int tccNum, unsigned int chNum,
247                             volatile char *buffer)
248 {
249     EDMA3CCPaRAMEntry paramSet;
250 
251     // 配置參數 RAM
252     // 接收緩存寄存器 / 發送保持寄存器 地址
253     paramSet.srcAddr = SOC_UART_2_REGS + 0;
254     paramSet.destAddr = (unsigned int)buffer;
255     paramSet.aCnt = MAX_ACNT;
256     paramSet.bCnt = RX_BUFFER_SIZE;
257     paramSet.cCnt = MAX_CCNT;
258 
259     // 源索引自增系數
260     paramSet.srcBIdx = 0;
261     // 目標索引自增系數 1 即一個字節
262     paramSet.destBIdx = 1;
263 
264     // 異步模式
265     paramSet.srcCIdx = 0;
266     paramSet.destCIdx = 0;
267     paramSet.linkAddr = (unsigned short)0xFFFFu;
268     paramSet.bCntReload = 0;
269     paramSet.opt = 0x00000000u;
270     paramSet.opt |= ((EDMA3CC_OPT_SAM) << EDMA3CC_OPT_SAM_SHIFT);
271     paramSet.opt |= ((tccNum << EDMA3CC_OPT_TCC_SHIFT) & EDMA3CC_OPT_TCC);
272     paramSet.opt |= (1 << EDMA3CC_OPT_TCINTEN_SHIFT);
273 
274     // 寫參數 RAM
275     EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, chNum, &paramSet);
276 
277     // 使能 EDMA3 通道
278     EDMA3EnableTransfer(SOC_EDMA30CC_0_REGS, chNum, EDMA3_TRIG_MODE_EVENT);
279 }
280 
281 /****************************************************************************/
282 /*                                                                          */
283 /*              回調函數                                                    */
284 /*                                                                          */
285 /****************************************************************************/
286 void callback(unsigned int tccNum, unsigned int status)
287 {
288     UARTDMADisable(SOC_UART_2_REGS, (UART_RX_TRIG_LEVEL_1 | UART_FIFO_MODE));
289 
290     flag = 1;
291 }
DMA模式

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM