ARM40-A5D27应用程序——SPI的用户态驱动(3)

ARM40-A5D27应用程序——SPI的用户态驱动(3)

2020.4.16

 SPI的用户态驱动有两种方式,read/write 或者 ioctl().
 read/write方式在同一时间只能read或write。如果要同时read和write,则需要用ioctl(Input Output Control).
 本文是一个ioctl的例子,操作AD7193,仅用于测试AD7193寄存器,不用于功能/性能测试。

一、例1

 (1)SPI应用程序的C语言源码
  文件包含 AD7193.h, AD7193.c,AD7193_test.c,代码见附录(1)。

 (2)交叉编译

arm-none-linux-gnueabi-gcc -o AD7193_test AD7193.c AD7193_test.c

 (3)执行程序与测试
  将交叉编译得到的AD7193_test文件拷贝到ARM40-A5D27板中,执行程序:

./AD7193_test

 打印为:

root@A5D27:/home# ./AD7193_test
AD7193_MODE=0x80060
AD7193_ch0_voltage=0x2eb
AD7193_ch1_voltage=0x2f1
AD7193_ch2_voltage=0x3e5
AD7193_ch3_voltage=0x2d3
AD7193_ch4_voltage=0x6bf
AD7193_ch5_voltage=0xffffff
AD7193_ch0_voltage=0x2e7
AD7193_ch1_voltage=0x2ed
AD7193_ch2_voltage=0x3ed
AD7193_ch3_voltage=0x2d3
AD7193_ch4_voltage=0x6bb
AD7193_ch5_voltage=0xffffff

 对AD7193的手册对比:
在这里插入图片描述

参考文章:

Documentation/spi/spidev_test.c
《Linux Device Drivers Development: Develop customized drivers for embedded Linux》
《ARM40-A527应用程序——SPI的用户态驱动(1)》
《ARM40-A527应用程序——SPI的用户态驱动(2)》
更多代码参考AD7193官方资料
荟聚计划:共商 共建 共享 Grant

附:

(1)AD7193.h 的代码

#ifndef __AD7193_H__
#define __AD7193_H__

/* AD7193 Register Map */
#define AD7193_REG_COMM         0 // Communications Register (WO, 8-bit) 
#define AD7193_REG_STAT         0 // Status Register         (RO, 8-bit) 
#define AD7193_REG_MODE         1 //  Register           (RW, 24-bit 
#define AD7193_REG_CONF         2 // Configuration Register  (RW, 24-bit)
#define AD7193_REG_DATA         3 // Data Register           (RO, 24/32-bit) 
#define AD7193_REG_ID           4 // ID Register             (RO, 8-bit) 
#define AD7193_REG_GPOCON       5 // GPOCON Register         (RW, 8-bit) 
#define AD7193_REG_OFFSET       6 // Offset Register         (RW, 24-bit 
#define AD7193_REG_FULLSCALE    7 // Full-Scale Register     (RW, 24-bit)

/* Communications Register Bit Designations (AD7193_REG_COMM) */
#define AD7193_COMM_WEN         (1 << 7)           // Write Enable. 
#define AD7193_COMM_WRITE       (0 << 6)           // Write Operation.
#define AD7193_COMM_READ        (1 << 6)           // Read Operation. 
#define AD7193_COMM_ADDR(x)     (((x) & 0x7) << 3) // Register Address. 
#define AD7193_COMM_CREAD       (1 << 2)           // Continuous Read of Data Register.

/* Configuration Register Bit Designations (AD7193_REG_CONF) */
#define AD7193_CONF_CHAN(x)     ((unsigned long)((x) & 0x3FF) << 8) // Channel select

/* Mode Register Bit Designations (AD7193_REG_MODE) */
#define AD7193_MODE_SEL(x)      (((unsigned long)(x) & 0x7) << 21) // Operation Mode Select

