钰平电子 发表于 2023-6-29 13:41:08

基于STC12C5A60S2单片机设计的四路温控采集系统MODBUS通讯协议资源分享

本帖最后由 钰平电子 于 2023-6-29 13:43 编辑













#include <STC12C5A60S2.H>
#include <intrins.h>
/****************************模拟量开始*********************************************/
#define ADC_POWER   0x80            //ADC 电源控制位
#define ADC_FLAG    0x10            //ADC 转换结束标志位
#define ADC_START   0x08            //ADC 开始转换控制位
#define ADC_SPEEDLL 0x00            //420 个时钟周期转换一次
#define ADC_SPEEDL0x20            //280 个时钟周期转换一次
#define ADC_SPEEDH0x40            //140 个时钟周期转换一次
#define ADC_SPEEDHH 0x60            //70 个时钟周期转换一次
/******************************模拟量结束*******************************************/
#define uchar unsigned char//宏定义无符号字符型
#define uint unsigned int//宏定义无符号整型
void InitADC();
uint GetADCResult(uchar px);
void Selectsort(uint A[],uchar n) ;//冒泡函数
uint volt;                                 //最终数据保存变量
uint Rs=0,Rs1=0;
long ad;
uint Temp,ad1;
uint adnum=4;
uintxdata adcj;         //保存测量的数据做冒泡处理
uchar j = 0;                                 //冒泡程序选择的最多数据个数
uintxdata TempH_Tab[] = {
3251,3094,2945,2804,2670,2543,2422,2307,2199,2095,1997,1904,1816,1732,1652,1577,1505,1437,1372,1310,//0~19
1252,1196,1143,1093,1045,1000,956 ,915 ,876 ,839 ,803 ,770 ,737 ,707 ,678 ,650 ,623 ,598 ,574 ,551 ,//20~39
529 ,508 ,488 ,469 ,450 ,433 ,416 ,400 ,385 ,370 ,357 ,343 ,330 ,318 ,306 ,295 ,284 ,274 ,264 ,255 ,//40~59
246 ,237 ,228 ,220 ,213 ,205 ,198 ,191 ,185 ,178 ,172 ,167 ,161 ,165 ,150 ,145 ,141 ,136 ,131 ,127 ,//60~79
123 ,119 ,115 ,111 ,108 ,104 ,101 ,98,95,92,89,86,83,81,78,76,73,71,69,67,//80~99
65,63,61,59,58,56,54,53,51,50,48,47,46,44,43,42,41,40,39,38,//100~119
37,36//120 121
};
uintGet_Temp(uint dat);// 获得温度数据
unsigned char T0RH = 0;//T0重载值的高字节
unsigned char T0RL = 0;//T0重载值的低字节
bit flagFrame = 0;//帧接收完成标志,即接收到一帧新数据
bit flagTxd = 0;    //单字节发送完成标志,用来替代TXD中断标志位
unsigned char cntRxd = 0;   //接收字节计数器
unsigned char pdata bufRxd;//接收字节缓冲区
void UartDriver();//串口驱动函数,监测数据帧的接收,调度功能函数,需在主循环中调用
void UartAction(unsigned char *buf, unsigned char len);//在接收到的数据帧后添加换车换行符后发回
void UartWrite(unsigned char *buf, unsigned char len);//串口数据写入,即串口发送函数,buf-待发送数据的指针,len-指定的发送长度
unsigned char UartRead(unsigned char *buf, unsigned char len);//串口数据读取函数,buf-接收指针,len-指定的读取长度,返回值-实际读到的长度
void UartRxMonitor(unsigned char ms);//串口接收监控,由空闲时间判定帧结束,需在定时中断中调用,ms-定时间隔
void ConfigUART(unsigned int baud);//串口配置函数,baud-通信波特率
void ConfigTimer0(unsigned int ms);//配置并启动T0,ms-T0定时时间
uchar Byte_Read(uint add);            //读一字节,调用前需打开IAP 功能
void Byte_Program(uint add, uint ch);//字节编程,调用前需打开IAP 功能
void Sector_Erase(uint add);            //擦除扇区
void IAP_Disable();                     //关闭IAP 功能
void PowerStorage();
void Delay();
#define ENABLE_ISP 0x81 //系统工作时钟<20MHz 时,对IAP_CONTR 寄存器设置此值
sbit out01=P2^1;
sbit out02=P2^2;
sbit out03=P2^3;
sbit out04=P2^4;
uint upperlimit={0xff,0xff,0xff,0xff};//上限
uint returndifference={0xff,0xff,0xff,0xff};//回差值
void UartAction(unsigned char *buf, unsigned char len){//在接收到的数据帧后添加换车换行符后发回      
                if((len==7)&&(buf==0xAA)&&(buf==0xBB)){
                        if((buf==0x06)&&(buf==0x00)&&(buf==0x00)&&(buf==0x00)&&(buf==0x00)){//温度查询
                              buf=0xcc;
                              buf=0x06;
                              buf=Temp;//第一路温度
                              buf=Temp;//第二路温度
                              buf=Temp;//第三路温度
                              buf=Temp;//第四路温度
                              buf=0xdd;
                              len=7;               
                              UartWrite(buf, len);      
                        }
                        if((buf==0x07)&&(buf==0x00)&&(buf==0x00)&&(buf==0x00)&&(buf==0x00)){//上限查询
                              buf=0xcc;
                              buf=0x07;
                              buf=upperlimit;//第一路温度
                              buf=upperlimit;//第二路温度
                              buf=upperlimit;//第三路温度
                              buf=upperlimit;//第四路温度
                              buf=0xdd;
                              len=7;               
                              UartWrite(buf, len);      
                        }
                        if((buf==0x08)&&(buf==0x00)&&(buf==0x00)&&(buf==0x00)&&(buf==0x00)){//回差值查询
                              buf=0xcc;
                              buf=0x08;
                              buf=returndifference;//第一路温度
                              buf=returndifference;//第二路温度
                              buf=returndifference;//第三路温度
                              buf=returndifference;//第四路温度
                              buf=0xdd;
                              len=7;               
                              UartWrite(buf, len);      
                        }
                        if((buf==0x09)&&(buf==0x00)&&(buf==0x00)){//上限设置
                              if((buf>=0x01)&&(buf<=0x04)){
                                        if(buf<0x79){
                                                if(buf==0x01){upperlimit=buf;buf=upperlimit;buf=0x01;}
                                 else if(buf==0x02){upperlimit=buf;buf=upperlimit;buf=0x02;}
                                 else if(buf==0x03){upperlimit=buf;buf=upperlimit;buf=0x03;}
                                 else if(buf==0x04){upperlimit=buf;buf=upperlimit;buf=0x04;}
                                        }else{//数据超过范围
                                                buf=0xff;
                                                if(buf==0x01){buf=0x01;upperlimit=0xff;}
                                 else if(buf==0x02){buf=0x02;upperlimit=0xff;}
                                 else if(buf==0x03){buf=0x03;upperlimit=0xff;}
                                 else if(buf==0x04){buf=0x04;upperlimit=0xff;}
                                        }
                                        PowerStorage();                        
                              }else{//没有找到地址
                                        buf=0xff;
                                        buf=0xff;
                              }
                                        buf=0xcc;
                                        buf=0x09;
                                        buf=0;
                                        buf=0;
                                        buf=0xdd;
                                        len=7;
                              UartWrite(buf, len);
                        }
                        if((buf==0x0A)&&(buf==0x00)&&(buf==0x00)){//回差设置
                              if((buf>=0x01)&&(buf<=0x04)){//在指定的工作路数
                                        if(buf<0x79){//回差值最大不能大于120度
                                                if(buf==0x01){//工作在第一路
                                                      //上限-回差值>=0并且上限是有效的
                                                      if(((upperlimit-buf)<0x78)&&(upperlimit!=0xff)){
                                                                returndifference=buf;
                                                                buf=returndifference;
                                                                buf=0x01;
                                                                buf=0x00;
                                                      }else{
                                                                buf=0xFF;
                                                                buf=0xff;
                                                                buf=0x01;                                                      
                                                      }
                                                }else if(buf==0x02){if(((upperlimit-buf)<0x78)&&(upperlimit!=0xff)){
                                                                returndifference=buf;buf=returndifference;buf=0x02;buf=0x00;}else{buf=0xFF;buf=0xff;buf=0x02;}
                                                }else if(buf==0x03){if(((upperlimit-buf)<0x78)&&(upperlimit!=0xff)){
                                                                returndifference=buf;buf=returndifference;buf=0x03;buf=0x00;}else{buf=0xFF;buf=0xff;buf=0x03;}
                                                }else if(buf==0x04){if(((upperlimit-buf)<0x78)&&(upperlimit!=0xff)){
                                                                returndifference=buf;buf=returndifference;buf=0x04;buf=0x00;}else{buf=0xFF;buf=0xff;buf=0x04;}
                                                }
                                                PowerStorage();
                                        }else{//数据超过范围
                                                buf=0xff;
                                                buf=0xFF;
                                                if(buf==0x01){buf=0x01;}
                                 else if(buf==0x02){buf=0x02;}
                                 else if(buf==0x03){buf=0x03;}
                                 else if(buf==0x04){buf=0x04;}
                                        }      
                              }else{//没有找到地址
                                        buf=0xff;
                                        buf=0xff;
                              }
                                        buf=0xcc;
                                        buf=0x0A;
                                       
                                        buf=0;
                                        buf=0xdd;
                                        len=7;
                              UartWrite(buf, len);
                        }
                }               
}

