搜索
bottom↓
回复: 80

一个AVR USART(RS232)低层驱动+中间层软件示例

[复制链接]

出0入0汤圆

发表于 2004-12-3 13:17:58 | 显示全部楼层 |阅读模式
一个USART(RS232)低层驱动+中间层软件示例



    一般教科书上提供的UART收发的程序往往是一段采用轮循(Polling)方式完成收发的简单代码。但对于高速的AVR来讲,采用这种方式大大降低了MUC的效率。在使用AVR时,应根据芯片本身的特点(片内大容量数据存储器RAM,更适合采用高级语言编写系统程序),编写高效可靠的UART收发接口(低层)程序。下面是一个典型的USART的接口程序。



#include <mega128.h>



#define RXB8 1

#define TXB8 0

#define UPE 2

#define OVR 3

#define FE 4

#define UDRE 5

#define RXC 7



#define FRAMING_ERROR (1<<FE)

#define PARITY_ERROR (1<<UPE)

#define DATA_OVERRUN (1<<OVR)

#define DATA_REGISTER_EMPTY (1<<UDRE)

#define RX_COMPLETE (1<<RXC)



// USART0 Receiver buffer

#define RX_BUFFER_SIZE0 8

char rx_buffer0[RX_BUFFER_SIZE0];

unsigned char rx_wr_index0,rx_rd_index0,rx_counter0;

// This flag is set on USART0 Receiver buffer overflow

bit rx_buffer_overflow0;



// USART0 Receiver interrupt service routine

#pragma savereg-

interrupt [USART0_RXC] void uart0_rx_isr(void)

{

char status,data;

#asm

         push r26

            push r27

            push r30

            push r31

            in   r26,sreg

            push r26

#endasm

status=UCSR0A;

data=UDR0;

if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)

   {

       rx_buffer0[rx_wr_index0]=data;

       if (++rx_wr_index0 == RX_BUFFER_SIZE0) rx_wr_index0=0;

       if (++rx_counter0 == RX_BUFFER_SIZE0)

          {

              rx_counter0=0;

              rx_buffer_overflow0=1;

          };

   };

#asm

       pop  r26

       out  sreg,r26

       pop  r31

       pop  r30

       pop  r27

       pop  r26

#endasm

}

#pragma savereg+



#ifndef _DEBUG_TERMINAL_IO_

// Get a character from the USART0 Receiver buffer

#define _ALTERNATE_GETCHAR_

#pragma used+

char getchar(void)

{

  char data;

  while (rx_counter0==0);

  data=rx_buffer0[rx_rd_index0];

  if (++rx_rd_index0 == RX_BUFFER_SIZE0) rx_rd_index0=0;

  #asm("cli")

    --rx_counter0;

  #asm("sei")

  return data;

}

#pragma used-

#endif



// USART0 Transmitter buffer

#define TX_BUFFER_SIZE0 8

char tx_buffer0[TX_BUFFER_SIZE0];

unsigned char tx_wr_index0,tx_rd_index0,tx_counter0;



// USART0 Transmitter interrupt service routine

#pragma savereg-

interrupt [USART0_TXC] void uart0_tx_isr(void)

{

  #asm

      push r26

      push r27

      push r30

      push r31

      in   r26,sreg

      push r26

  #edasm

  if (tx_counter0)

  {

         --tx_counter0;

       UDR0=tx_buffer0[tx_rd_index0];

       if (++tx_rd_index0 == TX_BUFFER_SIZE0) tx_rd_index0=0;

   };

  #asm

      pop  r26

      out  sreg,r26

      pop  r31

      pop  r30

      pop  r27

      pop  r26

  #endasm

}

#pragma savereg+



#ifndef _DEBUG_TERMINAL_IO_

// Write a character to the USART0 Transmitter buffer

#define _ALTERNATE_PUTCHAR_

#pragma used+

void putchar(char c)

{

  while (tx_counter0 == TX_BUFFER_SIZE0);

  #asm("cli")

   if (tx_counter0 || ((UCSR0A & DATA_REGISTER_EMPTY)==0))

   {

       tx_buffer0[tx_wr_index0]=c;

       if (++tx_wr_index0 == TX_BUFFER_SIZE0) tx_wr_index0=0;

       ++tx_counter0;

   }

   else

       UDR0=c;

  #asm("sei")

}

#pragma used-

#endif



// Standard Input/Output functions

#include <stdio.h>



// Declare your global variables here



void main(void)

