mtk spi驱动移植

来源:互联网 发布:java开发工作描述 编辑:程序博客网 时间:2024/05/01 15:58

SPI  apk->jni->ko的整体框架及部分代码介绍

SPI, 属于 串行外围设备接口, 是 Motorola 公司推出的一种同步串行接口技术. SPI 总线在物理上是通过接在外围设备微控制器(PICmicro) 上面的微处理控制单元 (MCU) 上叫作同步串行端口(Synchronous Serial Port) 的模块(Module)来实现的, 它允许 MCU 以全双工的同步串行方式, 与各种外围设备进行高速数据通信.

通常情况下, 我们只需要对上图所描述的四个管脚(pin) 进行编程即可控制整个 SPI 设备之间的数据通信:
        SCK, Serial Clock, 主要的作用是 Master 设备往 Slave 设备传输时钟信号, 控制数据交换的时机以及速率;
        CS,  用于 Master 设备片选 Slave 设备, 使被选中的 Slave 设备能够被 Master 设备所访问;
        MOSI,  在 Master 上面也被称为 Tx-Channel, 作为数据的出口, 主要用于 SPI 主设备发送给SPI从设备的接口;
        MISO, 在 Master 上面也被称为 Rx-Channel, 作为数据的入口, 主要用于SPI 主设备接收数据;


SPI  通信协议描述

总线工作方式有四种:

对应的CPOL和CPHA分别是时钟极性和时钟相位,我接下来采用的是模式3;


如何判断CPOL和CPHA

            如果起始的SCLK的电平是0,那么CPOL=0,如果是1,那么CPOL=1,然后看数据采样时刻,即时序图数据线上的数据那个矩形区域的中间所对应的位置,对应到上面SCLK时钟的位置,对应着是第一个边沿或是第二个边沿,即CPHA是0或1。(对应的是上升沿还是还是下降沿,要根据对应的CPOL的值,才能确定)。
(1)如何判断CPOL:SCLK的空闲时候的电压,是0还是1,决定了CPOL是0还是1;
(2)如何判断CPHA:而数据采样时刻对应着的SCLK的电平,是第一个边沿还是第二个边沿,对应着CPHA为0还是1。


SCLK的极性,相位,边沿之间的内在逻辑

来看一下SPI的CPOL和CPHA

看完上面的spi基础现在贴我驱动的部分代码,首先我们要知道在驱动中是先去注册一个设备,通过spi -- spi_board_info和
spi message这两者

static struct spi_board_info pg6317_spi_board_devs[] __initdata = 