void main(){
      P0M1=0x00;               
      P0M0=0xff;
      InitADC(); //AD转换初始化
    EA = 1;            //开总中断
    ConfigTimer0(1);   //配置T0定时1ms
    ConfigUART(9600);//配置波特率为9600
      upperlimit=Byte_Read(0x01);//掉电存储用到
      upperlimit=Byte_Read(0x02);//掉电存储用到
      upperlimit=Byte_Read(0x03);//掉电存储用到
      upperlimit=Byte_Read(0x04);//掉电存储用到
      returndifference=Byte_Read(0x05);//掉电存储用到
      returndifference=Byte_Read(0x06);//掉电存储用到
      returndifference=Byte_Read(0x07);//掉电存储用到
      returndifference=Byte_Read(0x08);//掉电存储用到
      while(1){
                ad =GetADCResult(adnum);               //选择是用p几口做测量   把测量的数据保存在ad1中
                adcj=ad;                         //把测量的结果在放到adcj中排序输出
                if(j>128){         //取128个值做处理
                        j=0;
                        Selectsort(adcj,128);          //冒泡函数取最稳定的值
                        ad =adcj;
                        ad1 = (100*ad)/(255-ad);//转换求测量值
                        if(ad1>=36&&ad1<=3251){//1210
                              Temp= Get_Temp(ad1)/10; // 温度转换 ;               
                        }else{
                              Temp=255;
                        }
                        adnum++;//数码管的位
                        if(adnum==8){//当位到7则置位0
                        adnum=4;
                        }
                }
                UartDriver();//调用串口动
      }
}
void PowerStorage(){
      Sector_Erase(0);            
      Byte_Program(0x01,upperlimit);
      Byte_Program(0x02,upperlimit);
      Byte_Program(0x03,upperlimit);
      Byte_Program(0x04,upperlimit);
      Byte_Program(0x05,returndifference);
      Byte_Program(0x06,returndifference);
      Byte_Program(0x07,returndifference);
      Byte_Program(0x08,returndifference);
}
void InterruptTimer0() interrupt 1{//T0中断服务函数,执行串口接收监控
    TH0 = T0RH;//重新加载重载值
    TL0 = T0RL;
    UartRxMonitor(1);//串口接收监控
}
void InterruptUART() interrupt 4{//串口中断服务函数
    if (RI) { //接收到新字节
      RI = 0;//清零接收中断标志位
      if (cntRxd < sizeof(bufRxd)) {//接收缓冲区尚未用完时,保存接收字节,并递增计数器
            bufRxd = SBUF;      // cntRxd++这个很重要,一开始 cntRxd < sizeof(bufRxd)当进入函数的次数增加,cntRxd慢慢变大,当传入的数据不满的时候就      用时间检测,判断是否是传输完成
      }
    }
    if (TI) { //字节发送完毕
      TI = 0;   //清零发送中断标志位
      flagTxd = 1;//设置字节发送完成标志
    }
}
void ConfigTimer0(unsigned int ms){//配置并启动T0,ms-T0定时时间
    unsigned long tmp;//临时变量   
    tmp = 11059200 / 12;      //定时器计数频率
    tmp = (tmp * ms) / 1000;//计算所需的计数值
    tmp = 65536 - tmp;      //计算定时器重载值
    tmp = tmp + 33;         //补偿中断响应延时造成的误差
    T0RH = (unsigned char)(tmp>>8);//定时器重载值拆分为高低字节
    T0RL = (unsigned char)tmp;
    TMOD &= 0xF0;   //清零T0的控制位
    TMOD |= 0x01;   //配置T0为模式1
    TH0 = T0RH;   //加载T0重载值
    TL0 = T0RL;
    ET0 = 1;      //使能T0中断
    TR0 = 1;      //启动T0
}
void ConfigUART(unsigned int baud){//串口配置函数,baud-通信波特率
    SCON= 0x50;//配置串口为模式1
    TMOD &= 0x0F;//清零T1的控制位
    TMOD |= 0x20;//配置T1为模式2
    TH1 = 256 - (11059200/12/32)/baud;//计算T1重载值
    TL1 = TH1;   //初值等于重载值
    ET1 = 0;       //禁止T1中断
    ES= 1;       //使能串口中断
    TR1 = 1;       //启动T1      
}
void UartWrite(unsigned char *buf, unsigned char len){//串口数据写入,即串口发送函数,buf-待发送数据的指针,len-指定的发送长度
    while (len--){   //循环发送所有字节
      flagTxd = 0;      //清零发送标志
      SBUF = *buf++;    //发送一个字节数据
      while (!flagTxd); //等待该字节发送完成
    }
}
unsigned char UartRead(unsigned char *buf, unsigned char len){//串口数据读取函数,buf-接收指针,len-指定的读取长度,返回值-实际读到的长度
    unsigned char i;
    if (len > cntRxd){//指定读取长度大于实际接收到的数据长度时,读取长度设置为实际接收到的数据长度
      len = cntRxd;
    }
    for (i=0; i<len; i++){//拷贝接收到的数据到接收指针上
      *buf++ = bufRxd;
    }
    cntRxd = 0;//接收计数器清零
    return len;//返回实际读取长度
}
void UartDriver(){//串口驱动函数,监测数据帧的接收,调度功能函数,需在主循环中调用
    unsigned char len;
    unsigned char pdata buf;
    if (flagFrame){ //有命令到达时,读取处理该命令
      flagFrame = 0;
      len = UartRead(buf, sizeof(buf)-2); //将接收到的命令读取到缓冲区中
      UartAction(buf, len);//传递数据帧,调用动作执行函数
    }
}
void UartRxMonitor(unsigned char ms){//串口接收监控,由空闲时间判定帧结束,需在定时中断中调用,ms-定时间隔
    static unsigned char cntbkp = 0;
    static unsigned char idletmr = 0;
    if (cntRxd > 0){//接收计数器大于零时,监控总线空闲时间
      if (cntbkp != cntRxd){//接收计数器改变,即刚接收到数据时,清零空闲计时
            cntbkp = cntRxd;
            idletmr = 0;
      }else{                   //接收计数器未改变,即总线空闲时,累积空闲时间
            if (idletmr < 30){//空闲计时小于30ms时,持续累加
                idletmr += ms;
                if (idletmr >= 30){//空闲时间达到30ms时,即判定为一帧接收完毕
                  flagFrame = 1;//设置帧接收完成标志
                }
            }
      }
    }else{
      cntbkp = 0;
    }
}
union union_temp16{
    uint un_temp16;
    ucharun_temp8;
}my_unTemp16;
//读一字节,调用前需打开IAP 功能,入口:DPTR = 字节地址,返回:A = 读出字节
uchar Byte_Read(uint add){
    IAP_DATA = 0x00;
    IAP_CONTR = ENABLE_ISP;         //打开IAP 功能, 设置Flash 操作等待时间
    IAP_CMD = 0x01;               //IAP/ISP/EEPROM 字节读命令

    my_unTemp16.un_temp16 = add;
    IAP_ADDRH = my_unTemp16.un_temp8;    //设置目标单元地址的高8 位地址
    IAP_ADDRL = my_unTemp16.un_temp8;    //设置目标单元地址的低8 位地址

    //EA = 0;
    IAP_TRIG = 0x5A;   //先送 5Ah,再送A5h 到ISP/IAP 触发寄存器,每次都需如此
    IAP_TRIG = 0xA5;   //送完A5h 后,ISP/IAP 命令立即被触发起动
    _nop_();
    //EA = 1;
    IAP_Disable();//关闭IAP 功能, 清相关的特殊功能寄存器,使CPU 处于安全状态,
                  //一次连续的IAP 操作完成之后建议关闭IAP 功能,不需要每次都关
    return (IAP_DATA);
}

