本帖最后由 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悬空,得到如下的测量结果: 
 
第一组数据根据驱动文件的驱动方式可知是无效的。观察第二组之后的数据可知采集得到的数据是有效的。至此,驱动完毕。 
 
 
 |