spi驱动代码
spiFLASH芯片WQ的单片机驱动代码

spiFLASH芯片WQ的单片机驱动代码#include "w25q80.h"// 注:W25Q80由256 BYTE 组成一个PAGE,不可PGAE擦除,可以进行BYTE PROGRAM 或者PAGE PROGRAM// 由16 PAGE 组成一个SECTOR,可SECTOR擦除// 由16 SECTOR组成一个BLOCK,可BLOCK 擦除// 由16 BLOCK 组成一个FULL MEMEORY,可FULL MEMORY 擦除// 所以,总容量是1M bytes// W25Q80主要命令字#define READ_ARRAY 0x03#define SECTOR_ERASE 0x20#define BYTE_OR_PAGE_PROGRAM 0x02#define WRITE_ENABLE 0x06#define WRITE_DISABLE 0x04#define READ_STATUS_REGISTER 0x05#define Manufacturer_DeviceID 0x9F// 定义W25Q80的CS脚对应MCU的IO#define W25Q80_CS P1_2// SPI硬件初始化void Spi_Init(void){PERCFG |= 0x02; // SPI1映射到P1口P1SEL |= 0xE0; // P15~P17作复用功能(clk mosi miso)P1SEL &= ~0x04; // P12作GPIOP1DIR |= 0x04; // P12作输出P1_2 = 1; // P12输出高电平U1CSR &= ~0xA0; // SPI主方式U1GCR &= ~0xC0; // CPOL=0 CPHA=0U1GCR |= 0x20; // MSBU1BAUD = 0; // 波特率设为sysclk/8U1GCR |= 0x11;// SPI发送与接收字节static u8 Spi_ReadWriteByte(u8 TxData){U1DBUF = TxData; // 发送数据while( !(U1CSR&0x02) ); // 等待发送完成U1CSR &= 0xFD; // 清除发送完成标志return U1DBUF;}// CS线拉低,使能芯片static void W25Q80_Enable( void ){volatile u8 i;W25Q80_CS = 0;for ( i=5; i>0; i-- ); // 延时}// CS线拉高,禁能芯片#define W25Q80_Disable() ( W25Q80_CS = 1 )// 设置FLASH芯片"写使能"static void SetW25Q80WriteEnable(void){W25Q80_Enable();Spi_ReadWriteByte(WRITE_ENABLE);W25Q80_Disable();}// 设置FLASH芯片"写禁能"//static void ClearW25Q80WriteEnable(void)//{// W25Q80_Enable();// Spi_ReadWriteByte(WRITE_DISABLE);// W25Q80_Disable();//}// 读取FLASH芯片的状态字节,可判断芯片是否busystatic u8 ReadW25Q80StatusRegister(void){u8 temp;W25Q80_Enable();Spi_ReadWriteByte(READ_STATUS_REGISTER);temp = Spi_ReadWriteByte(0xF0);W25Q80_Disable();return temp;}// 读取FLASH的内容,读取的字节数没有限制void ReadW25Q80Operation(u32 addr,u8 *databuf,u32 len){u32 i = 0;u8 temp;temp = ReadW25Q80StatusRegister();while( temp&0x01 ) // 等待FLASH芯片结束BUSY状态{temp = ReadW25Q80StatusRegister();if ( ++i>1000000 ) return;}W25Q80_Enable();Spi_ReadWriteByte(READ_ARRAY);Spi_ReadWriteByte((u8)(addr >>16));Spi_ReadWriteByte((u8)(addr >>8));Spi_ReadWriteByte((u8)addr);for ( i=0; i<len; i++ ){databuf[i] = Spi_ReadWriteByte(0xF0);}W25Q80_Disable();}// 写入FLASH,一次最多写256字节,注意不要越界(不要超过页边界)void WriteW25Q80Operation(u32 addr,u8 *databuf,u16 len){u32 i = 0;u8 temp;temp = ReadW25Q80StatusRegister();while( temp&0x01 ) // 等待FLASH芯片结束BUSY状态{temp = ReadW25Q80StatusRegister();if ( ++i>1000000 ) return;}SetW25Q80WriteEnable(); // 设置芯片写使能W25Q80_Enable();Spi_ReadWriteByte(BYTE_OR_PAGE_PROGRAM);Spi_ReadWriteByte((u8)(addr >>16));Spi_ReadWriteByte((u8)(addr >>8));Spi_ReadWriteByte((u8)addr);for ( i=0; i<len; i++ ){Spi_ReadWriteByte(databuf[i]);}W25Q80_Disable();}// 对FLASH进行SECTOR擦除void EraseW25Q80Operation(u32 addr){u32 i = 0;u8 temp;temp = ReadW25Q80StatusRegister();while( temp&0x01 ) // 等待FLASH芯片结束BUSY状态{temp = ReadW25Q80StatusRegister();if ( ++i>1000000 ) return;}SetW25Q80WriteEnable(); // 设置芯片写使能W25Q80_Enable();Spi_ReadWriteByte(SECTOR_ERASE);Spi_ReadWriteByte((u8)(addr >>16));Spi_ReadWriteByte((u8)(addr >>8));Spi_ReadWriteByte((u8)addr);W25Q80_Disable();}// 读取FLASH芯片的JEDEC ID信息void ReadW25Q80DeviceID(u8 *buf){W25Q80_Enable();Spi_ReadWriteByte(Manufacturer_DeviceID);buf[0] = Spi_ReadWriteByte(0xF0);buf[1] = Spi_ReadWriteByte(0xF0);buf[2] = Spi_ReadWriteByte(0xF0);W25Q80_Disable();}。
linuxSPI驱动——gpio模拟spi驱动(转载)