{



// USART0 initialization

// Communication Parameters: 8 Data, 1 Stop, No Parity

// USART0 Receiver: On

// USART0 Transmitter: On

// USART0 Mode: Asynchronous

// USART0 Baud rate: 9600

UCSR0A=0x00;

UCSR0B=0xD8;

UCSR0C=0x06;

UBRR0H=0x00;

UBRR0L=0x67;



// Global enable interrupts

#asm("sei")



while (1)

      {

          // Place your code here



      };

}



    这段由CVAVR程序生成器产生的UART接口代码是一个非常好的、高效可靠,并且值得认真学习和体会的。其特点如下:

   l.它采用两个8字节的接收和发送缓冲器来提高MCU的效率,如当主程序调用Putchar()发送数据时,如果UART口不空闲,就将数据放入发送缓冲器中,MCU不必等待,可以继续执行其它的工作。而UART的硬件发送完一个数据后,产生中断,由中断服务程序负责将发送缓冲器中数据依次送出。

   2.数据缓冲器结构是一个线性的循环队列,由读、写和队列计数器3个指针控制,用于判断队列是否空、溢出,以及当前数据在队列中的位置。

   3.用编译控制命令#pragma savereg-和#pragma savereg+,使得由CVAVR在生成的中断服务程序中不进行中断保护(CVAVR生成中断保护会将比较多的寄存器压入堆栈中),而在中断中嵌入汇编,只将5个在本中断中必须要保护的寄存器压栈。这样提高了UART中断处理的速度,也意味着提高了MCU的效率。

   4.由于在接口程序Putchar()、Getchar()和中断服务程序中都要对数据缓冲器的读、写和队列计数器3个指针判断和操作,为了防止冲突,在Putchar()、Getchar()中对3个指针操作时临时将中断关闭,提高了程序的可靠性。

    建议读者能逐字逐句地仔细分析该段代码,真正理解和领会每一句语句(包括编译控制命令的作用)的作用,从中体会和学习如何编写效率高,可靠性好,结构优良的系统代码。这段程序使用的方法和技巧,对编写SPI、I2C的串行通信接口程序都是非常好的借鉴。

    作为现在的单片机和嵌入式系统的工程师,不仅要深入全面的掌握芯片和各种器件的性能,具备丰富的硬件设计能力;同时也必须提高软件的设计能力。要学习和掌握有关数据结构、操作系统、软件工程、网络协议等方面的知识,具有设计编写大的复杂系统程序的能力。



USART应用实例

    使用ATmega128实现一个工业设备的主控制板,它与由ATmega8管理的按键和LED显示构成的控制面板距离在2米左右,两者之间采用USART通信联系。考虑到在实际应用中,俩者之间交换的数据很少,通信速度也不需要很高,重要的是保证通信的可靠和抗干扰,因此在硬件设计上采用电流环的连接方式,见图5.4。

   在图中通信双方采用光隔和三极管,将USART的电平变化变成电流变化后传送连接,如同工业上使用的20mA电流环通信一样,大大提高了通信的抗干扰能力。

通信协议和规程的制定:

   l.通信速率采用2400bps(速率太高时电流环的变化会跟不上)。

   2. 用户数据包采用定长格式,每个数据包长度为6个字节,其中第1个字节是数据包起始字节0xBB,第6字节为数据包结束字节0xEE,其它为用户命令、数据和系统状态参数。

   3.每次通信由A端发起,下发一个数据包;B端收到一个正确的数据包后,必须返回一个数据包应答。

   4.A端下发一个数据包后,在300ms内没有正确收到应答包时(在2400bps时传送6个字节的时间约为30ms),将再次重发;3次重发均不能正确收到应答包则报警。

   5.在系统正常工作时,A端每隔250ms下发一个数据包,B端如果在1s内没有正确收到一个下发的数据包,将进入安全保护程序。

   在这个应用实例中,USART接口的发送程序与前面给出的典型例程中的一样,而对USART的接收程序进行了改动和简化,使其更加符合在本系统中使用。



#define UART_BEGIN_STX        0xBB

#define UART_END_STX        0xEE

#define RX_BUFFER_SIZE0        6



char rx_buffer0[RX_BUFFER_SIZE0];

unsigned char rx_counter;

bit Uart_RecvFlag



// USART Receiver interrupt service routine

#pragma savereg-

interrupt [USART_RXC] void uart_rx_isr(void)

{

  unsigned char status,data;

  #asm

    push r26

    push r27

    push r30

    push r31

    in   r26,sreg

    push r26

  #endasm



  status=UCSRA;

  data=UDR;

  if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)

  {

    if (!Uart_RecvFlag)

    {   

       rx_buffer[rx_counter] = data;

    switch (rx_counter)

    {

            case 0:

        if (data == UART_BEGIN_STX)     rx_counter = 1;

        break;

       case 1:

       case 2:

       case 3:

       case 4:

        rx_counter++;

        break;

       case 5:

        rx_counter = 0;

        if (data == UART_END_STX)  Uart_RecvFlag = 1;

        break;

    }

      }

   }

   else

      rx_counter = 0;



   #asm

        pop  r26

    out  sreg,r26

    pop  r31

    pop  r30

    pop  r27

    pop  r26

  #endasm

}

#pragma savereg+

…………

void main(void)

{

  while(1)

  {

    if (Uart_RecvFlag)

    {

    …………            //处理收到的数据包

    Uart_RecvFlag = 0;        //允许USART接受新的数据包

     }

  …………            //处理其它任务

  }

}



    在这段代码中,接收中断服务程序直接对数据包的起始字符和结束字符进行判断,并完成对整个数据包的接收。当接收到正确的6个字符的数据包后,将“Uart_RecvFlag”标志置位,通知上层程序处理收到的数据。一旦“Uart_RecvFlag”标志置位后,中断服务程序将不再接收新的数据(放弃掉收到的字节),使得数据缓冲区不会溢出。

    上层程序的设计,应保证以200ms左右的间隔对“Uart_RecvFlag”标志位进行一次判断。一旦判断“Uart_RecvFlag”标志置位后,马上进行处理,回送应答数据。处理完后将“Uart_RecvFlag”标志清除,允许USART接收新的数据包。

还可以考虑在数据包中增加“数据包编号”和“数据校验”2个字节,以进一步提高通信的可靠性。




