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*)®isterValue;
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;
}