文章目录
前言
DEBUG 用的一些宏:
//将一字节十进制数字转换成二进制 debug用,一般用于查看寄存器每个位的数值
#define PRINTF_BINARY_PATTERN_INT8 "%c %c %c %c %c %c %c %c"
#define PRINTF_BYTE_TO_BINARY_INT8(i) \
(((i) & 0x80ll) ? '1' : '0'), \
(((i) & 0x40ll) ? '1' : '0'), \
(((i) & 0x20ll) ? '1' : '0'), \
(((i) & 0x10ll) ? '1' : '0'), \
(((i) & 0x08ll) ? '1' : '0'), \
(((i) & 0x04ll) ? '1' : '0'), \
(((i) & 0x02ll) ? '1' : '0'), \
(((i) & 0x01ll) ? '1' : '0')
#define PRINTF_BINARY_PATTERN_INT16 \
PRINTF_BINARY_PATTERN_INT8" "PRINTF_BINARY_PATTERN_INT8
#define PRINTF_BYTE_TO_BINARY_INT16(i) \
PRINTF_BYTE_TO_BINARY_INT8((i) >> 8), PRINTF_BYTE_TO_BINARY_INT8(i)
#define PRINTF_BINARY_PATTERN_INT32 \
PRINTF_BINARY_PATTERN_INT16" "PRINTF_BINARY_PATTERN_INT16
#define PRINTF_BYTE_TO_BINARY_INT32(i) \
PRINTF_BYTE_TO_BINARY_INT16((i) >> 16), PRINTF_BYTE_TO_BINARY_INT16(i)
#define PRINTF_BINARY_PATTERN_INT64 \
PRINTF_BINARY_PATTERN_INT32" "PRINTF_BINARY_PATTERN_INT32
#define PRINTF_BYTE_TO_BINARY_INT64(i) \
PRINTF_BYTE_TO_BINARY_INT32((i) >> 32), PRINTF_BYTE_TO_BINARY_INT32(i)
其实驱动难的真不是软件,而是硬件,特别是当数据手册又臭又长的时候,高通的数据手册会加倍臭长,特别是把寄存器设置给分散得很凌乱的时候……就怕设置少了器件埋下了惊天大雷好吧。
毕竟数据手册又不是只给程序员看的,设备特性才是人家的卖点。
我们要提炼出使用方法,也就是接线和协议,包括协议中的交互规则。
先声明一下,本人真的是很讨厌高通的驱动框架,非常的复杂。
高通的官方 spi 驱动:kernel/drivers/spispi-qup.c
但是一些操作还是可以拿出来说说的。
例如:驱动入口函数
int32_t QUP_Init(T_LcmConfig *pCfg)
{
T_QUP *pDev = &qupDev;
if(QUP_InitConfig() < 0)
{
LOG_ERROR("qup config init fail.\n");
return -1;
}
pDev->pBuf = (uint16_t *)kmalloc(sizeof(T_QUP), GFP_KERNEL);
if(pDev->pBuf == NULL)
{
LOG_ERROR("kmalloc fail.\n");
return -1;
}
if((spi_register_driver(&qup_SpiDrv) < 0) || (pDev->ucSpiFlag==0))
{
LOG_ERROR("register spi driver fail, please check qup kernel support.\n");
return -1;
}
if(Mutex_CreateHdl(&(pDev->tMutex)) < 0)
{
LOG_ERROR("Mutex_CreateHdl fail.\n");
goto Create_Mutex_Fail;
}
QUP_Reset();
QUP_Setup();
LOG_INFO("qup init success.\n");
return 0;
Create_Mutex_Fail:
spi_unregister_driver(&qup_SpiDrv);
return -1;
}
static int32_t qup_InitConfig(void)
{
T_QUP *pDev = &qupDev;
//配置设备管脚
pDev->tCfg.uiCs = 2;
......
//配置的管脚初始化
Gpio_SetDir(pDev->tCfg.uiCs, 1);
gpio_set_value(pDev->tCfg.uiCs, 1);
......
return 0;
}
之前我们都是把管脚初始化放在 probe 函数的,这里放在驱动入口函数也没问题,只要使用到硬件之前有初始化的就可以了。
而且之前获取 CS 管脚是通过 sysfs 文件系统获取的,而 sysfs 文件系统中的 spi 节点的 CS 属性也是通过设备树写的,这里是竟然是直接写编号!!!
再来看 probe 函数(节省):
static int32_t QUP_SpiProbe(struct spi_device *pSpiDev)
{
T_QUPDev *pDev = &qupDev;
int32_t iRet=-1;
LOG_DEBUG("probe spi device '%s' success and infomation as follows: \n",
pSpiDev->modalias);
LOG_DEBUG("max_speed_hz = %d, chip_select=%d, mode=0x%x, bits_per_word=%d",
pSpiDev->max_speed_hz, pSpiDev->chip_select,
pSpiDev->mode, pSpiDev->bits_per_word);
pSpiDev->max_speed_hz = pDev->tCfg.uiRate;
pSpiDev->cs_gpio = pDev->tCfg.uiCs; //把 CS 管脚分配给设备
pSpiDev->mode |= SPI_MODE_3;
iRet = spi_setup(pSpiDev);
if(iRet < 0)
{
LOG_ERROR("qup spi setup fail.\n");
return -1;
}
......
pDev->ucSpiFlag = 1;
return 0;
}
可以看出 probe 函数中最重要的操作就是把 CS 管脚赋给设备结构体,同时把设备结构体交给 spi_setup 去完成主控器时序参数的初始化。
这里的两个项目是根据公司的模块写的,和别的驱动不太一样,虽然还是驱动的框架,但是用途不太一样。
- 对于数量多而简单的驱动,没必要挨个注册,这里的驱动就是集成到主板驱动上,一个主板一个驱动;
- 对于复杂的驱动,类似网卡、无线网卡、USB,这些有厂商和内核提供驱动,一般作为模块编进系统;
- 将驱动写成 API 的方式集成到主板驱动的好处:
a. 可以让其他需要用到该驱动的模块直接调用;
b. 这些设备不需要应用工程师控制,减少文件系统的复杂度。
一、idt8v97003(无线收发器)
原理图
设备树
spi_0: spi@78b5000 {
status = "ok";
pinctrl-0 = <&spi_0_pins>; //选择spi控制器0的管脚配置,spi控制器0的管脚配置是处理器写死的,不要去改
pinctrl-names = "default";
cs-select = <0>; //选择编号为0的设备
}
其实配置 SPI 最重要的也就是配置管脚复用为 SPI 功能。
SPI 的的从设备有很多,但是我们只会控制 SPI 主控器,如何驱动信息是发给哪一个从设备的呢?
只能通过 CS 编号。SPI 控制器或许有多个 CS 管脚,但是这个不重要,因为不够的可以用 GPIO 代替。
只有被使能的管脚才能和主机通信。所以每次发消息前都是先使能,发完消息就失能。
数据手册
设备的地址空间是 0X00 到 0X49,寻址模式是 15 位。也就是说,每个寄存器有15个位。
在传输操作中,如果寄存器0x00 的第5位和第2位设置为1,地址自动递增,否则递减。
在递增模式下,如果地址达到0x49,则将其置为0x00。在递减模式下,如果地址达到0x00,则将其置为0x49。
默认是递增的。
SPI操作在CSB上有高到低的转换时开始,在CSB上有低到高的转换时停止。如果传输方向位R/nW为1,则为读操作,否则为写操作。
该设备支持多字节的读取或写操作。位A14到A0表示寄存器地址。设备读取或写入数据到这个地址,只要CSB保持在低水平就会继续。设备会根据地址编码位自动增加或减少地址。
MSB 模式:
如果要写数据到寄存器,那么先发写位+15位地址+8位寄存器数据。
如果要读寄存器的数据,那么先发读位+15位地址。然后就会收到8位数据。
LSB 模式:
首先操作是一样的,但是地址和读写位、数据位的顺序都是相反的。
MSB 模式写入多个字节:(寄存器地址会递增,前面说过了,默认是递增的;第一个例子是递减的)
还是之前的操作,但是第二个数据会自动写到下一个地址。
LSB 模式读多个字节:
略…
SPI 的时序,对于不连续的地址也可以,写入多个地址,然后接受多个地址的数据。
宽度大于8位的配置寄存器是为同步访问提供的双缓冲寄存器。
对于这些寄存器,需要先将多字节设置写入缓冲寄存器。新配置直到将1写入寄存器0x0F位0,以将它们从缓冲寄存器传输到活动寄存器。
传输位是自动清除的。多字节配置数据可以从缓冲寄存器或0x01位5寄存器中指定的缓冲区读取模式位进行回读取。
寄存器0x10到0x1D、0x22到0x25和0x29到0x2C都是双缓冲的。
提示:
其实正常j讲到这里就可以了,因为每个 SPI 模块的操作各异,相同点已经讲完了。
接下去的内容会让崩溃,抛开无效寄存器,假如每个寄存器有8个,每个位又有0/1两种情况,两种情况对应不用的功能。
那么就需要了解 0X49 X 8 X 2 个功能,也就是 1168 个功能…文章篇幅有限,会给举个例子,但是具体讲不了。
所有寄存器功能的地址归类,可以看到的是用不到的和没用的还是挺多的。
预设寄存器中每个位的意义:
每个位的功能选择:
再讲下去我怕浪费各位同学的青春……总之以后开发时,每个模块的寄存设置不一样,自己要自己学会看数据手册。
整理数据手册
数据手册又不是只给程序员看的,里面芯片介绍、热特性、,所以一定要把所有程序相关的转移出来,做到一目了然的效果。
/* IDT8V97003 register map:
* 0000~000F Preface registers
* 0010~0043 Control registers
* 0044~0049 Status registers
* 所有数据寄存器皆为8位
*/
/* Preface registers
* 0000-0001 Startup Control Resigters
* 0002 Reserved
* 0003-0006 Device Type, ID and Version Registers
* 0007 Reserved
* 0008-000B Unused
* 000C 000D Vendor ID Control Registers
* 000E-000F Unused
*/
#define IDT8V97003_SCR_0 0 //高位与对应低位互为镜像,且互为镜像的位需要同时设置 R/W
#define IDT8V97003_SCR_1 0x01 //D5:BufferReadMode R/W
#define IDT8V97003_DeviceType 0x03 //Device Type R default:0000_0110
#define IDT8V97003_DeviceID_0 0x04 //DeviceID R
#define IDT8V97003_DeviceID_1 0x05 //DeviceID R
#define IDT8V97003_DeviceVersion 0x06 //DeviceVersion R
#define IDT8V97003_VendorID_0 0x0C //Vendor ID R default:0010_0110
#define IDT8V97003_VendorID_1 0x0D //Vendor ID R default:0000_0100
/* Control registers
* 0010-0019 Feedback Divider Control Registers
* 001A-001D Phase Adjustments Control Registers
* 001E DSM Control Registers
* 001F-0020 Manual VCO and Digital Band Selection
* 0021 Calibration Control Registers
* 0022-0023 Band Select Clock Divider Control Registers
* 0024-0025 Reserved
* 0026-0027 Lock Detect Control Registers
* 0028 Power Down Control Registers
* 0029-002C Input Control Registers
* 002D-002F Charge Pump Current Control Registers
* 0030 Charge Pump Current Control Registers
* 0031-0032 Re-Sync Control Registers
* 0033-003B Output Control Registers
* 003C Reserved
* 003D Reserved
* 003E-0040 Unused or Reserved
* 0041-0043 Reserved
*/
/*FB分压器整数部分 R/W*/
#define IDT8V97003_NIntR_0 0x10 //default: 0001 1111 = d'31 最小值 d'12 R/W
#define IDT8V97003_NIntR_1 0x11
/*FB分压器小数部分,Nfrac是分数除比的分子值。可编程范围为0 ~ (MOD−1)。R/W*/
#define IDT8V97003_NFracR_0 0x12 //default: 1011 1100 1111 1111 1111 1110 1000 0110
#define IDT8V97003_NFracR_1 0x13
#define IDT8V97003_NFracR_2 0x14
#define IDT8V97003_NFracR_3 0x15
/*FB分压器模量部分 ;R/W */
#define IDT8V97003_NModR_0 0x16 //default: 1111 1111 1111 1111 1111 1110 0000 0000
#define IDT8V97003_NModR_1 0x17
#define IDT8V97003_NModR_2 0x18
#define IDT8V97003_NModR_3 0x19
/*相位调整控制寄存器。相位调整(相位值)必须小于模量(Nmod值)。 R/W*/
#define IDT8V97003_PhaseR_0 0x1A //default: d'1
#define IDT8V97003_PhaseR_1 0x1B
#define IDT8V97003_PhaseR_2 0x1C
#define IDT8V97003_PhasRe_3 0x1D
/*DSM控制寄存器 R/W*/
#define IDT8V97003_DSMOrderR 0x1E /* D7~D5 DSM Order 000=OFF 设备工作在整数模式,忽略小数部分。default:111
* D4~D3 Dither Gain 00=LSB Dither (Recommended) (default)
* D2 Shape Dither Enable 0/1=Shaped dither disabled/enabled
* D1 Dither Enable 0/1 = Dither off/on
*/
/*校准控制寄存器 R/W*/
#define IDT8V97003_CalibrationR_0 0x1F /* D7:ManuBandEn 启用校准模式 0/1=自动/手动压控振荡器和数字波段选择。
* D6/D5/D4:0
* D3~D0:VCOManu 手动选择VCO(ManuBandEn=1时) 0000/.../0111=VCO0/.../VCO7 default:1000
*/
#define IDT8V97003_CalibrationR_1 0x20 /* D7:0
* D6~D0:手动选择数字波段(ManuBandEn=1时) 0000000/.../1111111=Band/.../Band127 default:10000000
*/
#define IDT8V97003_CalibrationR_2 0x21 /* D7:ForceRelockVCO 0/1=正常操作(default)/VCO强制重新校准,自动清除。(当锁相环在整数模式下使用时,在编程反馈分压器值后,必须将该位设置为1。)
* D6:PhAdj 相位调整触发器 0/1=正常操作(default)/触发相位调整一次。自动清除。
* D5:BandSelDisable 禁用波段选择,当写入寄存器0x0010-0x0019时,该位将防止VCO重新校准。 0/1=是/否重新校准 default:0
* D4:ManualReSync 手动重新同步锁相环输出。0/1=正常操作(default)/同步锁相环输出到PFD参考的特定阶段,由相位调整控制寄存器<31:0>指定。自动清除。
* D3~D2:0
* D1~D0: 波段选择/校准分辨率。00/01/10/11=Reserved/Reserved/4x resolution/8x resolution(default)
*/
/*频带选择时钟分频控制寄存器 R/W*/
// This value should be set so that FPFD / BndSelDiv is < 100kHz and > 50kHz
#define IDT8V97003_BSCDCR_0 0x22 //BndSelDiv[12:0] default:1010 0000 0000
#define IDT8V97003_BSCDCR_1 0x23 //D7~D5:Unused D4:BndSelDiv<D12>
/*锁定检测控制寄存器 R/W*/
#define IDT8V97003_LDCR_0 0x26 /* D7~D5:Unused
* D4:LD_Disable 禁用锁定检测 0/1=禁用(default)/启用锁定检测电路
* D3:AutoRecalEn 启用自动重新校准 0/1=禁用(default)/启用(如果检测到LD上的解锁,则会自动重新校准)
*/
#define IDT8V97003_LDCR_1 0x27 /* D7~D6:Unused
* D5~4:LDPinMode 00/01=数字锁检测(默认);正常锁探测器功能/校准完成 10/11=Low/High
* D3:Unused
* D2~D0:锁探测器精度设置(ns) 000=0.375 (default) 001/010/100/101/110/111=0.75/1.5/2.4/5.2/5.2/8.5/8.5
*/
/*断电控制寄存器 R/W*/
#define IDT8V97003_PDCR 0x28 /* D7:Reserved
* D6:ref_vreg_pwrdwn 参考输入路径调节器断电控制 0/1=开启调节器(默认)/调节器断电
* D5:pdcp_vreg_pwrdwn 鉴相器和电荷泵调节器断电控制 0/1=开启(默认)/关闭
* D4:fb_vreg_pwrdwn 反馈分频调节器断电控制 0/1=开启(默认)/关闭
* D3:outA_vreg_pwrdwn 输出稳压器断电控制 0/1=开启(默认)/关闭
* D2:outBbuf_vreg_pwrdwn 输出路径调节器和输出b调节器的断电控制 0/1=开启(默认)/关闭
* D1:PDNAnaRegu 模拟调节器断电控制 该位设置为1将关闭以上的所有调节器 default:0
* D0:VCO_En 0/1=禁用所有VCO(将VCO电源电流IVCO降低到46mA(典型值))/正常模式
*/
/*输入控制寄存器 R/W*/
#define IDT8V97003_InputControl_0 0x29 //InputControl_0[D7~D0] + InputControl_1[D1~D0]=R[9:0],输入除法值(参考) default:d'1
#define IDT8V97003_InputControl_1 0x2A /* D7~D5:Unused
* D4:RefDoubler_Delay 为输入倍频器选择标准或扩展脉冲宽度延迟 0/1=标准脉冲宽度(默认值)。输入参考频率>50MHz时使用/延长脉冲宽度用于低频(<50MHz)
* D3:Input_Type 选择差分或单端输入 0/1=单端输入/差分输入(default)
* D2:RefDoubler_En 启用输入引用加倍器 0/1=disabled/enabled(default)
* D1~D0=R<9>~R<8>
*/
#define IDT8V97003_InputControl_2 0x2B /* D7:Mult_En MULT Enable 0/1=not enabled(default)/enabled
* D6:Mult_reset 重置参考倍频器模块(MULT) 0/1= is active (default)/is reset 当输入倍增器(MULT)正在使用时,
* 建议对设备进行适当的MULT设置,保持Mult_reset = 1并将其切换到低(活动),
* 然后使用TransferOn位(寄存器0F,位0)传输数据,用于双缓冲寄存器,并重新锁定锁相锁(ForceRelock)
* D5~D0:Mult<5:0> 输入时钟的倍频因子。当启用时,乘法器块(Mult)将相位检波器的输入频率乘到更高的频率
*/
#define IDT8V97003_InputControl_3 0x2C /* D7:Mult_mux_ena 为倍频器使能多路复用器 0/1=disabled(default)/enabled
* D6:Mult_d2s_ena 差分到单端块启用倍频器 0/1=disabled(default)/enabled
* D5:Mult_cp_ena 电荷泵启用倍频器 0/1=disabled(default)/enabled
* D4:Mult_force_vchi 倍增器电平强制控制在高电平 0/1=正常操作(default)/倍增器控制电压充电到VDD
* D3:Mult_force_vclow 倍增器电平强制控制在低电平 0/1=正常操作(default)/倍增器控制电压放电到GND
* 如果没有使用输入倍频器,建议将Mult_force_vclow位设置为1 (High)
* D2~D0:1 0 0
*/
/*充电泵控制寄存器 R/W*/
#define IDT8V97003_CPCCR_0 0x2D
#define IDT8V97003_CPCCR_1 0x2E
#define IDT8V97003_CPCCR_2 0x2F
#define IDT8V97003_CPCCR_3 0x30
/*重新同步控制寄存器 R/W*/
#define IDT8V97003_ReSyncControl_0 0x31 //D7~D0:1 0 0 0 1 0 0 0
#define IDT8V97003_ReSyncControl_1 0x32 //D7:AutoReSync 选择自动同步模式 0/1=设备不会将“锁相环输出与引用”同步到特定相位/在重新锁定后,设备将“锁相环输出与引用”同步到特定相位
/*输出控制寄存器 R/W*/
#define IDT8V97003_OutputControl_0 0x33 /* D7~D4:Unused
* D3~D0:RF_OUTA_pwr[3:0] RF_OUTA电源设置 设置RF_OUTA的输出功率。设置数越高输出功率越大,最高可达最大值,这取决于所使用的输出负载
0000/0001=OFF(default)/最小输出功率设置 1100-1111=最高输出功率
*/
#define IDT8V97003_OutputControl_1 0x34 /* D7:1
* D6:Unused
* D5:Mute_until_LD 0/1=输出与锁检测无关(默认)/“锁定检测”值为“高”时,才使能输出
* D4:RF_OUTA_ena 0/1= RF_OUTA is disabled (MUTED & default)/enabled
* D3~D0:1 1 1 0
*/
#define IDT8V97003_OutputControl_2 0x35 /* D7~D4:Unused
* D3~D0:RF_OUTB_pwr[3:0] RF_OUTB电源设置 设置数越高输出功率越大,最高可达最大值,这取决于所使用的输出负载
* 0000/0001=OFF(default)/最小输出功率设置 1100-1111=最高输出功率
*/
#define IDT8V97003_OutputControl_3 0x36 /* D7:1
* D6~D5:Unused
* D4:RF_OUTB_ena 0/1=RF_OUTB is disabled (MUTED & default)/enabled
* D3~D0:1 1 1 0
*/
/*0037~003A Reserved*/
#define IDT8V97003_OutputControl_4 0x3B /* D7:OutDoubler_Ena 射频输出倍频器使能 0/1=1x path (Divide by 1) path Enabled (default)/2x path Enabled (RF Output Doubler Enabled)
* 当VCO频率不大于9GHz时,OutDoubler_Ena只能设置为1。
* 对于除1(绕过Output Divider和Doubler), OutDoubler_Ena Bit和Out_Divider_Ena Bit都必须设置为0 (Low)。
* D6:OutDivider_Ena 射频输出分频器使能 0/1=1x or 2x path Enabled (default)/RF Output Divider Enabled
* 注意:倍频器和差分器两者不能同时使能
* D5:OutDoubler_Freq 射频输出加倍器频率设置 0/1=VCO频率7.0-9.0GHz(默认)/VCO频率5.5-7.0GHz
* D4~D3:Unused
* D2~D0:OutDiv[2:0] 射频输出分压器(M0)设置
000/001/011/100/101/110/111
=
Unused/Div By 2/Div By 4/Div By 8/Div By 16/Div By 32/Unused/Unused
*/
/* Status Registers
* 0044 Digital Lock and Calibration and VCO Status Registers
* 0045 Digital Band Status Registers
* 0046-0048 Reserved or Unused
* 0049 Loss of Lock Status Registers
*/
/*数字校准锁定和VCO状态寄存器 Read only*/
#define IDT8V97003_StatusRegisters_0 0x44 /* D7:DigLock 0/1=锁相环未锁定(default)/锁相环已锁定(根据寄存器0x27中的LDP设置)
* D6:BandSelDone 波段选择完成(校正完成) 0/1=未完成(default)/已完成
* D5~D4:Unused
* D3~D0:VcoSts[3:0] VCO当前状态 0000/.../0111=VCO0/.../VCO7 1000-1111:Unused
*/
/*数字波段状态寄存器 R*/
#define IDT8V97003_StatusRegisters_1 0x45 /* D7:Unused
* D6~D0:BandSts[6:0] 数字频带的当前状态
* 000 0000/.../111 1111=Band 0/.../Band 127
*/
#define IDT8V97003_StatusRegisters_2 0x46 //Reserved
#define IDT8V97003_StatusRegisters_3 0x47 //Reserved
#define IDT8V97003_StatusRegisters_4 0x48 //Unused
/*锁状态缺失寄存器*/
#define IDT8V97003_StatusRegisters_5 0x49
编写驱动
数据结构
#define 97003_REG_NUM 73 //寄存器数量
//idt8v97003寄存器参数
typedef struct
{
uint8_t uAddr; //寄存器地址
uint32_t uiData; //需要写入或者读出的数据
}T_97003RegParam;
//idt8v97003配置
typedef struct
{
uint32_t uiSdio; //三线input/output,四线input
uint32_t uiSdo; //三线input/output,四线output
uint32_t uiSclk; //时钟
uint32_t uiCsPin; //片选
uint32_t uiNreset; //复位引脚
}T_97003Config;
//an41908设备对象结构体
typedef struct
{
uint32_t uaRegCache[97003_REG_NUM]; //idt8v97003寄存器cache
uint8_t ucSpiFlag; //SPI设备探测成功标志, 1.成功 0.失败
T_97003Config tCfg; //idt8v97003配置
struct spi_device *pSpiDev; //spi设备指针
}T_97003Dev;
读写函数
//从机使能
static void Idt8v97003_EnableCs(void)
{
T_97003Dev *pDev = &gIDT8v97003Dev;
gpio_set_value(pDev->tCfg.uiCsPin, 1);
}
//从机失能
static void Idt8v97003_DisableCs(void)
{
T_97003Dev *pDev = &gIDT8v97003Dev;
gpio_set_value(pDev->tCfg.uiCsPin, 0);
}
/* 写寄存器数据
* uiAddr:从设备寄存器地址
* ucData:要写入的数据
*/
static uint32_t Idt8v97003_WriteReg(uint8_t uiAddr, uint8_t ucData)
{
int32_t iRet = -1;
T_97003Dev *pDev = &gIDT8v97003Dev;
struct spi_device *pSpidev = pDev->pSpiDev;
uint8_t ucTxBuf[4] = {0};
ucTxBuf[0] = 1<<7;
ucTxBuf[1] = uiAddr;
ucTxBuf[2] = ucData;
Idt8v97003_DisableCs();
iRet = spi_write(pSpidev, ucTxBuf, sizeof(ucTxBuf));
Idt8v97003_EnableCs();
if(iRet < 0)
{
LOG_ERROR("write REG fail.\n");
return -1;
}
LOG_DEBUG("IDT8V97003 SPI write reg:Addr = \"PRINTF_BINARY_PATTERN_INT8\" \"PRINTF_BINARY_PATTERN_INT8\"\nData = \"PRINTF_BINARY_PATTERN_INT8\"\n",
PRINTF_BYTE_TO_BINARY_INT8(ucTxBuf[0]),
PRINTF_BYTE_TO_BINARY_INT8(ucTxBuf[1]),
PRINTF_BYTE_TO_BINARY_INT8(ucTxBuf[2]));
return 0;
}
因为芯片业务是控制无线收发器,并没有要读取的数据,所以只需要实现 write 操作。
完成驱动结构体
static int32_t Idt8v97003_SpiProbe(struct spi_device *pSpiDev)
{
T_97003Dev *pDev = &gIDT8v97003Dev;
int32_t iRet=-1;
LOG_DEBUG(DBG_LENS_IRIS, "probe spi device '%s' success and infomation as follows: \n",
pSpiDev->modalias);
LOG_DEBUG(DBG_LENS_IRIS, "max_speed_hz = %d, chip_select=%d, mode=0x%x, bits_per_word=%d",
pSpiDev->max_speed_hz, pSpiDev->chip_select,
pSpiDev->mode, pSpiDev->bits_per_word);
pSpiDev->max_speed_hz = 2000000;
iRet = spi_setup(pSpiDev);
if(iRet < 0)
{
LOG_ERROR("Idt8v97003 spi setup fail.\n");
return -1;
}
pDev->pSpiDev = pSpiDev; //直接把初始化完成的设备结构体给了设备指针
pDev->ucSpiFlag = 1;
return 0;
}
static int32_t Idt8v97003_SpiRemove(struct spi_device *pSpiDev)
{
LOG_INFO("remove spi device '%s' success.\n", pSpiDev->modalias);
return 0;
}
static const struct of_device_id Idt8v97003_of_match[] = {
{ .compatible = "qcom,idt8v97003" },
{ /* Sentinel */ }
};
static struct spi_driver gIdt8v97003_SpiDrv =
{
.driver =
{
.name = "idt8v97003",
.owner = THIS_MODULE,
.of_match_table = Idt8v97003_of_match,
},
.probe = Idt8v97003_SpiProbe,
.remove = Idt8v97003_SpiRemove
};
驱动出入口
int32_t Idt8v97003_Init(T_97003Config *pCfg)
{
T_97003Dev *pDev = &gIDT8v97003Dev;
int32_t iRet=-1;
if(Idt8v97003_InitConfig() < 0)
{
LOG_ERROR("bu240 config init fail.\n");
goto Init_Config_Fail;
}
if((spi_register_driver(&gIdt8v97003_SpiDrv) < 0) || (pDev->ucSpiFlag==0))
{
LOG_ERROR("register spi driver fail, please check bu240 kernel support.\n");
goto Register_Spi_Fail;
}
Idt8v97003_InitReg();
LOG_INFO("Idt8v97003 init success.\n");
return 0;
Register_Spi_Fail:
Init_Config_Fail:
return -1;
}
void Idt8v97003_Exit(void)
{
spi_unregister_driver(&gIdt8v97003_SpiDrv);
LOG_INFO("Idt8v97003 exit success.\n");
}
驱动编写其实也挺自由的,但是也就在那几个函数之间找个顺序填充而已。这里也是驱动入口处完成设备结构体的配置和寄存器的初始化。
用到函数如下:
//设备结构体配置
static int32_t Idt8v97003_InitConfig(void)
{
T_97003Dev *pDev = &gIDT8v97003Dev;
pDev->tCfg.uiSdio = ;
pDev->tCfg.uiSclk = ;
pDev->tCfg.uiSdo = ;
pDev->tCfg.uiCsPin = ;
pDev->tCfg.uiNreset = ;
//配置的管脚初始化
Gpio_SetDir(pDev->tCfg.uiSdio, 1);
Gpio_SetDir(pDev->tCfg.uiCsPin, 1);
Gpio_SetDir(pDev->tCfg.uiNreset, 1);
gpio_set_value(pDev->tCfg.uiSdio, 1);
gpio_set_value(pDev->tCfg.uiCsPin, 1);
return 0;
}
// 从机寄存器初始化,按照数据手册写入数据
static void Idt8v97003_InitReg(void)
{
Idt8v97003_WriteReg();
}
二、Admv1139(ADC)
原理图
设备树
spi_1_pins: spi-1-pins {
pins = "gpio61", "gpio62", "gpio63", "gpio64";
function = "blsp1_spi";
drive-strength = <8>;
bias-disable;
};
spi_1: spi@78b6000 {
status = "ok";
pinctrl-0 = <&spi_1_pins>;
pinctrl-names = "default";
cs-select = <0>;
};
这里使用的是 spi 控制器1。一般最求性能的话,就不要把多个从设备都挂在同一个主机上。
数据手册
相比前一个,这个模块的寄存器相对来说寄存器少一些,但是功能复杂很多。
而且这个数据手册是我喜欢的类型,如下:
如果要设置这个寄存器,那么可以先选择 MSB 或者 LSB,然后在 spi message 里面 填充地址和写位,再接上上面这8位的具体值。
同样太多寄存器就不一一列举了。
下面是一个例程:
驱动编写
寄存器设置
//写寄存器初始参数
static T_Admv1139RegParam gAdmv1139_InitRegParam[] =
{
{0x00, 0x18},
{0x0A, 0x00},
{0x13, 0x01},
{0x14, 0x00},
{0x15, 0x00},
{0x17, 0x00},
{0x1A, 0x00},
{0x1C, 0xFF},
{0x1F, 0x0C},
{0x22, 0x00},
{0x81, 0x0E},
{0x83, 0x89},
{0x85, 0x0C},
{0x87, 0xEC},
{0xB7, 0x3F},
{0xB8, 0x3F},
{0xB9, 0x00},
{0xBA, 0x00},
{0xBB, 0x3F},
{0xBC, 0x3F},
{0xBD, 0x00},
{0xBE, 0x00},
{0xC5, 0x01},
{0xC6, 0x10},
{0xCB, 0x10},
{0xCC, 0x10},
{0xCD, 0x18},
{0x101, 0x04},
{0x103, 0x00},
{0x104, 0x0E},
{0x12B, 0x52},
{0x12C, 0x00},
{0x12D, 0x00},
{0x12E, 0x00},
{0x130, 0x14},
{0x131, 0x00},
{0x132, 0x00},
{0x133, 0x02},
{0x137, 0x7F},
{0x138, 0x00},
{0x139, 0x5C},
{0x141, 0x10},
{0x142, 0x10},
{0x143, 0x18},
{0x18C, 0x20},
{0x18F, 0x00},
{0x190, 0x00},
{0x192, 0x80},
{0x193, 0x00},
{0x197, 0x01}
};
//读寄存器初始参数
static T_Admv1139RegParam gAdmv1139_ReadIDParam[] =
{
{ADMV1139_REG0004, 0x50},
{ADMV1139_REG0005, 0x00}
};
//初始化寄存器
static void Admv1139_InitReg(void)
{
uint32_t i=0;
for(i=0; i<ARRAY_SIZE(gAdmv1139_InitRegParam); i++)
{
Admv1139_WriteReg(gAdmv1139_InitRegParam[i].uiAddr, gAdmv1139_InitRegParam[i].uiData);
}
}
/*
* 功能: Tx增益寄存器(0~15dB,1dB step)R/W
* 参数: ucTx_rf_dsa1,ucTx_rf_dsa2 0~15dB,1dB step
*/
static int32_t Admv1139_Tx_RF_DSA_Reg(uint8_t ucTx_rf_dsa1, uint8_t ucTx_rf_dsa2)
{
uint8_t ucTx_rf_dsa = (ucTx_rf_dsa2 << 4) | ucTx_rf_dsa1;
int32_t iRet = 0;
if((ucTx_rf_dsa1 < 0) || (ucTx_rf_dsa1 > 15) || (ucTx_rf_dsa2 < 0) || (ucTx_rf_dsa2 > 15))
{
LOG_ERROR("Write Tx_RF_DSA_Reg fail!Check the reg range in 0~15!\n");
}
iRet = Admv1139_WriteReg(0x15, ucTx_rf_dsa);
if(iRet < 0)
{
LOG_ERROR("write Tx_RF_DSA_Reg fail.\n");
return -1;
}
LOG_DEBUG("Set Tx_RF_DSA1:%ddB Tx_RF_DSA2:%ddB\n", ucTx_rf_dsa1, ucTx_rf_dsa2);
return 0;
}
/*
* 功能描述: Rx增益寄存器
* 输入参数: ucRx_rf_dsa, 0~15dB,1dB step ucRx_if_dsaq,ucRx_if_dsai 0~4dB,1dB step
*/
static int32_t Admv1139_Rx_RFIF_Reg(uint8_t ucRx_rf_dsa, uint8_t ucRx_if_dsaq, uint8_t ucRx_if_dsai)
{
uint8_t ucRx_rf_dsa = (0 << 4) | ucRx_rf_dsa;
uint8_t ucRx_if = (ucRx_if_dsaq << 4) | ucRx_if_dsai;
if((ucRx_rf_dsa < 0) ||
(ucRx_rf_dsa > 15) ||
(ucRx_if_dsaq < 0) ||
(ucRx_if_dsaq > 4) ||
(ucRx_if_dsai < 0) ||
(ucRx_if_dsai > 4))
{
LOG_ERROR("Write Tx_RF_DSA_Reg fail!Check the range of reg!\n");
}
Admv1139_WriteReg(0x14, ucRx_rf_dsa);
Admv1139_WriteReg(0x17, ucRx_if);
LOG_DEBUG("Set Rx_RF_DSA:%ddB Rx_IF_DSAQ:%ddB Rx_IF_DSAI:%ddB\n",
ucRx_rf_dsa, ucRx_if_dsaq, ucRx_if_dsai);
return 0;
}
/*
* 功能描述: LO nulling
* 输入参数: void
*/
static void Admv1139_LOnulling(void)
{
Admv1139_WriteReg(0x101, 0x0B); //初始化并使能ADC以及ADC时钟
Admv1139_WriteReg(0x12E, 0x02); //重置LO nulling loop
Admv1139_WriteReg(0x12D, 0x40); //使能LO nulling loop
Admv1139_WriteReg(0x137, 0x5F); //power up the ADC reference
Admv1139_WriteReg(0x103, 0x00); //select LO nulling for the ADC
Admv1139_WriteReg(0x12B, 0x53); //tap the envelop detector
Admv1139_WriteReg(0x130, 0x01); //set the optimum LO nulling
Admv1139_WriteReg(0x12D, 0x49); //start the LO nulling
msleep(1);
Admv1139_WriteReg(0x12D, 0x48); //finish the LO nulling
Admv1139_WriteReg(0x12B, 0x52); //disable envelop tapping
Admv1139_WriteReg(0x101, 0x08); //disable the ADC and the ADC clock
}
/*
* 功能: LO nulling disable
* 参数:
*/
static void Admv1139_LOnullingDisable(void)
{
Admv1139_WriteReg(0x12D, 0x30); //失能LO nulling loop
Admv1139_WriteReg(0x131, 0x00);
Admv1139_WriteReg(0x132, 0x00);
}
SPI 传输函数
//功能描述: Admv1139使能片选
static void Admv1139_EnableCs(void)
{
T_Admv1139Dev *pDev = &gAdmv1139Dev;
gpio_set_value(pDev->tCfg.uiCsPin, 1);
}
//Admv1139失能片选
static void Admv1139_DisableCs(void)
{
T_Admv1139Dev *pDev = &gAdmv1139Dev;
gpio_set_value(pDev->tCfg.uiCsPin, 0);
}
/*
* 功能: 写寄存器数据
* 参数: uiAddr 寄存器地址 ucData: 写入的寄存器数据
*/
static uint32_t Admv1139_WriteReg(uint16_t uiAddr, uint8_t ucData)
{
int32_t iRet = -1;
T_Admv1139Dev *pDev = &gAdmv1139Dev;
struct spi_device *pSpidev = pDev->pSpiDev;
uint16_t ucTxBuf[3] = {0};
ucTxBuf[0] = 1<<15 | uiAddr;
ucTxBuf[2] = ucData;
Admv1139_DisableCs();
iRet = spi_write(pSpidev, ucTxBuf, sizeof(ucTxBuf));
Admv1139_EnableCs();
if(iRet < 0)
{
LOG_ERROR("write REG fail.\n");
return -1;
}
LOG_DEBUG("Admv1139 SPI write reg:Addr = \"PRINTF_BINARY_PATTERN_INT8\" \"PRINTF_BINARY_PATTERN_INT8\"\nData = \"PRINTF_BINARY_PATTERN_INT8\"\n",
PRINTF_BYTE_TO_BINARY_INT8(ucTxBuf[0]),
PRINTF_BYTE_TO_BINARY_INT8(ucTxBuf[1]),
PRINTF_BYTE_TO_BINARY_INT8(ucTxBuf[2]));
return 0;
}
/*
* 功能: 读寄存器数据
* 参数: uiAddr 寄存器地址 ucData: 写入的寄存器数据
*/
static uint32_t Admv1139_ReadReg(uint8_t uiAddr, uint8_t ucData)
{
int32_t iRet = -1;
T_Admv1139Dev *pDev = &gAdmv1139Dev;
struct spi_device *pSpidev = pDev->pSpiDev;
uint8_t ucTxBuf[4] = {0};
ucTxBuf[0] = 1<<7;
ucTxBuf[1] = uiAddr;
ucTxBuf[2] = ucData;
Admv1139_DisableCs();
iRet = spi_write(pSpidev, ucTxBuf, sizeof(ucTxBuf));
Admv1139_EnableCs();
if(iRet < 0)
{
LOG_ERROR("write REG fail.\n");
return -1;
}
LOG_DEBUG("Admv1139 SPI write reg:Addr = \"PRINTF_BINARY_PATTERN_INT8\" \"PRINTF_BINARY_PATTERN_INT8\"\nData = \"PRINTF_BINARY_PATTERN_INT8\"\n",
PRINTF_BYTE_TO_BINARY_INT8(ucTxBuf[0]),
PRINTF_BYTE_TO_BINARY_INT8(ucTxBuf[1]),
PRINTF_BYTE_TO_BINARY_INT8(ucTxBuf[2]));
return 0;
}
注册驱动
static int32_t Admv1139_SpiProbe(struct spi_device *pSpiDev)
{
T_Admv1139Dev *pDev = &gAdmv1139Dev;
int32_t iRet=-1;
LOG_DEBUG(DBG_LENS_IRIS, "probe spi device '%s' success and infomation as follows: \n",
pSpiDev->modalias);
LOG_DEBUG(DBG_LENS_IRIS, "max_speed_hz = %d, chip_select=%d, mode=0x%x, bits_per_word=%d",
pSpiDev->max_speed_hz, pSpiDev->chip_select,
pSpiDev->mode, pSpiDev->bits_per_word);
pSpiDev->max_speed_hz = 2000000;
iRet = spi_setup(pSpiDev);
if(iRet < 0)
{
LOG_ERROR("bu240 spi setup fail.\n");
return -1;
}
pDev->pSpiDev = pSpiDev;
pDev->ucSpiFlag = 1;
return 0;
}
static int32_t Admv1139_SpiRemove(struct spi_device *pSpiDev)
{
LOG_INFO("remove spi device '%s' success.\n", pSpiDev->modalias);
return 0;
}
驱动出入口
static int32_t Admv1139_InitConfig(void)
{
T_Admv1139Dev *pDev = &gAdmv1139Dev;
T_DevType tDevType = DEV_NULL;
//根据平台产品型号配置并初始化管脚
tDevType = Config_GetDevType();
switch(tDevType)
{
case DEV_PTP4100:
{
pDev->tCfg.uiCenPin = ;
pDev->tCfg.uiSdo = ;
pDev->tCfg.uiSclk = ;
pDev->tCfg.uiCsPin = ;
pDev->tCfg.uiLoad = ;
pDev->tCfg.uiTx_en = ;
pDev->tCfg.uiRx_en = ;
break;
}
default:
{
LOG_ERROR("unsuppot device type=%d.\n", tDevType);
return -1;
}
}
//配置的管脚初始化
Gpio_SetDir(pDev->tCfg.uiCenPin, 1);
Gpio_SetDir(pDev->tCfg.uiSdo, 1);
Gpio_SetDir(pDev->tCfg.uiCsPin, 1);
Gpio_SetDir(pDev->tCfg.uiLoad, 1);
Gpio_SetDir(pDev->tCfg.uiTx_en, 1);
gpio_set_value(pDev->tCfg.uiCenPin, 1);
gpio_set_value(pDev->tCfg.uiSdo, 1);
gpio_set_value(pDev->tCfg.uiCsPin, 1);
gpio_set_value(pDev->tCfg.uiLoad, 0);
gpio_set_value(pDev->tCfg.uiTx_en, 0);
return 0;
}
//Admv1139驱动加载入口
int32_t Admv1139_Init(T_97003Config *pCfg)
{
T_Admv1139Dev *pDev = &gAdmv1139Dev;
int32_t iRet=-1;
if(Admv1139_InitConfig() < 0)
{
LOG_ERROR("bu240 config init fail.\n");
goto Init_Config_Fail;
}
if((spi_register_driver(&gAdmv1139_SpiDrv) < 0) || (pDev->ucSpiFlag==0))
{
LOG_ERROR("register spi driver fail, please check bu240 kernel support.\n");
goto Register_Spi_Fail;
}
Admv1139_InitReg();
LOG_INFO("Admv1139 init success.\n");
return 0;
Register_Spi_Fail:
Init_Config_Fail:
return -1;
}
//功能描述: Admv1139驱动卸载入口
void Admv1139_Exit(void)
{
spi_unregister_driver(&gAdmv1139_SpiDrv);
LOG_INFO("Admv1139 exit success.\n");
}
学习 SPI 驱动只要是学习一个框架,当然框架怎么好维护就怎么写。
代码中用到了几个封装好的函数:
/*
* 功能: 设置管脚方向
* 参数: uiPinId: 管脚编号
* uiDir: 管脚方向 0.input 1.output
*/
int32_t Gpio_SetDir(uint32_t uiPinId, uint32_t uiDir)
{
if(gpio_is_valid(uiPinId) == 0)
{
LOG_ERROR("gpio%d is invalid.\n", uiPinId);
return -1;
}
if(uiDir == 0)
gpio_direction_input(uiPinId);
else
gpio_direction_output(uiPinId, 0);
return 0;
}
/*
* 功能: 配置gpio
* 参数: uiPinId: 管脚编号
* uiDir: 管脚方向 0.input 1.output
* uiLevel: 默认电平
*/
int32_t Gpio_Config(uint32_t uiPinId, uint32_t uiDir, uint32_t uiLevel)
{
if(gpio_is_valid(uiPinId) == 0)
{
LOG_ERROR("gpio%d is invalid.\n", uiPinId);
return -1;
}
if(uiDir == 0)
gpio_direction_input(uiPinId);
else
gpio_direction_output(uiPinId, !!uiLevel);
return 0;
}