-----此内容被machao于2005-03-07,22:40:22编辑过

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入0汤圆

 楼主| 发表于 2004-12-27 02:31:53 | 显示全部楼层
如果在数据包中再增加“从机地址”的字节,下发呼叫从机回答。所有从机收到数据包后,其软件检查从机地址是否与自己的地址符合,如是呼叫自己,则回答,否则不于回答,便可实现多机通信。该方法不需要使用AVR的硬件多机通信功能识别,不需要第9位的地址/数据指示,实现也很方便。

出0入0汤圆

发表于 2004-12-27 09:02:06 | 显示全部楼层
楼上说的多机通讯方法是常用的多机通讯方法;给我感觉:各种现场总线协议多机通讯都采用这种方法;

出0入0汤圆

发表于 2004-12-27 15:17:32 | 显示全部楼层
重要的是保证通信的可靠和抗干扰,因此在硬件设计上采用电流环的连接方式,见图5.4。

armok 阿莫,能不能把图一并贴出,以供参考,谢谢

出0入0汤圆

 楼主| 发表于 2004-12-27 21:11:09 | 显示全部楼层
此段是《M128》书中的一个小节的摘选,现将图贴上,供参考。

出0入0汤圆

发表于 2005-2-16 18:07:57 | 显示全部楼层
感谢马潮老师提供的程序,已经应用在我的系统中了,还有你的《ATmege8》让我在短短一周就可以自己做程序了

出0入0汤圆

发表于 2005-3-5 15:46:00 | 显示全部楼层
我在ICC中重新定义了一下putchar函数

void putchar(char c)

{

  while(TX_counter==TX_buffer_size);

  CLI();

  if(TX_counter||((UCSRA&DATA_REGISTER_EMPTY)==0))

   {

    TX_buffer[TX_wr_index]=c;

    if(++TX_wr_index==TX_buffer_size) TX_wr_index=0;

    ++TX_counter;

    }

  else

    UDR=c;

SEI();

}



#include <stdio.h>

当我调用printf函数时,还会使用ICC库中自己的putchar函数吗?

出0入0汤圆

发表于 2005-4-8 15:08:24 | 显示全部楼层
今天上午来学习马老师的这个程序

发现了一个问题

我的程序为

//ICC-AVR application builder : 2005-4-8 9:28:47

// Target : M128

// Crystal: 16.000Mhz



#include <iom128v.h>

#include <macros.h>



#define _Crystal_  8000



void port_init(void)

{

PORTA = 0x00;

DDRA  = 0x00;

PORTB = 0x00;

DDRB  = 0x00;

PORTC = 0x00; //m103 output only

DDRC  = 0x00;

PORTD = 0x00;

DDRD  = 0x00;

PORTE = 0x00;

DDRE  = 0x00;

PORTF = 0x00;

DDRF  = 0x00;

PORTG = 0x00;

DDRG  = 0x00;

}



//UART1 initialize

// desired baud rate:9600

// actual baud rate:9615 (0.2%)

// char size: 8 bit

// parity: Disabled

void uart1_init(void)

{

UCSR1B = 0x00; //disable while setting baud rate

UCSR1A = 0x00;

UCSR1C = 0x06;

UBRR1L = 0x67; //set baud rate lo

UBRR1H = 0x00; //set baud rate hi

UCSR1B = 0xD8;

}



char rx_buffer1[RX_BUFFER_SIZE1];

unsigned char rx_wr_index1,rx_rd_index1,rx_counter1;

//This flag is set on USART1 Receiver buffer overflow

unsigned char rx_buffer_overflow1;



#pragma interrupt_handler uart1_rx_isr:31

void uart1_rx_isr(void)

{

//uart has received a character in UDR

char status,data;

status=UCSR1A;

data=UDR1;

if((status&((1<<FE1)|(1<<DOR1)|(1<<UPE1)))==0)

{

        rx_buffer1[rx_rd_index1]=data;

        if(++rx_wr_index1==RX_BUFFER_SIZE1) rx_wr_index1=0;

        if(++rx_counter1==RX_BUFFER_SIZE1)

        {

                rx_counter1=0;

                rx_buffer_overflow1=1;

        }

}

}



char Uart1_Getchar(void)

{

        char data;

        while(rx_counter1==0);

        data=rx_buffer1[rx_rd_index1];

        if((++rx_rd_index1==RX_BUFFER_SIZE1))

        rx_rd_index1=0;

        CLI();

        --rx_counter1;

        SEI();

        return data;

}       

char tx_buffer1[TX_BUFFER_SIZE1];

unsigned char tx_wr_index1,tx_rd_index1,tx_counter1;



#pragma interrupt_handler uart1_tx_isr:33

void uart1_tx_isr(void)

{

//character has been transmitted

if(tx_counter1)

{

        --tx_counter1;

        UDR1=tx_buffer1[tx_rd_index1];

        if(++tx_rd_index1==TX_BUFFER_SIZE1)

        tx_rd_index1=0;

  }

}

void Uart1_Putchar(unsigned char data)

{

        while(tx_counter1==TX_BUFFER_SIZE1);

        CLI();

        if((tx_counter1||((UCSR1A&(1<<UDRE1))==0)))

        {

                tx_buffer1[tx_wr_index1]=data;

                if(++tx_wr_index1==TX_BUFFER_SIZE1)

                {

                tx_wr_index1=0;

                ++tx_counter1;

                }

        }

        else

        UDR1=data;

        SEI();

}



