PIC单片机入门实战(八):PIC16F1824/PIC12F1822,MODBUS功能码01/03/05/06应用

作为一名使用PIC单片机近三十年的工程师,我最早从PIC16F87X系列起步,后来为了应对小型化、低成本的项目需求,又陆续使用了6脚、8脚、14脚等PIC单片机。

在众多PIC单片机中,PIC16F1824PIC12F1822是我认为最适合入门的两个型号。

它们不仅易于上手,而且引脚兼容性极佳——PIC16F18241-4脚和11-14脚与PIC12F1822完全重合,这意味着在设计初期,你可以灵活替换,无需重新绘制电路板。

今天就以PIC16F1824开发板为例,介绍一下内置的MODBUS-RTU功能码01/03/05/06的具体应用

一、MODBUS-RTU协议

MODBUS-RTU最常用的功能码主要就是01/03/05/06,掌握了这4个其他功能码也就基本会用了

具体内容可参考我的另一篇文章:MODBUS-RTU协议真不难!用四个核心功能码(01/03/05/06)带你玩转工业通讯(附代码)

二、EEPROM读写例程:

PIC单片机输入输出寄存器的控制字设置与其他如MSP430系列单片机不一样,一般其他系列单片机某一I/O口输出置1、输入置0PIC则相反,输出置0、输入置1,你可以把0想成O(对应OUT),把1想成I(对应IN),这样会不容易设置错。

以下是MODBUS-RTU读写示例(16M主频),具体配置及程序如下:

#include<pic16f1824.h>

__CONFIG(0x083c); //配置字1寄存器

__CONFIG(0x1412); //配置字2寄存器

#define LED1     RA0

#define LED2     RA1

#define LED3     RC0

#define LED4     RC1

#define RELAY1     RC5

#define RELAY2     RC4

#define KEY1     RA3

#define KEY2     RC3

#define KEY3     RC2

unsigned char txi,rc_tnt,time_tnt,rxtime_flag,key_value;

unsigned int crc_hl, half_sec,time_2s,result_ad

unsigned char trans[20];//发送缓存

unsigned char reciv[20];//接收缓存

unsigned char data_buf[20];//数据缓存

unsigned char eeprom_e2[12]; //EEPROM数据缓冲区

void delay(unsigned int v) //延时函数

{

  while(v--){};

}

void uart_send(unsigned char dat)//串口发送数据

{

       TXEN=1;

       TXREG=dat;

       while(!TRMT);

}

// EEPROM读函数:读取指定地址的数据

char eeprom_r(char addr)

   {

   char temp;

   GIE=0; //关闭全局中断,避免读写过程被打断

   EEADR=addr; //写入要读取的EEPROM地址(0-255

   RD=1; //触发读操作

   while(RD); //等待读操作完成

   temp=EEDATA; //取出读到的数据

   GIE=1; //重新开启全局中断

   return(temp);

   }

// EEPROM写函数:向指定地址写入数据

void eeprom_w(char addr,char data)

   {

   GIE=0; //关闭全局中断,避免读写过程被打断

   WREN=1; //使能写操作

   EEADR=addr; //写入目标地址

   EEDATA=data;//写入要存储的数据

//PIC单片机写EEPROM必须的两步

   EECON2=0x55;

   EECON2=0xaa;

   WR=1; //触发写操作

   while(WR); //等待写操作完成

   WREN=0; //禁用写操作

   GIE=1; //重新开启全局中断

   }

void calccrc(uchar crcbuf) //MODBUS-RTU校验码算法子程序

{

       uchar i;

       crc_hl=crc_hl^crcbuf;

       for(i=0;i<8;i++)

       {

              uchar TT;

              TT=crc_hl&1;

              crc_hl=crc_hl>>1;

              crc_hl=crc_hl&0x7fff;

              if(TT==1)

              crc_hl=crc_hl^0xa001;

              crc_hl=crc_hl&0xffff;

       }

}

// 中断服务程序
void interrupt isr(void) {
    if (TMR1IF) {               // 判断是否为Timer1中断
        TMR1L = 0xBF;           // 重载定时初值(低位)
        TMR1H = 0xF9;           // 重载定时初值(高位)
        half_sec++;

        if (half_sec > 1249) {  // 累计0.5
            time_2s++;          // 计数器+1
            half_sec = 0;
        }
       if(rxtime_flag==1) //串口数据接收计时
               {
                               time_tnt+=1;
                               }
        TMR1IF = 0;             // 清除中断标志
}
if(RCIF)//串口中断接收数据
            {
                           
                           reciv[rc_tnt]=RCREG;
                           rc_tnt+=1;
                           time_tnt=0;
                           rxtime_flag=1;

                           RCIF=0;
            }
}

