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

📄 uart.c

📁 状态机写的单片机串口程序 不错 值得看一下
💻 C
字号:
#define __UART_H

#include "..\\MAIN\MAIN.H"
#include "..\\ASCHEX\ASCHEX.H"
#include "..\\UART\UART.H"

UARTVALUE       idata Uart0;

#define IDLE_WAIT_START     0x00        // 空闲态,等待起始位
#define START_WAIT_END      0X01        // 收到起始位,等待结束位
#define END_WAIT_SEND       0X02        // 收到结束位,等待发送
#define SEND_START_BYTE     0x03        // 发送起始位
#define SEND_WAIT_IDLE      0X04        // 开始发送,等待发送完毕

// 接收中断
void    Uart0RxdInterrupt(void)    interrupt 4
{
    uint8 idata temp;
    if(RI)
    {
        RI = 0;
        temp = SBUF;
        switch( Uart0.Status )
        {
            case IDLE_WAIT_START:
                if( temp == ':' )   // 收到起始位
                {
                    Uart0.Status = START_WAIT_END;  //状态切换
                    Uart0.Point = 0;
                }
                break;
            case START_WAIT_END:
                if( temp == ':' )   //如果再次收到起始位,则重新开始接收
                {
                    Uart0.Point = 0;
                }
                else
                {
                    if( (temp == 0x0a) && (Uart0.Pool[Uart0.Point-1]==0x0d) )
                    {//判断结束位
                        Uart0.Pool[Uart0.Point] = temp;
                        Uart0.RxdLen = Uart0.Point;
                        Uart0.Status = END_WAIT_SEND;  //状态切换
                    }
                    else
                    {
                        Uart0.Pool[Uart0.Point] = temp;
                        Uart0.Point++;
                        Uart0.Point %= 100;
                    }
                }
                break;
            case END_WAIT_SEND:
            case SEND_WAIT_IDLE:
            default:
                break;
        }
    }
}

// 发送中断
void    Uart0TxdInterrupt(void)    interrupt 13
{
    if( TI )
    {
        TI = 0;
        if( Uart0.TxdLen > 0 )
        {
            SBUF = Uart0.Pool[Uart0.Point];
            Uart0.TxdLen--;
            Uart0.Point++;
            Uart0.Point %= 100;
        }
        else
        {  //发送完毕
            Uart0.Status = IDLE_WAIT_START;   //状态切换
        }
    }
}

void    UARTInit(void)
{
    uint8 idata i;
    SCON   = 0x50;          // 使能接收,串口模式1
    SSTAT  = 0x20;          // 选择独立的RX/TX中断,禁止双缓冲 
    BRGR0  = 0xF0;          // 
    BRGR1  = 0x02;          // 独立的波特率发生器的值,7.373M晶振时波特率为 9600
    BRGCON = 0x03;          // 使能独立的波特率发生器
    ESR    = 1;             // 开启接收中断
    EST    = 1;             // 禁止发送中断
    RS485EN = 0;            // 关闭 RS485 发送使能

    Uart0.Addr = GetAddr();  // 获取本机地址,允许带电更改本机地址,由硬件拨码开关决定

    for( i = 0;i < 100;i++ )
    {
        Uart0.Pool[i] = 0;
    }

    Uart0.TxdLen = 0;
    Uart0.RxdLen = 0; 
    Uart0.Point = 0;
    Uart0.Status = IDLE_WAIT_START;
}