void Uart1_Sendstring(unsigned char *string)

{

       

      while(*string)

      Uart1_Putchar(*string++);

}

//call this routine to initialize all peripherals

void init_devices(void)

{

//stop errant interrupts until set up

CLI(); //disable all interrupts

XDIV  = 0x00; //xtal divider

XMCRA = 0x40; //external memory

port_init();

uart1_init();



MCUCR = 0x80;

EICRA = 0x00; //extended ext ints

EICRB = 0x00; //extended ext ints

EIMSK = 0x00;

TIMSK = 0x00; //timer interrupt sources

ETIMSK = 0x00; //extended timer interrupt sources

SEI(); //re-enable interrupts

//all peripherals are now initialized

}







void        Delay(unsigned char time)

{

        unsigned int i;

        for(; time > 0; time--)

                for(i = 0; i < (_Crystal_>>3); i++)                        //        about one millisecond

                        _NOP();

}



void main(void)

{

    unsigned char data;

        SPH = 0x0F;

    SPL = 0xFF;

        init_devices();

        while(1)

        {

       

        Uart1_Putchar('a');

        Uart1_Sendstring("asdf");

        Delay(50);

        data=Uart1_Getchar();

        Uart1_Putchar(data);

}

}

几乎是一样的

我加了一个字符串的输出

问题就出在这一条上Uart1_Sendstring("asdf");

程序执行时,字符串输出不全,有时好象这条语句被跳过一样

void Uart1_Sendstring(unsigned char *string)

{

       

      while(*string)

      Uart1_Putchar(*string++);

}

这个是子程序

我用轮训方法的时候这样用过的

哪位帮忙看一下

出0入0汤圆

发表于 2005-4-8 21:46:04 | 显示全部楼层
没人回啊

出0入0汤圆

发表于 2005-4-8 22:42:22 | 显示全部楼层
又试了一下

发送字符串的时候

每发送一个字符

加一段沿时,可以成功

不知道为什么

出0入0汤圆

 楼主| 发表于 2005-4-9 00:23:16 | 显示全部楼层
在你的程序中,多处的函数调用可能有问题,主要是形参和实参的对应。如函数定义为char(字符型),而调用时为*char(字符指针型)。

出0入0汤圆

发表于 2005-4-9 14:36:09 | 显示全部楼层
Uart1_Putchar(*string++);

void Uart1_Putchar(unsigned char data) ;

这样调用应该是可以的啊



还有这个程序单步运行的话是可以的

出0入0汤圆

 楼主| 发表于 2005-4-9 15:37:34 | 显示全部楼层
考虑while(*string)?

出0入0汤圆

发表于 2005-4-9 19:13:17 | 显示全部楼层
while(*string)

当*string不为\0

继续啊

有问题吗?

出0入0汤圆

发表于 2005-4-9 19:42:44 | 显示全部楼层
另外有个问题

我在icc里用printf

stdio.h中已经定义了putchar和getchar

我又从新定义了

发生冲突怎么办

谢谢马老师

出0入0汤圆

发表于 2005-4-9 19:44:52 | 显示全部楼层
最新问题

我刚刚试了

puts和printf()

和我用void Uart1_Sendstring(unsigned char *string)

不加延时的情况一样啊



例如

我puts("abcdefghjk");

第一次输出ab

第2次输出ka

底3次输出ja

          h

          f

          d

。。。。。。
-----此内容被wowo于2005-04-09,20:06:01编辑过

出0入0汤圆

 楼主| 发表于 2005-4-10 12:11:47 | 显示全部楼层
这是在CVAVR中HELP里关于字符串使用的说明,请参考。



String constants must be enclosed in double quotation marks. E.g. "Hello world".

If you place a string between quotes as a function parameter, this string will automatically be considered as constant and will be placed in FLASH memory.

Example:



/ * this function displays a string located in SRAM */

void display_ram(char *s) {



/* .......  */



}



/ * this function displays a string located in FLASH */



void display_flash(char flash *s) {



/* .......  */



}



void main(void) {

/* this will not work !!! */

/* because the function addresses the string as */

/* it is located in SRAM, but the string "Hello world" */

/* is constant and is placed in FLASH */

display_ram("Hello world");



/* this will work !!! */

/* the function addresses the string as it is located in FLASH */

display_flash("Hello world");

}

出0入0汤圆

发表于 2005-4-11 09:01:39 | 显示全部楼层
谢谢马老师

可能就是字符串在flash 和在ram的问题

出0入0汤圆

发表于 2008-6-25 04:05:25 | 显示全部楼层
学习了

出0入0汤圆

发表于 2008-7-14 18:39:00 | 显示全部楼层
长经验了

出0入0汤圆

发表于 2008-7-15 10:41:26 | 显示全部楼层
学习了

出0入0汤圆

发表于 2009-3-3 01:11:58 | 显示全部楼层
谢老师;受精了!

出0入0汤圆

发表于 2009-3-3 08:17:42 | 显示全部楼层
学习

出0入0汤圆

发表于 2009-4-6 13:23:22 | 显示全部楼层
好帖,收藏了,谢谢马老师!

出0入0汤圆