linuxSPI驱动——gpio模拟spi驱动(转载)⼀:⾸先在我的平台注册platform_device,保证能让spi-gpio.c能执⾏到probe函数。
1: struct spi_gpio_platform_data {2: unsigned sck;3: unsigned mosi;4: unsigned miso;5:6: u16 num_chipselect;7: };1: //#define NCS GPIO_PB(2) //定义SS所对应的GPIO接⼝编号2: //#define SCLK GPIO_PB(0) //定义SCLK所对应的GPIO接⼝编号3: //#define MOSI GPIO_PB(4) //定义SCLK所对应的GPIO接⼝编号4: //#define MISO GPIO_PB(1)5: static struct spi_gpio_platform_data jz_spi_gpio_data = {6: .sck = GPIO_PB(0), //GPIO_SPI_SCK,7: .mosi = GPIO_PB(4), //GPIO_SPI_MOSI,8: .miso = GPIO_PB(1), //GPIO_SPI_MISO,9: .num_chipselect = 1,10: };11:12: struct platform_device jz_spi_gpio_device = {13: .name = "spi_gpio",14: .id = 0,15: .dev = {16: .platform_data = &jz_spi_gpio_data,17: },18: };注册platform device1: platform_device_register(&jz_spi_gpio_device);⼆:注册platform_driver在spi_gpio.c⾥⾯注册platform driver1: MODULE_ALIAS("platform:" DRIVER_NAME);2:3: static struct platform_driver spi_gpio_driver = {4: = DRIVER_NAME,5: .driver.owner = THIS_MODULE,6: .remove = __exit_p(spi_gpio_remove),7: };8:9: static int __init spi_gpio_init(void)10: {11: return platform_driver_probe(&spi_gpio_driver, spi_gpio_probe);12: }13: module_init(spi_gpio_init);14:15: static void __exit spi_gpio_exit(void)16: {17: platform_driver_unregister(&spi_gpio_driver);18: }19: module_exit(spi_gpio_exit);20:21:22: MODULE_DESCRIPTION("SPI master driver using generic bitbanged GPIO ");23: MODULE_AUTHOR("David Brownell");24: MODULE_LICENSE("GPL");三:具体算法分析1: struct spi_gpio {2: struct spi_bitbang bitbang; /* gpio 模拟spi算法相关的结构 */3: struct spi_gpio_platform_data pdata; /* spi platform data 对应模拟spi的四个gpio编号 */4: struct platform_device *pdev; /* 对应注册的 platform device */5: };1:2: static int __init spi_gpio_probe(struct platform_device *pdev)3: {4: int status;5: struct spi_master *master;6: struct spi_gpio *spi_gpio;7: struct spi_gpio_platform_data *pdata;8: u16 master_flags = 0;9:10: pdata = pdev->dev.platform_data; /* 存放spi的四根gpio */11: #ifdef GENERIC_BITBANG12: if (!pdata || !pdata->num_chipselect)13: return -ENODEV;14: #endif15:16: /* 申请注册四个gpio */17: status = spi_gpio_request(pdata, dev_name(&pdev->dev), &master_flags);18: if (status < 0) {19: return status;20: }21:22: /* alloc a spi master ,master->dev->p->driver_data = &master[1]*/23: master = spi_alloc_master(&pdev->dev, sizeof *spi_gpio);24: if (!master) {25: status = -ENOMEM;26: goto gpio_free;27: }28: /* spi_gpio指向⼀块空间, 即指向mstaer[1]29: pdev->dev->p->driver_data = spi_gpio;30: 初始化spi_gpio31: */32: spi_gpio = spi_master_get_devdata(master);33: platform_set_drvdata(pdev, spi_gpio);34:35: spi_gpio->pdev = pdev;36: if (pdata)37: spi_gpio->pdata = *pdata;38:39: master->flags = master_flags;40: master->bus_num = pdev->id;41: master->num_chipselect = SPI_N_CHIPSEL;42: master->setup = spi_gpio_setup; /* setup ⽐如cs引脚申请 */43: master->cleanup = spi_gpio_cleanup;44: /* spi_gpio->bitbang.master = master */45: spi_gpio->bitbang.master = spi_master_get(master);46: spi_gpio->bitbang.chipselect = spi_gpio_chipselect;47: /* spi_gpio->bitbang.txrx_word 数组函数四个元素指针,分别指向spi四种mode算法函数 */ 48: if ((master_flags & (SPI_MASTER_NO_TX | SPI_MASTER_NO_RX)) == 0) {49: spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0;50: spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_txrx_word_mode1;51: spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_txrx_word_mode2;52: spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_txrx_word_mode3;53: } else {54: spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_spec_txrx_word_mode0;55: spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_spec_txrx_word_mode1;56: spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_spec_txrx_word_mode2;57: spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_spec_txrx_word_mode3;58: }59: /* spi_gpio->bitbang.setup_transfer初始化传输的bits_per_word和speed */60: spi_gpio->bitbang.setup_transfer = spi_bitbang_setup_transfer;61: spi_gpio->bitbang.flags = SPI_CS_HIGH;62: /* spi_gpio->bitbang相关算法接⼝初始化 */63: status = spi_bitbang_start(&spi_gpio->bitbang);64: if (status < 0) {65: spi_master_put(spi_gpio->bitbang.master);66: gpio_free:67: if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO)68: gpio_free(SPI_MISO_GPIO);69: if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI)70: gpio_free(SPI_MOSI_GPIO);71: gpio_free(SPI_SCK_GPIO);72: spi_master_put(master);73: }74:75: return status;76: }四:总之最终让spi_gpi0整个对象存放了整个gpio模拟spi的算法结构;⽽pdev->dev->p->driver_data = spi_gpio;platform device和 platform driver两者match结果是:root@CarRadio:/# ls /sys/bus/platform/devices/spi_gpio.0/ driver modalias power spi0.0 spi_master subsystem uevent root@CarRadio:/# ls /sys/bus/platform/devices/spi_gpio.0/driver/ spi_gpio.0 uevent。
用户空间的spi驱动

