PIC单片机进阶实战(六):4-20mA/0-5V/0-10V数据采集

在智慧农业、工业控制等实际项目中,PIC16F1947 成为我近年来高频使用的核心芯片。其丰富的外设与双串口设计,非常适合构建多通道通信与控制系统。本次我将以一款实际投产数年的控制板为例,详解其电流互感器(5A/10A)数据采集。

该控制板集成了以下8大功能,覆盖多数物联网与工控场景:

1、6路继电器输出;

2、2路DC24V或12V直流电机正反转控制;

3、1路4G模块数据收发;

4、1路LORA无线数据收发或RS485数据收发;

5、2路无源开关量输入;

6、1路电流互感器(5A/10A)数据采集;

7、1路数模转换(数字量转4-20mA/0-5V/0-10V);

8、1路模数转换(4-20mA/0-5V/0-10V转数字量)。

一、 4-20mA/0-5V/0-10V**采集硬件电路:**

上图J2是4-20mA/0-5V/0-10V输入,为了电流电压输入通用,R22、R23是分压电阻,如果是4-20mA 输入,可采用图中的阻值,如果是0-5V或0-10V,R22、R23阻值最好都大于1K,不至于电流过大,可以自己计算两个阻值,保证VIN+输入在2V以内就可以, U4是18位A/D芯片MCP3421,VIN+、VIN-用作差分输入时可以到18位精度,VIN-接GND时可以到17位精度,R20、R21是两个10K上拉电阻,通过设置MCP3421寄存器可使参考电压为2.048V。

二、例程:

以下是采集示例(PIC16F1947采用16M主频),具体程序如下:

#include<pic16f1947.h>

__CONFIG(0x007c); //内部时钟16M

__CONFIG(0x1010); //内部时钟16M

复制代码
unsigned char dres_485=1,sec_5s=0,half_sec=0 ,flag_rev=0,flag_rev2=0,leijia_tnt2,elecl,elech,elechh;
复制代码
unsigned int  time_tnt,time_tnt2,sec_05s,rev_rnt=0,rev_rnt2=0,tran_rnt=0,tran_rnt2=0,sec_1s=0;   
复制代码
复制代码
unsigned char eeprom_e2[30]; //EEPROM缓存
复制代码
unsigned long result_3421[30];
复制代码
unsigned char trans[50];
复制代码
unsigned char reciv[200]; //串口1接收缓存
复制代码
unsigned char reciv2[200]; //串口2接收缓存
复制代码
复制代码
#define SDA  RE3    //电流采集数据I/O
复制代码
#define SCL  RE2   //电流采集时钟I/O
复制代码
#define byte unsigned char
复制代码
void delay(unsigned int v)
复制代码
{
复制代码
  while(v!=0)v--;
复制代码
}
复制代码
void somenop(void)
复制代码
{
复制代码
               asm("nop");
复制代码
               asm("nop");
复制代码
               asm("nop");
复制代码
               asm("nop");
复制代码
               asm("nop");
复制代码
               asm("nop");
复制代码
               asm("nop");
复制代码
               asm("nop");
复制代码
               asm("nop");
复制代码
               asm("nop");
复制代码
               
复制代码
} 
复制代码
 void I2CStart(void)  
复制代码
 {  
复制代码
  TRISE3=0;  
复制代码
  SDA=1;  
复制代码
  somenop();  
复制代码
  SCL=1;  
复制代码
  somenop(); 
复制代码
  somenop();  
复制代码
  SDA=0;  
复制代码
  somenop();
复制代码
  somenop();   
复制代码
  SCL=0; 
复制代码
  somenop();  
复制代码
 }  
复制代码
  
复制代码
  void I2CStop(void)  
复制代码
  {  
复制代码
  SCL=0;  
复制代码
  somenop();  
复制代码
  SDA=0;  
复制代码
  somenop();
复制代码
  somenop();   
复制代码
  SCL=1;  
复制代码
  somenop(); 
复制代码
  somenop();  
复制代码
  SDA=1;  
复制代码
  somenop(); 
复制代码
//  TRISC6=1;  
复制代码
  }  
复制代码
  
