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