目录
- 前言
- 一、配置内容
- 二、驱动代码实现
- 三、总结
前言
本笔记总结linux下使用gpio模拟spi时序的方法,基于arm64架构的一个SOC,linux内核版本为linux5.10.xxx,以驱动三线spi(时钟线sclk,片选cs,sdata数据读和写使用同一个数据线进行分时复用做半双工,笔记中使用mosi作为读写的数据线进行配置)的hmc704x为例;
一、配置内容
设备树配置
1.在芯片的引脚模式寄存器中配置 cs ,sclk , mosi ,miso为 gpio模式;
2.在spi控制器下添加pinctrl-x = 引脚引用,对引脚的模式进行生效
;
3.在spi控制器的节点下添加spi从设备节点,并定义cs,sclk,mosi,miso的gpio属性:
spics = <引用>;
spiclk = <引用>;
spimiso = <引用>;
spimosi = <引用>;
二、驱动代码实现
1、gpio请求
int spics_gpio = 0;
int spiclk_gpio = 0;
int spimiso_gpio = 0;
int spimosi_gpio = 0;
void xxx_probe(xxxx)
{
spics_gpio = of_get_named_gpio(spi->dev.of_node,"spics",0);
if(spics_gpio<0){
printk("faild to request gpio!\n");
}
gpio_request(spics_gpio,NULL);
gpio_direction_output(spics_gpio,0);
spiclk_gpio = of_get_named_gpio(spi->dev.of_node,"spiclk",0);
if(spics_gpio<0){
printk("faild to request gpio!\n");
}
printk("get GPIO%d\n",spiclk_gpio);
gpio_request(spiclk_gpio,NULL);
gpio_direction_output(spiclk_gpio,0);
spimiso_gpio = of_get_named_gpio(spi->dev.of_node,"spimiso",0);
if(spimiso_gpio<0){
printk("faild to request gpio!\n");
}
gpio_request(spimiso_gpio,NULL);
gpio_direction_output(spimiso_gpio,0);
spimosi_gpio = of_get_named_gpio(spi->dev.of_node,"spimosi",0);
if(spimosi_gpio<0){
printk("faild to request gpio!\n");
}
gpio_request(spimosi_gpio,NULL);
gpio_direction_output(spimosi_gpio,0);
}
2、读写函数实现
读写时序
读函数
#define HMC7044_WRITE (0 << 15)
#define HMC7044_READ (1 << 15)
#define HMC7044_CNT(x) (((x) - 1) << 13)
#define HMC7044_ADDR(x) ((x) & 0xFFF)
static int xxx_read(
unsigned int reg,
unsigned int *val)
{
u16 cmd,i;
u32 data,datatemp;
uint8_t tempvalue = 0;
cmd = HMC7044_READ | HMC7044_CNT(1) | HMC7044_ADDR(reg);
data = cmd;
gpio_direction_output(spimosi_gpio,0);
gpio_set_value(spics_gpio,1);
gpio_set_value(spiclk_gpio,0);
gpio_set_value(spimosi_gpio,0);
udelay(5);
gpio_set_value(spics_gpio,0);
for(i=0;i<16;i++)
{
gpio_set_value(spiclk_gpio,0);
datatemp = ((data>>(15-i)) & 0x1);
if(datatemp>0) gpio_set_value(spimosi_gpio,1);
else gpio_set_value(spimosi_gpio,0);
udelay(5);
gpio_set_value(spiclk_gpio,1);
udelay(5);
}
gpio_direction_input(spimosi_gpio);
for(i=0;i<8;i++)
{
gpio_set_value(spiclk_gpio,0);
udelay(5);
tempvalue<<=1;
gpio_set_value(spiclk_gpio,1);
if(gpio_get_value(spimosi_gpio))
{
tempvalue++;
}
udelay(5);
}
gpio_set_value(spics_gpio,1);
*val = tempvalue;
return 0;
}
写函数
static int xxx_write(
unsigned int reg,
unsigned int val)
{
u16 cmd,i;
u32 data,datatemp;
cmd = HMC7044_WRITE | HMC7044_CNT(1) | HMC7044_ADDR(reg);
data = (cmd << 8) | (val & 0xFF);
gpio_direction_output(spimosi_gpio,0);
gpio_set_value(spics_gpio,1);
gpio_set_value(spiclk_gpio,0);
gpio_set_value(spimosi_gpio,0);
udelay(5);
gpio_set_value(spics_gpio,0);
for(i=0;i<24;i++)
{
gpio_set_value(spiclk_gpio,0);
datatemp = ((data>>(23-i)) & 0x1);
if(datatemp>0) gpio_set_value(spimosi_gpio,1);
else gpio_set_value(spimosi_gpio,0);
udelay(5);
gpio_set_value(spiclk_gpio,1);
udelay(5);
}
gpio_set_value(spics_gpio,1);
return 0;
}
三、总结
具体的芯片读写协议不一样,按具体spi时序协议做相应修改即可;