复制代码
void  WaitAck(void)  
复制代码
  {  
复制代码
  unsigned char i=20;  
复制代码
               TRISE3=1; 
复制代码
   SDA=1;  
复制代码
  somenop();  
复制代码
  SCL=1;  
复制代码
/* lpp:    if(SDA==0)
复制代码
               {
复制代码
                               rc_flag=64;
复制代码
               }
复制代码
               else
复制代码
               {
复制代码
                               SCL=0;  
复制代码
        somenop();
复制代码
                               somenop();   
复制代码
        SCL=1; 
复制代码
                               somenop();   
复制代码
        SCL=0;
复制代码
                               goto lpp;
复制代码
               } */
复制代码
  while(i--){  
复制代码
            if(SDA==1)  
复制代码
                    {  
复制代码
                    SCL=0;  
复制代码
                    somenop();
复制代码
                                                                            somenop();   
复制代码
                    SCL=1;  
复制代码
                    }  
复制代码
            else  
复制代码
                goto loop;  
复制代码
              
复制代码
            }  
复制代码
loop:  
复制代码
  SCL=0;  
复制代码
  somenop(); 
复制代码
               TRISE3=0; 
复制代码
  return;  
复制代码
  
复制代码
  
复制代码
  }  
复制代码
  
复制代码
void SendAck(void)  
复制代码
{  
复制代码
SDA=0;  
复制代码
somenop();  
复制代码
SCL=1;  
复制代码
somenop();  
复制代码
somenop(); 
复制代码
SCL=0;  
复制代码
somenop(); 
复制代码
  
复制代码
}  
复制代码
  
复制代码
void SendNotAck(void)  
复制代码
{  
复制代码
 SDA=1;  
复制代码
   somenop();  
复制代码
SCL=1;  
复制代码
  somenop();  
复制代码
  somenop(); 
复制代码
  SCL=0;  
复制代码
  somenop(); 
复制代码
  
复制代码
}  
复制代码
  
复制代码
void I2CSendbyte(byte ch)  
复制代码
{  
复制代码
 unsigned char i=8;  
复制代码
 TRISE3=0; 
复制代码
 while(i--)  
复制代码
    {  
复制代码
     SCL=0;  
复制代码
     somenop();  
复制代码
     if((ch&0x80)!=0)
复制代码
               {
复制代码
                               SDA=1;
复制代码
               } 
复制代码
               else SDA=0; 
复制代码
     ch<<=1;  
复制代码
     somenop();  
复制代码
     SCL=1;  
复制代码
     somenop(); 
复制代码
     somenop();  
复制代码
    }  
复制代码
   SCL=0;  
复制代码
   somenop(); 
复制代码
}  
复制代码
  
复制代码
byte I2CRecebyte(void)  
复制代码
{  
复制代码
复制代码
 unsigned char i=8;  
复制代码
 byte ddata=0;  
复制代码
 TRISE3=1;
复制代码
// SDA=1;  
复制代码
 while(i--){  
复制代码
  ddata<<=1;  
复制代码
  SCL=0;  
复制代码
  somenop();  
复制代码
  SCL=1;  
复制代码
  somenop();  
复制代码
  somenop(); 
复制代码
  ddata|=SDA;  
复制代码
 }  
复制代码
 SCL=0;  
复制代码
 somenop(); 
复制代码
 TRISE3=0; 
复制代码
 return ddata;  
复制代码
}  
复制代码
  
复制代码
void WRITE_MCP3421(unsigned char wr_data)  
复制代码
{  
复制代码
 I2CStart();  
复制代码
 I2CSendbyte(0xd0);  
复制代码
 WaitAck();  
复制代码
 I2CSendbyte(wr_data);  
复制代码
 WaitAck();  
复制代码
 I2CStop();  
复制代码
  
复制代码
}  
复制代码
  
复制代码
 void READ_MCP3421(void)  
复制代码
 {  
复制代码
I2CStart();  
复制代码
 I2CSendbyte(0xd1);  
复制代码
  WaitAck();  
复制代码
  elechh=I2CRecebyte();  
复制代码
  SendAck();  
复制代码
  elech=I2CRecebyte();  
复制代码
  SendAck();
复制代码
  elecl=I2CRecebyte();  
复制代码
  SendAck();  
复制代码
  mcp_busy=I2CRecebyte();  
复制代码
 SendNotAck();  
复制代码
  I2CStop();  
复制代码
 }