如果想要在用户空间编写spi驱动,这就要在内核的arch/.../mach-*/board-*.c 中声明一个spi_board_info,它的名字一定要是“spidev”,比如:struct spi_board_info info ={.modalias = ";spidev";,.max_speed_hz = 5000000,.bus_num = 0,.chip_select = 0,};return spi_register_board_info(&;info, 1);这样只要控制器驱动加载了,spidev模块就会和这个设备绑定,并为设备申请一个设备号,主设备号为153,次设备号和设备加载的次序有关。
目前spidev支持最多32个设备。
设备的名字是spidevX.D,其中X是总线编号,D是设备的片选号。
如果正确安装并配置了udev,/dev目录下便会生成spidevX.D设备节点。
直接对这些设备节点操作就行了。
spidev的设备节点的接口包括open/close/read/write/ioctl。
~~~~~~~~~~~~~~~~~~~~~~~~~其中open/close没有什么特别之处。
read/write的话有大小的限制,读写的大小默认不能超过4096字节。
这个大小是一个模块加载参数,可以修改。
允许多个用户同时打开设备节点,spidev使用mutext进行互斥,多个用户同时读写时只有一个活动的用户,其他用户睡眠。
spidev的ioctl命令。
~~~~~~~~SPI_IOC_RD_MODE:读取spi_device的mode。
SPI_IOC_RD_LSB_FIRST:如果是SPI_LSB_FIRST的方式则返回1。
SPI_IOC_RD_BITS_PER_WORD:读取spi_device的bits_per_word.SPI_IOC_RD_MAX_SPEED_HZ:读取spi_device的max_speed_hz.SPI_IOC_WR_MODE:设置spi_device的mode,并调用spi_setup立即使设置生效。
AT45DB041D驱动第三部分即SPI驱动(测试可用)