//字节编程,调用前需打开IAP 功能,入口:DPTR = 字节地址, A= 须编程字节的数据
void Byte_Program(uint add, uint ch){
    IAP_CONTR = ENABLE_ISP;         //打开 IAP 功能, 设置Flash 操作等待时间
    IAP_CMD = 0x02;               //IAP/ISP/EEPROM 字节编程命令

    my_unTemp16.un_temp16 = add;
    IAP_ADDRH = my_unTemp16.un_temp8;    //设置目标单元地址的高8 位地址
    IAP_ADDRL = my_unTemp16.un_temp8;    //设置目标单元地址的低8 位地址

    IAP_DATA = ch;                  //要编程的数据先送进IAP_DATA 寄存器
    //EA = 0;
    IAP_TRIG = 0x5A;   //先送 5Ah,再送A5h 到ISP/IAP 触发寄存器,每次都需如此
    IAP_TRIG = 0xA5;   //送完A5h 后,ISP/IAP 命令立即被触发起动
    _nop_();
    //EA = 1;
    IAP_Disable();//关闭IAP 功能, 清相关的特殊功能寄存器,使CPU 处于安全状态,
                  //一次连续的IAP 操作完成之后建议关闭IAP 功能,不需要每次都关
}

//擦除扇区, 入口:DPTR = 扇区地址
void Sector_Erase(uint add){
    IAP_CONTR = ENABLE_ISP;         //打开IAP 功能, 设置Flash 操作等待时间
    IAP_CMD = 0x03;               //IAP/ISP/EEPROM 扇区擦除命令

    my_unTemp16.un_temp16 = add;
    IAP_ADDRH = my_unTemp16.un_temp8;    //设置目标单元地址的高8 位地址
    IAP_ADDRL = my_unTemp16.un_temp8;    //设置目标单元地址的低8 位地址

    //EA = 0;
    IAP_TRIG = 0x5A;   //先送 5Ah,再送A5h 到ISP/IAP 触发寄存器,每次都需如此
    IAP_TRIG = 0xA5;   //送完A5h 后,ISP/IAP 命令立即被触发起动
    _nop_();
    //EA = 1;
    IAP_Disable();//关闭IAP 功能, 清相关的特殊功能寄存器,使CPU 处于安全状态,
                  //一次连续的IAP 操作完成之后建议关闭IAP 功能,不需要每次都关
}