/* Mode Register: AD7193_MODE_SEL(x) options */
#define AD7193_MODE_CONT                0 // Continuous Conversion Mode.
#define AD7193_MODE_SINGLE              1 // Single Conversion Mode.
#define AD7193_MODE_IDLE                2 // Idle Mode.
#define AD7193_MODE_PWRDN               3 // Power-Down Mode.D7193_MODE_SEL(x)      (((unsigned long)(x) & 0x7) << 21) // Operation Mode Select.
#define AD7193_MODE_DAT_STA     ((unsigned long)1 << 20)           // Status Register transmission.
#define AD7193_MODE_CLKSRC(x)   (((unsigned long)(x) & 0x3) << 18) // Clock Source Select.
#define AD7193_MODE_AVG(x)      (((unsigned long)(x) & 0x3) << 16) // Fast settling filter.
#define AD7193_MODE_SINC3       (1 << 15)                          // SINC3 Filter Select.
#define AD7193_MODE_ENPAR       (1 << 13)                          // Parity Enable.
#define AD7193_MODE_CLKDIV      (1 << 12)                          // Clock divide by 2 (AD7190/2 only).
#define AD7193_MODE_SCYCLE      (1 << 11)                          // Single cycle conversion.
#define AD7193_MODE_REJ60       (1 << 10)                          // 50/60Hz notch filter.
#define AD7193_MODE_RATE(x)     ((x) & 0x3FF)                      // Filter Update Rate Select.
#define AD7193_MODE_CAL_INT_ZERO        4 // Internal Zero-Scale Calibration.
#define AD7193_MODE_CAL_INT_FULL        5 // Internal Full-Scale Calibration.
#define AD7193_MODE_CAL_SYS_ZERO        6 // System Zero-Scale Calibration.
#define AD7193_MODE_CAL_SYS_FULL        7 // System Full-Scale Calibration.
#define AD7193_MODE_CAL_INT_ZERO        4 // Internal Zero-Scale Calibration.
#define AD7193_MODE_CAL_INT_FULL        5 // Internal Full-Scale Calibration.
#define AD7193_MODE_CAL_SYS_ZERO        6 // System Zero-Scale Calibration.
#define AD7193_MODE_CAL_SYS_FULL        7 // System Full-Scale Calibration.

/* Mode Register: AD7193_MODE_CLKSRC(x) options */
#define AD7193_CLK_EXT_MCLK1_2          0 // External crystal. The external crystal
                                          // is connected from MCLK1 to MCLK2.
#define AD7193_CLK_EXT_MCLK2            1 // External Clock applied to MCLK2 
#define AD7193_CLK_INT                  2 // Internal 4.92 MHz clock. 
                                          // Pin MCLK2 is tristated.
#define AD7193_CLK_INT_CO               3 // Internal 4.92 MHz clock. The internal
                                          // clock is available on MCLK2.

/* Configuration Register Bit Designations (AD7193_REG_CONF) */
#define AD7193_CONF_CHOP        ((unsigned long)1 << 23)            // CHOP enable.
#define AD7193_CONF_REFSEL      ((unsigned long)1 << 20)            // REFIN1/REFIN2 Reference Select.
#define AD7193_CONF_PSEUDO      ((unsigned long)1 << 18)            // Pseudo differential analog inputs.
#define AD7193_CONF_CHAN(x)     ((unsigned long)((x) & 0x3FF) << 8) // Channel select.
#define AD7193_CONF_BURN        (1 << 7)                            // Burnout current enable.
#define AD7193_CONF_REFDET      (1 << 6)                            // Reference detect enable.
#define AD7193_CONF_BUF         (1 << 4)                            // Buffered Mode Enable.
#define AD7193_CONF_UNIPOLAR    (1 << 3)                            // Unipolar/Bipolar Enable.
#define AD7193_CONF_GAIN(x)     ((x) & 0x7)                         // Gain Select.

/* Configuration Register: AD7193_CONF_CHAN(x) options */
//                            Pseudo Bit = 0           Pseudo Bit = 1
#define AD7193_CH_0      0 // AIN1(+) - AIN2(-);       AIN1 - AINCOM
#define AD7193_CH_1      1 // AIN3(+) - AIN4(-);       AIN2 - AINCOM
#define AD7193_CH_2      2 // AIN5(+) - AIN6(-);       AIN3 - AINCOM
#define AD7193_CH_3      3 // AIN7(+) - AIN8(-);       AIN4 - AINCOM
#define AD7193_CH_4      4 // AIN1(+) - AIN2(-);       AIN5 - AINCOM
#define AD7193_CH_5      5 // AIN3(+) - AIN4(-);       AIN6 - AINCOM
#define AD7193_CH_6      6 // AIN5(+) - AIN6(-);       AIN7 - AINCOM
#define AD7193_CH_7      7 // AIN7(+) - AIN8(-);       AIN8 - AINCOM
#define AD7193_CH_TEMP   8 //           Temperature sensor
#define AD7193_CH_SHORT  9 // AIN2(+) - AIN2(-);       AINCOM(+) - AINCOM(-) 

