Recently, I have tried to use MCP3008 in the kernel module to make a device driver for the linear sensor.
When I write a code to read data through MCP3008 with wiringPi, it's possible to obtain the sensor value correctly, so I guess the connection between Raspberry Pi3 and MCP3008 has no problem.
But when I load the kernel module of the following code, the function "readadc(1)" in "gpiomod_init" returns strage value, but the function in timer call_back function"blink_timer_func" returns correct value. And when I checked SPI_CS(CE0) (Pin 26) with oscilloscope, only the CS signal for the function in timer call_back function is displayed.
I wonder why the function to access SPI does not work in the "gpiomod_init" and it works fine in the timer call_back.
Does anyone tell me what I should add/modify the code to make the function to access SPI work in the "gpiomod_init"?
Thanks
Kensuke TAKITA.
Code: Select all
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);