简介
Si24R1 是一颗工作在 2.4GHz ISM 频段, 专为低功耗无线场合设计, 集成嵌入式ARQ 基带协议引擎的无线收发器芯片。 工作频率范围为 2400MHz-2525MHz, 共有 126个 1MHz 带宽的信道。 内部集成高 PSRR 的 LDO 电源, 保证 1.9-3.6V 宽电源范围内稳定工作。
Si24R1 采用 GFSK/FSK 数字调制与解调技术。 数据传输速率可以调节, 支持2Mbps,1Mbps,250Kbps 三种数据速率。 高的数据速率可以在更短的时间完成同样的数据收发, 因此可以具有更低的功耗。 芯片输出功率可调节, 根据实际应用场合配置相应适合的输出功率, 节省系统的功耗。
Si24R1 针对低功耗应用场合进行了特别优化, 在关断模式下, 所有寄存器值与 FIFO值保持不变, 关断电流为 1uA; 在待机模式下, 时钟保持工作, 电流为 15uA, 并且可以在最长 130uS 时间内开始数据的收发。Si24R1 操作简便, 只需要 MCU 通过 SPI 接口对芯片少数几个寄存器配置即可以实现数据的收发通信。 嵌入式 ARQ 基带引擎基于包通信原理, 支持多种通信模式, 可以手动或全自动 ARQ 协议操作。 内部集成收发 FIFO, 保证芯片与 MCU 数据连续传输,增强型 ARQ 基带协议引擎能处理所有高速操作, 大大降低 MCU 的系统消耗。
Si24R1 具有非常低的系统应用成本, 只需要一个 MCU 和少量外围无源器件即可以组成一个无线数据收发系统。 数字 I/O 兼容 2.5V/3.3V/5V 等多种标准 I/O 电压, 可以与各种 MCU 端口直接连接。
Si24R1 芯片内部有状态机, 控制着芯片在不同工作模式之间的转换。
Si24R1 可配置为 Shutdown、 Standby、 Idle-TX、 TX 和 RX 五种工作模式。 状态
转换图如图 3-1 所示。
3.1.1 Shutdown 工作模式
在 Shutdown 工作模式下, Si24R1 所有收发功能模块关闭, 芯片停止工作, 消耗电流最小, 但所有内部寄存器值和 FIFO 值保持不变, 仍可通过 SPI 实现对寄存器的读写。设置 CONFIG 寄存器的 PWR_UP 位的值为 0, 芯片立即返回到 Shutdown 工作模式。
3.1.2 Standby 工作模式
在 Standby 工作模式, 只有晶体振荡器电路工作, 保证了芯片在消耗较少电流的同时能够快速启动。 设置 CONFIG 寄存器下的 PWR_UP 位的值为 1, 芯片待时钟稳定后进入 Standby 模式。 芯片的时钟稳定时间一般为 1.5~2ms, 与晶振的性能有关。 当引脚CE=1 时, 芯片将由 Standby 模式进入到 Idle-TX 或 RX 模式, 当 CE=0 时, 芯片将由Idle-TX、 TX 或 RX 模式返回到 Standby 模式。
3.1.3 Idle-TX 工作模式
在 Idle-TX 工作模式下, 晶体振荡器电路及时钟电路工作。 相比于 Standby 模式,芯片消耗更多的电流。 当发送端 TX FIFO 寄存器为空, 并且引脚 CE=1 时, 芯片进入到Idle-TX 模式。 在该模式下, 如果有新的数据包被送到 TX FIFO 中, 芯片内部的电路将立即启动, 切换到 TX 模式将数据包发送。在 Standby 和 Idle-TX 工作模式下, 所有内部寄存器值和 FIFO 值保持不变, 仍可通过 SPI 实现对寄存器的读写。
3.1.4 TX 工作模式
当需要发送数据时, 需要切换到 TX 工作模式。 芯片进入到 TX 工作模式的条件为:TX FIFO 中有数据, CONFIG 寄存器的 PWR_UP 位的值为 1, PRIM_RX 位的值为 0,同时要求引脚 CE 上有一个至少持续 10us 的高脉冲。 Idle-TX 模式切换到 TX 模式的时间为 120us~130us 之间, 但不会超过 130us。 单包数据发送完成后, 如果 CE=1, 则由TX FIFO 的状态来决定芯片所处的工作模式, 当 TX FIFO 还有数据, 芯片继续保持在TX 工作模式, 并发送下一包数据; 当 TX FIFO 没有数据, 芯片返回 Idle-TX 模式; 如果 CE=0, 立即返回 Standby 模式。 数据发射完成后, 芯片产生数据发射完成中断。
3.1.5 RX 工作模式
当需要接收数据时, 需要切换到 RX 工作模式。 芯片进入到 RX 工作模式的条件为:设置寄存器 CONFIG 的 PWR_UP 位的值为 1, PRIM_RX 位的值为 1, 并且引脚 CE=1。芯片由 Standby 模式切换到 RX 模式的时间为 120~130us。 当接收到数据包的地址与芯片的地址相同, 并且 CRC 检查正确时, 数据会自动存入 RX FIFO, 并产生数据接收中断。 芯片最多可以同时存三个有效数据包, 当 FIFO 已满, 接收到的数据包被自动丢掉。
在接收模式下, 可以通过 RSSI 寄存器检测接收信号功率。 当接收到的信号强度大于-60dBm 时, RSSI 寄存器的 RSSI 位的值将被设置为 1。 否则, RSSI=0。。 RSSI 寄存器的更新方法有两种: 当接收到有效的数据包后, RSSI 会自动更新, 此外, 将芯片从RX 模式换到 Standby 模式时 RSSI 也会自动更新。 RSSI 的值会随温度的变化而变化,范围在±5dBm 以内。
简单介绍完Si24R1 芯片的工作原理,下面开始Linux kernel中驱动代码的实现。
#ifndef __SI24R1_REG_H
#define __SI24R1_REG_H
#include <linux/types.h>
#include <linux/cdev.h>
/********************** SI24R1 Register Address **********************/
#define TX_ADR_WIDTH 5 //
#define RX_ADR_WIDTH 5 //
#define TX_PLOAD_WIDTH 32
#define RX_PLOAD_WIDTH 32
/** Define interface to Si24R1 **/
// SPI(Si24R1) commands
#define SI24R1_R_REGISTER 0x00 // Define read command to register
#define SI24R1_W_REGISTER 0x20 // Define write command to register
#define R_RX_PAYLOAD 0x61 // Define RX payload register address
#define W_TX_PAYLOAD 0xA0 // Define TX payload register address
#define FLUSH_TX 0xE1 // Define flush TX register command
#define FLUSH_RX 0xE2 // Define flush RX register command
#define REUSE_TX_PL 0xE3 // Define reuse TX payload register command
#define R_RX_PL_WID 0x60 // Define Read RX payload width for the R_RX_PAYLOAD in the RX FIFO
#define W_ACK_PAYLOAD 0xA8 // Define Write Payload to be transmitted together with ACK packet on PIPExxx
// xxx valid in the range from 000 to 101)
#define W_TX_PAYLOAD_NOACK 0xB0 // Define Write TX Disables AUTOACK on this specific packet
#define NOP 0xFF // Define No Operation, might be used to read status register
// SPI(Si24R1) registers(addresses)
#define CONFIG 0x00 // 'Config' register address
#define EN_AA 0x01 // 'Enable Auto Acknowledgment' register address
#define EN_RXADDR 0x02 // 'Enabled RX addresses' register address
#define SETUP_AW 0x03 // 'Setup address width' register address
#define SETUP_RETR 0x04 // 'Setup Auto. Retrans' register address
#define RF_CH 0x05 // 'RF channel' register address
#define RF_SETUP 0x06 // 'RF setup' register address
#define STATUS 0x07 // 'Status' register address
#define OBSERVE_TX 0x08 // 'Observe TX' register address
#define CD 0x09 // 'Carrier Detect' register address
#define RX_ADDR_P0 0x0A // 'RX address pipe0' register address
#define RX_ADDR_P1 0x0B // 'RX address pipe1' register address
#define RX_ADDR_P2 0x0C // 'RX address pipe2' register address
#define RX_ADDR_P3 0x0D // 'RX address pipe3' register address
#define RX_ADDR_P4 0x0E // 'RX address pipe4' register address
#define RX_ADDR_P5 0x0F // 'RX address pipe5' register address
#define TX_ADDR 0x10 // 'TX address' register address
#define RX_PW_P0 0x11 // 'RX payload width, pipe0' register address
#define RX_PW_P1 0x12 // 'RX payload width, pipe1' register address
#define RX_PW_P2 0x13 // 'RX payload width, pipe2' register address
#define RX_PW_P3 0x14 // 'RX payload width, pipe3' register address
#define RX_PW_P4 0x15 // 'RX payload width, pipe4' register address
#define RX_PW_P5 0x16 // 'RX payload width, pipe5' register address
#define FIFO_STATUS 0x17 // 'FIFO Status Register' register address
#define DYNPD 0x1C // 'Enable dynamic paload length' register address
#define FEATURE 0x1D // Feature Register address
#define TX_FULL 0x01 //bit0 TX FIFO full flag
#define MAX_RT 0x10 //bit4 最大重发次数中断位
#define TX_DS 0x20 //bit5 TX发送完成中断
#define RX_DR 0x40 //bit6 RX接收到数据中断
#define TRX_OK 0x60 // TX_DS 与 RX_DR均产生,适用于ACK with Payload
#define RXFIFO_EMPTY 0x0e // RX FIFO为空
/** Data Structure defined **/
enum work_mode {
SHUTDOWN = 0,
STANDBY = 1,
IDLE_TX = 2,
MOD_TX = 3,
MOD_RX = 4,
MOD_TX_NOACK = 5,
MOD_RX_NOACK = 6
};
extern uint8_t TX_ADDRESS[TX_ADR_WIDTH]; //
extern uint8_t RX_ADDRESS[RX_ADR_WIDTH]; //
extern uint8_t TX_BUF[TX_PLOAD_WIDTH];
extern uint8_t RX_BUF[RX_PLOAD_WIDTH];
extern uint8_t g_cFirstPacketFlag;
#endif /* __SI24R1_REG_H */
si24r1_main.c文件内容
/*********************************头文件***********************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/compat.h>
#include <asm/uaccess.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/input.h>
#include <linux/irqreturn.h>
#include <linux/list.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/wakelock.h>
#include <linux/fb.h>
#include <linux/power_supply.h>
#include <linux/notifier.h>
#include <linux/workqueue.h>
#include <linux/jiffies.h>
#include <linux/hrtimer.h>
#include <linux/time.h>
#include <linux/regulator/consumer.h>
#include <linux/completion.h>
#include <linux/version.h>
#include <linux/miscdevice.h>
#include <linux/power_supply.h>
#ifdef CONFIG_OF
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#endif
#include "si24r1_spi.h"
#include "si24r1_reg.h"
/**********************************宏定义************************************/
/*设备信息*/
#define SI24R1_RFID_MAJOR 186 //主设备号
#define ALLOC_DEVNO
#define SI24R1_DEV_NAME "si24r1_rfid" //字符设备名称
#define SI24R1_CLASS_NAME "si24r1_rfid" //类名
#define BIRD_SI24R1_TX_MODE //给硬件射频测试
/*********************************全局变量定义*********************************/
const static unsigned int bufsiz = 10240*10;
static int reg_status = -1;
static struct wake_lock wakelock;
static struct work_struct bird_rfid_eint_work;
static struct workqueue_struct * bird_rfid_eint_workqueue = NULL;
#ifdef BIRD_SI24R1_HW_TIMER_TEST
#define SI24R1_TIMER_CYCLE 2000 //定时器周期 1000ms
struct hrtimer si24r1_timer;
#endif
struct si24r1_data *si24r1_spidev;
int g_irq_happend = -1; //中断测试使用
static char DRIVER_VERSION[20]="DRIVER_1.1.0 @BIRD";
static int irq_num=-1;
//static uint16_t rfid_si24r1_id[20]={0xa062,0x0631,0x0131};
/********************************结构体定义************************************/
/**
* struct si24r1_ioc_transfer 描述芯片通过ioctl传递的数据
* @cmd 命令值
* @addr 地址
* @buf 数据缓存区
* @actual_len 要传送的数据的字节数
*/
struct si24r1_ioc_transfer {
unsigned char cmd;
unsigned short addr;
unsigned char *buf;
unsigned short actual_len;
};
/***********************************函数定义***************************************/
/**
* @brief si24r1_enable_irq 使能中断
*
* @param [in] si24r1_data 芯片数据的结构体指针
*
* @return 无返回值
*/
static void si24r1_enable_irq(struct si24r1_data *si24r1_data)
{
if(!si24r1_data->irq_enabled){
enable_irq(si24r1_data->irq);
si24r1_data->irq_enabled = true;
}
}
/**
* @brief si24r1_disable_irq 关闭中断
*
* @param [in] si24r1_data 芯片数据的结构体指针
*
* @return 无返回值
*/
static void si24r1_disable_irq(struct si24r1_data *si24r1_data)
{
if(si24r1_data->irq_enabled){
disable_irq_nosync(si24r1_data->irq);
si24r1_data->irq_enabled = false;
}
}
/**
* @brief si24r1_irq_handler 中断处理函数
*
* @param [in] irq 中断号
* @param [in] dev 芯片数据的结构体指针
*
* @return 总是返回IRQ_HANDLED
*/
static irqreturn_t si24r1_irq_handler(int irq,void *dev)
{
//struct si24r1_data *si24r1_data = (struct si24r1_data *)dev;
//uint8_t status = 0;
g_irq_happend = 1;
//si24r1_dbg("[Sky] got_an_irq...\n");
//si24r1_disable_irq(si24r1_data);
//si24r1_dbg("[Sky] disable irq ...\n");
#if 1
queue_work(bird_rfid_eint_workqueue, &bird_rfid_eint_work);
#endif
//si24r1_enable_irq(si24r1_data);
//si24r1_dbg("[Sky] enable irq ...\n");
return IRQ_HANDLED;
}
void bird_rfid_eint_work_callback(struct work_struct *work)
{
uint8_t status = 0;
uint8_t cmd = FLUSH_TX;
si24r1_dbg("[Sky] ENTER ...\n");
//wake_lock(&wakelock);
wake_lock_timeout(&wakelock, msecs_to_jiffies(2000));
#if 1
/** read STATUS reg **/
read_SFR(SI24R1_R_REGISTER + STATUS, &status);
si24r1_dbg("[Sky] get si24r1 status: 0x%02x \n", status);
if(status & TX_DS)
{
write_SFR(SI24R1_W_REGISTER + STATUS, status); //clear TX_DS flag
si24r1_dbg("[Sky] Clear TX_DS flag ...\n");
si24r1_spi_send_cmd(&cmd, 1); //FLUSH_TX
}
if(status & RX_DR)
{
write_SFR(SI24R1_W_REGISTER + STATUS, status); //clear RX_DR flag
si24r1_dbg("[Sky] Clear RX_DR flag ...\n");
si24r1_reg_read(R_RX_PAYLOAD, RX_BUF, RX_PLOAD_WIDTH); // 读 FIFO 到 RX_BUF[]
}
#endif
#ifdef BIRD_SI24R1_HW_TIMER_TEST
#ifdef BIRD_SI24R1_TX_MODE
//si24r1_reg_write(W_TX_PAYLOAD, TX_BUF, TX_PLOAD_WIDTH); //W_TX_PAYLOAD 写 FIFO
si24r1_reg_write(W_TX_PAYLOAD_NOACK, TX_BUF, TX_PLOAD_WIDTH); //W_TX_PAYLOAD_NOACK 写 FIFO
#endif
hrtimer_start(&si24r1_timer, ktime_set(SI24R1_TIMER_CYCLE / 1000, (SI24R1_TIMER_CYCLE % 1000) * 1000000),HRTIMER_MODE_REL);
#endif
}
#ifdef BIRD_SI24R1_HW_TIMER_TEST
static enum hrtimer_restart si24r1_timer_func(struct hrtimer *timer)
{
printk("###### si24r1_timer_func ###### \n");
//schedule_work(&bird_rfid_eint_work);
queue_work(bird_rfid_eint_workqueue, &bird_rfid_eint_work);
return HRTIMER_NORESTART;
}
#endif
void si24r1_release_gpio(struct si24r1_data* si24r1_data)
{
if (gpio_is_valid(si24r1_data->irq_gpio)){
gpio_free(si24r1_data->irq_gpio);
}
if (gpio_is_valid(si24r1_data->ce_gpio)){
gpio_free(si24r1_data->ce_gpio);
}
}
int si24r1_set_ce_gpio(struct si24r1_data *si24r1_data, unsigned int level)
{
//seting output high
if(level != 0){
gpio_set_value(si24r1_data->ce_gpio, 1);
//seting output low
}else{
gpio_set_value(si24r1_data->ce_gpio, 0);
}
return 0;
}
/**
* @brief si24r1_spi_setup 设置spi参数
*
* @param [in] spi 指向spi_device结构体的指针
*
* @return 成功返回0,失败返回负数
*/
static int si24r1_spi_setup(struct spi_device *spi)
{
spi->mode = SPI_MODE_0;
spi->bits_per_word = 8;
spi->max_speed_hz = 6*1000*1000;
//spi->chip_select = 0;
return spi_setup(spi);
}
/**
* @brief si24r1_read 芯片读操作,从fp读取count字节数据到buf
*
* @param [in] fp file结构体指针,表示打开的一个文件
* @param [in] buf 用户空间的buffer指针
* @param [in] count 要读取的字节数
* @param [in] offset 文件偏移指针
*
* @return 成功返回读取的字节数,失败返回负数
*/
static ssize_t si24r1_read(struct file *fp, char __user *buf, size_t count, loff_t *offset)
{
struct si24r1_data *si24r1_data;
unsigned long missing;
int status = -1;
if(NULL == buf)
return -EINVAL;
if(count > bufsiz)
return -EMSGSIZE;
si24r1_data = si24r1_spidev;//(struct si24r1_data*)fp->private_data;
mutex_lock(&si24r1_data->buf_lock);
missing = copy_to_user(buf,®_status,count);
if(missing == count ){
status = -EFAULT;
}else{
status = count - missing;
}
mutex_unlock(&si24r1_data->buf_lock);
return status;
}
/**
* @brief si24r1_write 芯片写操作,将buf的count个字节的数据写入fp
*
* @param [in] fp file结构体指针,表示打开的一个文件
* @param [in] buf 用户空间的buffer指针,表示要写入的数据
* @param [in] count 要写入的字节数,注意不能超过128个字节
* @param [in] offset 文件偏移
*
* @return 成功返回写入的字节数,失败返回负数
*/
static ssize_t si24r1_write(struct file *fp, const char __user *buf, size_t count, loff_t *offset)
{
int status = -1;
struct si24r1_data *si24r1_data;
unsigned long missing;
unsigned char buffer[128] = {0};
if(count > 128)
return -EMSGSIZE;
si24r1_data = si24r1_spidev;//(struct si24r1_data*)fp->private_data;
missing = copy_from_user(buffer,buf,count);
if(missing != 0){
status = -EFAULT;
}
return status;
}
/**
* @brief si24r1_open 芯片打开操作
*
* @param [in] inode inode结构体指针
* @param [in] fp file结构体指针
*
* @return 成功返回0 失败返回负数
*/
static int si24r1_open(struct inode *inode, struct file *fp)
{
return 0;
}
/**
* @brief si24r1_release 芯片关闭操作,close的时候调用
*
* @param [in] inode inode结构体指针
* @param [in] fp file结构体指针
*
* @return 总是返回0
*/
static int si24r1_release(struct inode *inode, struct file *fp)
{
return 0;
}
/**
* @brief si24r1_ioctl 芯片IO控制函数
*
* @param [in] fp file结构体指针
* @param [in] cmd 命令
* @param [in] arg 参数,通常是一个指针
*
* @return 成功返回0,失败返回负数
*/
static long si24r1_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
int err = 0;
int retval = 0;
int missing = 0;
uint8_t command = 0;
struct si24r1_ioc_transfer *ioc = NULL;
struct si24r1_data *si24r1_data;
struct spi_device *spi;
si24r1_dbg("[Sky] ENTER ...\n");
/*命令合法性判断,magic码为'K'*/
if(_IOC_TYPE(cmd) != SI24R1_IOC_MAGIC)
return -ENOTTY;
/*内核交互环境判断是否可以读写*/
if(_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE,(void __user*)arg,_IOC_SIZE(cmd));
if(err==0 && _IOC_DIR(cmd)&_IOC_WRITE)
err = !access_ok(VERIFY_READ,(void __user*)arg,_IOC_SIZE(cmd));
if(err)
return -EFAULT;
/*获取spi_device*/
si24r1_data = si24r1_spidev;//(struct si24r1_data*)fp->private_data;
spin_lock_irq(&si24r1_data->spin_lock);
spi = spi_dev_get(si24r1_data->spi);
spin_unlock_irq(&si24r1_data->spin_lock);
if(spi == NULL)
return -ESHUTDOWN;
/* use the buffer lock here for triple duty:
* - prevent I/O (from us) so calling spi_setup() is safe;
* - prevent concurrent SPI_IOC_WR_* from morphing
* data fields while SPI_IOC_RD_* reads them;
* - SI24R1_IOC_SPI_MESSAGE needs the buffer locked "normally".
*/
mutex_lock(&si24r1_data->buf_lock);
switch(cmd)
{
/*irq test*/
case SI24R1_IOC_GET_IRQ_STATE:
si24r1_dbg("irq state: %d\n",g_irq_happend);
missing = copy_to_user((u8 __user *)arg,&g_irq_happend,4);
if(missing != 0){
si24r1_dbg("SI24R1_RFID_IOC_GET_IRQ_STATE Failed to copy key from user space\n");
retval = -EFAULT;
}
g_irq_happend=0;
break;
/*set CE GPIO status*/
case SI24R1_IOC_SET_CE_GPIO:
si24r1_dbg("get cmd CHIP ENABLE\n");
retval = __get_user(command,(u8 __user*)arg);
if(retval == 0){
retval = si24r1_set_ce_gpio(si24r1_data,command);
if(retval < 0){
si24r1_dbg("Failed to send spi cmd,cmd = 0x%x",command);
retval = -EFAULT;
break;
}
}
break;
/*使能中断*/
case SI24R1_IOC_ENABLE_IRQ:
si24r1_enable_irq(si24r1_data);
si24r1_dbg("enable irq\n");
break;
/*关闭中断*/
case SI24R1_IOC_DISABLE_IRQ:
si24r1_disable_irq(si24r1_data);
si24r1_dbg("disable irq\n");
break;
/*spi数据处理*/
case SI24R1_IOC_SPI_MESSAGE:
si24r1_dbg("spi message\n");
/*为chips_ioc_transfer 申请内存*/
ioc = kzalloc(sizeof(*ioc),GFP_KERNEL);
if(NULL == ioc){
si24r1_dbg("Failed to allocat mem for ioc\n");
retval = -EFAULT;
break;
}
/*获取用户空间的参数*/
missing = copy_from_user(ioc,(u8 __user*)arg,sizeof(*ioc));
if(missing != 0){
si24r1_dbg("Failed to copy from userspace to kernel space\n");
retval = -EFAULT;
break;
}
/*判断参数合法性*/
if((ioc->actual_len > bufsiz) || (ioc->actual_len == 0)){
si24r1_dbg("the length of bytes:%d transferred not suported\n",ioc->actual_len);
retval = -EMSGSIZE;
break;
}
si24r1_dbg("ioc->cmd = %x\n",ioc->cmd);
/*写寄存器*/
if(ioc->cmd == SI24R1_W_REGISTER){
si24r1_dbg("write sfr\n");
missing = copy_from_user(si24r1_data->buffer,(u8 __user*)ioc->buf,ioc->actual_len);
if(missing != 0){
si24r1_dbg("Failed to copy from userspace to kernel\n");
retval = -EFAULT;
break;
}
si24r1_dbg("si24r1_data->buffer[0] = 0x%x,ioc->actual_len = %d\n",si24r1_data->buffer[0],ioc->actual_len);
retval = si24r1_reg_write(ioc->addr,si24r1_data->buffer,ioc->actual_len);
if(retval < 0){
si24r1_dbg("write sfr error,retval = %d\n",retval);
retval = -EFAULT;
break;
};
/*读寄存器*/
}else if(ioc->cmd == SI24R1_R_REGISTER){
si24r1_dbg("read sfr\n");
retval = si24r1_reg_read(ioc->addr,si24r1_data->buffer,ioc->actual_len);
if(retval < 0){
si24r1_dbg("read sfr error,retval = %d\n",retval);
retval = -EFAULT;
break;
};
si24r1_dbg("si24r1_data->buffer[0] =0x%x,ioc->actual_len = %d\n",si24r1_data->buffer[0],ioc->actual_len);
missing = copy_to_user((u8 __user*)ioc->buf,si24r1_data->buffer,ioc->actual_len);
if(missing != 0){
si24r1_dbg("Failed to copy from kernel to user\n");
retval = -EFAULT;
break;
}
}else{
si24r1_dbg("error cmd for ioc\n");
retval = -EFAULT;
}
break;
/* 发送spi命令 */
case SI24R1_IOC_SPI_SEND_CMD:
si24r1_dbg("spi send cmd...\n");
retval = __get_user(command,(u8 __user*)arg);
if(retval == 0){
si24r1_dbg("spi cmd from userspace is:0x%x\n",command);
retval = si24r1_spi_send_cmd(&command,1);
if(retval < 0){
si24r1_dbg("Failed to send spi cmd,cmd = 0x%x",command);
retval = -EFAULT;
break;
}
}
break;
/* 发送 data */
case SI24R1_IOC_TX_DATA_CMD:
si24r1_dbg("si24r1 rfid Tx data...\n");
retval = si24r1_reg_write(W_TX_PAYLOAD_NOACK, TX_BUF, TX_PLOAD_WIDTH); // 写 FIFO
if(retval < 0){
si24r1_dbg("si24r1 rfid Tx data Failed !!! \n");
retval = -EFAULT;
break;
}
break;
/* 接收 data */
case SI24R1_IOC_RX_DATA_CMD:
si24r1_dbg("si24r1 rfid Rx data...\n");
//retval = si24r1_reg_read(R_RX_PAYLOAD, RX_BUF, RX_PLOAD_WIDTH); // 读 FIFO
missing = copy_to_user((u8 __user *)arg, RX_BUF, sizeof(RX_BUF));
if(missing < 0){
si24r1_dbg("si24r1 rfid get Rx data Failed !!! \n");
retval = -EFAULT;
break;
}
memset(RX_BUF, 0, RX_PLOAD_WIDTH); //clear RX_BUF[]
break;
case SI24R1_IOC_GET_DRIVER_VERSION:
si24r1_dbg("si24r1 rfid get driver version...\n");
missing = copy_to_user((u8 __user *)arg,DRIVER_VERSION,20);
if(missing != 0){
si24r1_dbg("SI24R1_RFID_IOC_GET_IRQ_STATE Failed to copy key from user space\n");
retval = -EFAULT;
}
break;
/* 设置 Si24R1 工作模式 */
case SI24R1_IOC_MODE_CONFIG:
si24r1_dbg("work mode config ...\n");
retval = __get_user(command,(u8 __user*)arg);
if(retval == 0){
si24r1_dbg("get work mode from userspace is:0x%x\n",command);
retval = si24r1_set_work_mode(command);
if(retval < 0){
si24r1_dbg("Failed to set Si24R1 work mode, mode = %d", command);
retval = -EFAULT;
break;
}
}
break;
default:
break;
}
if(NULL != ioc){
kfree(ioc);
ioc = NULL;
}
mutex_unlock(&si24r1_data->buf_lock);
spi_dev_put(spi);
return retval;
}
#ifdef CONFIG_COMPAT
/**
* @brief si24r1_compat_ioctl 作兼容用的芯片IO控制函数
*
* @param [in] fp file结构体指针
* @param [in] cmd 命令
* @param [in] arg 参数,通常是一个指针
*
* @return 成功返回0,失败返回负数
*/
static long si24r1_compat_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
return si24r1_ioctl(fp, cmd, arg);
}
#else
#define si24r1_compat_ioctl NULL
#endif
static const struct file_operations si24r1_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = si24r1_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = si24r1_compat_ioctl,
#endif
.open = si24r1_open,
.release = si24r1_release,
.read = si24r1_read,
.write = si24r1_write,
};
static int si24r1_get_gpio_info_from_dts(struct si24r1_data *si24r1_data)
{
#ifdef CONFIG_OF
int ret;
struct device_node *node = NULL;
node = of_find_compatible_node (NULL, NULL, "si24r1, rfid");
si24r1_dbg ("get pinctrl success! \n");
si24r1_data->ce_gpio = of_get_named_gpio(node, "si24r1-ce-gpios", 0);
si24r1_data->irq_gpio = of_get_named_gpio(node, "si24r1-irq-gpios", 0); //73
si24r1_data->irq = gpio_to_irq(si24r1_data->irq_gpio);
if (gpio_is_valid(si24r1_data->ce_gpio) && gpio_is_valid(si24r1_data->irq_gpio)) {
si24r1_dbg ("gpio_is_valid success! \n");
ret = gpio_request(si24r1_data->ce_gpio, "si24r1-ce-gpios");
if (ret) {
si24r1_dbg ("gpio_request failed ce_gpio");
return ret;
}
ret = gpio_request(si24r1_data->irq_gpio, "si24r1-irq-gpios");
if (ret) {
si24r1_dbg ("gpio_request failed bf_irq");
return ret;
}
ret = gpio_direction_output(si24r1_data->ce_gpio, 1);
if (ret) {
si24r1_dbg ("gpio_direction_output failed ce_gpio");
return ret;
}
ret = gpio_direction_input(si24r1_data->irq_gpio);
if (ret) {
si24r1_dbg ("gpio_direction_output failed irq_gpio");
return ret;
}
si24r1_data->irq = gpio_to_irq(si24r1_data->irq_gpio);
if (si24r1_data->irq < 0) {
si24r1_dbg ("gpio_to_irq failed irq_gpio");
return ret;
}
} else {
si24r1_dbg ("gpio_is_valid failed");
ret = -1;
return ret;
}
#endif
return 0;
}
static int si24r1_probe(struct spi_device *spi)
{
int status = 0;
uint8_t temp = 0, ret = 0;
struct si24r1_data * si24r1_data;
printk("[Sky] si24r1_probe...\n");
for(temp = 0; temp < TX_PLOAD_WIDTH; temp++)
{
TX_BUF[temp]= temp;
}
si24r1_data = kzalloc(sizeof(struct si24r1_data),GFP_KERNEL);
if(NULL == si24r1_data){
si24r1_dbg("kzalloc mem for si24r1_data error\n");
return -ENOMEM;
}
si24r1_data->buffer = kzalloc(TX_PLOAD_WIDTH+1, GFP_KERNEL);
si24r1_spidev = si24r1_data;
spin_lock_init(&si24r1_data->spin_lock);
mutex_init(&si24r1_data->buf_lock);
/*初始化SPI参数*/
si24r1_data->spi = spi;
if(si24r1_spi_setup(spi) < 0){
si24r1_dbg("spi_setup error\n");
return -ENODEV;
}
if (si24r1_get_gpio_info_from_dts(si24r1_data) < 0) {
si24r1_dbg("gpio init error\n");
return -ENODEV;
}
#ifdef ALLOC_DEVNO
ret = alloc_chrdev_region(&si24r1_data->devt, 0, 1, SI24R1_DEV_NAME);
if (ret) {
si24r1_dbg("bird si24r1 alloc_chrdev_region fail: %d\n", ret);
status = -ENODEV;
} else {
si24r1_dbg("bird si24r1 major: %d, minor: %d\n", MAJOR(si24r1_data->devt), MINOR(si24r1_data->devt));
}
cdev_init(&si24r1_data->si24r1_cdev, &si24r1_fops);
si24r1_data->si24r1_cdev.owner = THIS_MODULE;
ret = cdev_add(&si24r1_data->si24r1_cdev, si24r1_data->devt, 1);
if (ret) {
si24r1_dbg("bird si24r1 cdev_add fail: %d\n", ret);
return ret;
}
#else
ret = register_chrdev(SI24R1_RFID_MAJOR, SI24R1_DEV_NAME, &si24r1_fops);
if (ret != 0) {
si24r1_dbg("bird si24r1 Unable to register chardev on major=%d (%d)\n", SI24R1_RFID_MAJOR, ret);
return ret;
}
si24r1_data->devt = MKDEV(SI24R1_RFID_MAJOR, 0);
#endif
si24r1_data->si24r1_class = class_create(THIS_MODULE, SI24R1_CLASS_NAME);
if (IS_ERR(si24r1_data->si24r1_class)) {
si24r1_dbg("bird si24r1 Unable to create class, err = %d\n", (int)PTR_ERR(si24r1_data->si24r1_class));
return PTR_ERR(si24r1_data->si24r1_class);
}
si24r1_data->si24r1_device = device_create(si24r1_data->si24r1_class, NULL, si24r1_data->devt, NULL, SI24R1_DEV_NAME);
if(!si24r1_data->si24r1_device){
si24r1_dbg("bird si24r1 device_create fail\n");
goto err;
}
si24r1_dbg("bird si24r1 create device class Done.\n");
//设置共享数据
spi_set_drvdata(spi,si24r1_data);
wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "si24r1_wakelock");
#ifdef BIRD_SI24R1_HW_TIMER_TEST
/* 设置timer transmit */
hrtimer_init(&si24r1_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
si24r1_timer.function = si24r1_timer_func;
#endif
bird_rfid_eint_workqueue = create_singlethread_workqueue("rfid_si24r1_eint");
INIT_WORK(&bird_rfid_eint_work, bird_rfid_eint_work_callback);
//注册中断处理函数,可以查看/proc/interrupts目录确认是否注册成功 IRQF_TRIGGER_FALLING
status = request_irq(si24r1_data->irq,si24r1_irq_handler, IRQF_TRIGGER_LOW | IRQF_NO_SUSPEND | IRQF_ONESHOT,"si24r1_irq",si24r1_data);
if(status != 0){
si24r1_dbg("request_irq error\n");
goto err;
}
irq_num = si24r1_data->irq; //
si24r1_data->irq_enabled = true;
#if 0
//使能irq唤醒
status = enable_irq_wake(si24r1_data->irq);
if(status != 0){
free_irq(si24r1_data->irq,si24r1_data);
si24r1_dbg("enable_irq_wake error\n");
goto err;
}
#endif
/* 初始化 RFID 寄存器状态 */
status = read_SFR(SI24R1_R_REGISTER + CONFIG, &temp);
si24r1_dbg("read CONFIG reg:0x%02x; status:0x%02x\n", temp, status);
status = write_SFR(SI24R1_W_REGISTER + CONFIG, 0x00); //PWR_UP=0, Shutdown mode
if(status < 0){
si24r1_dbg("Failed to set idle mode\n");
return status;
}
status = read_SFR(SI24R1_R_REGISTER + CONFIG, &temp);
si24r1_dbg("read CONFIG reg:0x%02x; status:0x%02x\n", temp, status);
#if 0
#ifdef BIRD_SI24R1_TX_MODE
si24r1_set_work_mode(MOD_TX_NOACK); //MOD_TX
#else
si24r1_set_work_mode(MOD_RX_NOACK); //MOD_RX
#endif
status = read_SFR(SI24R1_R_REGISTER + CONFIG, &temp);
si24r1_dbg("read CONFIG reg:0x%02x; status:0x%02x\n", temp, status);
#endif
#ifdef BIRD_SI24R1_HW_TIMER_TEST
hrtimer_start(&si24r1_timer, ktime_set(SI24R1_TIMER_CYCLE / 1000, (SI24R1_TIMER_CYCLE % 1000) * 1000000),HRTIMER_MODE_REL);
#endif
return 0;
err:
device_destroy(si24r1_data->si24r1_class, si24r1_data->devt);
class_destroy(si24r1_data->si24r1_class);
#ifdef ALLOC_DEVNO
cdev_del(&si24r1_data->si24r1_cdev);
unregister_chrdev_region(si24r1_data->devt, 1);
#else
unregister_chrdev(SI24R1_RFID_MAJOR, SI24R1_DEV_NAME);
#endif
si24r1_release_gpio(si24r1_data);
kfree(si24r1_data->buffer);
kfree(si24r1_data);
si24r1_spidev = NULL;
return -1;
}
static int si24r1_remove(struct spi_device *spi)
{
struct si24r1_data* si24r1_data = spi_get_drvdata(spi);
/* make sure ops on existing fds can abort cleanly */
/*注销共享数据*/
spin_lock_irq(&si24r1_data->spin_lock);
si24r1_data->spi = NULL;
spi_set_drvdata(spi, NULL);
spin_unlock_irq(&si24r1_data->spin_lock);
/*释放中断号*/
if (si24r1_data->irq !=0)
free_irq(si24r1_data->irq, si24r1_data);
wake_lock_destroy(&wakelock);
destroy_workqueue(bird_rfid_eint_workqueue);
#ifdef BIRD_SI24R1_HW_TIMER_TEST
hrtimer_cancel(&si24r1_timer);
#endif
/*注销设备*/
device_destroy(si24r1_data->si24r1_class, si24r1_data->devt);
/*注销类*/
class_destroy(si24r1_data->si24r1_class);
#ifdef ALLOC_DEVNO
cdev_del(&si24r1_data->si24r1_cdev);
unregister_chrdev_region(si24r1_data->devt, 1);
#else
unregister_chrdev(SI24R1_RFID_MAJOR, SI24R1_DEV_NAME);
#endif
si24r1_release_gpio(si24r1_data);
/*注销互斥锁*/
mutex_destroy(&si24r1_data->buf_lock);
if(si24r1_data != NULL){
kfree(si24r1_data->buffer);
kfree(si24r1_data);
si24r1_spidev = NULL;
}
return 0;
}
#ifdef CONFIG_OF
static struct of_device_id si24r1_of_match_table[] = {
{.compatible = "si24r1, rfid"},
{},
}
MODULE_DEVICE_TABLE(of,si24r1_of_match_table);
#endif
static struct spi_driver si24r1_spi_driver = {
.driver = {
.name = SI24R1_DEV_NAME,
.owner = THIS_MODULE,
.bus = &spi_bus_type,
#ifdef CONFIG_OF
.of_match_table = si24r1_of_match_table,
#endif
},
.probe = si24r1_probe,
.remove = si24r1_remove,
};
static int __init si24r1_init(void)
{
int status;
printk("[Sky] si24r1_init\n");
status = spi_register_driver(&si24r1_spi_driver);
if(status<0){
printk("[Sky] spi_driver_register error\n");
si24r1_dbg("spi_driver_register error\n");
return status;
}
return 0;
}
static void __exit si24r1_exit(void)
{
spi_unregister_driver(&si24r1_spi_driver);
}
module_init(si24r1_init);
module_exit(si24r1_exit);
MODULE_AUTHOR("Sky.Ye @Bird");
MODULE_DESCRIPTION("User mode SPI device interface");
MODULE_LICENSE("GPL");
MODULE_ALIAS("spi: si24r1");
si24r1_spi.h文件内容如下:
#ifndef __SI24R1_SPI_H
#define __SI24R1_SPI_H
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/wait.h>
/**********************IO Magic**********************/
#define CONFIG_OF 1
#define SI24R1_IOC_MAGIC 0xA0
/* define commands */
#define SI24R1_IOC_INIT _IOWR(SI24R1_IOC_MAGIC, 0, unsigned int)
#define SI24R1_IOC_EXIT _IOWR(SI24R1_IOC_MAGIC, 1, unsigned int)
#define SI24R1_IOC_SET_CE_GPIO _IOW(SI24R1_IOC_MAGIC, 2,unsigned char)
#define SI24R1_IOC_SPI_MESSAGE _IOWR(SI24R1_IOC_MAGIC, 3,struct si24r1_ioc_transfer)
#define SI24R1_IOC_SPI_SEND_CMD _IOW(SI24R1_IOC_MAGIC,4,unsigned char)
#define SI24R1_IOC_ENABLE_IRQ _IO(SI24R1_IOC_MAGIC,5)
#define SI24R1_IOC_DISABLE_IRQ _IO(SI24R1_IOC_MAGIC,6)
#define SI24R1_IOC_MODE_CONFIG _IOW(SI24R1_IOC_MAGIC,7,void*)
#define SI24R1_IOC_GET_IRQ_STATE _IOR(SI24R1_IOC_MAGIC, 8,unsigned int)
#define SI24R1_IOC_GET_DRIVER_VERSION _IOR(SI24R1_IOC_MAGIC, 9, void*)
#define SI24R1_IOC_ENTER_SLEEP_MODE _IOWR(SI24R1_IOC_MAGIC, 10, unsigned int)
#define SI24R1_IOC_TX_DATA_CMD _IOWR(SI24R1_IOC_MAGIC, 11, void*)
#define SI24R1_IOC_RX_DATA_CMD _IOWR(SI24R1_IOC_MAGIC, 12, void*)
/* Data Structures */
struct param {
unsigned char cmd;
unsigned short addr;
unsigned short data;
};
struct config {
struct param *p_param;
int num;
};
struct si24r1_data {
uint8_t *buffer;
int users;
int irq;
int irq_gpio;
int ce_gpio;
int pwr_gpio;
bool irq_enabled;
spinlock_t spin_lock;
struct class *si24r1_class;
struct device *si24r1_device;
struct cdev si24r1_cdev;
dev_t devt;
struct mutex buf_lock;
struct fasync_struct *async;
struct spi_device *spi;
struct pinctrl *pinctrl;
struct pinctrl_state *eint_as_int, *ce_output0, *ce_output1;
struct pinctrl_state *si24r1_spi1_mi_as_spi1_mi,*si24r1_spi1_mi_as_gpio,*si24r1_spi1_mo_as_spi1_mo,*si24r1_spi1_mo_as_gpio;
struct pinctrl_state *si24r1_spi1_clk_as_spi1_clk,*si24r1_spi1_clk_as_gpio,*si24r1_spi1_cs_as_spi1_cs,*si24r1_spi1_cs_as_gpio;
};
typedef struct si24r1_data si24r1_data_t;
#define DEBUG
#ifdef DEBUG
#define si24r1_dbg(fmt, args...) do {\
printk("[si24r1] %5d:<%s> "fmt,__LINE__,__func__,##args);\
} while(0)
#else
#define si24r1_dbg(fmt, args...)
#endif
extern int si24r1_spi_send_cmd(uint8_t *cmd,uint16_t len);
int si24r1_reg_read(uint16_t addr,uint8_t *data,uint16_t len);
int si24r1_reg_write(uint16_t addr,uint8_t *data,uint16_t len);
int read_SFR(uint16_t addr,uint8_t *data);
int write_SFR(uint16_t addr,uint8_t data);
int si24r1_set_work_mode(int mode);
int si24r1_set_ce_gpio(struct si24r1_data *si24r1_data, unsigned int level);
#endif /* __SI24R1_SPI_H */
si24r1_spi.c文件内容如下:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/spi/spi.h>
#include "si24r1_spi.h"
#include "si24r1_reg.h"
extern struct si24r1_data *si24r1_spidev;
uint8_t TX_ADDRESS[TX_ADR_WIDTH] = {0x01, 0x02, 0x03, 0x04, 0x05}; //定义一个静态发送地址
uint8_t RX_ADDRESS[RX_ADR_WIDTH] = {0x01, 0x02, 0x03, 0x04, 0x05}; //定义一个静态发送地址
uint8_t TX_BUF[TX_PLOAD_WIDTH];
uint8_t RX_BUF[TX_PLOAD_WIDTH];
uint8_t g_cFirstPacketFlag = 0;
/*
* We can't use the standard synchronous wrappers for file I/O; we
* need to protect against async removal of the underlying spi_device.
*/
static void si24r1_dev_complete(void* arg)
{
complete(arg);
}
static ssize_t
si24r1_dev_sync(si24r1_data_t* si24r1_dev, struct spi_message* message)
{
DECLARE_COMPLETION_ONSTACK(done);
int status;
message->complete = si24r1_dev_complete;
message->context = &done;
spin_lock_irq(&si24r1_dev->spin_lock);
if (si24r1_dev->spi == NULL) {
si24r1_dbg("spi device is NULL");
status = -ESHUTDOWN;
}
else {
status = spi_async(si24r1_dev->spi, message);
}
spin_unlock_irq(&si24r1_dev->spin_lock);
if (status == 0) {
wait_for_completion(&done);
status = message->status;
if (status == 0) {
status = message->actual_length;
}
else {
si24r1_dbg("spi async return error %d", status);
}
}
else {
si24r1_dbg("spi async error %d", status);
}
return status;
}
static inline ssize_t
si24r1_dev_sync_write(si24r1_data_t* si24r1_dev, size_t len)
{
int ret;
struct spi_transfer t = {
.tx_buf = si24r1_dev->buffer,
.len = len,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
ret = si24r1_dev_sync(si24r1_dev, &m);
if (ret == 0) {
ret = len;
}
return ret;
}
static inline ssize_t
si24r1_dev_sync_read(si24r1_data_t* si24r1_dev, size_t len)
{
int ret;
struct spi_transfer t = {
.tx_buf = si24r1_dev->buffer,
.rx_buf = si24r1_dev->buffer,
.len = len,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
ret = si24r1_dev_sync(si24r1_dev, &m);
if (ret == 0) {
ret = len;
}
return ret;
}
static inline ssize_t
si24r1_dev_wr(si24r1_data_t* si24r1_dev, u8* buf, u16 buflen)
{
#if 1
struct spi_transfer t = {
.tx_buf = buf,
.rx_buf = buf,
.len = buflen,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
return si24r1_dev_sync(si24r1_dev, &m);
#else
return spi_write_then_read(si24r1_dev->spi, buf, 1, buf + 1, buflen - 1);
#endif
}
/**
* @brief si24r1_reg_read
*
* @param [in] addr
* @param [out] data
* @param [in] len
*
* @return
*/
int si24r1_reg_read(uint16_t addr,uint8_t *data,uint16_t len)
{
int status = -1;
memset(si24r1_spidev->buffer, 0, RX_PLOAD_WIDTH+1);
si24r1_spidev->buffer[0] = (uint8_t)(addr & 0x00FF);
//status = spi_write_then_read(si24r1_spidev->spi, tx_buf, 1, data, len);
status = si24r1_dev_sync_read(si24r1_spidev, len+1);
if(status < 0){
kfree(si24r1_spidev->buffer);
si24r1_spidev->buffer = NULL;
si24r1_dbg("Failed to read reg from addr = 0x%x,len = %d\n",addr,len);
}
memcpy(data, si24r1_spidev->buffer+1, len);
return status;
}
/**
* @brief si24r1_reg_write
*
* @param [in] addr
* @param [in] data
* @param [in] len
*
* @return
*/
int si24r1_reg_write(uint16_t addr,uint8_t *data,uint16_t len)
{
int status = -1;
/*
uint8_t tx_buf[255] = {0};
uint8_t i = 0;
tx_buf[0] = (uint8_t)(addr & 0x00FF);
for(i = 0; i < len; i++)
{
tx_buf[i+1] = (uint8_t)(*(data+i) & 0x00FF);
}
*/
memset(si24r1_spidev->buffer, 0, TX_PLOAD_WIDTH+1);
si24r1_spidev->buffer[0] = (uint8_t)(addr & 0x00FF);
memcpy(si24r1_spidev->buffer+1, data, len);
//status = spi_write_then_read(si24r1_spidev->spi, tx_buf, len+1, NULL, 0);
status = si24r1_dev_sync_write(si24r1_spidev, len+1);
if(status < 0){
kfree(si24r1_spidev->buffer);
si24r1_spidev->buffer = NULL;
si24r1_dbg("Failed to write reg at addr = 0x%x,len = %d\n",addr,len);
}
return status;
}
int read_SFR(uint16_t addr,uint8_t *data)
{
return si24r1_reg_read(addr,data,1);
}
int write_SFR(uint16_t addr,uint8_t data)
{
return si24r1_reg_write(addr,&data,1);
}
/**
* @brief si24r1_spi_send_cmd
*
* @param [in] cmd spiÃüÁî
* @param [in] len spiÃüÁîÊýŸÝ³€¶È
*
* @return ³É¹Š·µ»Ø0£¬Ê§°Ü·µ»ØžºÊý
*/
int si24r1_spi_send_cmd(uint8_t *cmd,uint16_t len)
{
int status = -1;
status = spi_write_then_read(si24r1_spidev->spi,cmd,len,NULL,0);
if(status < 0){
si24r1_dbg("Failed to send spi cmd\n");
}
return status;
}
/**
* @brief si24r1_set_work_mode
*
* @param [in] mode
*
* @return
*/
int si24r1_set_work_mode(int mode)
{
int status = -1;
//uint8_t temp = 0;
switch(mode)
{
case STANDBY: //idle
write_SFR(SI24R1_W_REGISTER + CONFIG, 0x02); //PWR_UP = 1
si24r1_set_ce_gpio(si24r1_spidev, 0); //CE = 0
si24r1_dbg("set si24r1 into STANDBY mode...\n");
break;
case IDLE_TX: //deep sleep
write_SFR(SI24R1_W_REGISTER + CONFIG, 0x02); //PWR_UP = 1
si24r1_set_ce_gpio(si24r1_spidev, 1); //CE = 1
si24r1_dbg("set si24r1 into IDLE_TX mode...\n");
break;
case MOD_RX: //normal
si24r1_dbg("Config si24r1 into RX mode...\n");
si24r1_set_ce_gpio(si24r1_spidev, 0); //CE = 0
status = write_SFR(SI24R1_W_REGISTER + SETUP_AW, 0x03); // 5 byte Address width
si24r1_reg_write(SI24R1_W_REGISTER + RX_ADDR_P0, RX_ADDRESS, RX_ADR_WIDTH); //写入接收通道0地址
write_SFR(SI24R1_W_REGISTER + EN_AA, 0x01); // 使能通道0的自动应答
write_SFR(SI24R1_W_REGISTER + EN_RXADDR, 0x01); // 使能通道0的接收地址
write_SFR(SI24R1_W_REGISTER + FEATURE, 0x06); // 使能动态负载长度,ACK(with PAYLOAD)
write_SFR(SI24R1_W_REGISTER + DYNPD, 0x01); // 使能DPL_P0
write_SFR(SI24R1_W_REGISTER + RF_CH, 0x40); // 选择射频信道
write_SFR(SI24R1_W_REGISTER + RF_SETUP, 0x0f); // 数据传输率 2Mbps, 7dBm TX power
write_SFR(SI24R1_W_REGISTER + CONFIG, 0x0f); // IRQ enable CRC使能,16位CRC校验,PWR_UP=1, RX mode
if(status < 0){
si24r1_dbg("Failed to set si24r1 into RX mode !\n");
}
si24r1_set_ce_gpio(si24r1_spidev, 1); //CE = 1
break;
case MOD_TX: //normal
si24r1_dbg("Config si24r1 into TX mode...\n");
si24r1_set_ce_gpio(si24r1_spidev, 0);
status = write_SFR(SI24R1_W_REGISTER + SETUP_AW, 0x03);
si24r1_reg_write(SI24R1_W_REGISTER + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH); //写入发送地址
si24r1_reg_write(SI24R1_W_REGISTER + RX_ADDR_P0, RX_ADDRESS, RX_ADR_WIDTH); //写入接收通道0地址
si24r1_reg_write(W_TX_PAYLOAD, TX_BUF, TX_PLOAD_WIDTH);
write_SFR(SI24R1_W_REGISTER + EN_AA, 0x01); // 使能通道0的自动应答
write_SFR(SI24R1_W_REGISTER + EN_RXADDR, 0x01); // 使能通道0的接收地址
write_SFR(SI24R1_W_REGISTER + SETUP_RETR, 0x25); // 自动重发延时等待 750us, 自动重发5次(ACK with PAYLOAD(>15 bytes in 2Mbps),ARD must be 500us or more)
write_SFR(SI24R1_W_REGISTER + FEATURE, 0x06); // 使能动态负载长度,ACK(with PAYLOAD)
write_SFR(SI24R1_W_REGISTER + DYNPD, 0x01); // 使能DPL_P0
write_SFR(SI24R1_W_REGISTER + RF_CH, 0x40); // 选择射频信道 0x40
write_SFR(SI24R1_W_REGISTER + RF_SETUP, 0x0f); // 数据传输率 2Mbps, 7dBm TX power
write_SFR(SI24R1_W_REGISTER + CONFIG, 0x0e); // IRQ enable CRC使能,16位CRC校验,PWR_UP=1, TX mode
if(status < 0){
si24r1_dbg("Failed to set si24r1 into TX mode !\n");
}
si24r1_set_ce_gpio(si24r1_spidev, 1);
break;
case MOD_TX_NOACK: //normal
si24r1_dbg("Config si24r1 into TX_NOACK mode...\n");
si24r1_set_ce_gpio(si24r1_spidev, 0);
write_SFR(SI24R1_W_REGISTER + FEATURE, 0x01); // 使能 W_TX_PAYLOAD_NOACK 命令
write_SFR(SI24R1_W_REGISTER + SETUP_AW, 0x03); // 5 byte Address width
si24r1_reg_write(SI24R1_W_REGISTER + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH); //写入发送地址
//si24r1_reg_write(W_TX_PAYLOAD_NOACK, TX_BUF, TX_PLOAD_WIDTH); // 写 FIFO
write_SFR(SI24R1_W_REGISTER + DYNPD, 0x01); // 使能DPL_P0
write_SFR(SI24R1_W_REGISTER + RF_CH, 0x40); //0x40 选择射频信道 0~125
write_SFR(SI24R1_W_REGISTER + RF_SETUP, 0x0f); //0x0f 数据传输率 2Mbps, 7dBm TX power 0x9f--RF测试发射功率
status = write_SFR(SI24R1_W_REGISTER + CONFIG, 0x0e); //IRQ enable CRC使能,16位CRC校验,PWR_UP=1, TX mode
if(status < 0){
si24r1_dbg("Failed to set si24r1 into TX mode !\n");
}
si24r1_set_ce_gpio(si24r1_spidev, 1);
break;
case MOD_RX_NOACK: //normal
si24r1_dbg("Config si24r1 into RX_NOACK mode...\n");
si24r1_set_ce_gpio(si24r1_spidev, 0); //CE = 0
//write_SFR(SI24R1_W_REGISTER + SETUP_AW, 0x03); // 5 byte Address width
si24r1_reg_write(SI24R1_W_REGISTER + RX_ADDR_P0, RX_ADDRESS , RX_ADR_WIDTH); // 写入接收通道0地址
write_SFR(SI24R1_W_REGISTER + EN_RXADDR, 0x01); // 使能通道0的接收地址
//write_SFR(SI24R1_W_REGISTER + FEATURE, 0x01); // 使能 W_TX_PAYLOAD_NOACK 命令
//write_SFR(SI24R1_W_REGISTER + DYNPD, 0x01); // 使能DPL_P0 0x05
write_SFR(SI24R1_W_REGISTER + RX_PW_P0, RX_PLOAD_WIDTH); //设置接收通道0负载数据宽度
write_SFR(SI24R1_W_REGISTER + RF_CH, 0x40); // 选择射频信道 0x40
write_SFR(SI24R1_W_REGISTER + RF_SETUP, 0x0f); //0x08 数据传输率 2Mbps, 7dBm TX power
status = write_SFR(SI24R1_W_REGISTER + CONFIG, 0x0f); // IRQ enable CRC使能,16位CRC校验,PWR_UP=1, PRX mode
if(status < 0){
si24r1_dbg("Failed to set si24r1 into TX mode !\n");
}
si24r1_set_ce_gpio(si24r1_spidev, 1);
break;
case SHUTDOWN: //关断模式
si24r1_dbg("Config si24r1 into SHUTDOWN mode...\n");
status = write_SFR(SI24R1_W_REGISTER + CONFIG, 0x00); //PWR_UP=0
if(status < 0){
si24r1_dbg("Failed to set si24r1 into SHUTDOWN mode !\n");
}
break;
default:
si24r1_dbg("unrecognized mode,mode = 0x%x\n",mode);
status = -1;
break;
}
return status;
}
MODULE_AUTHOR("Sky.Ye@BIRD");
MODULE_DESCRIPTION("BIRD RFID Si24R1 driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:Spreadtrum");
编译Makefile文件:
#
# Makefile for chiponeic fingureprint sensor
#
obj-y += si24r1_main.o si24r1_spi.o
基于展讯SC9820E平台的***.dtsi文件中spi1配置参数:
&spi1 {
clocks = <&clk_aon_eb0_gates 9>, <&clk_hs_spi>, <&clk_twpll_192m>;
status = "okay";
sprd,spi-interval = <1>;
sprd,spi-mode = <0>;
sprd,dma-mode = <0>;
si24r1_rfid {
compatible = "si24r1, rfid";
reg = <0>;
spi-max-frequency = <6000000>;
si24r1-ce-gpios = <&ap_gpio 137 0>;
si24r1-irq-gpios = <&ap_gpio 138 0>;
};
};
简单的测试应用程序:
发射端测试文件RFID_Tx_test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#define SI24R1_IOC_MAGIC 0xA0
/* define commands */
#define SI24R1_IOC_INIT _IOWR(SI24R1_IOC_MAGIC, 0, unsigned int)
#define SI24R1_IOC_EXIT _IOWR(SI24R1_IOC_MAGIC, 1, unsigned int)
#define SI24R1_IOC_SET_CE_GPIO _IOW(SI24R1_IOC_MAGIC, 2,unsigned char)
#define SI24R1_IOC_SPI_MESSAGE _IOWR(SI24R1_IOC_MAGIC, 3,struct si24r1_ioc_transfer)
#define SI24R1_IOC_SPI_SEND_CMD _IOW(SI24R1_IOC_MAGIC,4,unsigned char)
#define SI24R1_IOC_ENABLE_IRQ _IO(SI24R1_IOC_MAGIC,5)
#define SI24R1_IOC_DISABLE_IRQ _IO(SI24R1_IOC_MAGIC,6)
#define SI24R1_IOC_MODE_CONFIG _IOW(SI24R1_IOC_MAGIC,7,void*)
#define SI24R1_IOC_GET_IRQ_STATE _IOR(SI24R1_IOC_MAGIC, 8,unsigned int)
#define SI24R1_IOC_GET_DRIVER_VERSION _IOR(SI24R1_IOC_MAGIC, 9, void*)
#define SI24R1_IOC_ENTER_SLEEP_MODE _IOWR(SI24R1_IOC_MAGIC, 10, unsigned int)
#define SI24R1_IOC_TX_DATA_CMD _IOWR(SI24R1_IOC_MAGIC, 11, void*)
#define SI24R1_IOC_RX_DATA_CMD _IOWR(SI24R1_IOC_MAGIC, 12, void*)
/** Data Structure defined **/
enum work_mode {
SHUTDOWN = 0,
STANDBY = 1,
IDLE_TX = 2,
MOD_TX = 3,
MOD_RX = 4,
MOD_TX_NOACK = 5,
MOD_RX_NOACK = 6
};
/**
* struct si24r1_ioc_transfer 描述芯片通过ioctl传递的数据
* @cmd 命令值
* @addr 地址
* @buf 数据缓存区
* @actual_len 要传送的数据的字节数
*/
struct si24r1_ioc_transfer {
unsigned char cmd;
unsigned short addr;
unsigned char *buf;
unsigned short actual_len;
};
#define maxlenth 32
struct RFID_DATA {
char name[maxlenth + 1];
};
struct RFID_DATA rfid_data;
void main(int argc, char* argv[])
{
char *devpath = "/dev/si24r1_rfid";
int fd = 0, ret = 0;
char * name = NULL;
char mode = 0;
int status = 0;
/*
if(argc < 3)
{
printf("%s usage: cmd is 0 or 1,arg is 0 or 1\n",argv[0]);
exit(1);
}
*/
if((fd = open(devpath, O_RDWR|O_NOCTTY|O_NDELAY)) < 0) {
printf("Open %s failed!\n", devpath);
exit(1);
}
printf("Open %s success!\n", devpath);
ret = ioctl(fd, SI24R1_IOC_GET_DRIVER_VERSION, &rfid_data.name[0]);
name = rfid_data.name;
printf("[Sky] get SI24R1 RFID driver version: %s \n", name);
ret = ioctl(fd, SI24R1_IOC_GET_IRQ_STATE, &status);
printf("[Sky] get SI24R1 RFID IRQ state: %d \n", status);
mode = MOD_TX_NOACK;
ret = ioctl(fd, SI24R1_IOC_MODE_CONFIG, &mode);
printf("[Sky] set RFID work mode: %d \n",mode);
ret = ioctl(fd, SI24R1_IOC_TX_DATA_CMD, &rfid_data);
printf("[Sky] SI24R1 RFID Tx data... \n");
ret = ioctl(fd, SI24R1_IOC_GET_IRQ_STATE, &status);
printf("[Sky] get SI24R1 RFID IRQ state: %d \n", status);
return ;
}
接收端测试文件RFID_Rx_test.c
void main(int argc, char* argv[])
{
char *devpath = "/dev/si24r1_rfid";
int fd = 0, ret = 0;
char * name = NULL;
char mode = 0;
int status = 0;
char i = 0;
/*
if(argc < 3)
{
printf("%s usage: cmd is 0 or 1,arg is 0 or 1\n",argv[0]);
exit(1);
}
*/
if((fd = open(devpath, O_RDWR|O_NOCTTY|O_NDELAY)) < 0) {
printf("Open %s failed!\n", devpath);
exit(1);
}
printf("Open %s success!\n", devpath);
ret = ioctl(fd, SI24R1_IOC_GET_DRIVER_VERSION, &rfid_data.name[0]);
name = rfid_data.name;
printf("[Sky] get SI24R1 RFID driver version: %s \n", name);
ret = ioctl(fd, SI24R1_IOC_GET_IRQ_STATE, &status);
printf("[Sky] get SI24R1 RFID IRQ state: %d \n", status);
mode = MOD_RX_NOACK;
ret = ioctl(fd, SI24R1_IOC_MODE_CONFIG, &mode);
printf("[Sky] set RFID work mode: %d \n",mode);
ret = ioctl(fd, SI24R1_IOC_RX_DATA_CMD, &rfid_data.buffer[0]);
printf("[Sky] SI24R1 RFID Rx data... \n");
for(i = 0; i < maxlenth; i++)
{
printf("SI24R1 Rx buffer[%d] = %d \n", i, rfid_data.buffer[i]);
}
ret = ioctl(fd, SI24R1_IOC_GET_IRQ_STATE, &status);
printf("[Sky] get SI24R1 RFID IRQ state: %d \n", status);
return ;
}
两块主板,一个配置为发射模式,一个配置为接收模式;发射端发出数据后,在接收端可以接收到数据并打印出来。Si24R1收发功能验证OK。