• 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驱动告一段落。

  • 相关阅读:
    redis操作
    MySQL架构
    MySQL查询缓存
    MySQL数据备份与还原
    Sql性能优化
    Notepad++中每一行的开头和结尾添加引号?
    分组聚合
    Python3用scan和delete命令批量清理redis数据
    VUE+django
    python转化13位时间戳
  • 原文地址:https://www.cnblogs.com/codecamel/p/7723972.html
Copyright © 2020-2023  润新知