/* Configuration Register: AD7193_CONF_GAIN(x) options */
//                                             ADC Input Range (5 V Reference)
#define AD7193_CONF_GAIN_1              0 // Gain 1    +-2.5 V
#define AD7193_CONF_GAIN_8              3 // Gain 8    +-312.5 mV
#define AD7193_CONF_GAIN_16             4 // Gain 16   +-156.2 mV
#define AD7193_CONF_GAIN_32             5 // Gain 32   +-78.125 mV
#define AD7193_CONF_GAIN_64             6 // Gain 64   +-39.06 mV
#define AD7193_CONF_GAIN_128            7 // Gain 128  +-19.53 mV

#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>

int pabort(const char *s);
int spi_device_setup(int fd);
int AD7193_SoftReset(int fd);
unsigned long AD7193_GetRegisterValue(unsigned char registerAddress);
int AD7193_SetRegisterValue(unsigned char registerAddress, unsigned long registerValue);
void AD7193_ChannelSelect(unsigned short channel);
void AD7193_Calibrate(unsigned char mode, unsigned char channel);
unsigned long AD7193_SingleConversion(void);
void AD7193_RangeSetup(unsigned char polarity, unsigned char range);
#endif /* __AD7193_H__ */

(2)AD7193.c 的代码

#include "AD7193.h"

static const char *device = "/dev/spidev0.0";
extern int spi_fd;

int pabort(const char *s)
{
        perror(s);
        return -1;
}

int spi_device_setup(int fd)
{
        int mode, speed, a, b, i;
        int bits = 8;

        // set spi mode: mode 3
        mode = SPI_MODE_3;
        a = ioctl(fd, SPI_IOC_WR_MODE32, &mode);        /* write mode */
        b = ioctl(fd, SPI_IOC_RD_MODE32, &mode);        /* read mode */
        if ((a < 0) || (b < 0)) {
                pabort("can't set spi mode\n");
        }

        // Clock max speed in Hz
        speed = 1 * 1000 * 1000;        /* 1 MHz */
        a = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); /* write speed */
        b = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed); /* read speed */
        if ((a < 0) || (b < 0)) {
                pabort("Fail to set max speed hz\n");
        }

        /* 
         * setting SPI to MSB first
         * Here, 0 means "not to use LSB first"
         * In order to use LSB first, argument should be > 0
         */
        i = 0;
        a = ioctl(fd, SPI_IOC_WR_LSB_FIRST, &i);
        b = ioctl(fd, SPI_IOC_RD_LSB_FIRST, &i);
        if ((a < 0) || (b < 0)) {
                pabort("Fail to set MSB first\n");
        }

        // setting SPI to 8 bits per word
        bits = 8;
        a = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
        b = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
        if ((a < 0) || (b < 0)) {
                pabort("Fail to set bits per word\n");
        }
}

int AD7193_SoftReset(int fd)
{
        int ret;
        char tx[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

        struct spi_ioc_transfer tr = {
                .tx_buf = (unsigned long)tx,
                .len = 6,
        };

        ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
        usleep(1000*200);       // >70ms
        return ret;
}

unsigned long AD7193_GetRegisterValue(unsigned char registerAddress)
{
        int ret;
        char rxbuf[3] = {0, 0, 0};
        unsigned long buffer = 0x0;

        char tx = AD7193_COMM_READ | AD7193_COMM_ADDR(registerAddress);

        struct spi_ioc_transfer tr[] = {
                {
                        .tx_buf = (unsigned long)&tx,
                        .len = 1,
                }, {
                        .rx_buf = (unsigned long)rxbuf,
                        .len = 3,
                },
        };

        ret = ioctl(spi_fd, SPI_IOC_MESSAGE(2), &tr);

        buffer = (buffer << 8) + rxbuf[0];
        buffer = (buffer << 8) + rxbuf[1];
        buffer = (buffer << 8) + rxbuf[2];

        return buffer;
}

int AD7193_SetRegisterValue(unsigned char registerAddress, unsigned long registerValue)
{
        int ret;
        int bytesNumber = 4;
        unsigned char txbuf[] = {0, 0, 0, 0};
        unsigned char* dP = (unsigned char*)&registerValue;

        txbuf[0] = AD7193_COMM_WRITE | AD7193_COMM_ADDR(registerAddress);

        if((registerAddress == AD7193_REG_COMM) || (registerAddress == AD7193_REG_GPOCON))
                bytesNumber = 2;

        txbuf[1] = *(dP+2);
        txbuf[2] = *(dP+1);
        txbuf[3] = *dP;

        struct spi_ioc_transfer tr = {
                .tx_buf = (unsigned long)txbuf,
                .len = bytesNumber,
        };

        ret = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr);
        usleep(1000*200);
        return ret;
}

