一个AVR USART(RS232)低层驱动+中间层软件示例
一个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;
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 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=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
popr26
outsreg,r26
popr31
popr30
popr27
popr26
#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;
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;
unsigned char tx_wr_index0,tx_rd_index0,tx_counter0;
// USART0 Transmitter interrupt service routine
#pragma savereg-
interrupt 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;
if (++tx_rd_index0 == TX_BUFFER_SIZE0) tx_rd_index0=0;
};
#asm
popr26
outsreg,r26
popr31
popr30
popr27
popr26
#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=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;
unsigned char rx_counter;
bit Uart_RecvFlag
// USART Receiver interrupt service routine
#pragma savereg-
interrupt 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 = 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
popr26
outsreg,r26
popr31
popr30
popr27
popr26
#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编辑过 如果在数据包中再增加“从机地址”的字节,下发呼叫从机回答。所有从机收到数据包后,其软件检查从机地址是否与自己的地址符合,如是呼叫自己,则回答,否则不于回答,便可实现多机通信。该方法不需要使用AVR的硬件多机通信功能识别,不需要第9位的地址/数据指示,实现也很方便。 楼上说的多机通讯方法是常用的多机通讯方法;给我感觉:各种现场总线协议多机通讯都采用这种方法; 重要的是保证通信的可靠和抗干扰,因此在硬件设计上采用电流环的连接方式,见图5.4。
armok 阿莫,能不能把图一并贴出,以供参考,谢谢 此段是《M128》书中的一个小节的摘选,现将图贴上,供参考。http://cache.amobbs.com/bbs_upload782111/files_1/armok0122123.jpg 感谢马潮老师提供的程序,已经应用在我的系统中了,还有你的《ATmege8》让我在短短一周就可以自己做程序了 我在ICC中重新定义了一下putchar函数
void putchar(char c)
{
while(TX_counter==TX_buffer_size);
CLI();
if(TX_counter||((UCSRA&DATA_REGISTER_EMPTY)==0))
{
TX_buffer=c;
if(++TX_wr_index==TX_buffer_size) TX_wr_index=0;
++TX_counter;
}
else
UDR=c;
SEI();
}
#include <stdio.h>
当我调用printf函数时,还会使用ICC库中自己的putchar函数吗? 今天上午来学习马老师的这个程序
发现了一个问题
我的程序为
//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;
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=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;
if((++rx_rd_index1==RX_BUFFER_SIZE1))
rx_rd_index1=0;
CLI();
--rx_counter1;
SEI();
return data;
}
char tx_buffer1;
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;
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=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++);
}
这个是子程序
我用轮训方法的时候这样用过的
哪位帮忙看一下 没人回啊 又试了一下
发送字符串的时候
每发送一个字符
加一段沿时,可以成功
不知道为什么 在你的程序中,多处的函数调用可能有问题,主要是形参和实参的对应。如函数定义为char(字符型),而调用时为*char(字符指针型)。 Uart1_Putchar(*string++);
void Uart1_Putchar(unsigned char data) ;
这样调用应该是可以的啊
还有这个程序单步运行的话是可以的 考虑while(*string)? while(*string)
当*string不为\0
继续啊
有问题吗? 另外有个问题
我在icc里用printf
stdio.h中已经定义了putchar和getchar
我又从新定义了
发生冲突怎么办
谢谢马老师 最新问题
我刚刚试了
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编辑过 这是在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");
} 谢谢马老师
可能就是字符串在flash 和在ram的问题 学习了 长经验了 学习了 谢老师;受精了! 学习 好帖,收藏了,谢谢马老师! 马老师,关于通讯远距使用电流环,如您第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。 mark 要认真研读 超好的基础知识 mark 这个程序要是加入错误处理就更好了.我上一个程序.加入错误处理的.这样在主程序轮询的时候只要检测错误情况就可以进行的数据收发,不需要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;
static u8 tx_in;
static u8 tx_out;
static u8 tx_cnt;
static u8 rx_buff;
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;
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 = 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 = *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;
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;
...
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;
u8in;
u8out;
u8cnt;
u8flag;
}UART_STRUCT;
static u8 tx_buff;
static u8 rx_buff;
static UART_STRUCTTxStruct;
static UART_STRUCTRxStruct;
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.接收缓冲区满后没有进行溢出处理.想加的朋友自己考虑.其实很简单,加一个标志位即可完成. 好,学习了 收藏,学习了。 有个纯C语言的问题,关于位左移,右移的疑惑,若把变量a左移1位,书上都是a<<1,但程序中的“UCSRB |= (1<<RXCIE)|(1<<TXCIE)|(1<<RXEN)|(1<<TXEN); ”把寄存器的某位放于后面,怎么解释啊,百思不得其解啊,谢谢了,高手勿笑! http://www avrvi com/start/avr_yiwei.html AVR与虚拟仪器的网站
终于找到答案了,有同样疑惑的,可到那看看,去学习了!!为什么把名字屏蔽了,郁闷啊 长经验了 谢谢了! markmarkmark MARK 好 xuexi./emotion/em078.gif mark UCSRB |= (1<<RXCIE)|(1<<TXCIE)|(1<<RXEN)|(1<<TXEN); 回复【33楼】springvirus
有个纯C语言的问题,关于位左移,右移的疑惑,若把变量a左移1位,书上都是a<<1,但程序中的“UCSRB |= (1<<RXCIE)|(1<<TXCIE)|(1<<RXEN)|(1<<TXEN); ”把寄存器的某位放于后面,怎么解释啊,百思不得其解啊,谢谢了,高手勿笑!
-----------------------------------------------------------------------
1<<a a=2.意思是将00000001向左移2位则是000000100,再结合|位符,那么这个位就一定为1。这样你理解吗 学习一下.谢谢! mark! 先顶再学! mark 学习 mark 顶。 好贴,学习了。 记号 很好用! mark mark 长经验了,虽然最后一个程序没看懂! 好好学习一下! 谢谢马老师! 认真研读 收藏,学习了 mark to not win, me mark 要学习 mark . mark 串口用队列不错 记号! 正在学习中.. 好帖! mark 学习了 回复【1楼】machao
-----------------------------------------------------------------------
马老师您好:你书得14章串口通讯,我看了,现在疑惑了,我没有学这个上位机编程,现在是学VB还是VC++,哪一个更实用,易于入手,就是学完了就可以直接应用!谢谢 好好学习一下 不错不错啊
machao 发表于 2004-12-27 21:11 static/image/common/back.gif
此段是《M128》书中的一个小节的摘选,现将图贴上,供参考。
电源没有隔离 好好学习一下 wjjcyy 发表于 2012-6-30 10:29 static/image/common/back.gif
电源没有隔离
图中2个电源,互相不干涉,怎么叫没有隔离?你仔细分析过吗? machao 发表于 2012-7-1 22:18 static/image/common/back.gif
图中2个电源,互相不干涉,怎么叫没有隔离?你仔细分析过吗?
......仔细分析了。。。全图要打开的。。。。。。我当时用的是两个光耦。。 wjjcyy 发表于 2012-7-2 10:35 static/image/common/back.gif
......仔细分析了。。。全图要打开的。。。。。。我当时用的是两个光耦。。 ...
图中是用了两个光耦的,有问题么?到底电源隔离了没有?
这里只有大家知道我,没人知道你是谁,所以没有必要吞吞吐吐吧? machao 发表于 2012-7-2 14:07 static/image/common/back.gif
图中是用了两个光耦的,有问题么?到底电源隔离了没有?
这里只有大家知道我,没人知道你是谁,所以没有 ...
隔离了。。。 马老师,这种USART低层驱动+中间层软件的方法能否应用于51系列的单片机。 最近想移植到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_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 1using 1
{
TH0=0x00;
TL0=0x00;
if(slave_rec_flag)
{
work_mode = slave_rx_buffer;
graph_mode = slave_rx_buffer;
play_num = slave_rx_buffer;
color_data = slave_rx_buffer;
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=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;
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工作方式都为116位 产生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位置怎样判断串口处于忙状态,有数据未发送完成。谢谢! 没有人指导我这个学生菜鸟?顶一下。 这个要认真学习下。
页:
[1]