发表于 2009-4-8 20:53:34 | 显示全部楼层
马老师,关于通讯远距使用电流环,如您第5楼所图示。光耦选型需通断速率高的例如6N37,TLP117、512、4N25/26等均通断速率不够快。发射用的三极管同样也要通断速率高的开关管,C945、9014、8050等常用的均经测试能够使用。其中C945和9014的波形最好,但距离和总线负荷重时会有击穿的现象,8050在距离远的状况下,波形会有畸变,但勉强可以通讯。
测试环境为PC to N×MCU(N大于350),PC端使用MAX232电平转换为0-5V,PC端最远端距离400米。通讯波特率9600。

出0入0汤圆

发表于 2009-6-16 21:45:18 | 显示全部楼层
mark 要认真研读

出0入0汤圆

发表于 2009-6-16 22:37:44 | 显示全部楼层
超好的基础知识

出0入0汤圆

发表于 2009-6-17 11:17:28 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-7-21 12:06:38 | 显示全部楼层
这个程序要是加入错误处理就更好了.我上一个程序.加入错误处理的.这样在主程序轮询的时候只要检测错误情况就可以进行的数据收发,不需要while的等待.并且是标准c些的,移植性比较好.

编译环境IAR4.20A


uart.h文件

#ifndef _UART_H_
#define        _UART_H_

#define        UART_TX_BUFF_SIZE        50
#define        UART_RX_BUFF_SIZE        20


#define        UART_FLAG_START_TX        BIT0


typedef        enum
{
        UART_ERR_TX_FULL        = 1,
        UART_ERR_RX_EMPTY        = 2,
        UART_ERR_NO_ERROR        = 3,
}UART_ERROR;


extern        void        InitUart();
extern        UART_ERROR        UartPutByte(pu8 pbyte);
extern        UART_ERROR        UartGetByte(pu8 pbyte);
extern        UART_ERROR        UartPutStream(pu8 pstream, u8 size);


#endif



uart.c文件

#include "uart.h"


static        u8        tx_buff[UART_TX_BUFF_SIZE];
static        u8        tx_in;
static        u8        tx_out;
static        u8        tx_cnt;


static        u8        rx_buff[UART_RX_BUFF_SIZE];
static        u8        rx_in;
static        u8        rx_out;
static        u8        rx_cnt;

static        u8        flag;



#pragma        vector=USART_TXC_vect
__interrupt        void        UartTxIntHandle()
{
        if(tx_cnt>0)
        {
                tx_cnt--;
                UDR        = tx_buff[tx_out++];
                if(tx_out==UART_TX_BUFF_SIZE)
                {
                        tx_out        = 0;
                }
        }
        else
        {
                flag        &= ~(1<<UART_FLAG_START_TX);
        }
}


#pragma        vector=USART_RXC_vect
__interrupt        void        UartRxIntHandle()
{
        u8        tmp;

        tmp        = UDR;

        if(rx_cnt<=UART_RX_BUFF_SIZE)
        {
                rx_cnt++;
                rx_buff[rx_in++]        = tmp;
                if(rx_in==UART_RX_BUFF_SIZE)
                {
                        rx_in        = 0;
                }
        }
}

void        InitUart()
{
        tx_in        = 0;
        tx_out        = 0;
        tx_cnt        = 0;

        rx_in        = 0;
        rx_out        = 0;
        rx_cnt        = 0;

        flag        = 0x00;

        UCSRB        |= (1<<RXCIE)|(1<<TXCIE)|(1<<RXEN)|(1<<TXEN);
        UBRRH        = 0;
        UBRRL        = 7;
}


UART_ERROR        UartPutByte(pu8 pbyte)
{
        CPU_REG_DEF;

        ENTER_CRITICAL();
        if(tx_cnt<=UART_TX_BUFF_SIZE)
        {
                if(flag&(1<<UART_FLAG_START_TX))
                {
                        tx_cnt++;
                        tx_buff[tx_in++]        = *pbyte;
                        if(tx_in==UART_TX_BUFF_SIZE)
                        {
                                tx_in        = 0;
                        }
                }
                else
                {
                        flag        |= 1<<UART_FLAG_START_TX;
                        UDR                = *pbyte;
                }
                EXIT_CRITICAL();

                return UART_ERR_NO_ERROR;
        }
        EXIT_CRITICAL();

        return UART_ERR_TX_FULL;
}


UART_ERROR        UartGetByte(pu8 pbyte)
{
        CPU_REG_DEF;

        ENTER_CRITICAL();
        if(rx_cnt>0)
        {
                rx_cnt--;
                *pbyte        = rx_buff[rx_out];
                EXIT_CRITICAL();

                if(++rx_out==UART_RX_BUFF_SIZE)
                {
                        rx_out        = 0;
                }

                return UART_ERR_NO_ERROR;
        }
        EXIT_CRITICAL();

        return UART_ERR_RX_EMPTY;
}

UART_ERROR        UartPutStream(pu8 pstream, u8 size)
{
        pu8        ptmp;

        if(size==0)
        {
                ptmp        = pstream;

                while(*ptmp++);

                size        = ptmp-pstream-1;
        }

        if(size>UART_TX_BUFF_SIZE-tx_cnt)
        {
                return UART_ERR_TX_FULL;
        }

        while(size--)
        {
                UartPutByte(pstream++);
        }

        return UART_ERR_NO_ERROR;
}


程序里需要几个类型及宏定义在以下的cpu.h中定义.

#ifndef _CPU_H_
#define        _CPU_H_

#define        CPU_FREQ                7372800L



// 无符号类型定义
typedef        unsigned char        u8;
typedef        unsigned int        u16;
typedef        unsigned long        u32;

