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

📄 main.c

📁 单片机c语言程序设计100例--基于PIC+PROTEUS
💻 C
字号:
//-----------------------------------------------------------------
//  名称: MODBUS总线通信仿真(从机程序)
//-----------------------------------------------------------------
//  说明: 本例运行时,从机接收主机命令,然后将A/D转换值通过485回发给主机显示.
//
//-----------------------------------------------------------------
#define _XTAL_FREQ 4000000UL
#define INT8U  unsigned char
#define INT16U unsigned int
#define INT32U unsigned long
#include <pic.h>
#include <stdio.h>
//配置字,注意禁用MCLR,因为RA3/MCLR/VPP引脚用于485从机地址次高位输入
__CONFIG(MCLRDIS & WDTDIS & INTIO);
volatile INT8U recv_Data[4];           //串口接收数据缓冲区(4字节)
volatile INT8U recv_idx = 0;           //串口接收数据缓冲区索引
volatile INT8U sl_Addr;                //485从机地址           
INT16U CRC;                            //16位CRC校验结果
//-----------------------------------------------------------------
#define  LED_Ptr   RC0                 //本机收/发指示灯
#define  RDE_485   RC3                 //RS485通信控制端
#define  ADC_REQ   65                  //要求从机返回A/D值的自定义命令码(范围65~72)
//19200波特率每字符时间为: 1/19200*(1+8+2) ≈  572us
//帧  间: 3.5个字符时间为: 572 * (3.5 + 1) ≈ 2574us
//字节间: 1.5个字符时间为: 572 * (1.5 + 1) ≈ 1430us
#define FRAME_SPAN  2574                  //相临帧之间的间隔时间
#define BYTE_SPAN   1430                  //帧内字节之间的间隔时间
bit     b, F_T1, T_BYTE, T_FRAME, Recv_OK;//相关标识位
INT8U   AD_Result[2];                     //2字节的A/D转换结果
//-----------------------------------------------------------------
// 宏定义: 发送一字节并等待发送结束
//-----------------------------------------------------------------
#define Send_Byte(x)                  \
{                                     \
    LED_Ptr = ~LED_Ptr;               \
    RDE_485 = 1;                      \
    TXREG = x; while (TRMT == 0);     \
    __delay_us(9);                    \
}

//-----------------------------------------------------------------
// 宏定义: 设置TIMER1的定时初值并设相关标志位
//-----------------------------------------------------------------
#define Set_TIMER1(x)                 \
{                                     \
   TMR1H = (65536 - x) >> 8;          \
   TMR1L = (65536 - x) & 0x0F;        \
   TMR1IF = T_BYTE = T_FRAME = 0;     \
   F_T1 = (x == FRAME_SPAN) ? 1 : 0;  \
   if (F_T1) recv_idx = 0;            \
}

//-----------------------------------------------------------------
// CRC16校验函数 (基于该函数可得出512字节的校验码表,改用查表法进行校验)
// 多项式: X ^ 16 + X ^ 15 + X ^ 2 + 1, 去高位逆序表示:0xA001
//-----------------------------------------------------------------
void CRC16(INT8U d)
{



}

//-----------------------------------------------------------------
// 模/数转换函数
//-----------------------------------------------------------------
void AD_Convert()
{
    __delay_us(10);
    GODONE = 1;                          //启动转换
    while (ADIF == 0);                   //等待转换结束
    ADIF = 0;                            //AD中断标志清0
    AD_Result[1] = ADRESH;               //保存A/D结果的高字节
    AD_Result[0] = ADRESL;               //保存A/D结果的低字节
}

//-----------------------------------------------------------------
// 串口初始化
//-----------------------------------------------------------------
void Serial_port_init()
{
    SYNC = 0;                            //选择异步通信模式
    BRGH = 1;                            //选择高速波特率发生模式
    TXEN = 1;                            //允许发送数据
    SPBRG = _XTAL_FREQ/16/19200 - 1;     //设置波特率为19200  
    SPEN = 1;                            //串行通信端口打开
    CREN = 1;                            //使能连续接收串行数据
}

//-----------------------------------------------------------------
// 外设初始化(定时器,485等)
//-----------------------------------------------------------------
void Per_Initialize()
{   
    GIE = 0;                             //禁止中断
    TRISC = 0B00110000;                  //设置串行通讯端口及LED指示灯端口方向
    LED_Ptr = 0;                         //收/发指示灯关闭
    RDE_485 = 1;                         //禁止从机485接收
    PORTA = 0x00;                        //初始化PORTA
    WPUA = 0xFF;                         //使能PORTA相应引脚的弱上拉
    TRISA = 0xFF;                        //PORTA所有引脚全部设为输入
    ANSEL = 0x01;                        //PORTA的RA0设为模拟输入
    sl_Addr = (PORTA >> 1) & 0x0F;       //获取所设置的本机地址
    Serial_port_init();                  //串口初始化  
    OSCCON = 0B01101000;                 //使用默认的4M内部振荡器
    ADCON0 = 0B10000001;                 //选择0通道,A/D结果右对齐,使能A/D
    ADCON1 = 0B01010000;                 //AD转换时钟选择位ADCS<2:0>=101,选择FOSC/16   
    RCIE = 1;                            //允许串口接收中断
    TMR1IE = 1;                          //允许TMR1溢出中断
    PEIE = 1;                            //允许外设中断(TMR1,USART均为PIC外设)
    GIE = 1;                             //开中断
    RDE_485 = 0;                         //允许从机485接收(禁止发送)    
    TMR1ON = 1;                          //启动TIMER1(默认为1:1分频)
    __delay_us(2);
    Set_TIMER1(FRAME_SPAN);              //用TIMER1控制相邻帧之间的时间间隔
    Recv_OK = 0;                         //初始时三个标识全部设为0 
}

//-----------------------------------------------------------------
// 主程序
//-----------------------------------------------------------------    
void main()
{
    Per_Initialize();                    //外设初始化
    while(1)
    {
       //如果本从机已经接收到完整的四个字节数据
       if (Recv_OK)
       {



       }
    }
}

//-----------------------------------------------------------------
// 485从机中断服务程序
//----------------------------------------------------------------- 
void interrupt ISR()
{  
    INT8U R;
    //----------------------TIMER1定时器溢出中断--------------------
    if (TMR1IF)
    {   TMR1IF = 0;
        //F_T1: 标识TIMER1定时器当前用于实现帧间隔时间定时还时字节间隔时间定时
        //F_T1 = 0时,将帧间隔时间(3.5字符)到达设为假,字节间隔时间到达设为真
        //F_T1 = 1时,将帧间隔时间(3.5字符)到达设为真,字节间隔时间到达设为假


     
    }
    //-------------------------串口接收中断-------------------------
    if (RCIF)
    {   
        LED_Ptr = ~LED_Ptr;                     //从机收/发指示灯闪烁
        R = RCREG;                              //从串口(来自485)读取一字节
        RCIF = 0;                               //清标志位(此行可省略)
        Recv_OK = 0;                            //先暂时设接收成功标志为假
        //---------------------------------------------------------
        //如果当前要接收的是第0字节
        if (recv_idx == 0) 
        {  

        }
        //---------------------------------------------------------
        //否则要接收的是第0字节(即地址字节)之后的数据
        else 
        {   



        }
        //---------------------------------------------------------
    }
}

⌨️ 快捷键说明

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