ZYNQ 7000 XADC

Zynq-Linux移植学习笔记之16-Zynq下linux XADC驱动

XADC模块的使用方法,一是直接用FPGA JTAG专用接口访问,这时XADC模块工作在缺省模式;二是在设计中例化XADC模块,这是可以通过FPGA逻辑或ZYNQ器件的PS到ADC模块的专用接口访问。例化XADC模块有两种基本形式:一是ISE和PlanAhead环境下LogiCOREIP核的形式调用,二是EDK环境下对LogiCOREIP核的调用。这两种调用方法相信对使用过XILINX产品的朋友来说操作界面与步骤都很熟悉,这里就不赘述了,主要是想说明下XADC模块原语,以期对这模块有个基本的了解。

1、  简介

XADC是zynq芯片内部进行温度和电压检测的模块,通过(http://www.wiki.xilinx.com/xadc)这篇wiki可以知道,XADC控制器有两种表现形式,一种是位于PS内部,即文档中提到的the PS-XADC interface for the PS software to control the XADC,另一种是位于PL内部,通过IP核的方式实现。目前常用的是第一种。

通过ug480_7Series_XADC这篇文档得知,The ADCs can access up to 17 external analog input channels,也就是除了内部原有的监测项外,XADC最多支持17路扩展通道,可以参看下图。


对硬件有了基本的了解后,就可以进行devicetree的设置了。

2、  devicetree配置

由于是在PS部分,XADC控制器设置就像其他控制器一样即可,system.hdf中给出了地址:


Devicetree设置如下:

  1. xadc@f8007100 {  
  2.         compatible= "xlnx,zynq-xadc-1.00.a";  
  3.         reg =<0xf8007100 0x20>;  
  4.         interrupts= <0 7 4>;  
  5.        interrupt-parent = <&gic>;  
  6.         clocks =<&pcap_clk>;  
  7.    
  8.        xlnx,channels {  
  9.            #address-cells = <1>;  
  10.            #size-cells = <0>;  
  11.            channel@0 {  
  12.                 reg= <0>;  
  13.             };  
  14.            channel@1 {  
  15.                 reg= <1>;  
  16.             };  
  17.            channel@8 {  
  18.                 reg= <8>;  
  19.             };  
  20.         };  
  21.     };  


这里只启用了三个channel,如果启用更多channel可以在devicetree中进行添加。

3、  kernel配置

内核中已经包含了xadc的驱动程序,位置是driversiioadc,最后三个xilinx打头的文件。将驱动加入内核配置过程如下:







如果为了省事,也可以直接在defconfig文件中加入配置信息,这样默认是选中的。


4、  测试

加载devicetree,uimage,uramdisk后能在sys目录下找到检测信息:


打开in_temp0_raw能看到数字,注意,该数字是原始数字,需要转换才能得到正确的摄氏度。


该数字不断变化,表明温度在变化。其他电压信息类似。

为了更直观的展现监测信息,可以做一个用户app来打印,代码如下:

  1. #define MAX_PATH_SIZE   200  
  2. #define MAX_NAME_SIZE   50  
  3. #define MAX_VALUE_SIZE  100  
  4.   
  5. #define MAX_CMD_NAME_SIZE 100  
  6. #define MAX_UNIT_NAME_SIZE 50  
  7.   
  8. #define SYS_PATH_IIO    "/sys/bus/iio/devices/iio:device0"  
  9.   
  10. #define VCC_INT_CMD     "xadc_get_value_vccint"  
  11. #define VCC_AUX_CMD     "xadc_get_value_vccaux"  
  12. #define VCC_BRAM_CMD        "xadc_get_value_vccbram"  
  13. #define VCC_TEMP_CMD        "xadc_get_value_temp"  
  14. #define VCC_EXT_CH_CMD      "xadc_get_value_ext_ch"  
  15.   
  16.   
  17.   
  18. static const int mV_mul = 1000;  
  19.   
  20. static const int multiplier = 1 << 12;  
  21.   
  22. static char gNodeName[MAX_NAME_SIZE];  
  23.   
  24. enum EConvType  
  25. {  
  26.     EConvType_None,  
  27.     EConvType_Raw_to_Scale,  
  28.     EConvType_Scale_to_Raw,  
  29.     EConvType_Max  
  30. };  
  31.   
  32. enum XADC_Param  
  33. {  
  34.     EParamVccInt,  
  35.     EParamVccAux,  
  36.     EParamVccBRam,  
  37.     EParamTemp,  
  38.     EParamVAux0,  
  39.     EParamMax  
  40. };  
  41.   
  42. struct command  
  43. {  
  44.     const enum XADC_Param parameter_id;  
  45.     const char cmd_name[MAX_CMD_NAME_SIZE];  
  46.     const char unit[MAX_UNIT_NAME_SIZE];  
  47. };  
  48.   
  49. struct command command_list[EParamMax] = {  
  50.                 {EParamVccInt,  VCC_INT_CMD, "mV"},  
  51.                 {EParamVccAux,  VCC_AUX_CMD, "mV"},  
  52.                 {EParamVccBRam, VCC_BRAM_CMD, "mV"},  
  53.                 {EParamTemp,    VCC_TEMP_CMD, "Degree Celsius"},  
  54.                 {EParamVAux0,   VCC_EXT_CH_CMD, "mV"}  
  55. };  
  56.   
  57. struct XadcParameter  
  58. {  
  59.     const char name[MAX_NAME_SIZE];  
  60.     float value;  
  61.     float (* const conv_fn)(float,enum EConvType);  
  62. };  
  63.   
  64. /* 
  65. struct XadcParameter gXadcData[EParamMax] = { 
  66.     [EParamVccInt] = { "in_voltage0_vccint_raw",    0, conv_voltage}, 
  67.     [EParamVccAux] = { "in_voltage4_vccpaux_raw",   0, conv_voltage}, 
  68.     [EParamVccBRam]= { "in_voltage2_vccbram_raw",   0, conv_voltage}, 
  69.     [EParamTemp]   = { "in_temp0_raw",      0, conv_temperature}, 
  70.     [EParamVAux0]  = { "in_voltage8_raw",       0, conv_voltage_ext_ch} 
  71. }; 
  72. */  
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <errno.h>  
  4. #include <unistd.h>  
  5. #include <dirent.h>  
  6. #include <stdlib.h>  
  7. #include <sys/ioctl.h>  
  8. #include <fcntl.h>  
  9. #include <ctype.h>  
  10. #include <pthread.h>  
  11. #include <assert.h>  
  12.   
  13. #include "xadc_core.h"  
  14.   
  15. //utility functions  
  16. float conv_voltage(float input, enum EConvType conv_direction)  
  17. {  
  18.     float result=0;  
  19.   
  20.     switch(conv_direction)  
  21.     {  
  22.     case EConvType_Raw_to_Scale:  
  23.         result = ((input * 3.0 * mV_mul)/multiplier);  
  24.         break;  
  25.     case EConvType_Scale_to_Raw:  
  26.         result = (input/(3.0 * mV_mul))*multiplier;  
  27.         break;  
  28.     default:  
  29.         printf("Convertion type incorrect... Doing no conversion ");  
  30.         //  intentional no break;  
  31.     case EConvType_None:  
  32.         result = input;  
  33.         break;  
  34.     }  
  35.   
  36.     return result;  
  37. }  
  38.   
  39. float conv_voltage_ext_ch(float input, enum EConvType conv_direction)  
  40. {  
  41.     float result=0;  
  42.   
  43.     switch(conv_direction)  
  44.     {  
  45.     case EConvType_Raw_to_Scale:  
  46.         result = ((input * mV_mul)/multiplier);  
  47.         break;  
  48.     case EConvType_Scale_to_Raw:  
  49.         result = (input/mV_mul)*multiplier;  
  50.         break;  
  51.     default:  
  52.         printf("Convertion type incorrect... Doing no conversion ");  
  53.         //  intentional no break;  
  54.     case EConvType_None:  
  55.         result = input;  
  56.         break;  
  57.     }  
  58.   
  59.     return result;  
  60. }  
  61.   
  62. float conv_temperature(float input, enum EConvType conv_direction)  
  63. {  
  64.     float result=0;  
  65.   
  66.     switch(conv_direction)  
  67.     {  
  68.     case EConvType_Raw_to_Scale:  
  69.         result = ((input * 503.975)/multiplier) - 273.15;  
  70.         break;  
  71.     case EConvType_Scale_to_Raw:  
  72.         result = (input + 273.15)*multiplier/503.975;  
  73.         break;  
  74.     default:  
  75.         printf("Conversion type incorrect... Doing no conversion ");  
  76.         //  intentional no break;  
  77.     case EConvType_None:  
  78.         result = input;  
  79.         break;  
  80.     }  
  81.   
  82.     return result;  
  83. }  
  84.   
  85.   
  86. void get_iio_node()  
  87. {  
  88.     struct dirent **namelist;  
  89.     int i,n;  
  90.     char value=0;  
  91.     int fd = -1;  
  92.     char upset[20];  
  93.     float raw_data=0;  
  94.     float true_data=0;  
  95.     int offset=0;  
  96.     int currpos;  
  97.   
  98.     n = scandir(SYS_PATH_IIO, &namelist, 0, alphasort);  
  99.   
  100.     for (i=0; i < n; i++)  
  101.     {  
  102.         sprintf(gNodeName,"%s/%s", SYS_PATH_IIO, namelist[i]->d_name);  
  103.   
  104.         fd = open(gNodeName, O_RDWR );  
  105.   
  106.         if(strstr(gNodeName,"temp"))  
  107.         {  
  108.             if(strstr(gNodeName,"raw"))  
  109.             {  
  110.                 offset=0;  
  111.                 while(offset<5)  
  112.                 {     
  113.                     lseek(fd,offset,SEEK_SET);  
  114.                     read(fd,&value,sizeof(char));     
  115.                     upset[offset]=value;  
  116.                     offset++;  
  117.                 }     
  118.                 upset[offset]='';  
  119.                 raw_data=atoi(upset);  
  120.                 true_data=conv_temperature(raw_data, EConvType_Raw_to_Scale);  
  121.                 printf("%s is %f cent ",namelist[i]->d_name,true_data);  
  122.             }  
  123.         }  
  124.         else if(strstr(gNodeName,"voltage"))  
  125.         {  
  126.             if(strstr(gNodeName,"raw"))  
  127.             {  
  128.                 offset=0;  
  129.                 while(offset<5)  
  130.                 {  
  131.                     lseek(fd,offset,SEEK_SET);  
  132.                     read(fd,&value,sizeof(char));     
  133.                     upset[offset]=value;  
  134.                     offset++;  
  135.                 }     
  136.                 upset[offset]='';  
  137.                 raw_data=atoi(upset);  
  138.                 true_data=conv_voltage(raw_data, EConvType_Raw_to_Scale);  
  139.                 printf("%s is %f mv ",namelist[i]->d_name,true_data);  
  140.             }  
  141.         }  
  142.         close(fd);  
  143.   
  144.     }  
  145. }  
  146.   
  147. int main(int argc, char *argv[])  
  148. {  
  149.     get_iio_node();  
  150.     return 0;  
  151. }  


在编写这段代码时遇到好几个问题,首先用read函数取值取到的其实是 文件中ASCII码字符,没有什么好办法只好改成一位一位读取存入字符串然后调用atoi函数转换为int数字。其次,用lseek函数获取文件大小时得 到的值都为4096,但是这些文件并不是目录,不知道为何大小都显示的是linux最基本的文件块大小,最后只好投机取巧根据每个文件中的数字位数进行取 值,例如temp和vcc这些文件只有4位,那么就从文件中读4位。最后输出结果如下:


上图中温度为39度,和实际情况差不多。至此XADC驱动告一段落。

原文地址:https://www.cnblogs.com/codecamel/p/7723972.html