// 只读无符号类型定义
typedef        unsigned char const   uc8;
typedef        unsigned int const            uc16;
typedef        unsigned long const    uc32;

// 无符号指针类型定义
typedef        unsigned char*        pu8;
typedef        unsigned int*        pu16;
typedef        unsigned long*        pu32;

// 不可变无符号类型定义
typedef        volatile unsigned char        vu8;
typedef        volatile unsigned int        vu16;
typedef        volatile unsigned long        vu32;

// 不可变只读无符号类型定义
typedef        volatile unsigned char const        vuc8;
typedef        volatile unsigned int const                vuc16;
typedef        volatile unsigned long const        vuc32;

// 有符号类型定义
typedef        signed char                s8;
typedef        signed int                s16;
typedef        signed long                s32;

// 只读有符号类型定义
typedef        signed char const        sc8;
typedef        signed int const        sc16;
typedef        signed long const        sc32;

// 有符号指针类型定义
typedef        signed char*        ps8;
typedef        signed int*        ps16;
typedef        signed long*        ps32;

// 不可变有符号类型定义
typedef        volatile signed char        vs8;
typedef        volatile signed int        vs16;
typedef        volatile signed long        vs32;

// 不可变只读有符号类型定义
typedef        volatile signed char const        vsc8;
typedef        volatile signed int const        vsc16;
typedef        volatile signed long const        vsc32;

// 浮点类型定义
typedef        float                fp32;

// 布尔类型定义
//typedef         unsigned char        bool;

// cpu寄存器类型定义
typedef        unsigned char        c_reg;

// 标志位定义
typedef unsigned char        FLAG;


typedef        enum
{
        FALSE        = 0,
        TRUE        = !FALSE
}bool;


#define        BIT0        0
#define        BIT1        1
#define        BIT2        2
#define        BIT3        3
#define        BIT4        4
#define        BIT5        5
#define        BIT6        6
#define        BIT7        7

#define        FLAG0        0x01
#define        FLAG1        0x02
#define        FLAG2        0x04
#define        FLAG3        0x08
#define        FLAG4        0x10
#define        FLAG5        0x20
#define        FLAG6        0x40
#define        FLAG7        0x80


#define        setb(value, offset)        value|=1<<(offset)
#define        clrb(value, offset)        value&=~(1<<(offset))
#define        cplb(value, offset)        value^=1<<(offset)
#define        getb(value, offset)        (value&(1<<(offset)))

#define        MSB8        0x80
#define        LSB8        0x01
#define        MSB16        0x8000
#define        LSB16        0x0001
#define        MSB32        0x80000000
#define        LSB32        0x00000001





#define        CPU_REG_DEF        c_reg cpu_reg

#define        ENTER_CRITICAL()        cpu_reg=SREG;        \
                        __enable_interrupt()

#define        EXIT_CRITICAL()        SREG=cpu_reg



#endif




调用示例:

u8    tmp;

if(UartGetByte(&tmp)==UART_ERR_NO_ERROR)
{
      缓冲区中有数据进行协议或其他处理......
}
else
{
      缓冲区中没有数据执行错误代码,或者else可以省略,只处理有数据的情况.
}

发送示例:
发送一般都发送数据流,所以字节发送一般不需要.把发送字节函数开放出来其实没有必要.
u8 tmpbuff[N];
...
if(UartPutStream(tmpbuff, 2)==UART_ERR_NO_ERROR)
{
    发送成功,进行成功处理
}
else
{
    发送失败,可以重新发送或是其他处理
}

2.发送字符串
if(UartPutStream("hello", 0)==UART_ERR_NO_ERROR)
{
    发送成功,进行成功处理
}
else
{
    发送失败,可以重新发送或是其他处理
}
将参数2设置为0函数认为发送字符串.


说明:发送和接收的变量都可以使用结构体来完成.如:
typedef struct
{
    pu8 buff;
    u8  in;
    u8  out;
    u8  cnt;
    u8  flag;
}UART_STRUCT;


static        u8        tx_buff[UART_TX_BUFF_SIZE];
static        u8        rx_buff[UART_RX_BUFF_SIZE];
static     UART_STRUCT  TxStruct;
static     UART_STRUCT  RxStruct;

void InitUart()
{
    ....
    TxStruct.buff = tx_buff;
    TxStruct.in   = 0;
    TxStruct.out  = 0;
    TxStruct.cnt  = 0;
    TxStruct.flag = 0x00;

    RxStruct.buff = rx_buff;
    ....
}

或者in,out都用指针来做.

之所以没使用结构体或者使用指针来做是因为,为了提高底层函数的执行速度,以及节约占用SRAM的空间.

由于8位mcu的代码空间和sram比较小,所以做成分立的变量不进行结构封装,可以省出一些flash及其sram空间留给更需要的地方.并且底层函数只留出几个接口(UartGetByte(),UartPutStream())在使用的时候不需要考虑内部的架构.可以更好的偏向面向对象编程思想.

2.接收缓冲区满后没有进行溢出处理.想加的朋友自己考虑.其实很简单,加一个标志位即可完成.

出0入0汤圆

发表于 2009-7-21 19:43:22 | 显示全部楼层
好,学习了

出0入0汤圆

发表于 2009-8-3 16:01:48 | 显示全部楼层
收藏,学习了。

出0入0汤圆

