⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 avr中的串口模拟.c

📁 基于LINUX操作系统下的各种详细配置(如FTP
💻 C
字号:
/*  
编译环境ICCAVR ,MCU:MEGA16@8MHZ 
模拟串口实现的方法: 
方法1、使用外部中断接收+函数发送/定时发送。这个方法适合高波特率通信。 
方法2、使用定时器定时查询。适合较低的波特率通信,容易实现全双工通信。 

以下是用方法2实现的,要非常注意的地方是: 
1、优先级控制宏,实现串口模拟定时器中断优先级最高, 
   防止定时漂移,实现100%正确率。 
2、定时器的精确定时,防止采样漂移。 
3、使用宏方便移植 

作者:busy 
邮件:ZBY8020@163.COM 
*/  

#include<iom16v.h> 
#include<macros.h> 

#define	U8	unsigned char 
#define	bool    unsigned char 
#define	U16	unsigned int 

/*++++++++++++++++++++++++++++++++++=宏定义=++++++++++++++++++++++++++++++++++++*/ 

/* 宏中断优先级的使用: 
   1、要保存你所使用到的所有中断的使能位,然后关闭它们,仅开模拟串口定时器中断。 
   恢复时,只需恢复所有中断的使能位即可。 
   2、进入其它除了模拟串口定时器中断外,先调用 IRQ_IP_OPEN() ;然后 
   是你在该中断处理的代码,退出该中断是调用 IRQ_IP_CLOSE() ;然后退出该中断。 
*/ 

// 中断优先级控制中要保持的变量 
volatile U8	saveUCSRB;	// 串口中断 
volatile U8	saveGICR;	// 外部中断 
volatile U8	saveTIMSK;	// 定时器中断 

// 中断优先级开启宏定义 
/* 在这儿屏蔽其它非高优先级中断 */
#define IRQ_IP_OPEN()	do {				\
			    saveGICR		= GICR;	\
			    saveTIMSK		= TIMSK;\
			    saveUCSRB		= UCSRB;\
			    GICR		= 0x00;	\	// 屏蔽INT0.2中断
			    TIMSK		= 0x40;	\	// 屏蔽TIMER0中断,只开TIMER2中断模拟串口
			    UCSRB	       &= 0x1F;	\	// 屏蔽UART0中断
			    SEI();
			} while (0)

// 中断优先级恢复宏定义 
#define IRQ_IP_CLOSE()	do {			\
			    CLI();		\	// 在这儿恢复其它非高优先级中断
			    GICR  = saveGICR;	\	// 恢复INT0.2中断
			    TIMSK = saveTIMSK;	\	// 恢复TIMER0.2中断
			    UCSRB = saveUCSRB;	\	// 恢复UART0接收、空中断
			} while (0) 

// 脚定义 
#define GET_VM232_RX()	(PIND	&   (1<<PD0))	// 端口D的PD0模拟接收 
#define SET_VM232_TX()	(PORTD	|=  (1<<PD1))	// 端口D的PD1发送 
#define CLR_VM232_TX()	(PORTD	&= ~(1<<PD1))	// 端口D的PD1发送 

#define GET_VM485_RX()	(PIND	&   (1<<PD2))	// 端口D的PD2模拟接收 
#define SET_VM485_TX()	(PORTD	|=  (1<<PD3))	// 端口D的PD3发送 
#define CLR_VM485_TX()	(PORTD	&= ~(1<<PD3))	// 端口D的PD3发送 


/*++++++++++++++++++++++=系统52US定时器,用于模拟2个串口=++++++++++++++++++++++++*/ 

// 模拟串口的状态码 
enum { 
    START, 
    SDATA, 
    STOP 
}; 

U8 inRS232	= 0;	// 模拟232的数据接收输入指针 
U8 outRS232	= 0;	// 模拟232的数据接收取出指针 
U8 vmRS232Buf[20];	// 模拟232的数据接收缓冲区 

U8 inRS485	= 0;	// 模拟485的数据接收输入指针 
U8 outRS485	= 0;	// 模拟485的数据接收取出指针 
U8 vmRS485Buf[20];	// 模拟485的数据接收缓冲区 