#define SPI_GLOBALS#include "config.h"/**************************************************************************** ******函数名称:void SPI_Init( void )****函数描述:SPI接口初始化程序****参 数:无****返 回:无***************************************************************************** *****/void SPI_Init( void ){// 设置硬件SPI的通讯脚: P0.15---SCK, P0.17---MISO, P0.18---MOSI PINSEL0 |= 0xC0000000;PINSEL1 |= 0x0000003C;// MSTR = 1, SPI 处于主模式,8位,CPOL=0,CPHA=0,SPI时钟分频=8S0SPCCR = 0x08; //0x08S0SPCR = 0x20;}/**************************************************************************** ******函数名称:void SPI_Send( uint8 data )****函数描述:SPI接口发送单字节函数****参 数:data---待发送数据****返 回:无***************************************************************************** *****/void SPI_Send( uint8 data ){S0SPDR = data;while ( !(S0SPSR & 0x80) ); // 等待SPIF置位,即等待数据发送完毕S0SPSR = 0; // 清除SPIF标志}/**************************************************************************** ******函数名称:void SPI_Send_nBytes( uint8 *buf, uint32 Length )****函数描述:SPI接口发送多字节函数****参 数:*buf---发送缓冲区 Length---发送字节数****返 回:无***************************************************************************** *****/void SPI_Send_nBytes( uint8 *buf, uint32 Length ){uint32 i;for(i=0; i<Length; i++){SPI_Send(*buf);buf++;}}/**************************************************************************** ******函数名称:uint8 SPI_Recv( void )****函数描述:SPI接口接收单字节函数****参 数:无****返 回:S0SPDR---接收到的数据***************************************************************************** *****/uint8 SPI_Recv( void ){S0SPDR = 0xff;// 发送该数据用以产生时钟,数据本身没有用处while ( !(S0SPSR & 0x80) ); // 等待SPIF置位,即等待数据发送完毕S0SPSR = 0;// 清除SPIF标志return (uint8)(S0SPDR);// 返回接收到的数据}/**********************************************************************************函数名称:void SPI_Recv_nBytes( uint8 *buf, uint32 Length )****函数描述:SPI接口接收多字节函数****参 数:*buf---接收缓冲区 Length---接收字节数****返 回:无**********************************************************************************/void SPI_Recv_nBytes( uint8 *buf, uint32 Length ){uint32 i;for (i=0; i<Length; i++){*buf = SPI_Recv();buf++;}return;}/************************************************************************************************************ End Of File ************************************************************************************************************/。
ARM—Linux下SPI设备的添加与驱动实现