void main() {

unsigned char  i,j;

    // 初始化I/O
    TRISA = 0x1A;
TRISC = 0x0C;
WPUA3=1;
    PORTA = 0x00; 
    PORTC = 0x03; //RC0RC1输出高电平,初始上电LED3LED4为熄灭状态,RC5RC4控制的两个继电器RELAY1RELAY2为释放状态。

    // 外设功能选择
    APFCON0 = 0x84;  // 设置RA0TXRA1RX

    // 模拟输入关闭(除指定引脚)
    ANSELA = 0x10;
    ANSELC = 0x00;
    
PIE1 = 0x01;     
 INTCON = 0x40;   // 使能外围中断
OSCCON = 0x78;   // 内部时钟16MHz
WDTCON=0x1c;  //看门狗16

    //以下为Timer1配置
    T1CON = 0x60;    // 预分频器设为1:4Timer1关闭
    TMR1IF = 0;
    TMR1L = 0xBF;    // 定时初值
    TMR1H = 0xF9;
    TMR1IE = 1;      // 使能Timer1中断
T1CON |= 0x01;   // 启动Timer1

//串口设置
    SYNC=0;                                     //异步模式
BRGH=1;                                        //加速
BRG16=0;
               SPEN=1;                                              //允许串行通讯
               CREN=1;
               TXEN=1;
               RCIE=1;
               SPBRG=103;     //9600波特率
//             SPBRG=8;     //115200波特率
               PEIE=1;
    GIE = 1;         // 全局中断使能
for(i=0;i<4;i++)
               {
                               eeprom_e2[i]=eeprom_r(i);
               } 
               if(eeprom_e2[0]==0x66)
               {
                               dres_485=eeprom_e2[1]; //读保存的地址
               } 
               if(eeprom_e2[2]==0x77)
               {
                               SPBRG=eeprom_e2[3]; //读保存的波特率
               }
while(1) {
asm("clrwdt");

if(time_2s>3) //2秒采集一次18B20温度,一次AD转换
                           {
                                          adc_ra4(0x0d);
                                          data_buf[3]=result_ad>>8;
                                          data_buf[4]=result_ad&0xff;
                                          GetTemp();
                                          wendu_18b20=(unsigned int)(Temper*10); //小数点后移一位
                                          data_buf[5]=wendu_18b20>>8;
                                          data_buf[6]=wendu_18b20&0xff;

                                          time_2s=0;
                           }
if((rxtime_flag==1)&&(time_tnt>19))
                           {
                                          crc_hl=0xffff;
                                          for(txi=0;txi<(rc_tnt-2);txi++)
                                          {
                                                         calccrc(reciv[txi]);
                                          }
                                          if(rc_tnt==8)
                                          {
                                                         
                                                         if(((reciv[0]==dres_485)||(reciv[0]==0))&&(reciv[1]==0x03)&&(reciv[6]==(crc_hl&0xff))&&(reciv[7]==(crc_hl>>8)))
                                                         {
                                                                         if(reciv[0]==0) //读取本机地址
                                                                         {
                                                                                        trans[0]=dres_485;
                                                                                        trans[1]=reciv[1];
                                                                                        trans[2]=2;
                                                                                        trans[3]=0;           
                                                                                        trans[4]=dres_485;
                                                                                        crc_hl=0xffff;
                                                                                        for(txi=0;txi<5;txi++)
                                                                                        {
                                                                                                       calccrc(trans[txi]);
                                                                                        }
                                                                                        trans[5]=crc_hl&0xff;
                                                                                        trans[6]=crc_hl>>8;
                                                                                        RA2=1;
                                                                                        for(txi=0;txi<7;txi++)
                                                                                        {
                                                                                                       uart_send(trans[txi]);
                                                                                        }
                                                                                        RA2=0;
                                                                         }
                                                                         else //读数据
                                                                         {
                                                                                        data_buf[0]=dres_485;
                                                                                        data_buf[1]=3;
                                                                                        data_buf[2]=4;
                                                                         
                                                                                        
                                                                                        
                                                                                        crc_hl=0xffff;
                                                                                        for(txi=0;txi<7;txi++)
                                                                                        {
                                                                                                       calccrc(data_buf[txi]);
                                                                                        }
                                                                                        data_buf[7]=crc_hl&0xff;
                                                                                        data_buf[8]=crc_hl>>8;
                                                                                        RA2=1;
                                                                                        for(txi=0;txi<9;txi++)
                                                                                        {
                                                                                                       uart_send(data_buf[txi]);//发送A/D采集与18B20温度数据
                                                                                        } 
                                                                                        RA2=0;                                                               }
                                                         }
                                                        if((reciv[0]==dres_485)&&(reciv[1]==0x06)&&(reciv[6]==(crc_hl&0xff))&&(reciv[7]==(crc_hl>>8)))
                                                         {
                                                                         if((reciv[2]==0)&&(reciv[3]==100))  //本机地址设置
                                                                         {
                                                                                        eeprom_e2[0]=0x66;
                                                                                        eeprom_e2[1]=dres_485=reciv[5];
                                                                                        for(i=0;i<2;i++)
                                                                                     {
                                                                                                   eeprom_w(i,eeprom_e2[i]);
                                                                                        }
                                                                         }
                                                                         if((reciv[2]==0)&&(reciv[3]==100))  //本机波特率设置
                                                                         {
                                                                                        eeprom_e2[2]=0x77;
                                                                                        eeprom_e2[3]=SPBRG=reciv[5];
                                                                                        for(i=2;i<4;i++)
                                                                                     {
                                                                                                   eeprom_w(i,eeprom_e2[i]);
                                                                                        }
                                                                         }
                                                                         RA2=1;
                                                                         for(txi=0;txi<8;txi++)
                                                                         {
                                                                                        uart_send(reciv[txi]);
                                                                         }
                                                                         RA2=0;
                                                         }
                                                        if((reciv[0]==dres_485)&&(reciv[1]==0x01)&&(reciv[6]==(crc_hl&0xff))&&(reciv[7]==(crc_hl>>8)))
                                                         {
                                                                         trans[0]=dres_485;
                                                                         trans[1]=1;
                                                                         trans[2]=1;
                                                                                                       
                                                                         trans[3]=flag_relay_on;
                                                                         crc_hl=0xffff;
                                                                         for(txi=0;txi<4;txi++)
                                                                         {
                                                                                        calccrc(trans[txi]);
                                                                         }
                                                                         trans[4]=crc_hl&0xff;
                                                                         trans[5]=crc_hl>>8;
                                                                         RA2=1;
                                                                         for(txi=0;txi<6;txi++)
                                                                         {
                                                                                        uart_send(trans[txi]);
                                                                         }
                                                                         RA2=0;
                                                         }
                                                        if((reciv[0]==dres_485)&&(reciv[1]==0x05)&&(reciv[6]==(crc_hl&0xff))&&(reciv[7]==(crc_hl>>8)))
                                                         {
                                                                         if(reciv[3]==0)
                                                                         {
                                                                                        if(reciv[4]==0xff) //启动继电器1
                                                                                        {
                                                                                                       flag_relay_on&=0xfe;
                                                                                                       flag_relay_on|=1;
                                                                                                       RELAY1=1;
                                                                                                       
                                                                                        }
                                                                                        if(reciv[4]==0) //停止
                                                                                        {
                                                                                                       flag_relay_on&=0xfe;
                                                                                                       RELAY1=0;
                                                                                                                                                                    
                                                                                        }
                                                                         }
                                                                         if(reciv[3]==1)
                                                                         {
                                                                                        if(reciv[4]==0xff) //启动继电器2
                                                                                        {
                                                                                                       flag_relay_on&=0xfd;
                                                                                                       flag_relay_on|=2;
                                                                                                       RELAY2=1;
                                                                                                       
                                                                                        }
                                                                                        if(reciv[4]==0) //停止
                                                                                        {
                                                                                                       flag_relay_on&=0xfd;
                                                                                                       RELAY2=0;
                                                                                                                                                                    
                                                                                        }
                                                                         }
                                                                         RA2=1;
                                                                         for(txi=0;txi<8;txi++)
                                                                         {
                                                                                        uart_send(reciv[txi]);
                                                                         }
                                                                         RA2=0;                 
                                                         }
                                          
                                          }
                                          rc_tnt=0;
                                          time_tnt=0;
                                          rxtime_flag=0;
                           }
        //可加入其他任务
    }
}


三、本系列文章规划

本文是《PIC单片机入门实战》系列的第一篇,后续将逐步展开以下内容,带你从零构建一个完整的嵌入式控制系统:

序号

主题

内容概要

1

振荡器与Timer1

时钟配置与定时中断

2

I/O输出控制

驱动2路继电器与4LED

3

I/O按键输入

3路按键扫描与电平变化中断

4

UART通信

RS232/RS485数据交互

5

A/D转换应用

1路电位器电压读取

6

温度传感器采集

1DS18B20输入

7

EEPROM存储

数据存储与读取

8

MODBUS-RTU集成

功能码01/03/05/06数据读写

PIC单片机入门实战》这8片文章内容来源于我自己画的电路原理图及程序,有对PIC单片机感兴趣想学习的朋友可以关注我,免费赠送资料(包括原理图、数据手册、各种例程等)。

有需要这款开发板的朋友也可以关注联系我。

后续干货不断,咱们一起在单片机的世界里,共同进步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值