复制代码
复制代码
void interrupt isr(void)
复制代码
{             
复制代码
               if(TMR1IF)
复制代码
               {
复制代码
                               TMR1L=0xbf;
复制代码
                               TMR1H=0xf9;
复制代码
                               sec_05s+=1;
复制代码
                               sec_1s+=1;
复制代码
                               if(sec_05s>624) //0.5s
复制代码
                               {
复制代码
                                              half_sec+=1;
复制代码
                                              sec_5s+=1;
复制代码
                                              
复制代码
                                              
复制代码
复制代码
                                              sec_05s=0;
复制代码
                               }
复制代码
                               
复制代码
                                
复制代码
                               TMR1IF=0;
复制代码
                               
复制代码
               
复制代码
               } 
复制代码
               
复制代码
               
复制代码
复制代码
}
复制代码
void main()
复制代码
{
复制代码
               unsigned int i,j; 
复制代码
               unsigned char tmp;
复制代码
               ANSELF=0;
复制代码
               ANSELG=0;
复制代码
//             ADCON0=0;
复制代码
               TRISC=0x80;
复制代码
               TRISB=0x87;
复制代码
               OPTION_REG&=0X7F;
复制代码
               WPUB7=1;
复制代码
               TRISD=0x00;
复制代码
               TRISE=0x00;
复制代码
               TRISF=0x00;
复制代码
               
复制代码
               TRISG=0x0c;
复制代码
//             TRISG=0x00;
复制代码
复制代码
               PORTC=0x38;
复制代码
               PORTD=0x00;
复制代码
               PORTE=0x00;
复制代码
               PORTF=0x00;
复制代码
               PORTG=0x00;
复制代码
//             OSCCON=0x0;
复制代码
               OSCSTAT=0xa0;
复制代码
               OSCCON=0x78; //内部时钟16M
复制代码
//             OSCCON=0xf0; //32M
复制代码
//             OSCSTAT=0xf0;//32M
复制代码
               PIE1=0x21;
复制代码
               INTCON=0x00;
复制代码
               T1CON=0x70;
复制代码
               TMR1IF=0;
复制代码
               TMR1L=0xbf; //0.8ms
复制代码
               TMR1H=0xf9;
复制代码
               TMR1IE=1;
复制代码
               T1CON=0x71; 
复制代码
复制代码
PEIE=1;
复制代码
GIE=1;
复制代码
while(1)
复制代码
               {
复制代码
                               asm("clrwdt");
复制代码
                               
复制代码
                               if(sec_5s>3) //2秒采集一次
复制代码
                                              {
复制代码
          WRITE_MCP3421(0x8c);  //设置17位A/D,参考电压2.048V
复制代码
                                                                            delay(10);
复制代码
                                                                            READ_MCP3421();
复制代码
    result_3421[1]=elechh;
复制代码
                                                                            result_3421[1]<<=8;
复制代码
                                                                            result_3421[1]|=elech;
复制代码
                                                                            result_3421[1]<<=8;
复制代码
                                                                            result_3421[1]|=elecl;
复制代码
    //根据采集数据计算电流或电压
复制代码
}
复制代码
                               
复制代码
                               
复制代码
               }
复制代码
}
三、本系列文章规划

《PIC单片机进阶实战》系列共六篇,后续将逐步展开以下内容:

|----|-------------------|---------------------------|
| 序号 | 主题 | 内容概要 |
| 1 | UART 通信 | 双串口配置、波特率转换、数据透传 |
| 2 | 4G 模块数据收发 | AT指令控制、4G模块数据透传 |
| 3 | 电流互感器数据采集 | 5A、10A电流采集 |
| 4 | 数模转换 | 12位模拟量输出4-20mA/0-5V/0-10V |
| 5 | 输入输出 | 开关量输入、控制继电器与直流电机 |
| 6 | 模数转换 | 4-20mA/0-5V/0-10V模拟量采集 |

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

有需要这两款控制板的朋友也可以关注联系我。

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

相关推荐
码农三叔3 小时前
《卷2:人形机器人的环境感知与多模态融合》
人工智能·嵌入式硬件·算法·机器人·人形机器人
Heart of Dream3 小时前
[STM32 HAL源码解析] 为什么中断里要判断挂起寄存器?为什么非要用回调函数?
单片机·嵌入式硬件
liwulin05064 小时前
【ESP32-S3】WINDOWS+VMware+ROS2+YDLIDA X2导航初步调试
windows·stm32·单片机
dozenyaoyida6 小时前
RS预览失败问题分析和解决
网络·经验分享·嵌入式硬件·tcp·wifi6兼容性·视频预览卡顿
forAllforMe7 小时前
STM32 驱动CAN接口的拉线位移传感器
stm32·单片机·嵌入式硬件
专利观察员7 小时前
专利透视:长鑫科技背后的DRAM芯片变局
人工智能·科技·物联网
Struggle to dream8 小时前
STM32---关于DMA的入门详解
stm32·单片机·嵌入式硬件
攻城狮7号8 小时前
物联网时代2026年时序数据库选型指南
数据库·物联网·时序数据库·apache iotdb
BackCatK Chen8 小时前
第16篇:TMC2240多轴联动软件设计|2轴_3轴同步控制框架(保姆级)
嵌入式硬件·自动化·tmc2240·多轴联动·同步控制·2轴联动·3轴联动