发表于 2009-8-14 10:45:17 | 显示全部楼层
有个纯C语言的问题,关于位左移,右移的疑惑,若把变量a左移1位,书上都是a<<1,但程序中的“UCSRB |= (1<<RXCIE)|(1<<TXCIE)|(1<<RXEN)|(1<<TXEN); ”把寄存器的某位放于后面,怎么解释啊,百思不得其解啊,谢谢了,高手勿笑!

出0入0汤圆

发表于 2009-8-14 10:56:28 | 显示全部楼层
http://www avrvi com/start/avr_yiwei.html       AVR与虚拟仪器的网站

终于找到答案了,有同样疑惑的,可到那看看,去学习了!!为什么把名字屏蔽了,郁闷啊

出0入0汤圆

发表于 2009-8-14 10:56:53 | 显示全部楼层
长经验了

出0入0汤圆

发表于 2009-8-14 11:20:28 | 显示全部楼层
谢谢了!

出0入0汤圆

发表于 2009-8-30 14:06:52 | 显示全部楼层
mark  mark  mark

出0入0汤圆

发表于 2009-11-8 09:37:47 | 显示全部楼层
MARK

出0入0汤圆

发表于 2009-11-10 19:07:18 | 显示全部楼层

出0入0汤圆

发表于 2010-1-4 23:08:48 | 显示全部楼层
xuexi

出0入0汤圆

发表于 2010-1-15 11:16:17 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-3-20 21:29:58 | 显示全部楼层
UCSRB |= (1<<RXCIE)|(1<<TXCIE)|(1<<RXEN)|(1<<TXEN); 回复【33楼】springvirus
有个纯C语言的问题,关于位左移,右移的疑惑,若把变量a左移1位,书上都是a&lt;&lt;1,但程序中的“UCSRB |= (1&lt;&lt;RXCIE)|(1&lt;&lt;TXCIE)|(1&lt;&lt;RXEN)|(1&lt;&lt;TXEN); ”把寄存器的某位放于后面,怎么解释啊,百思不得其解啊,谢谢了,高手勿笑!
-----------------------------------------------------------------------

1<<a a=2.意思是将00000001向左移2位则是000000100,再结合|位符,那么这个位就一定为1。这样你理解吗

出10入10汤圆

发表于 2010-3-20 21:39:49 | 显示全部楼层
学习一下.谢谢!

出235入235汤圆

发表于 2010-3-21 12:00:15 | 显示全部楼层
mark!

出0入0汤圆

发表于 2010-3-21 12:54:00 | 显示全部楼层
先顶再学!

出0入0汤圆

发表于 2010-3-22 16:09:25 | 显示全部楼层
mark 学习

出0入0汤圆

发表于 2010-3-22 16:15:26 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-4-22 17:23:15 | 显示全部楼层
顶。

出0入0汤圆

发表于 2010-6-9 02:23:42 | 显示全部楼层
好贴,学习了。

出0入0汤圆

发表于 2010-6-9 20:07:34 | 显示全部楼层
记号

出0入0汤圆

发表于 2010-6-24 01:02:37 | 显示全部楼层
很好用!

出0入0汤圆

发表于 2010-6-24 01:23:38 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-6-29 09:45:27 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-6-29 19:45:46 | 显示全部楼层
长经验了,虽然最后一个程序没看懂!

出0入0汤圆

发表于 2010-8-13 16:27:06 | 显示全部楼层
好好学习一下!

出0入0汤圆

发表于 2010-12-4 00:34:28 | 显示全部楼层
谢谢马老师! 认真研读

出0入0汤圆

发表于 2011-3-20 16:26:10 | 显示全部楼层
收藏,学习了

出0入0汤圆

发表于 2011-3-20 17:29:10 | 显示全部楼层
mark to not win, me

出0入0汤圆

发表于 2011-3-20 22:15:33 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-4-6 22:03:31 | 显示全部楼层
要学习

出0入0汤圆

发表于 2011-4-6 22:07:40 | 显示全部楼层
mark .

出0入0汤圆

发表于 2011-4-13 20:37:06 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-4-13 21:42:54 | 显示全部楼层
串口用队列不错

出0入0汤圆

发表于 2011-8-25 10:45:14 | 显示全部楼层
记号!

出0入0汤圆

发表于 2011-8-25 23:19:44 | 显示全部楼层
正在学习中.. 好帖!

出0入0汤圆

发表于 2011-11-12 00:18:49 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-11-15 18:31:42 | 显示全部楼层
学习了
头像被屏蔽

出0入0汤圆

发表于 2011-12-16 16:48:05 | 显示全部楼层
回复【1楼】machao
-----------------------------------------------------------------------

马老师您好:你书得14章串口通讯,我看了,现在疑惑了,我没有学这个上位机编程,现在是学VB还是VC++,哪一个更实用,易于入手,就是学完了就可以直接应用!谢谢

出0入0汤圆

发表于 2012-6-1 11:18:24 | 显示全部楼层
好好学习一下

出0入0汤圆

发表于 2012-6-16 12:10:23 | 显示全部楼层
不错不错啊

出0入0汤圆

发表于 2012-6-30 10:29:20 | 显示全部楼层
machao 发表于 2004-12-27 21:11
此段是《M128》书中的一个小节的摘选,现将图贴上,供参考。

电源没有隔离

出0入0汤圆

发表于 2012-6-30 13:14:32 | 显示全部楼层
好好学习一下

出0入0汤圆

 楼主| 发表于 2012-7-1 22:18:49 | 显示全部楼层
