树莓派拓展模拟量采集(AD)功能

1.前言

树莓派自身不带有模拟量采集功能(A/D)功能,当需要AD功能时,常通过IIC外接一个A/D模块来实现,如8位A/D芯片PCA9685。本文首先简要介绍PCA9685特性,然后基于树莓派的Bcm2835库开发PCA9685的驱动库。树莓派安装Bcm2835库参考这篇文章

2.PCA9685

PCA9685芯片有4路8位的A/D采集通道和一路8位D/A输出通道,其他通过IIC与外部通信。其芯片引脚见下图,其工作电压VDD可以2.5-6V,参考电压VREF为A/D转化参考电压可以与VDD一致也可不一致。芯片IIC地址可以通过物理引脚A0,A1,A2配置,若三引脚全接GND,芯片地址0x48。某宝中有对该芯片封装成的模块,其实物图和原理图分别见下图。
树莓派拓展模拟量采集(AD)功能
树莓派拓展模拟量采集(AD)功能
树莓派拓展模拟量采集(AD)功能
按照芯片手册的IIC协议,要采集AD通道,首先要发送一个一条控制指令,该指令由一个字节CONTROL BYTE组成(如果还要D/A输出,该指令有两个字节组成,CONTROL BYTE和一个字节的AD输出电压)。CONTROL BYTE可以配置1.模拟输出使能;1.模拟采集模式,单边输入模式和多种差分输入模式;3.AUTO-INCREMENT FLAG;4.A/D采集通道。发送该条指令后,再读取一个字节的数据即为指定通道的A/D值,再乘以分辨率就得到该通道的电压。
树莓派拓展模拟量采集(AD)功能

3.树莓派基于Bcm2835编写驱动库

使用C++语言编写该库,为PCA9685芯片编写一个类,其头文件pca9685.h

#ifndef PCF8591_H
#define PCF8591_H

#include <bcm2835.h>
#include <cstdio>
#include <cstdint>

/**
 * A class to control the 8-bits A/D(D/A) device PCF8591
 */
class PCF8591{
	public:
		PCF8591(uint8_t addr = 0x48);
		bool init();
		void setRefV(float refV);
		uint8_t readAD(uint8_t channel);
		float readVoltage(uint8_t channel);
		
	private:
		uint8_t _addr;
		float _refV;
		char sendBuf[3];
		uint8_t errCode;
};

#endif

pca9685.cpp文件

#include "pcf8591.h"

PCF8591::PCF8591(uint8_t addr)
{
	_addr = addr;
	_refV = 5.03f; //设置参考电压Vref, 该值为A/D和D/A的参考电压,需要使用万用表测量出来
}

/**
 * Before MS5837::init(), please bcm2835_init() in main.c!
 **/
bool PCF8591::init()
{
	printf("PCF8591 Init...\n");
	if(!bcm2835_i2c_begin())
	{
		printf("bcm2835_i2c_begin failed at %s%d\n",__FILE__,__LINE__);
		return false;
	}
	bcm2835_i2c_setSlaveAddress(_addr);
	bcm2835_i2c_set_baudrate(100000);
	return true;
}

void PCF8591::setRefV(float refV)
{
	_refV = refV;  //设置参考电压Vref, 该值为A/D和D/A的参考电压,需要使用万用表测量出来
}

/**
 * brief   Read the channel A/D
 * param   channel 0-3
 * return  0-255
 */
uint8_t PCF8591::readAD(uint8_t channel)
{
	bcm2835_i2c_setSlaveAddress(_addr);
	//发送CONTROL BYTE
	sendBuf[0] = 0x00 | channel;
	if((errCode = bcm2835_i2c_write(sendBuf,1)))
		printf("bcm2835_i2c_write failed at %s%d, errCode = 0x%x\n",__FILE__,__LINE__, errCode);
	//读取1字节数据	
	char byte;
	if((errCode = bcm2835_i2c_read(&byte,1)))
		printf("bcm2835_i2c_read failed at %s%d, errCode = 0x%x\n",__FILE__,__LINE__, errCode);
	return byte;
}

/**
 * brief read the channel voltage
 * param channel 0-3
 * return 0-_refV
 */
float PCF8591::readVoltage(uint8_t channel)
{
	return readAD(channel)/255.0f*_refV;
}

一个测试例程testMain.cpp

#include "pcf8591.h"
#include <iostream>
using namespace std;

int main()
{
	bcm2835_init();
	PCF8591 pcf8591;
	if(pcf8591.init())
		cout << "PCF8591 init successfully" << endl;
	else
	{
		cout << "PCF8591 init failed" << endl;
		exit(-1);
	}
	int i = 10;
	while(i)
	{
		int byte = pcf8591.readAD(3);
		cout << byte << endl;
		bcm2835_delay(100);
	}
	return 0;
}

// g++ testMain.cpp pcf8591.cpp -lbcm2835 -o testMain

驱动库可以直接github下载

上一篇:python练习:进制转换


下一篇:A Distributional Perspective on Reinforcement Learning