void vm_rs232_rx(U8 dataBit) // 模拟232接收函数 
{ 
    static U8 status	= START; 
    static U8 cnt	= 0; 
    static U8 number	= 0; 
    static U8 rData; 
    switch (status) 
    { 
    case START: 
	if (dataBit) { 
	    cnt = 0; 
	} 
	else { 
	    if (++cnt > 2) {                     
		cnt = 0; 
		number = 0; 
		status = SDATA; 
	    }
	}
	break; 
    case SDATA: 
	if(++cnt > 3) 
	{ 
	    cnt = 0; 
	    if(dataBit)			// 数据存放在最高位,每收到一个bit,右移一位
	    {				
		rData |= 0x80; 
	    } 
	    else 
	    { 
		rData &= 0x7F; 
	    } 
	     
	    if(++number < 8) 
	    { 
		rData >>= 1; 
	    } 
	    else 
	    { 
		number = 0; 
		status = STOP; 
	    } 
	} 
	break; 
    case STOP: 
	if(++cnt > 3) 
	{ 
	    cnt = 0; 
	    if(dataBit) 
	    { 
		vmRS232Buf[inRS232++] = rData; 
		if(inRS232 >= 20) // 环型缓冲区 
		{ 
		    inRS232 = 0; 
		} 
		// 在这模拟的接收中断,不太实用 
	    } 
	    status = START; 
	} 
	break; 
    default: 
	cnt = 0; 
	status = START; 
	break; 
    } 
} 

void vm_rs485_rx(U8 dataBit) // 模拟485接收函数 
{ 
    static U8 status = START; 
    static U8 cnt = 0; 
    static U8 number = 0; 
    static U8 rData; 
    switch(status) 
    { 
        case START: 
            if(dataBit) 
            { 
                cnt = 0; 
            } 
            else 
            { 
                if(++cnt > 2) 
                {                     
                    cnt = 0; 
                    number = 0; 
                    status = SDATA; 
                } 
            } 
            break; 
        case SDATA: 
            if(++cnt > 3) 
            { 
                cnt = 0; 
                if(dataBit) 
                { 
                    rData |= 0x80; 
                } 
                else 
                { 
                    rData &= 0x7F; 
                } 
                 
                if(++number < 8) 
                { 
                    rData >>= 1; 
                } 
                else 
                { 
                    number = 0; 
                    status = STOP; 
                } 
            } 
            break; 
        case STOP: 
            if(++cnt > 3) 
            { 
                cnt = 0; 
                if(dataBit) 
                { 
                    vmRS485Buf[inRS485++] = rData; 
                    if(inRS485 >= 20) // 环型缓冲区 
                    { 
                        inRS485 = 0; 
                    } 
                    // 在这模拟的接收中断,不太实用 
                } 
                status = START; 
            } 
            break; 
        default: 
            cnt = 0; 
            status = START; 
            break; 
    } 
} 

bool flgVmRs232tx = 0; // 模拟232是否有数据要发送的标志,1有数据要发送,完成清零 
U8 vmRS232SBUF; // 模拟232要发送的一字节缓冲 
bool flgVmRs485tx = 0; // 模拟485是否有数据要发送的标志,1有数据要发送,完成清零 
U8 vmRS485SBUF; // 模拟485要发送的一字节缓冲 

void vm_rs232_tx(void) // 模拟232发送函数 
{ 
    static U8 status = START; 
    static U8 cnt = 0; 
    static U8 number = 0; 
    if(flgVmRs232tx) 
    { 
        switch(status) 
        { 
            case START: 
                CLR_VM232_TX(); 
                if(++cnt > 3) 
                { 
                    cnt = 0; 
                    number = 0; 
                    status = SDATA; 
                } 
                break; 
            case SDATA: 
                if(vmRS232SBUF & 0x01) 
                { 
                    SET_VM232_TX(); 
                } 
                else 
                { 
                    CLR_VM232_TX(); 
                } 
                if(++cnt > 3) 
                { 
                    cnt = 0; 
                    if(++number < 8) 
                    { 
                        vmRS232SBUF >>= 1; 
                    } 
                    else 
                    { 
                        //number = 0; 
                        status = STOP; 
                    } 
                }                 
                break; 
            case STOP: 
                SET_VM232_TX(); 
                if(++cnt > 3) 
                { 
                    cnt = 0; 
                    status = START; 
                    flgVmRs232tx = 0; 
                    // 在这模拟的发送中断,不太实用 
                } 
                break; 
            default: 
                SET_VM232_TX(); 
                cnt = 0; 
                status = START; 
                flgVmRs232tx = 0; 
                break; 
        } 
    } 
} 

void vm_rs485_tx(void) // 模拟485发送函数 
{ 
    static U8 status = START; 
    static U8 cnt = 0; 
    static U8 number = 0; 
    if(flgVmRs485tx) 
    { 
        switch(status) 
        { 
            case START: 
                CLR_VM485_TX(); 
                if(++cnt > 3) 
                { 
                    cnt = 0; 
                    number = 0; 
                    status = SDATA; 
                } 
                break; 
            case SDATA: 
                if(vmRS485SBUF & 0x01) 
                { 
                    SET_VM485_TX(); 
                } 
                else 
                { 
                    CLR_VM485_TX(); 
                } 
                if(++cnt > 3) 
                { 
                    cnt = 0; 
                    if(++number < 8) 
                    { 
                        vmRS485SBUF >>= 1; 
                    } 
                    else 
                    { 
                        //number = 0; 
                        status = STOP; 
                    } 
                }                 
                break; 
            case STOP: 
                SET_VM485_TX(); 
                if(++cnt > 3) 
                { 
                    cnt = 0; 
                    status = START; 
                    flgVmRs485tx = 0; 
                    // 在这模拟的发送中断,不太实用 
                } 
                break; 
            default: 
                SET_VM485_TX(); 
                cnt = 0; 
                status = START; 
                flgVmRs485tx = 0; 
                break; 
        } 
    } 
} 