wjjcyy 发表于 2012-6-30 10:29
电源没有隔离

图中2个电源,互相不干涉,怎么叫没有隔离?你仔细分析过吗?

出0入0汤圆

发表于 2012-7-2 10:35:40 | 显示全部楼层
machao 发表于 2012-7-1 22:18
图中2个电源,互相不干涉,怎么叫没有隔离?你仔细分析过吗?

......仔细分析了。。。全图要打开的。。。。。。我当时用的是两个光耦。。

出0入0汤圆

 楼主| 发表于 2012-7-2 14:07:12 | 显示全部楼层
wjjcyy 发表于 2012-7-2 10:35
......仔细分析了。。。全图要打开的。。。。。。我当时用的是两个光耦。。 ...

图中是用了两个光耦的,有问题么?到底电源隔离了没有?
这里只有大家知道我,没人知道你是谁,所以没有必要吞吞吐吐吧?

出0入0汤圆

发表于 2012-7-2 22:57:29 | 显示全部楼层
machao 发表于 2012-7-2 14:07
图中是用了两个光耦的,有问题么?到底电源隔离了没有?
这里只有大家知道我,没人知道你是谁,所以没有 ...

隔离了。。。

出0入0汤圆

发表于 2012-7-18 18:03:42 | 显示全部楼层
马老师,这种USART  低层驱动+中间层软件的方法能否应用于51系列的单片机。

出0入0汤圆

发表于 2012-7-18 18:22:42 | 显示全部楼层
最近想移植到51系统中,但始终不能通信。
程序如下:
void send_data(uchar tx_data)
{
TB8 = 0;
if(voice_flag)
{
   TB8 = 1;
   voice_flag=0;
}
  while(tx_count==TX_BUFFER_SIZE);
  ES=0;
  if(!TI)                                       // 1
  {
    tx_buffer[tx_wr_index]=tx_data;
    if(++tx_wr_index==TX_BUFFER_SIZE)  tx_wr_index=0;
    ++tx_count;
  }
  else
    SB UF = tx_data;
  ES=1;
}

void send_frame_data()
{
   send_data(BEGIN_DATA);
       send_data(work_mode);
       send_data(graph_mode);
       send_data(play_num);
       send_data(color_data);
       send_data(END_DATA);
}

void Time0() interrupt 1  using 1
{
   TH0=0x00;
  TL0=0x00;
  if(slave_rec_flag)
    {
       work_mode = slave_rx_buffer[1];
       graph_mode = slave_rx_buffer[2];
       play_num = slave_rx_buffer[3];
       color_data = slave_rx_buffer[4];
       slave_rec_flag = 0;
    }
}

void Serial(void) interrupt 4 using 3
{
uchar data_buf;
  if(RI)
{
   RI=0;
  dmx_flag=0;
  data_buf = SBUF;
  if(!slave_rec_flag)
    {
        if(RB8)
        {
            mic_led = 1;
            voice_speed=4;
            voice_flag1=1;
            voice_screen = 0;
        }
        slave_rx_buffer[rx_index]=data_buf;
        rx_index++;
        switch(rx_index)
        {
          case 1:
               if(data_buf != BEGIN_DATA)
                 rx_index=0;
               break;
          case 6:
               rx_index =0;
               if(data_buf == END_DATA)
                  slave_rec_flag=1;
               break;
        }
     }
   }
  if(TI)
  {
    TI=0;
    if(tx_count)
    {
     TB8=0;
     if(voice_flag)
     {
       voice_flag=0;
       TB8 = 1;
     }
     tx_count--;
     SBUF = tx_buffer[tx_rd_index];
    if(++tx_rd_index==TX_BUFFER_SIZE)
       tx_rd_index = 0;
   }
}

void main()
{
   while(1)
  {
     send_frame_data();
  }
}
void init(void)
{
uchar iii;
IE=0X93;                        //EA=1;ES=1;ET0=1;ET1=1;EX0=1;开全局中断,定时器T0,T1中断 串行中断,外部中断
TMOD=0X11;                      //定时器T0,T1工作方式都为1  16位 产生32.78MS的定时
TH0=0X00;
TL0=0X00;
TR0=1;
IT0=1;                      //INT0边沿触发
////////////////////////////////定时器1中断使能定时20US///////////////////////
/*ET1=1;
TR1=1;
TH1=0xFF;
TL1=0x37;*/
//----------------------------波特率设置----------------------------//
////////////////////40MHZ使用定时器2溢出中断产生2500KBIT波特率 9位数据/////////
PCON = 0x00;
T2CON = 0x30;
SCON = 0xD0;
TR2 = 1;
RCAP2H = 0xFF;
//RCAP2L = 0xFB;
RCAP2L = 0xFD;
IP = 0x10;
play_num=0;
SetColor(OFF_COLOR);
while(iii<40)
{
   delay_time(30000);
   iii++;
}
zoom_xy=30;
play_time=0;
slave_rec_flag=0;
//TI=1;
}

请问一个马达老师,在51系列串口通信机制里,在标示1位置怎样判断串口处于忙状态,有数据未发送完成。谢谢!

出0入0汤圆

发表于 2012-7-20 15:36:43 | 显示全部楼层
没有人指导我这个学生菜鸟?顶一下。

出75入4汤圆

发表于 2020-7-20 15:40:07 | 显示全部楼层
这个要认真学习下。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-24 12:52

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表