策 略 和 接 口。 面 向用 户 接 口层 提 供 统 一 的 接 口, 便 S I 以 P
设 备 驱 动 通 过 总线 控 制 器 进 行 数 据 收 发 , 主 体 框 架 程 序 其
是 GP O模 拟 S I 序 s ibt a g C和 可 进 行 同/ 步 消 I P 时 p i n. b 异
d vc 置 定 义 S I 机 信 号 与 从 机 的 片 选 信 号 和 中 断 ei e配 P主
信 号 。si ei 配 置 指 定 具 体 S I 备 对 应 的 S I 机 p dvc — e P设 P 主
号 、 用的 S I 信模 式与传 输速度 和其使 用 的中断等 。 采 P通
构 图如图 l 示 。 所
甲台依赖 层(1 n1 eidL V r Pa 1R l ae1 e 、
2 借 用 通 用 s i e . 动 新 设 备 p —d v C驱
对于一些常规的 S I 备 , 存储器 、 度传感 器等 , P设 如 温 可 以将 其 连 接 到 选 定 的 S I主机 控 制 器 总 线 上 , 用 系 统 P 借 提 供 的 通 用 si e . p —d v C程 序 直 接 实 现 串 行 通 信 操 作 。需 要 做 的 是 在 平 台 匹 配 文 件 ma h X X C中 添 加 或 修 改 片 c— X.
—
息传 输 等操 作 的 s iC p. 。用 户 接 口层 即设 备 驱 动 层 , 用 为 户 提 供 了通 过 S I 线 访 问 具 体 设 备 的 接 口 , 括 S I P总 包 P 设 备 和 S I 动 程 序 。ARM —L n x下 S I总 线 的层 次 结 P驱 iu P
件 mah X X C 如 ma h s d c 1 . 。 c— X . , c m k 1 0 C
Linux下SPI驱动测试程序

L i n u x下S P I驱动测试程序-CAL-FENGHAI-(2020YEAR-YICAI)_JINGBIANLinux下的SPI总线驱动(一)2013-04-12 15:08:46分类:LINUX版权所有,转载请说明转自一.SPI理论介绍SPI总线全名,串行外围设备接口,是一种串行的主从接口,集成于很多微控制器内部。
和I2C使用2根线相比,SPI总线使用4根线:MOSI (SPI 总线主机输出/ 从机输入)、 MISO (SPI总线主机输入/从机输出)、SCLK(时钟信号,由主设备产生)、CS(从设备使能信号,由主设备控制)。
由于SPI总线有专用的数据线用于数据的发送和接收,因此可以工作于全双工,当前市面上可以找到的SPI外围设备包括RF芯片、智能卡接口、E2PROM、RTC、触摸屏传感器、ADC。
SCLK信号线只由主设备控制,从设备不能控制信号线。
同样,在一个基于SPI的设备中,至少有一个主控设备。
这样传输的特点:这样的传输方式有一个优点,与普通的串行通讯不同,普通的串行通讯一次连续传送至少8位数据,而SPI允许数据一位一位的传送,甚至允许暂停,因为SCLK 时钟线由主控设备控制,当没有时钟跳变时,从设备不采集或传送数据。
也就是说,主设备通过对SCLK时钟线的控制可以完成对通讯的控制。
SPI还是一个数据交换协议:因为SPI的数据输入和输出线独立,所以允许同时完成数据的输入和输出。
不同的SPI设备的实现方式不尽相同,主要是数据改变和采集的时间不同,在时钟信号上沿或下沿采集有不同定义,具体请参考相关器件的文档。
在点对点的通信中,SPI接口不需要进行寻址操作,且为全双工通信,显得简单高效。
在多个从设备的系统中,每个从设备需要独立的使能信号,硬件上比I2C 系统要稍微复杂一些。
二.SPI驱动移植我们下面将的驱动的移植是针对Mini2440的SPI驱动的移植Step1 :在Linux Source Code中修改arch/arm/mach-s3c2440/文件,加入头文件:#include <linux/spi/>#include <../mach-s3c2410/include/mach/>然后加入如下代码:static struct spi_board_info s3c2410_spi0_board[] ={[0] = {.modalias = "spidev", us_num = 0, hip_select = 0, rq = IRQ_EINT9, ax_speed_hz = 500 * 1000, in_cs = S3C2410_GPG(2),.num_cs = 1, us_num = 0, pio_setup = s3c24xx_spi_gpiocfg_bus0_gpe11_12_13, odalias = "spidev",.bus_num = 1,.chip_select = 0,.irq = IRQ_EINT2,.max_speed_hz = 500 * 1000,}};static struct s3c2410_spi_info s3c2410_spi1_platdata = {.pin_cs = S3C2410_GPG(3),.num_cs = 1,.bus_num = 1,.gpio_setup = s3c24xx_spi_gpiocfg_bus1_gpg5_6_7,};Step2:在mini2440_devices[]平台数组中添加如下代码:&s3c_device_spi0,&s3c_device_spi1,Step3:最后在mini2440_machine_init函数中加入如下代码:&s3c2410_spi0_platdata;spi_register_board_info(s3c2410_spi0_board, ARRAY_SIZE(s3c2410_spi0_board));&s3c2410_spi1_platdata;spi_register_board_info(s3c2410_spi1_board, ARRAY_SIZE(s3c2410_spi1_board));Step4:最后需要修改arch/arm/plat-s3c24xx/KConfig文件找到config S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13boolhelpSPI GPIO configuration code for BUS0 when connected toGPE11, GPE12 and GPE13.config S3C24XX_SPI_BUS1_GPG5_GPG6_GPG7boolhelpSPI GPIO configuration code for BUS 1 when connected toGPG5, GPG6 and GPG7.修改为config S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13bool "S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13"helpSPI GPIO configuration code for BUS0 when connected toGPE11, GPE12 and GPE13.config S3C24XX_SPI_BUS1_GPG5_GPG6_GPG7bool "S3C24XX_SPI_BUS1_GPG5_GPG6_GPG7"helpSPI GPIO configuration code for BUS 1 when connected toGPG5, GPG6 and GPG7.Step5:最后make menuconfig配置,选中System Type和SPI support相应文件Step6:执行make生成zInage,将编译好的内核导入开发板,并且编译测试程序运行即可。
spi driver

今天折腾了一天的SPI设备的驱动加载,甚至动用了逻辑分析仪来查看spi总线的波形,主要包括两个SPI设备,at45db321d和 mcp2515,一个是串行的dataflash,一个是can总线设备芯片。
前者对于我们来说非常重要,我们可以借助该设备对uboot和kernel 以及根文件系统进行更新。
预备知识:设备和驱动是如何匹配的?系统的热插拔是如何实现的?首先一点,设备和驱动是严格区分的,设备是设备,驱动是驱动,设备通过struct device来定义,当然用户也可以将该结构体封装到自己定义的device 结构体中,例如,struct platform_device,这是我们采用platform_bus_type 总线的设备定义的结构体形式:include/linux/platform_device.h文件中:struct platform_device {const char* name;u32 id;struct device dev;u32 num_resources;struct resource* resource;};只要是9260的外围模块,就像IIC硬件控制器,SPI硬件控制器,都被完全的定义成这种结构体的格式,这种结构体主要包含了硬件资源和名称,硬件资源分为寄存器和IRQ两种。
platform_device通过向内核注册struct device dev这个结构体来告诉内核加载这个设备,方法就是 device_register(&platform_device->dev)内核不关心你使用的是platform_device还是spi_device,内核只关心你的struct device结构体,内核通过这个struct device结构体自然能够顺藤摸瓜找到你是platform_device还是spi_device,这就是linux最引以为傲的contian_of()大法。
GPIO模拟SPI通讯接口的驱动

GPIO模拟SPI通讯接口的驱动一,某些时候我们会不得不使用GPIO来模拟SPI,I2C等通讯接口,如本例中,需要使用SPI接口发送9位的数据,如果使用linux内核提供的SPI子系统来做这个驱动是无法实现9位传输数据的。
二,用GPIO模拟SPI总的来说是比较简单,把相应的管脚配置成GPIO功能,再按需要配置管脚的输入输出方向,然后根据SPI总线的时序设定IO口的电平。
三,驱动代码如下,以备今后作参考:(linux-2.6.28 + TCC8900, 这个驱动是用来控制LCD的初始化的(型号为LW350AC9001))#include <linux/module.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/ioport.h>#include <asm/uaccess.h>#include <linux/delay.h>#include <bsp.h>#include <asm/io.h>#define PDEBUG#ifdef PDEBUG#define PLOG(fmt,args...) printk(fmt,##args)#else#define PLOG(fmt,args...) /*do nothing*/#endif#define SPI_CMD 0#define SPI_DATA 1#define FUN_GPIO 0#define PIN_SDO 15 //GPIOF[15]#define PIN_SDI 14#define PIN_SCLK 16#define PIN_CS 29 //GPIOC[29]#define GPC_BASE 0xF0102080#define GPF_BASE 0xF0102140#define OFFSET_DAT 0x0#define OFFSET_EN 0x4#define OFFSET_FUN0 0x24#define OFFSET_FUN1 0x28#define OFFSET_FUN2 0x2c#define OFFSET_FUN3 0x30// select pin used for gpiostatic int tcc_set_pin_fun(int pin, int fun){if(pin<8)tcc_writel(((tcc_readl(GPF_BASE+OFFSET_FUN0) & ~(0x0f<<(4*pin))) | (fun<<(4 * pin))), GPF_BASE+OFFSET_FUN0);else if(pin<16)tcc_writel(((tcc_readl(GPF_BASE+OFFSET_FUN1) & ~(0x0f<<(4*(pin-8)))) | (fun<<(4 * (pin-8)))), GPF_BASE+OFFSET_FUN1);else if(pin<24)tcc_writel(((tcc_readl(GPF_BASE+OFFSET_FUN2) & ~(0x0f<<(4*(pin-16)))) | (fun<<(4 *(pin-16)))),GPF_BASE+OFFSET_FUN2);else if(pin<32)tcc_writel(((tcc_readl(GPF_BASE+OFFSET_FUN3) & ~(0x0f<<(4*(pin-24)))) | (fun<<(4 *(pin-24)))),GPF_BASE+OFFSET_FUN3);return 0;}static int tcc_set_cs_fun(void){tcc_writel(((tcc_readl(GPC_BASE+OFFSET_FUN3) & ~(0x0f<<(4*(PIN_CS-24)))) ),GPC_BASE+OFFSET_FUN3);return 0;}// set gpio direction, output: 1 for output, 0 for inputstatic int tcc_set_gpio_direction(int pin, int output){tcc_writel(((tcc_readl(GPF_BASE+OFFSET_EN) & ~(1<<pin)) | (output<< pin)),GPF_BASE+OFFSET_EN);return 0;}static int tcc_set_cs_output(void){tcc_writel(((tcc_readl(GPC_BASE+OFFSET_EN) | (1<< PIN_CS)) ),GPC_BASE+OFFSET_EN);return 0;}// set gpio pin level, high: 1, low: 0static int tcc_set_gpio_data(int pin, int level){tcc_writel(((tcc_readl(GPF_BASE+OFFSET_DAT) & ~(1<<pin) )| (level<< pin)),GPF_BASE+OFFSET_DAT);return 0;}static int tcc_set_cs_data(int level){tcc_writel(((tcc_readl(GPC_BASE+OFFSET_DAT) & ~(1<<PIN_CS) )| (level<< PIN_CS)), GPC_BASE+OFFSET_DAT);return 0;}// get gpio pin level, high: 1, low: 0static int tcc_get_gpio_data(int pin){return ((tcc_readl(GPF_BASE+OFFSET_DAT) >>pin) & 1);}void SPI_init(void){tcc_set_pin_fun(PIN_SDO, FUN_GPIO); //configure pin sdo and sda as GPIOtcc_set_pin_fun(PIN_SDI, FUN_GPIO);tcc_set_pin_fun(PIN_SCLK, FUN_GPIO);tcc_set_gpio_direction(PIN_SDO,1);tcc_set_gpio_direction(PIN_SDI,1);tcc_set_gpio_direction(PIN_SCLK,1);tcc_set_gpio_data(PIN_SDO,1);tcc_set_gpio_data(PIN_SDI,1);tcc_set_gpio_data(PIN_SCLK,1);tcc_set_cs_fun();tcc_set_cs_output();tcc_set_cs_data(1);}void SPI_send(bool is_parameter,unsigned char w_data){unsigned char vsignbit;//send DNC-bittcc_set_gpio_data(PIN_SCLK,0);tcc_set_gpio_data(PIN_SDO,is_parameter);ndelay(20);tcc_set_gpio_data(PIN_SCLK,1);ndelay(20);for(vsignbit=0x80;vsignbit>0;vsignbit>>=1) {tcc_set_gpio_data(PIN_SCLK,0);if(w_data&vsignbit)tcc_set_gpio_data(PIN_SDO,1);elsetcc_set_gpio_data(PIN_SDO,0);ndelay(20);tcc_set_gpio_data(PIN_SCLK,1);ndelay(20);}tcc_set_gpio_data(PIN_SDO,1);}unsigned char SPI_read(void){unsigned char vsignbit,r_data=0;tcc_set_gpio_direction(PIN_SDI,1);tcc_set_gpio_data(PIN_SDI,1);tcc_set_gpio_direction(PIN_SDI,0);for(vsignbit=0x80;vsignbit>0;vsignbit>>=1) {tcc_set_gpio_data(PIN_SCLK,0);ndelay(20);if(tcc_get_gpio_data(PIN_SDI)){r_data = r_data|vsignbit;}tcc_set_gpio_data(PIN_SCLK,1);ndelay(20);}return r_data;}static void set_value(){SPI_send(SPI_CMD, 0xB9);SPI_send(SPI_DATA, 0xFF);SPI_send(SPI_DATA, 0x83);SPI_send(SPI_DATA, 0x63);SPI_send(SPI_CMD, 0xB1);SPI_send(SPI_DATA, 0x81);SPI_send(SPI_DATA, 0x30);SPI_send(SPI_DATA, 0x02);SPI_send(SPI_DATA, 0x13);SPI_send(SPI_DATA, 0x11);SPI_send(SPI_DATA, 0x00);SPI_send(SPI_DATA, 0x3A);SPI_send(SPI_DATA, 0x42);SPI_send(SPI_DATA, 0x3F);SPI_send(SPI_DATA, 0x3F);SPI_send(SPI_CMD, 0x11);mdelay(150);SPI_send(SPI_CMD, 0x36);SPI_send(SPI_DATA, 0x08);SPI_send(SPI_CMD, 0x3A);SPI_send(SPI_DATA, 0x77);SPI_send(SPI_CMD, 0xB3);SPI_send(SPI_DATA,0x09);SPI_send(SPI_CMD, 0xB4);SPI_send(SPI_DATA, 0x08);SPI_send(SPI_DATA, 0x12);SPI_send(SPI_DATA, 0x72);SPI_send(SPI_DATA, 0x12);SPI_send(SPI_DATA, 0x06);SPI_send(SPI_DATA, 0x03);SPI_send(SPI_DATA, 0x54);SPI_send(SPI_DATA, 0x03);SPI_send(SPI_DATA, 0x4E);SPI_send(SPI_DATA, 0x00);SPI_send(SPI_DATA, 0x00);SPI_send(SPI_CMD, 0xBF);SPI_send(SPI_DATA, 0x00);SPI_send(SPI_DATA, 0x01);SPI_send(SPI_CMD, 0xB6);SPI_send(SPI_DATA, 0x00);SPI_send(SPI_CMD, 0xCC);SPI_send(SPI_DATA, 0x0A);mdelay(10);SPI_send(SPI_DATA, 0x40);SPI_send(SPI_DATA, 0x42);SPI_send(SPI_DATA, 0xC1);SPI_send(SPI_DATA, 0x4B);SPI_send(SPI_DATA, 0xA7);SPI_send(SPI_DATA, 0x06);SPI_send(SPI_DATA, 0x0D);SPI_send(SPI_DATA, 0x51);SPI_send(SPI_DATA, 0x56);SPI_send(SPI_DATA, 0x18);SPI_send(SPI_DATA, 0x56);SPI_send(SPI_DATA, 0x17);SPI_send(SPI_DATA, 0x89);SPI_send(SPI_DATA, 0x11);SPI_send(SPI_DATA, 0x00);SPI_send(SPI_DATA, 0x40);SPI_send(SPI_DATA, 0x42);SPI_send(SPI_DATA, 0xC1);SPI_send(SPI_DATA, 0x4B);SPI_send(SPI_DATA, 0xA7);SPI_send(SPI_DATA, 0x06);SPI_send(SPI_DATA, 0x0D);SPI_send(SPI_DATA, 0x51);SPI_send(SPI_DATA, 0x56);SPI_send(SPI_DATA, 0x18);SPI_send(SPI_DATA, 0x56);SPI_send(SPI_DATA, 0x17);SPI_send(SPI_DATA, 0x89);SPI_send(SPI_DATA, 0x11);mdelay(5);SPI_send(SPI_CMD, 0x29);}static int __init spi_lcd_init(void){PLOG("Register lcd spi control.\n");SPI_init();tcc_set_cs_data(0);ndelay(20);set_value();tcc_set_cs_data(1);return 0;}static void __exit spi_lcd_exit(void){printk(KERN_INFO "unregister lcd spi control.\n");}module_init(spi_lcd_init);module_exit(spi_lcd_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("J.H.Luo<sparkle-cliz@>");MODULE_VERSION("0.1");MODULE_DESCRIPTION("lcd spi control driver");四,测试结果,insmod此驱动模块之后,LCD显示出开机LOGO了,说明SPI控制成功。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
while((SPI2->SR&1<<0)==0)//等待接收完一个byte
{
retry++;
if(retry>=0XFFFE)return 0;//超时退出
}
return SPI2->DR;//返回收到的数据
}
#ifndef__SPI_H
#define __SPI_H
#include "sys.h"
//APB1时钟一般为36Mhz
voidSPБайду номын сангаас2_SetSpeed(u8SpeedSet)
{
SpeedSet&=0X07;//限制范围
SPI2->CR1&=0XFFC7;
SPI2->CR1|=SpeedSet<<3;//设置SPI2速度
SPI2->CR1|=1<<6;//SPI设备使能
}
//SPI2读写一个字节
#include "spi.h"
//////////////////////////////////////////////////////////////////////////////////
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//////////////////////////////////////////////////////////////////////////////////
// SPI总线速度设置
#define SPI_SPEED_20
#define SPI_SPEED_41
#define SPI_SPEED_82
#define SPI_SPEED_163
#define SPI_SPEED_324
#define SPI_SPEED_645
#define SPI_SPEED_1286
//这里只针对SPI口初始化
GPIOB->CRH&=0X000FFFFF;
GPIOB->CRH|=0XBBB00000;//PB13/14/15复用
GPIOB->ODR|=0X7<<13;//PB13/14/15上拉
SPI2->CR1|=0<<10;//全双工模式
SPI2->CR1|=1<<9;//软件nss管理
//以下是SPI模块的初始化代码,配置成主机模式,访问SD Card/W25Q64/NRF24L01
//SPI口初始化
//这里针是对SPI2的初始化
voidSPI2_Init(void)
{
RCC->APB2ENR|=1<<3;//PORTB时钟使能
RCC->APB1ENR|=1<<14;//SPI2时钟使能
#define SPI_SPEED_2567
void SPI2_Init(void);//初始化SPI2口
void SPI2_SetSpeed(u8SpeedSet); //设置SPI2速度
u8 SPI2_ReadWriteByte(u8TxData);//SPI2总线读写一个字节
#endif
SPI2->CR1|=1<<8;
SPI2->CR1|=1<<2;//SPI主机
SPI2->CR1|=0<<11;//8bit数据格式
SPI2->CR1|=1<<1;//空闲模式下SCK为1 CPOL=1
SPI2->CR1|=1<<0;//数据采样从第二个时间边沿开始,CPHA=1
//对SPI2属于APB1的外设.时钟频率最大为36M.
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
SPI2->CR1|=3<<3;//Fsck=Fpclk1/256
SPI2->CR1|=0<<7;//MSBfirst
SPI2->CR1|=1<<6;//SPI设备使能
SPI2_ReadWriteByte(0xff);//启动传输
}
//SPI2速度设置函数
//SpeedSet:0~7
//SPI速度=fAPB1/2^(SpeedSet+1)
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI2_ReadWriteByte(u8TxData)
{
u16retry=0;
while((SPI2->SR&1<<1)==0)//等待发送区空
{
retry++;
if(retry>=0XFFFE)return 0;//超时退出
}
SPI2->DR=TxData;//发送一个byte