void AD7193_ChannelSelect(unsigned short channel)
{
        unsigned long oldRegValue = 0x0;
        unsigned long newRegValue = 0x0;

        oldRegValue = AD7193_GetRegisterValue(AD7193_REG_CONF);
        oldRegValue &= ~(AD7193_CONF_CHAN(0x3FF));
        newRegValue = oldRegValue | AD7193_CONF_CHAN(1 << channel);
        AD7193_SetRegisterValue(AD7193_REG_CONF, newRegValue);
}

void AD7193_Calibrate(unsigned char mode, unsigned char channel)
{
        unsigned long oldRegValue = 0x0;
        unsigned long newRegValue = 0x0;

        AD7193_ChannelSelect(channel);
        oldRegValue = AD7193_GetRegisterValue(AD7193_REG_MODE);
        oldRegValue &= ~AD7193_MODE_SEL(0x7);
        newRegValue = oldRegValue | AD7193_MODE_SEL(mode);
        AD7193_SetRegisterValue(AD7193_REG_MODE, newRegValue);
}

unsigned long AD7193_SingleConversion(void)
{
        unsigned long command = 0x0;
        unsigned long regData = 0x0;

        command = AD7193_MODE_SEL(AD7193_MODE_SINGLE) |
                AD7193_MODE_CLKSRC(AD7193_CLK_INT) |
                AD7193_MODE_RATE(0x060);

        AD7193_SetRegisterValue(AD7193_REG_MODE, command);
        regData = AD7193_GetRegisterValue(AD7193_REG_DATA);
        return regData;
}

void AD7193_RangeSetup(unsigned char polarity, unsigned char range)
{
        unsigned long oldRegValue = 0x0;
        unsigned long newRegValue = 0x0;

        oldRegValue = AD7193_GetRegisterValue(AD7193_REG_CONF);
        oldRegValue &= ~(AD7193_CONF_UNIPOLAR | AD7193_CONF_GAIN(0x7));
        newRegValue = oldRegValue |
                AD7193_CONF_PSEUDO |
                (polarity * AD7193_CONF_UNIPOLAR) |
                AD7193_CONF_GAIN(range);
        AD7193_SetRegisterValue(AD7193_REG_CONF, newRegValue);
}

(3)AD7193_test.c 的代码

#include "AD7193.h"

int spi_fd;
static const char *device = "/dev/spidev0.0";

int main(int argc, char *argv[])
{
        int i, channel;
        int ret = 0;
        unsigned long result = 0;

        spi_fd = open(device, O_RDWR);
        if (spi_fd < 0)
                pabort("can't open device");

        ret = spi_device_setup(spi_fd);
        if(ret)
                goto end;

        AD7193_SoftReset(spi_fd);

        result = AD7193_GetRegisterValue(AD7193_REG_MODE);
        printf("AD7193_MODE=0x%x\n", result);

        /* Calibrate channel 0~5. */
        for(channel = 0; channel < 6; channel++) {
                AD7193_Calibrate(AD7193_MODE_CAL_INT_ZERO, channel);
                AD7193_Calibrate(AD7193_MODE_CAL_INT_FULL, channel);
        }

        AD7193_RangeSetup(1, AD7193_CONF_GAIN_1);       //unipolar, Pseudo(AIN1-AINCOM), ADC's input range [0, 4.096V]

        for(i = 0; i < 2; i++) {
                for(channel = 0;  channel < 6; channel++) {
                        AD7193_ChannelSelect(channel);
                        result = AD7193_SingleConversion();
                        printf("AD7193_ch%d_voltage=0x%x\n", channel, result);
                }
        }

end:
        close(spi_fd);
        return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值