本帖最后由 ky123 于 2018-3-2 09:25 编辑
最近做项目,尝试着使用Raspberry Pi2来进行开发。Raspberry Pi2还是挺强大的,只是感觉可用的IO口很少。。项目中需要用到ADC模块,就去淘宝上淘一个安富莱德AD7606,感觉做工挺不错的还。不过价格也是贵哈,120大洋买的。
AD7606是8路、16位的AD,真双极性-5~+5或者-10~+10,采样率为200ksps(不可设定),可以选择使用并行模式、字节模式以及SPI模式。考虑到树莓派可怜的IO口数量,妥妥的选择用SPI模式。
板子默认是上图左边的并行模式,因此拿到手之后需要改动用一下电阻到右边的样子。再详细的资料大家可以去淘宝搜一下,上面讲的很详细。废话不多说,下面上接线图。
AD7606是带了数字滤波器的,可以通过OS1~OS3来配置,这里我接到了固定电平(000,不滤波)上。有需要的话也可以接到gpio上,我发的驱动程序里面已经预留好了接口。然后rage口是配置量程的,我也接的的固定电平(选择了-5~+5),同样的也可以接在gpio上。AD7606的时序还是很简单的,是标准的SPI接口。由于其所有配置都通过管教的电平来完成,所用的SPI只有SCLK和MISO。
下图即为AD7606串行模式下的时序图。转换时序选择了在转换后读取,比较方便配置(比较好写^_^),然后是下面的SPI
转换时序图
通信时序图
的时序图。SPI的通信有4种模式,如下图所示,对比可以知道是第二种,即CPOL = 1, CPHA = 0。根据linux/spi/spidev.h中的定
义可知为- /*----- SET SPI MODE -----
- SPI_MODE_0 (0,0) CPOL = 0, CPHA = 0, Clock idle low, data is clocked in on rising edge, output data (change) on falling edge
- SPI_MODE_1 (0,1) CPOL = 0, CPHA = 1, Clock idle low, data is clocked in on falling edge, output data (change) on rising edge
- SPI_MODE_2 (1,0) CPOL = 1, CPHA = 0, Clock idle high, data is clocked in on falling edge, output data (change) on rising edge
- SPI_MODE_3 (1,1) CPOL = 1, CPHA = 1, Clock idle high, data is clocked in on rising, edge output data (change) on falling edge
- spi_mode = SPI_MODE_0;
复制代码 应该为SPI_MODE_2。然而,当我在wiringPi的官网查看reference时却发现只提到有以下两个函数:- int wiringPiSPISetup (int channel, int speed) ;
- int wiringPiSPIDataRW (int channel, unsigned char *data, int len) ;
复制代码 并不能设置模式啊!!!这和说好不一样啊,坑爹的wiringPi为毛没有把mode接口留出来呢。不甘心之下就去github上查阅了wiringPiSPI.h的源文件,发现了猫腻:- #ifdef __cplusplus
- extern "C" {
- #endif
- int wiringPiSPIGetFd (int channel) ;
- int wiringPiSPIDataRW (int channel, unsigned char *data, int len) ;
- int wiringPiSPISetupMode (int channel, int speed, int mode) ;
- int wiringPiSPISetup (int channel, int speed) ;
- #ifdef __cplusplus
- }
- #endif
复制代码 原来如此,mode还是可选的,只是作者没有提而已。。再查阅一下wiringPiSPISetup函数原型可知,其默认选择了mode0,所以我们在使用时应该使用wiringPiSPISetupMode函数。
接下来直接上驱动的核心代码:(参考安富莱提供的8051程序进行移植)- uint8_t ADC_val[CH_NUM * 2];
- void AD7606_Init(){
- AD7606_IOSet();
- AD7606_SetDF(0); //default:OS2 OS1 OS0 = 000
- AD7606_SetInputRange(0); //default:range = -5 ~ +5
- AD7606_Reset();
- }
- //hook function,used to set input range.
- void AD7606_SetInputRange(int _ucRange){
-
- }
- //hook function,used to set digital filter.
- void AD7606_SetDF(uint8_t _ucMode){
-
- }
- //reset function
- void AD7606_Reset(){
- digitalWrite(CS , HIGH);
- digitalWrite(CVAB , HIGH);
- /* AD7606 is high level reset,at least 50ns */
- digitalWrite(RST, LOW);
- //delayMicroseconds(1);
- digitalWrite(RST, HIGH);
- //delayMicroseconds(1);
- digitalWrite(RST, LOW);
- }
- void AD7606_IOSet(){
- pinMode(RST , OUTPUT);
- pinMode(CVAB, OUTPUT);
- pinMode(CS , OUTPUT);
- pinMode(BUSY, INPUT);
- pullUpDnControl(BUSY, PUD_UP);
- digitalWrite(RST, LOW);
- digitalWrite(CS , HIGH);
- digitalWrite(CVAB , HIGH);
- }
- void AD7606_StartConv(){
- /* Conv in rising edge,at least 25ns */
- digitalWrite(CVAB, LOW);
- //delayMicroseconds(1);
- digitalWrite(CVAB, HIGH);
- }
- //Software Poll
- bool AD7606_StartADC(){
- if (digitalRead(BUSY) == 0){
- digitalWrite(CS, LOW);
- if (wiringPiSPIDataRW (SPI_CHANNEL_0, ADC_val, CH_NUM * 2) == -1){
- printf("SPI failure: %s\n", strerror(errno));
- }
- digitalWrite(CS, HIGH);
- AD7606_StartConv();
- while(digitalRead(BUSY) == 0);
- return true;
- }
- else{
- printf("Value is not ready: %s\n", strerror(errno));
- return false;
- }
- }
复制代码 这里为需要软件控制数字滤波器以及采样范围的留下了两个hook function,可以自己扩展。然后上电的reset函数,根据手册reset的时间不应少于50ns,而Raspberry Pi2的gpio口电平转换最快是4MHz左右,因此无需额外的延时。在AD7606_StartConv函数中进行了采样结果转换开始的操作。根据手册,我将CVA和CVB口并联起来,给一次信号接足够。按照安富莱提供的资料的说法,CVAB最好接到有pwm输出能力的口上,所以我选择了gpio1,不过后来发现没什么特别之处,换成别的口应该也没什么问题。最后就是AD7606_StartADC函数,由于本次的项目需要用到软件查询的方式采样,所以这里我仅仅写了软件查询,没有写中断的方式。根据函数可以知道,采样的第一组数据是无效的。最终的采样结果是8个16位的数,我保存在了一个extern变量- extern uint8_t ADC_val[CH_NUM * 2];
复制代码 中,每个结果占数组的两个位置。因此,在调用AD7606_StartADC()后,从ADC_val数组中即可方便的获得采样结果。
接下来就是上电实测。使用稳压电源输出3.0000V,稳压电源本身测得输出为3.0037V,万用表测得为3.007V。使用
AD7606的通道1进行采样,通道2~7接地,通道8悬空,得到如下的测量结果:
第一组数据根据驱动文件的驱动方式可知是无效的。观察第二组之后的数据可知采集得到的数据是有效的。至此,驱动完毕。
|