void    UartProcess(void)
{
    uint8  idata LrcData;
    uint8  idata Addr,FunCode;
    uint16 idata RegAddr,RegNum;
    uint16 idata ybuf[15];
    uint8  idata i,temp;

    switch( Uart0.Status )
    {
        case END_WAIT_SEND:   //处于解码状态

            asc_to_hex(Uart0.RxdLen - 2);    //解包
            LrcData = Uart0.Pool[(Uart0.RxdLen - 2) / 2 - 1];
    
            if( LrcData == LRC8((Uart0.RxdLen - 2) / 2 - 1) )
            {
                LrcData = 1;
            }
            else
            {
                LrcData = 0;
            }
    
            //DEBUG用
//            LrcData = 1;
            //DEBUG用

            if( LrcData == 1 )  //如果校验正确才继续 解码
            {
                Addr = Uart0.Pool[0];       //获得地址
                if( Addr == Uart0.Addr )    //地址判断,正确才应答处理
                {
                    FunCode = Uart0.Pool[1];    //获得功能码
                    RegAddr = ((Uart0.Pool[2] - (uint8)Uart0.Addr) << 8) + Uart0.Pool[3];
                                                                // 计算 寄存器首地址
                    RegNum = (Uart0.Pool[4]<<8) + Uart0.Pool[5];
                                                                // 计算 寄存器数量
                    switch( FunCode )
                    {
                        case 0x03:  //  读保持寄存器值。暂时不写 0x03命令对应的功能。
                            
                            break;
                        case 0x04:  //  读取输入寄存器的值
                            if( (RegNum>0) && (RegNum<13) && ( (RegAddr + RegNum)<13 ) && (RegAddr < 12) )
                            {
                                for( i = 0;i < RegNum;i++ )        // 
                                {
                                    ybuf[i] = Reg.wreg[RegAddr+i]; // 
                                }
                                MsReadAnswer(FunCode,i,ybuf);      // 读 输入寄存器 回答
                            }
                            else
                            {
                                ExcepAnswer(FunCode|0x80,0x03);             // 回答 读地址错误
                            }

                            break;
                        case 0x10:  //
                            if( (RegNum*2) == Uart0.Pool[6] )       // 写字节数和参数长度是否配套
                            {
                                if( (RegNum==1) && (RegAddr==0x0b ) )
                                {                                   // 判断寄存器首地址和寄存器长度是否正确
                                    temp = 7;
                                    for( i = 0;i < RegNum;i++ )
                                    {                               // 注意大端小端,51系列单片机是大端模式,高字节数据放低地址
                                        Reg.breg[ (RegAddr + i) * 2 + 0 ] = Uart0.Pool[temp++];
                                        Reg.breg[ (RegAddr + i) * 2 + 1 ] = Uart0.Pool[temp++];
                                    }
                                    Flag.DaOutNew = 1;
                                    MsWriteAnswer(FunCode,RegAddr,RegNum);
                                                                    // 回答写地址 命令
                                }
                                else
                                {                                   // 如果地址错误
                                    ExcepAnswer(0x90,0x03);         // 回答写地址错误
                                }
                            }
                            else
                            {
                                ExcepAnswer(0x90,0x02);             // 非法长度
                            }
                            break;
                        default:
                            ExcepAnswer(FunCode|0x80,0x01);        // 回答功能码错误
                            break;
                    }
                Uart0.Status = SEND_START_BYTE;         // 发送起始位状态
                }
                else                        //地址不对返回接收等待起始位状态
                {
                    Uart0.Status = IDLE_WAIT_START;
                }
            }
            else                //校验出错返回接收等地啊起始位状态
            {
                Uart0.Status = IDLE_WAIT_START;
            }
            break;
        case IDLE_WAIT_START:       //发送完毕等待开始位状态
                                //对RS485总线需要操作
            RS485EN = 0;

            //DEBUG
//            ChangeLed(0);
            //DEBUG

            break;
        case SEND_START_BYTE:   //发送起始位状态,对总线有操作

            RS485EN = 1;    //使能发送控制
            Uart0.Point = 0;
            TI = 0;
            SBUF = 0x3a;

            Uart0.Status = SEND_WAIT_IDLE;  //发送状态,等待发送结束

            break;
        case START_WAIT_END:
        case SEND_WAIT_IDLE:
            break;
        default:
            break;
    }
}

/****************************************************************************
* 
****************************************************************************/
uint8 LRC8(uint8 idata usDataLen)
{
    uint8 idata uchLRC = 0;                       //
    uint8 idata i;

    for( i = 0;i < usDataLen;i++ )
    {
        uchLRC += Uart0.Pool[i];                   //
    }

    return( (uint8)(-((char)uchLRC)) );           //
}

void MsReadAnswer(uint8 fun_code,uint16 reg_num,uint16 *data_buf)
{
    uint8 i,j;

    Uart0.Pool[0] = (uint8)(Uart0.Addr);                  //
    Uart0.Pool[1] = fun_code;                            //
    Uart0.Pool[2] = (uint8)(reg_num*2);                  //
   
    i = 3; 
    for(j=0;j<reg_num;j++)                              // 这里转换的时候注意芯片的大端模式和小端模式的区别
    {                                                   // 一般而言,51系列单片机是大端模式,飞利浦的ARM是小端模式
        Uart0.Pool[i] = (uint8)((*(data_buf+j)>>8)&0x00ff);
                                                        // 高字节数据放低地址
        i++;
        Uart0.Pool[i] = (uint8)(*(data_buf+j)&0x00ff);   // 低字节数据放高地址
        i++;
    }

    Uart0.Pool[i] = LRC8(i);                            //
    hex_to_asc(i+1);                                    //
}

void ExcepAnswer(uint8 FunCode,uint8 ExcepCode)
{
    Uart0.Pool[0] = (uint8)(Uart0.Addr);                   //
    Uart0.Pool[1] = FunCode;                              //
    Uart0.Pool[2] = ExcepCode;                            //
     
    Uart0.Pool[3] = LRC8(3);                            //
    hex_to_asc(4);                                       //
}

void MsWriteAnswer(uint8 fun_code,uint16 reg_addr,uint16 reg_num)
{  
    Uart0.Pool[0] = (uint8)(Uart0.Addr);                   //
    Uart0.Pool[1] = fun_code;                             //
    
    Uart0.Pool[2] = (uint8)(Uart0.Addr);                   //
    Uart0.Pool[3] = (uint8)( (reg_addr) & 0x00ff );       //
   
    Uart0.Pool[4] = (uint8)((reg_num>>8) & 0x00ff );      //
    Uart0.Pool[5] = (uint8)(reg_num & 0x00ff);            //

    Uart0.Pool[6] = LRC8(6);                            //
    hex_to_asc(7);                                       //
}

⌨️ 快捷键说明

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