void IAP_Disable(){
    //关闭IAP 功能, 清相关的特殊功能寄存器,使CPU 处于安全状态,
    //一次连续的IAP 操作完成之后建议关闭IAP 功能,不需要每次都关
    IAP_CONTR = 0;      //关闭IAP 功能
    IAP_CMD   = 0;      //清命令寄存器,使命令寄存器无命令,此句可不用
    IAP_TRIG= 0;      //清命令触发寄存器,使命令触发寄存器无触发,此句可不用
    IAP_ADDRH = 0;
    IAP_ADDRL = 0;
}
void delay(uint x){//延时程序 0.1秒
      uint m,n,s;
      for(m=x;m>0;m--)
      for(n=20;n>0;n--)
      for(s=248;s>0;s--);
}
void InitADC(){// AD转换初始化程序
    P1ASF = 0xff;                                    //设置P1口为开漏模式,使用AD功能
    ADC_RES = 0;                                     //AD数据寄存器清空
    ADC_CONTR = ADC_POWER | ADC_SPEEDLL;//打开AD电源,转换周期420
    delay(2);                                    //延时
}
uint GetADCResult(uchar px){         // 10位AD ;
    ADC_CONTR = ADC_POWER | ADC_SPEEDLL | px | ADC_START;//开始转换
    _nop_();                        //延时一个机器周期
    _nop_();                                                //延时一个机器周期
    _nop_();                                                //延时一个机器周期
    _nop_();                                                //延时一个机器周期
    while (!(ADC_CONTR & ADC_FLAG));//等待转换结束
    ADC_CONTR &= ~ADC_FLAG;         //关闭AD转换
    return ADC_RES;               //返回数据
}
void Selectsort(uint A[],uchar n){
   uchar i,j,min;
         uint temp;
   for(i=0;i<n;i++) {
          min=i;
          for(j=i+1;j<=n;j++){/* 从j往前的数据都是排好的,所以从j开始往下找剩下的元素中最小的 */
               if(A>A){/* 把剩下元素中最小的那个放到A中 */
                temp=A;
                A=A;
                A=temp;
               }
          }
    }
}
uintGet_Temp(uint dat){// 获得温度数据
      uchar k=0;
      uinttmp=0,buf1,buf2;
      while(1){
                        if(dat<TempH_Tab&&dat>TempH_Tab){
                              buf1=TempH_Tab-dat;
                              buf2=TempH_Tab-TempH_Tab;
                              if(buf2>19){
                                        tmp=k*10+buf1/(buf2/10);
                              }else{
                                        tmp=k*10+(buf1*10)/buf2;      
                              }
                              break;
                        }else if(dat==TempH_Tab){
                              tmp=k*10;
                              break;
                        }
                        if(k>120)
                              break;
                k++;
      }
      returntmp;
}




wzaefd 发表于 2023-11-5 12:38:24

{:4_174:}谢谢楼主 无私奉献!

lhtlhtl 发表于 2023-12-28 21:48:20

谢谢楼主 无私奉献!

zhangfuhg 发表于 2024-6-1 07:09:07

{:4_165:}这个应用好有价值,学习了!

SiegeLion 发表于 2024-6-9 21:41:13

感谢楼主分享{:4_243:}
页: [1]
查看完整版本: 基于STC12C5A60S2单片机设计的四路温控采集系统MODBUS通讯协议资源分享