{[0] = {.modalias = DRVNAME_PG6137,.max_speed_hz = 8*1000*1000, //8* 1000*1000,  // 25MHz.bus_num = 0,.chip_select = 0,.mode = SPI_MODE_3, //选择模式我模块是用的模式3,cpol=1,cpha=1;.controller_data = &spi_conf_mt65xx,},};

#define GPIO_SPI_CS_PIN (GPIO65 | 0x80000000)#define GPIO_SPI_SCK_PIN (GPIO66 | 0x80000000)#define GPIO_SPI_MISO_PIN (GPIO67 | 0x80000000)#define GPIO_SPI_MOSI_PIN (GPIO68 | 0x80000000)void pg6137_hw_init(void){mt_set_gpio_mode(GPIO_SPI_CS_PIN, 0);mt_set_gpio_dir(GPIO_SPI_CS_PIN,GPIO_DIR_OUT);mt_set_gpio_out(GPIO_SPI_CS_PIN, GPIO_OUT_ONE);mt_set_gpio_mode(GPIO_SPI_SCK_PIN, 1);mt_set_gpio_dir(GPIO_SPI_SCK_PIN,GPIO_DIR_OUT);mt_set_gpio_pull_enable(GPIO_SPI_SCK_PIN, GPIO_PULL_ENABLE);mt_set_gpio_pull_select(GPIO_SPI_SCK_PIN, GPIO_PULL_DOWN);mt_set_gpio_mode(GPIO_SPI_MISO_PIN, 1);mt_set_gpio_dir(GPIO_SPI_MISO_PIN,GPIO_DIR_IN);mt_set_gpio_pull_enable(GPIO_SPI_MISO_PIN, GPIO_PULL_ENABLE);mt_set_gpio_pull_select(GPIO_SPI_MISO_PIN, GPIO_PULL_DOWN);mt_set_gpio_mode(GPIO_SPI_MOSI_PIN, 1);mt_set_gpio_dir(GPIO_SPI_MOSI_PIN,GPIO_DIR_OUT);mt_set_gpio_pull_enable(GPIO_SPI_MOSI_PIN, GPIO_PULL_ENABLE);mt_set_gpio_pull_select(GPIO_SPI_MOSI_PIN, GPIO_PULL_DOWN);printk("pg6137_hw_init  Set Gpio Ok\n");}

上面这段代码是我初始化我的spi口,我是用的是开发板默认spi口,但是需要注意我把cs端置为了普通IO口来控制,因为我在调试的时候花了很长时间去调试,我的mosi能够正常发送(示波器测到波形完全正常),CLK和CS都是正常的可miso总是接收错误的数据后面改了很久才知道是片选默认的时间不够,因此我设置为普通引脚在我读和写的时候分别延时,最终数据接收正确。


由于我没有用ioctl也没去完善这部分代码我就直接上read和write的代码
static ssize_t beidou_pg6317_write(struct file *filp, const char *buff, size_t len, loff_t * off){char spi_dat[50];int i;int state = -1;copy_from_user(spi_dat , buff , len); #if 0printk("********************* \n");for(i=0;i<50;i++){spi_dat[i]=cpu_to_be32(spi_dat[i]);printk("0x%x",spi_dat[i]);}#endiffor(i = 0; i < len ; i++)printk("spi_dat[%d]=%x---",i,spi_dat[i]);printk(" \n");mt_set_gpio_mode(GPIO_SPI_CS_PIN, 0);mt_set_gpio_dir(GPIO_SPI_CS_PIN,GPIO_DIR_OUT);mt_set_gpio_out(GPIO_SPI_CS_PIN, GPIO_OUT_ZERO);udelay(1000);  //增加延时的时间;    state = spi_writebuf(spi_device , spi_dat , len);  mt_set_gpio_mode(GPIO_SPI_CS_PIN, 0);mt_set_gpio_dir(GPIO_SPI_CS_PIN,GPIO_DIR_OUT);mt_set_gpio_out(GPIO_SPI_CS_PIN, GPIO_OUT_ONE);udelay(1000);   printk("beidou_pg6317_write state=%d\n",state);printk("beidou_pg6317_write ok\n");if(state >= 0)return len;return -1;   }
int spi_writebuf(struct spi_device *spi , u8 *tarnsfer_data , u8 len){struct spi_transfer pg6317_spi_xfer;  struct spi_message pg6317_spi_msg;spi_message_init(&pg6317_spi_msg);  pg6317_spi_xfer.tx_buf = tarnsfer_data;  pg6317_spi_xfer.rx_buf = tarnsfer_data;pg6317_spi_xfer.len = len;  pg6317_spi_xfer.bits_per_word = 8;  spi_message_add_tail(&pg6317_spi_xfer, &pg6317_spi_msg);  printk("spi_writebuf ok\n");return spi_sync(spi, &pg6317_spi_msg); }


static ssize_t beidou_pg6317_read(struct file *filp, char *buffer, size_t length,loff_t * offset)  {u32 spi_dat[50];u8 i;int state = -1;mt_set_gpio_mode(GPIO_SPI_CS_PIN, 0);mt_set_gpio_dir(GPIO_SPI_CS_PIN,GPIO_DIR_OUT);mt_set_gpio_out(GPIO_SPI_CS_PIN, GPIO_OUT_ZERO);udelay(1000);state = spi_readbuf(spi_device , spi_dat , length);printk("beidou_pg6317_read state=%d\n",state);mt_set_gpio_mode(GPIO_SPI_CS_PIN, 0);mt_set_gpio_dir(GPIO_SPI_CS_PIN,GPIO_DIR_OUT);mt_set_gpio_out(GPIO_SPI_CS_PIN, GPIO_OUT_ONE);printk("********************* \n");for(i=0;i<50;i++){spi_dat[i]=cpu_to_be32(spi_dat[i]);}printk("********************* \n");copy_to_user(buffer , (char *)spi_dat , length);printk("beidou_pg6317 read ok\n");if(state >= 0)return length;return -1;}
int spi_readbuf(struct spi_device *spi , u8 *tarnsfer_data , u8 receive_len){int read_state = 0;struct spi_transfer pg6317_spi_xfer;  struct spi_message pg6317_spi_msg;spi_message_init(&pg6317_spi_msg);  pg6317_spi_xfer.tx_buf = tarnsfer_data;  pg6317_spi_xfer.rx_buf = tarnsfer_data; pg6317_spi_xfer.len = receive_len;pg6317_spi_xfer.bits_per_word = 8;  spi_message_add_tail(&pg6317_spi_xfer, &pg6317_spi_msg);  read_state = spi_sync(spi, &pg6317_spi_msg);printk("spi_readbuf ok\n");return read_state;}


读和写的代码很简单我就不一一说明了
下面这部分代码是spi的一些参数设置,需要重视

static int SPI_Init(struct spi_device *db){struct mt_chip_conf* spi_par; // added 2015-12-3    spi_par = (struct mt_chip_conf *) db->controller_data;spi_par->setuptime = 15;//20;//15; spi_par->holdtime = 15;//20;//15; spi_par->high_time = 100; //500; // 2      //10--6m   15--4m   20--3m  30--2m  [ 60--1m 120--0.5m  300--0.2m]spi_par->low_time = 100; //500;  //2spi_par->cs_idletime = 20; //25; //20//spi_par->ulthgh_thrsh = 0;//spi_par->sample_sel = 0;//spi_par->cs_pol = 0;spi_par->rx_mlsb = 1; spi_par->tx_mlsb = 1; spi_par->tx_endian = 0;spi_par->rx_endian = 0;spi_par->cpol = 1; //0;spi_par->cpha = 1; // 0;spi_par->com_mod = DMA_TRANSFER;spi_par->pause = 1;spi_par->finish_intr = 1;spi_par->deassert = 0;spi_setup(db);}
 
spi_par为从设备变量spi_device,相关参数如下:
1.high_time  &  low_time:SCK 推荐速率
2. rx_mlsb & tx_mlsb:传输数据时,先从低bit发送还是高bit发送,1时代表先从低bit发送
3. tx_endian/rx_endian: 传输数据时,以大端模式传输或者小端模式传输,只对DMA传输有效,0时,代表小端模式传
输; fifo mode为小端模式。
4.com_mod: DMA或者fifo 传输,如下图设定即可
5. cpol:时钟极性选择
–CPOL=0,空闲电平为低电平,即高电平时输入数据有效
–CPOL=1时,空闲电平为高电平,即低电平时输入数据有效
6. cpha:时钟相位选择位 
–CPHA=0,在奇次边沿采样,即每个周期的前沿采样,后沿输出
–CPHA=1,在偶次边沿采样,即每个周期的后沿采样,前沿输出


spi的底层驱动就差不多完事

现在我们来看看jni层干了些什么?

JNIEXPORT jint JNICALL Java_com_keymantek_serialport_utils_TesamSPI_init(JNIEnv *env, jobject thiz){int ret = -1;int fd_state=0,i=0,read_long=0,write_count = 0;fd_fps = open("/dev/pg6317_spi", O_RDWR);LOGE("open fps add by ccy %d!\n",fd_fps);if(fd_fps < 0)    {LOGE("open error %d!\n",fd_fps);return ret;    }ret =0;return ret;}
首先是一个打开设备的函数


JNIEXPORT jint JNICALL Java_com_keymantek_serialport_utils_TesamSPI_deInit(JNIEnv *env, jobject thiz){int ret;LOGE("deInit is comming by ccy!\n");LOGE("close fps add by ccy %d!\n",fd_fps);close(fd_fps);g_fd = -1;fd_fps = -1;if(fd_fps <=0){ret = 0;}else{ret = -1;LOGE("close fps and sri failed!\n");}return ret;}
close(fd)


JNIEXPORT jint JNICALL Java_com_keymantek_serialport_utils_TesamSPI_sendData  (JNIEnv *env, jobject obj,jbyteArray data,jint offset,jint length){struct spi_info spi;jint m;jint j;jint n;jint l;LOGE("ioctrll is enter by ccy!\n");jint ver = 0;jint ret = -1;unsigned char ccy[length];unsigned char array[length];(*env)->GetByteArrayRegion(env,data,0,length,array);LOGE("FPS1196 length is by ccy %d\n",length);for(l=0;l<length;l++){memcpy(&ccy[l],&array[offset+l],1);}for(n=0;n<length;n++){LOGE("FPS1196 ccydata[%d] is by ccy %x\n",n,ccy[n]);}memcpy(spi.tx,ccy,length);//copy到spi.tx去我使用write写给我的设备;spi.count = length;spi.off = offset;for(m = 0;m <length;m++){LOGE("FPS1196 txdata[%d] is by ccy %x\n",m,spi.tx[m]);}if(write(fd_fps, spi.tx, spi.count) > 0){LOGE("add by ccy1 for ioctl to spi IOCTL_FPS_WRITE%d!\n",fd_fps);ret = length;}else{LOGE("fps is close by ccy failed!\n" );}return ret;}

写函数;


JNIEXPORT jint JNICALL Java_com_keymantek_serialport_utils_TesamSPI_recvData(JNIEnv *env, jobject obj,jbyteArray data,jint offset){struct spi_info spi;jint m;jint j;jint n;jint l;int len;LOGE("ioctrll is enter by ccy!\n");jint ver = 0;jint ret = -1;unsigned char *pBuffer = (*env)->GetByteArrayElements(env,data,NULL);jsize arrayLength = (*env)->GetArrayLength(env, data);len = (int) arrayLength;if(pBuffer == NULL){LOGE("GetByteArrayElements failed add by ccy!");return;}LOGE("Go read!");if( read(fd_fps, spi.rx, len) > 0){LOGE("add by ccy1 for ioctl to spi IOCTL_FPS_READ %d!\n",len);ret=len;}else{LOGE("fps is close by ccy failed!\n" );}//read(fd_fps, spi.rx, sizeof(spi.rx));//ret=sizeof(spi.rx);for(l=3;l<len;l++){memcpy(&pBuffer[l-3+offset],&spi.rx[0+l],1);//由于我读回来模块前3个字节是乱的所以我在这里需要去掉}for(j = 0;j < len ; j++){//LOGE("FPS1196_READ rxdata[%d] is by ccy %x\n",j,spi.rx[j]);LOGE("FPS1196_READ pBuffer[%d] is by ccy %x\n",j,pBuffer[j]);}(*env)->ReleaseByteArrayElements(env,data,pBuffer,0);LOGE("ccy is end11!!!\n");LOGE("ccy is end!!!\n"); return ret;}
读函数;

最后就是apk调用我的open.write.read函数来进行读写操作了;

public void onClick(View v) {// TODO Auto-generated method stubswitch (v.getId()) {case R.id.button1: int a=tesamSPI.Open(); Log.d("ccy", "init()="+a); int b=tesamSPI.Read( buf, 0, 8); Log.d("ccy", "sendData()="+b); int c=tesamSPI.Rece( rx,0); Log.d("ccy", "Rece()="+c); Log.d("ccy", "发送"+bytesToHexString(rx)); int d=tesamSPI.Close(); Log.d("ccy", "Close()="+d); tv.setText("接收"+bytesToHexString(rx)); startBtn.setEnabled(false); stopBtn.setEnabled(true);break;case R.id.button2://Log.e("ccy", "init"); tv.setText(""); startBtn.setEnabled(true); stopBtn.setEnabled(false);break;default:break;}      }

差不多我的spi从底层到顶层的框架就是这样了

对了我是以字节形式输出的所以需要写一个字节转十六进制的代码这个百度即可!


0 0
原创粉丝点击