// 波特率都是2400BIT/S ,416US/BIT 
// 5中取3 ,每104US抽样(也可以3中取2) 
// 52US中断交替采样2个模拟串口 

//TIMER2 initialize - prescale:8 
// WGM: Normal 
// desired value: 52uSec 
// actual value: 52.000uSec (0.0%) 
void timer2_init(void) 
{ 
    TCCR2 = 0x00; //stop 
    ASSR  = 0x00; //set async mode 
    TCNT2 = 0xd3; //setup 
    OCR2  = 0x34; 
    TCCR2 = 0x02; //start 
} 

#pragma interrupt_handler timer2_ovf_isr:5 
void timer2_ovf_isr(void) // 最高优先级中断 
{ 
    static bool sw = 0; // 模拟串口交替使用定时器开关,实现104US采样 
    volatile U8 vmRS232, vmRS485; // 记录2模拟串口接收脚的高低电平 
     
    TCNT2 = 0xd3; //reload counter value 
     
    // 在这采样接收脚保证不漂移 
    vmRS232 = GET_VM232_RX(); 
    vmRS485 = GET_VM485_RX(); 
    // 在这增加串口接收检测 
     
    if(sw) // 乒乓操作 
    { 
        vm_rs232_tx(); 
        vm_rs232_rx(vmRS232);         
        // 在这增加串口 
        sw = 0; 
    } 
    else 
    { 
        vm_rs485_tx(); 
        vm_rs485_rx(vmRS485); 
        // 在这增加串口 
        sw = 1; 
    } 
} 

/*+++++++++++++++++++++++++++++++++= MAIN =++++++++++++++++++++++++++++++++++++++*/ 

void port_init(void) 
{ 
    PORTA = 0xFF; 
    DDRA  = 0x00; 
    PORTB = 0xFF; 
    DDRB  = 0x00; 
    PORTC = 0xFF; 
    DDRC  = 0x00; 
    PORTD = 0xFF; 
    DDRD  = 0x0A; // PD1 PD3 输出 
} 

void init_devices(void) 
{ 
    //stop errant interrupts until set up 
    CLI(); //disable all interrupts 
    port_init(); 
    timer2_init(); 
     
    MCUCR = 0x00; 
    GICR  = 0x00; 
    TIMSK = 0x40; //timer interrupt sources 
    SEI(); //re-enable interrupts 
    //all peripherals are now initialized 
} 

void main(void) 
{ 
    init_devices(); 
     
    // 收到什么就回发什么 
    while(1) 
    { 
        if(outRS232 != inRS232) 
        {             
            while(flgVmRs232tx); 
            vmRS232SBUF = vmRS232Buf[outRS232++]; 
            flgVmRs232tx = 1; 
            if(outRS232 >= 20) // 接收、发送都是环型缓冲区 
            { 
                outRS232 = 0; 
            } 
        } 
        if(outRS485 != inRS485) 
        {             
            while(flgVmRs485tx); 
            vmRS485SBUF = vmRS485Buf[outRS485++]; 
            flgVmRs485tx = 1; 
            if(outRS485 >= 20) // 接收、发送都是环型缓冲区 
            { 
                outRS485 = 0; 
            } 
        }     
    } 
} 

/*++++++++++++++++++++++++++++++++++=end=++++++++++++++++++++++++++++++++++++++++*/ 

/* 
   以上是我开始做的2个模拟串口测试全部程序,大家可以按照自己的需要修改使用。 
    
   而在我的工程中我改成了模拟接收完一帧(MAX 30BYTES)数据才请求处理。发送 
   则是用了一个50BYTES环型缓冲区实现随意的发送数据,使用起来非常方便。 
    
   以上调试中,请大家务必设置好定时器的准确定时。因为实现US级的定时,ICCAVR 
   的‘应用生成’设置不准!另外,单片机的工作频率尽可能高些!!! 
    
   以上只是一个实现的思想,不足之处请大家指点,我不用3中取2,只是想模拟100% 
   的正确率,在这请大家调试后有什么心得也写出来让大家分享!! 
*/ 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -