Closed punkyoon closed 6 years ago
49fcb5afec0853c8859987b36e30bbbee18fcff0
이것저것 찾다가 줍줍했읍니다..
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
static void pabort(const char *s)
{
perror(s);
abort();
}
static const char *device = "/dev/spidev0.0";
static const int MCP3208_STARTBIT= 0x10; /* Always 1 */
static const int MCP3208_SE= 0x08; /* Single end mode (bit3='1') */
// SPI mode must be setup in here!!
static uint8_t mode=SPI_MODE_0;
// 0 : clock normaly Lo & dat on Rising edge, CE active Lo, MSB first
// options for mode setup
/*
#define SPI_CPHA 0x01 Clock Phase
#define SPI_CPOL 0x02 Clock Pol
#define SPI_MODE_0 (0|0)
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04 CS pol
#define SPI_LSB_FIRST 0x08 bit order
#define SPI_3WIRE 0x10
#define SPI_LOOP 0x20
#define SPI_NO_CS 0x40 CS on/off
#define SPI_READY 0x80
*/
static uint8_t bits = 8;
static uint32_t speed = 100000;
static uint16_t delay;
static uint8_t ad_mode=0;
static uint8_t channel=0;
static void transfer(int fd, uint8_t cmd[], uint8_t cmd_size)
{
int ret;
uint8_t rx[cmd_size];
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)cmd,
.rx_buf = (unsigned long)rx,
.len = cmd_size,
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};
// printf("transfer: size of command= %d \n",cmd_size);
while(1){
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
pabort("can't send spi message");
/* data uses rx1, rx2 */
for (ret = 1; ret < cmd_size ; ret++) {
if (!(ret % 6))
puts("");
printf("%x ",rx[ret]);
// printf("%.2X", rx[ret]);
}
printf("\n");
sleep(1);
}
}
static void print_usage(const char *prog)
{
printf("Usage: %s [-Dsdsc]\n", prog);
puts(" -D --device device to use (default /dev/spidev0.0)\n"
" -s --speed max speed (Hz)\n"
" -d --delay delay (usec)\n"
" -m --mode Single End mode\n"
" -c --channel channel number \n"
);
exit(1);
}
static void parse_opts(int argc, char *argv[])
{
while (1) {
static const struct option lopts[] = {
{ "device", 1, 0, 'D' },
{ "speed", 1, 0, 's' },
{ "delay", 1, 0, 'd' },
{ "mode", 0, 0, 'm' },
{ "channel", 1, 0, 'c' },
{ NULL, 0, 0, 0 },
};
int c;
c = getopt_long(argc, argv, "D:s:d:b:mc:", lopts, NULL);
if (c == -1)
break;
switch (c) {
case 'D':
device = optarg;
break;
case 's':
speed = atoi(optarg);
break;
case 'd':
delay = atoi(optarg);
break;
case 'm':
ad_mode=1;
break;
case 'c':
channel=atoi(optarg);
break;
default:
print_usage(argv[0]);
break;
}
}
}
int main(int argc, char *argv[])
{
int ret = 0;
int fd;
uint8_t commands[]={0x11,0x22,0XFF};
parse_opts(argc, argv);
fd = open(device, O_RDWR);
if (fd < 0)
pabort("can't open device");
/*
* spi mode
*/
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
if (ret == -1)
pabort("can't set spi mode");
ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
if (ret == -1)
pabort("can't get spi mode");
/*
* bits per word
*/
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1)
pabort("can't set bits per word");
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
if (ret == -1)
pabort("can't get bits per word");
/*
* max speed hz
*/
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1)
pabort("can't set max speed hz");
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -1)
pabort("can't get max speed hz");
// printf("spi mode: %d\n", mode);
// printf("bits per word: %d\n", bits);
// printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);
//
// printf("AD mode=%d, channel=%d\n",ad_mode,channel);
// printf("channel=%d \n",channel);
// printf("mode =%d \n",MCP3208_SE*ad_mode);
// printf("command =0x%x \n",MCP3208_STARTBIT | MCP3208_SE*(ad_mode)|channel);
// put the command code into two bytes to fit hole transmission into 24 bit(8x3)
// See MCP3208 datasheet
// xxxx x1.. ..XX XXXX XXXX XXXX
// ||| ||
// ||2 10 Channel select(3bits)
// ||
// |SE/!DIFF mode
// StartBit
channel=channel & 0x07;
commands[0]=(MCP3208_STARTBIT | MCP3208_SE*(ad_mode & 1) | channel)/4;
commands[1]=(channel*64) & 0xFF;
transfer(fd, commands, ARRAY_SIZE(commands));
close(fd);
return ret;
}
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/mutex.h>
#define DRIVER_NAME "mcp3208"
#define MCP320X_PACKET_SIZE 3
#define MCP320X_DIFF 0
#define MCP320X_SINGLE 1
#define MCP3208_CHANNELS 4
struct mcp3208_drvdata {
struct spi_device *spi;
struct mutex lock;
unsigned char tx[MCP320X_PACKET_SIZE] ____cacheline_aligned;
unsigned char rx[MCP320X_PACKET_SIZE] ____cacheline_aligned;
struct spi_transfer xfer ____cacheline_aligned;
struct spi_message msg ____cacheline_aligned;
};
/* set bus_num & chip in parameter */
static int spi_bus_num = 0;
static int spi_chip_select = 0;
module_param( spi_bus_num, int, S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR );
module_param( spi_chip_select, int, S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR );
static unsigned int mcp3208_get_value( struct mcp3208_drvdata *data, int channel )
{
unsigned int r = 0;
unsigned char c = channel & 0x03;
mutex_lock( &data->lock );
data->tx[0] = 1 << 2; // start bit
data->tx[0] |= 1 << 1; // Single
data->tx[1] = c << 6; // channel
data->tx[2] = 0;
if( spi_sync( data->spi, &data->msg) ) {
printk(KERN_INFO "spi_sync_transfer returned non zero\n" );
}
mutex_unlock(&data->lock);
r = (data->rx[1] & 0x0F) << 8;
r |= data->rx[2];
return r;
}
static int ch_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct mcp3208_drvdata *data = (struct mcp3208_drvdata *)dev_get_drvdata(dev);
int c = attr->attr.name[2] - 0x30;
int v = 0;
if( (c > -1) && (c < MCP3208_CHANNELS) ) {
v = mcp3208_get_value(data, c);
}
return snprintf (buf, PAGE_SIZE, "%d\n", v);
}
static DEVICE_ATTR(ch0, S_IRUSR|S_IRGRP|S_IROTH, ch_show,NULL );
static DEVICE_ATTR(ch1, S_IRUSR|S_IRGRP|S_IROTH, ch_show,NULL );
static DEVICE_ATTR(ch2, S_IRUSR|S_IRGRP|S_IROTH, ch_show,NULL );
static DEVICE_ATTR(ch3, S_IRUSR|S_IRGRP|S_IROTH, ch_show,NULL );
static struct spi_board_info mcp3208_info = {
.modalias = "mcp3208",
.max_speed_hz = 1000000,
.bus_num = 0,
.chip_select = 0,
.mode = SPI_MODE_3,
};
static int mcp3208_probe(struct spi_device *spi)
{
struct mcp3208_drvdata *data;
int ret;
printk(KERN_INFO "mcp3208 probe\n");
/* set SPI */
spi->max_speed_hz = mcp3208_info.max_speed_hz;
spi->mode = mcp3208_info.mode;
spi->bits_per_word = 8;
if( spi_setup( spi ) ) {
printk(KERN_ERR "spi_setup returned error\n");
return -ENODEV;
}
data = kzalloc( sizeof(struct mcp3208_drvdata), GFP_KERNEL );
if(data == NULL ) {
printk(KERN_ERR "%s: no memory\n", __func__ );
return -ENODEV;
}
data->spi = spi;
mutex_init( &data->lock );
data->xfer.tx_buf = data->tx;
data->xfer.rx_buf = data->rx;
data->xfer.bits_per_word = 8;
data->xfer.len = MCP320X_PACKET_SIZE;
data->xfer.cs_change = 0;
data->xfer.delay_usecs = 0;
data->xfer.speed_hz = 1000000;
spi_message_init_with_transfers( &data->msg, &data->xfer, 1 );
spi_set_drvdata( spi, data );
ret = device_create_file( &spi->dev, &dev_attr_ch0 );
if(ret) {
printk(KERN_ERR "failed to add ch0 attribute\n" );
}
ret = device_create_file( &spi->dev, &dev_attr_ch1 );
if(ret) {
printk(KERN_ERR "failed to add ch1 attribute\n" );
}
ret = device_create_file( &spi->dev, &dev_attr_ch2 );
if(ret) {
printk(KERN_ERR "failed to add ch2 attribute\n" );
}
ret = device_create_file( &spi->dev, &dev_attr_ch3 );
if(ret) {
printk(KERN_ERR "failed to add ch3 attribute\n" );
}
return 0;
}
static int mcp3208_remove(struct spi_device *spi)
{
struct mcp3208_drvdata *data;
data = (struct mcp3208_drvdata *)spi_get_drvdata(spi);
device_remove_file( &spi->dev, &dev_attr_ch0 );
device_remove_file( &spi->dev, &dev_attr_ch1 );
device_remove_file( &spi->dev, &dev_attr_ch2 );
device_remove_file( &spi->dev, &dev_attr_ch3 );
kfree(data);
printk(KERN_INFO "mcp3208 removed\n");
return 0;
}
static struct spi_device_id mcp3208_id[] = {
{ "mcp3208", 0 },
{ },
};
MODULE_DEVICE_TABLE(spi, mcp3208_id);
static struct spi_driver mcp3208_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
.id_table = mcp3208_id,
.probe = mcp3208_probe,
.remove = mcp3208_remove,
};
static void spi_remove_device(struct spi_master *master, unsigned int cs)
{
struct device *dev;
char str[128];
snprintf(str, sizeof(str), "%s.%u", dev_name(&master->dev), cs);
// find SPI device
dev = bus_find_device_by_name(&spi_bus_type, NULL, str);
// if exist, delete device
if( dev ){
printk(KERN_INFO "Delete %s\n", str);
device_del(dev);
}
}
static int mcp3208_init(void)
{
struct spi_master *master;
struct spi_device *spi_device;
spi_register_driver(&mcp3208_driver);
mcp3208_info.bus_num = spi_bus_num;
mcp3208_info.chip_select = spi_chip_select;
master = spi_busnum_to_master(mcp3208_info.bus_num);
if( ! master ) {
printk( KERN_ERR "spi_busnum_to_master returned NULL\n");
spi_unregister_driver(&mcp3208_driver);
return -ENODEV;
}
// Since it is exclusive to spidev 0.0 in the initial state, it must be removed
spi_remove_device(master, mcp3208_info.chip_select);
spi_device = spi_new_device( master, &mcp3208_info );
if( !spi_device ) {
printk(KERN_ERR "spi_new_device returned NULL\n" );
spi_unregister_driver(&mcp3208_driver);
return -ENODEV;
}
return 0;
}
module_init(mcp3208_init);
static void mcp3208_exit(void)
{
struct spi_master *master;
master = spi_busnum_to_master(mcp3208_info.bus_num);
if( master ) {
spi_remove_device(master, mcp3208_info.chip_select );
}
else {
printk( KERN_INFO "mcp3208 remove error\n");
}
spi_unregister_driver(&mcp3208_driver);
}
module_exit(mcp3208_exit);
MODULE_AUTHOR("Satoshi Yoneda");
MODULE_LICENSE("GPL v2");
언젠가 필요할지도 모르는... app단에서 gpio 값 읽어오는 파이썬 코드.
https://github.com/mrichardson23/mrBBIO/blob/master/mrbbio.py
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
static const char DEVICE[] = "/dev/spidev0.1";
static uint8_t MODE = SPI_MODE_0;
static uint8_t BITS = 8;
static uint32_t CLOCK = 2000000;
static uint16_t DELAY = 0;
#define DIO11 17
#define DIO12 27
static struct timer_list blink_timer;
struct file *mcp3008;
static int setupadc(void)
{
if (mcp3008->f_op->unlocked_ioctl(mcp3008, SPI_IOC_WR_MODE,(long unsigned int)(&MODE)) == -1) {
printk(KERN_INFO "Can't set MODE");
return -1;
}
if (mcp3008->f_op->unlocked_ioctl(mcp3008, SPI_IOC_RD_MODE,(long unsigned int)(&MODE)) == -1) {
printk(KERN_INFO "Can't set MODE");
return -1;
}
if (mcp3008->f_op->unlocked_ioctl(mcp3008, SPI_IOC_WR_BITS_PER_WORD,(long unsigned int)(&BITS)) == -1) {
printk(KERN_INFO "Can't set number of BITS");
return -1;
}
if (mcp3008->f_op->unlocked_ioctl(mcp3008, SPI_IOC_RD_BITS_PER_WORD,(long unsigned int)(&BITS)) == -1) {
printk(KERN_INFO "Can't set number of BITS");
return -1;
}
if (mcp3008->f_op->unlocked_ioctl(mcp3008, SPI_IOC_WR_MAX_SPEED_HZ,(long unsigned int)(&CLOCK)) == -1) {
printk(KERN_INFO "Can't set write CLOCK");
return -1;
}
if (mcp3008->f_op->unlocked_ioctl(mcp3008, SPI_IOC_RD_MAX_SPEED_HZ,(long unsigned int)(&CLOCK)) == -1) {
printk(KERN_INFO "Can't set read CLOCK");
return -1;
}
return 0;
}
uint16_t readadc(uint8_t channel)
{
uint8_t tx[3];
uint8_t rx[3];
uint16_t ret ;
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = ARRAY_SIZE(tx),
.delay_usecs = DELAY,
.speed_hz = CLOCK,
.bits_per_word = BITS,
};
tx[0] = 0x01 ;
tx[1] = 0x80 | (channel&7)<<4;
tx[2] = 0;
if (mcp3008->f_op->unlocked_ioctl(mcp3008, SPI_IOC_MESSAGE(1),(long unsigned int)(&tr)) == -1) {
printk(KERN_INFO "IO Error");
}
ret = (((uint16_t)rx[1] << 8) & 0x300) | ((uint16_t)rx[2] & 0xFF) ;
return (ret);
}
static void blink_timer_func(unsigned long data)
{
printk(KERN_INFO "spi read data1 %d\n", readadc(1));
ndelay(1000);
printk(KERN_INFO "spi read data2 %d\n", readadc(1));
ndelay(1000);
blink_timer.expires = jiffies + (1*HZ);
add_timer(&blink_timer);
}
static int __init gpiomod_init(void)
{
int ret = 0;
mcp3008 = filp_open(DEVICE,O_RDWR,O_RDWR);
printk(KERN_INFO "mcp3008 opened %d \n",PTR_RET(mcp3008));
if (IS_ERR(mcp3008)) {
printk(KERN_INFO "error no is %ld\n", PTR_ERR(mcp3008));
}
if (!mcp3008) {
printk(KERN_INFO "can't open device file \n");
}
if(setupadc() == -1){
printk(KERN_INFO "prepare failed \n");
}
ndelay(1000);
printk(KERN_INFO "spi read data %d\n", readadc(1));
ndelay(1000);
printk(KERN_INFO "%s\n", __func__);
ret = gpio_request_one(DIO11, GPIOF_OUT_INIT_LOW, "DIO11");
if (ret) {
printk(KERN_ERR "Unable to request DIO11: %d\n", ret);
return ret;
}
ret = gpio_request_one(DIO12, GPIOF_OUT_INIT_LOW, "DIO12");
if (ret) {
printk(KERN_ERR "Unable to request DIO12: %d\n", ret);
return ret;
}
init_timer(&blink_timer);
blink_timer.function = blink_timer_func;
blink_timer.expires = jiffies + (1*HZ);
add_timer(&blink_timer);
return ret;
}
static void __exit gpiomod_exit(void)
{
printk(KERN_INFO "%s\n", __func__);
del_timer_sync(&blink_timer);
if(!IS_ERR_OR_NULL(mcp3008))
filp_close(mcp3008, 0);
gpio_set_value(DIO11, 0);
gpio_set_value(DIO12, 0);
gpio_free(DIO11);
gpio_free(DIO12);
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kensuke TAKITA");
MODULE_DESCRIPTION("Kernel module using SPI.");
module_init(gpiomod_init);
module_exit(gpiomod_exit);