文档库 最新最全的文档下载
当前位置:文档库 › GPIO模拟SPI通讯接口的驱动

GPIO模拟SPI通讯接口的驱动

GPIO模拟SPI通讯接口的驱动
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

#include

#include

#include

#include

#include

#include

#include

#include

#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 gpio

static 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 input

static int tcc_set_gpio_direction(int pin, int output)

{

tcc_writel(((tcc_readl(GPF_BASE+OFFSET_EN) & ~(1<

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: 0

static int tcc_set_gpio_data(int pin, int level)

{

tcc_writel(((tcc_readl(GPF_BASE+OFFSET_DAT) & ~(1<

GPF_BASE+OFFSET_DAT);

return 0;

}

static int tcc_set_cs_data(int level)

{

tcc_writel(((tcc_readl(GPC_BASE+OFFSET_DAT) & ~(1<

return 0;

}

// get gpio pin level, high: 1, low: 0

static 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 GPIO

tcc_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-bit

tcc_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);

else

tcc_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");

MODULE_VERSION("0.1");

MODULE_DESCRIPTION("lcd spi control driver");

四,测试结果,insmod此驱动模块之后,LCD显示出开机LOGO了,说明SPI控制成功。

------------------------------------------

本文乃原创!

转载请注明出处:https://www.wendangku.net/doc/b73701437.html,/

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作GPIO P1DIR |= 0x04; // P12作输出 P1_2 = 1; // P12输出高电平 U1CSR &= ~0xA0; // SPI主方式 U1GCR &= ~0xC0; // CPOL=0 CPHA=0 U1GCR |= 0x20; // MSB U1BAUD = 0; // 波特率设为sysclk/8 U1GCR |= 0x11;

用GPIO模拟SPI协议的实现

一SPI协议概括 SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,比如AT91RM9200. SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。也是所有基于SPI的设备共有的,它们是SDI (数据输入),SDO(数据输出),SCK(时钟),CS(片选)。 (1)SDO –主设备数据输出,从设备数据输入 (2)SDI –主设备数据输入,从设备数据输出 (3)SCLK –时钟信号,由主设备产生 (4)CS –从设备使能信号,由主设备控制

其中CS是控制芯片是否被选中的,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),对此芯片的操作才有效。这就允许在同一总线上连接多个SPI设备成为可能。 接下来就负责通讯的3根线了。通讯是通过数据交换完成的,这里先要知道SPI是串行通讯协议,也就是说数据是一位一位的传输的。这就是SCK时钟线存在的原因,由SCK提供时钟脉冲,SDI,SDO则基于此脉冲完成数据传输。数据输出通过SDO线,数据在时钟上升沿或下降沿时改变,在紧接着的下降沿或上升沿被读取。完成一位数据传输,输入也使用同样原理。这样,在至少8次时钟信号的改变(上沿和下沿为一次),就可以完成8位数据的传输。 要注意的是,SCK信号线只由主设备控制,从设备不能控制信号线。同样,在一个基于SPI的设备中,至少有一个主控设备。这样传输的特点:这样的传输方式有一个优点,与普通的串行通讯不同,普通的串行通讯一次连续传送至少8位数据,而SPI允许数据一位一位的传送,甚至允许暂停,因为SCK时钟线由主控设备控制,当没有时钟跳变时,从设备不采集或传送数据。也就是说,主设备通过对SCK时钟线的控制可以完成对通讯的控制。SPI还是一个数据交换协议:因为SPI的数据输入和输出线独立,所以允许同时完成数据的输入和输出。不同的SPI设备的实现方式不尽相同,主要是数据改变和采集的时间不同,在时钟信号上沿或下沿采集有不同定义,具体请参考相关器件的文档。

spi驱动代码

#include "spi.h" ////////////////////////////////////////////////////////////////////////////////// //本程序只供学习使用,未经作者许可,不得用于其它任何用途 ////////////////////////////////////////////////////////////////////////////////// //以下是SPI模块的初始化代码,配置成主机模式,访问SD Card/W25Q64/NRF24L01 //SPI口初始化 //这里针是对SPI2的初始化 void SPI2_Init(void) { RCC->APB2ENR|=1<<3; //PORTB时钟使能 RCC->APB1ENR|=1<<14; //SPI2时钟使能 //这里只针对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管理 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) //APB1时钟一般为36Mhz void SPI2_SetSpeed(u8 SpeedSet) { SpeedSet&=0X07; //限制范围 SPI2->CR1&=0XFFC7; SPI2->CR1|=SpeedSet<<3; //设置SPI2速度 SPI2->CR1|=1<<6; //SPI设备使能 } //SPI2 读写一个字节

IO口模拟SPI口

模块名称:spi.h 模块说明: c51单片机的i/o模拟spi操作 创建时间: 2005/03/09 创建者: xichen ******************************************************************************* */ #ifndef SPI_H #define SPI_H sbit SPIS_N = P2^1; sbit SPIC = P2^3; sbit SPID = P2^2; sbit SPIQ = P2^4; extern void spi_reset(); extern void spi_write(unsigned char spi_bValue); extern unsigned char spi_read();

#endif /****************************************************************************** * 模块名称:spi.c 模块说明: c51单片机的i/o模拟spi操作 创建时间: 2005/03/09 创建者: xichen ******************************************************************************* */ #include "includes.h" #define set_spi_cs() SPIS_N =1 #define clr_spi_cs() SPIS_N =0 #define set_spi_clk() SPIC =1 #define clr_spi_clk() SPIC =0 #define set_spi_di() SPID =1 #define clr_spi_di() SPID =0 #define read_spi_do() SPIQ

关于使用STM32硬件SPI驱动NRF24L01

关于使用STM32硬件SPI驱动NRF24L01+ 今天是大年初一总算有时间做点想做很久的事了,说到NRF2401可能很多电子爱好者都有用过或是软件模拟驱动又或是用单片机自带的硬件SPI来驱动,但不管是用哪种方法来驱动我想都在调试方面耗费了不少的时间(可能那些所谓的电子工程师不会出现这种情况吧!)网上的资料确实很多,但大多数都并没有经过发贴人认真测试过,有的只是理论上可以行的通但上机测试只能说是拿回来给他修改。本文作者也是经过无助的多少天才算是调试成功了(基于STM32硬件SPI,软件模拟的以前用51单片机已经调通了今天就不准备再拿来讲了,当然如果以后有朋友有需要也可以告诉我,我也可以重新写一篇关于51的驱动的只要有时间是没有问题的。)因为我用的是STM32F103C8T6的系统而且是刚接触不知道别的系统和我用的这个系统有多大的差别所以我不会整个代码全贴上来就完事了,我将就着重思路配合代码写出来,这样对于刚接触单片机的朋友会有很好的作用,但是还有一点请大家要原谅,可能会存在一些说的不好的地方,毕竟我没有经过正规渠道系统地学习过电子知识,对于前辈来说存在这样那样的问题不可避免的,在此也希望大家指教! 贴个图先:

NRF2401+的资料大家上网查一下,我输字的速度有点不好说!下面我来说一下整个调试流程吧 1.先把STM32串口一调通(因为我不知道STM32 I/O口不知可不可以像51那样并口输出数据,如果可以那就更方便啰)。 2.与NRF2401建立起通信(这个才是问题的关键);

3.利用读NRF2401的一个状态寄存器(STATUS)的值并通过串口发送到PC后通过51下载软件的串口助手显示出来(如果你用液晶来调试那你太有才了,你液晶和NRF2401存在牵连可能就会给寻找不成功的原因造成困难,而且还有不少硬件工作要做)在这说一下本文只调试发送程序,致于接收只改一个程序参数就行了。 我们先来调试STM32F103C8T6的串口1吧(也就是USART1)!它是STM32F103C8T6的片上外设之一,使用它时相对来说简单了不少。首先我要说一下我们要使用STM32的片上外设那么我们必须先对其进行初始化,实际上就是经过这段初始化代码让外设根据我们的要求来工作: void USART1_AllInit(void)//意思是USART1的所有初始化工作,我的英文不好所以可能涵数名可能也不怎么规范 { RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟,它是在APB2这条总线上的 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能GPIOA时钟,它也是在APB2这条总线上的,因为USART1要用到GPIOA的端口所以也要初始化 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; GPIO_Init(GPIOA,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;

单片机软件模拟SPI接口—加深理解SPI总线协议

SPI — SPI SPI(Serial Peripheral Interfacer ) SPI RAM EEPROM FlashROM A D D A LED LED I O UART SPI I O SPI I O AT89C205l SPI EEPROM 93CA6 1 I O SPI 93C46 SPI 93CA6 SPI 4 I O (SK) DO DI CS (MSB) (LsB) 93C46 SPI 2

SPI SPI AT89C2051 SPI 1 AT89C2051 EEPROM 93C46 P1 0 SPI SDO P1 2 SPI SCK P1 3 SPI SCS P1 1 SPI SDI P1 2(SCK) 0( ) AT89C2051 P1 0 1 (1) 2 (10) 6 (A5A4A3A2A1A0) P1 1 1 (0) l6 ( ) AT89C2051 P1 0 1 (1) 2 (01) 6 (A5A4A3A2A1A0) P1 0 l6 ( ) (WEN)) 1 (1) 2 (00) 6 (11XXXX) (WDS)) 1 (1) 2 (00) 6 (00XXXX) C51 SPI // I/O sbit SDO=P1^0 sbit SDI=P1^1 sbit SCK=P1^ 2 sbit SCS=P1^3 sbit ACC_7= ACC^7 unsigned int SpiRead(unsigned char add) { unsigned char i unsigned int datal6 add&=0x3f /*6 */ add |=0x80 /* l0*/ SDO=1 /* 1 */ SCK=0 SCK=1 for(i=0 i<8 i++)/* */ { if(add&0x80==1) SDO=1 else SDO=0 SCK=0 /* */ SCK=1 add<<= 1 } SCK=1 /* 1 */

51单片机spi驱动sd卡程序

AT89C52单片机驱动SD卡系统设计 本文详细阐述了用AT89C52单片机对SD卡进行操作的过程,提出了一种不带SD卡控制器,MCU读写SD卡的方法,实现了SD卡在电能监测及无功补偿数据采集系统中的用途。 长期以来,以Flash Memory为存储体的SD卡因具备体积小、功耗低、可擦写以及非易失性等特点而被广泛应用于消费类电子产品中。特别是近年来,随着价格不断下降且存储容量不断提高,它的应用范围日益增广。当数据采集系统需要长时间地采集、记录海量数据时,选择SD卡作为存储媒质是开发者们一个很好的选择。在电能监测以及无功补偿系统中,要连续记录大量的电压、电流、有功功率、无功功率以及时间等参数,当单片机采集到这些数据时可以利用SD作为存储媒质。本文主要介绍了SD卡在电能监测及无功补偿数据采集系统中的应用方案 设计方案 应用AT89C52读写SD卡有两点需要注意。首先,需要寻找一个实现AT89C52单片机与SD卡通讯的解决方案;其次,SD卡所能接受的逻辑电平与AT89C52提供的逻辑电平不匹配,需要解决电平匹配问题 通讯模式 SD卡有两个可选的通讯协议:SD模式和SPI模式。SD模式是SD卡标准的读写方式,但是在选用SD模式时,往往需要选择带有SD卡控制器接口的MCU,或者必须加入额外的SD卡控制单元以支持SD卡的读写。然而,AT89C52单片机没有集成SD卡控制器接口,若选用SD模式通讯就无形中增加了产品的硬件成本。在SD卡数据读写时间要求不是很严格的情况下,选用SPI模式可以说是一种最佳的解决方案。因为在SPI模式下,通过四条线就可以完成所有的数据交换,并且目前市场上很多MCU都集成有现成的SPI接口电路,采用SPI模式对SD卡进行读写操作可大大简化硬件电路的设计。 虽然AT89C52不带SD卡硬件控制器,也没有现成的SPI接口模块,但是可以用软件模拟出SPI总线时序。本文用SPI总线模式读写SD卡。 电平匹配 SD卡的逻辑电平相当于3.3V TTL电平标准,而控制芯片AT89C52的逻辑电平为5V CMOS电平标准。因此,它们之间不能直接相连,否则会有烧毁SD卡的可能。出于对安全工作的考虑,有必要解决电平匹配问题。 要解决这一问题,最根本的就是解决逻辑器件接口的电平兼容问题,原则主要有两条:一为输出电平器件输出高电平的最小电压值,应该大于接收电平器件识别为高电平的最低电压值;另一条为输出电平器件输出低电平的最大电压值,应该小于接收电平器件识别为低电平的最高电压值。 一般来说,通用的电平转换方案是采用类似SN74ALVC4245的专用电平转换芯片,这类芯片不仅可以用作升压和降压,而且允许两边电源不同步。但是,这个方案代价相对昂贵,而且一般的专用电平转换芯片都是同时转换8路、16路或者更多路数的电平,相对本系统仅仅需要转换3路来说是一种资源的浪费。 考虑到SD卡在SPI协议的工作模式下,通讯都是单向的,于是在单片机向

51单片机模拟 SPI 总线的方法

51单片机模拟 SPI 总线的方法 1 引言 SPI(Serial Peripheral Interface--串行外设接口)总线系统是一种同步串行外设接口,它可以使MCU与各种外围设备以串行方式进行通信以交换信息。外围设置FLASHRAM、网络控制器、LCD显示驱动器、A/D转换器和MCU等。SPI总线系统可直接与各个厂家生产的多种标准外围器件直接接口,该接口一般使用4条线:串行时钟线(SCK)、主机输入/从机输出数据线MISO、主机输出/从机输入数据线MOSI和低电平有效的从机选择线SS(有的SPI接口芯片带有中断信号线INT或INT、有的SPI接口芯片没有主机输出/从机输入数据线MOSI)。由于SPI系统总线一共只需3~4位数据线和控制即可实现与具有SPI总线接口功能的各种I/O器件进行接口,而扩展并行总线则需要8根数据线、8~16位地址线、2~3位控制线,因此,采用SPI总线接口可以简化电路设计,节省很多常规电路中的接口器件和I/O口线,提高设计的可靠性。由此可见,在MCS51系列等不具有SPI接口的单片机组成的智能仪器和工业测控系统中,当传输速度要求不是太高时,使用SPI总线可以增加应用系统接口器件的种类,提高应用系统的性能。 2 SPI总线的组成 利用SPI总线可在软件的控制下构成各种系统。如1个主MCU和几个从MCU、几个从MCU 相互连接构成多主机系统(分布式系统)、1个主MCU和1个或几个从I/O设备所构成的各种系统等。在大多数应用场合,可使用1个MCU作为控机来控制数据,并向1个或几个从外围器件传送该数据。从器件只有在主机发命令时才能接收或发送数据。其数据的传输格式是高位(MSB)在前,低位(LSB)在后。SPI总线接口系统的典型结构。 当一个主控机通过SPI与几种不同的串行I/O芯片相连时,必须使用每片的允许控制端,这可通过MCU的I/O端口输出线来实现。但应特别注意这些串行I/O芯片的输入输出特性:首先是输入芯片的串行数据输出是否有三态控制端。平时未选中芯片时,输出端应处于高阻态。若没有三态控制端,则应外加三态门。否则MCU的MISO端只能连接1个输入芯片。其次是输出芯片的串行数据输入是否有允许控制端。因此只有在此芯片允许时,SCK脉冲才把串行数据移入该芯片;在禁止时,SCK对芯片无影响。若没有允许控制端,则应在外围用门电路对SCK进行控制,然后再加到芯片的时钟输入端;当然,也可以只在SPI总线上连接1个芯片,而不再连接其它输入或输出芯片。 3 在MCS-51系列单片机中的实现方法 对于不带SPI串行总线接口的MCS-51系列单片机来说,可以使用软件来模拟SPI的操作,包括串行时钟、数据输入和数据输出。对于不同的串行接口外围芯片,它们的时钟时序是不同的。对于在SCK的上升沿输入(接收)数据和在下降沿输出(发送)数据的器件,一般应将其串行时钟输出口P1.1的初始状态设置为1,而在允许接收后再置P1.1为0。这样,MCU 在输出1位SCK时钟的同时,将使接口芯片串行左移,从而输出1位数据至MCS-51单片机的P1.3口(模拟MCU的MISO线),此后再置P1.1为1,使MCS-51系列单片机从P1.0(模拟MCU 的MOSI线)输出1位数据(先为高位)至串行接口芯片。至此,模拟1位数据输入输出便宣告完成。此后再置P1.1为0,模拟下1位数据的输入输出……,依此循环8次,即可完成1次通过SPI总线传输8位数据的操作。对于在SCK的下降沿输入数据和上升沿输出数据的器件,则应取串行时钟输出的初始状态为0,即在接口芯片允许时,先置P1.1为1,以便外围接口芯片输出1位数据(MCU接收1位数据),之后再置时钟为0,使外围接口芯片接收1位数据(MCU发送1位数据),从而完成1位数据的传送。 图2所示为MCS-51系列单片机与存储器X25F008(E2PROM)的硬件连接图,图2中,P1.0

93c66与spi驱动程序

SPI总线在单片机系统中的实现 2007-04-28 10:56 来源:mcuzb //-----------------------函数声明,变量定义------------------------------------------#include #include sbit SCK=P1^0; // 将p1.0口模拟时钟输出 sbit MOSI=P1^1; // 将p1.1口模拟主机输出 sbit MISO=P1^2; // 将p1.2口模拟主机输入 sbit SS1=P1^3; // 将p1.3口模拟片选 #define delayNOP(); {_nop_();_nop_();_nop_();_nop_();}; //-----------------------------------------------------------------------------------// 函数名称: SPISendByte // 入口参数: ch // 函数功能:发送一个字节 //-----------------------------------------------------------------------------------void SPISendByte(unsigned char ch) { unsigned char idata n=8; // 向SDA上发送一位数据字节,共八位 SCK = 1 ; //时钟置高 SS1 = 0 ; //选择从机 while(n--) { delayNOP(); SCK = 0 ; //时钟置低 if((ch&0x80) == 0x80) // 若要发送的数据最高位为1则发送位1 { MOSI = 1; // 传送位1 } else { MOSI = 0; // 否则传送位0 } delayNOP(); ch = ch<<1; // 数据左移一位 SCK = 1 ; //时钟置高 } } //-----------------------------------------------------------------------------------// 函数名称: SPIreceiveByte

51单片机模拟spi串行接口程序

51单片机模拟spi串行接口程序 51单片机模拟spi串行接口程序,在keilc51下编写 sbit CS=P3^5; sbit CLK= P1^5; sbit DataI=P1^7; sbit DataO=P1^6; #define SD_Disable() CS=1 //片选关 #define SD_Enable() CS=0 //片选开 unsigned char SPI_TransferByte(unsigned char val) { unsigned char BitCounter; for(BitCounter=8; BiCounter!=0; BitCounter--) { CLK=0; DataI=0; // write if(val&0x80) DataI=1; val<<=1; CLK=1; if(DataO)val|=1; // read } CLK=0; return val; }sbit CLK= P1^5; sbit DataI=P1^7;

sbit DataO=P1^6; #define SD_Disable() CS=1 //片选关 #define SD_Enable() CS=0 //片选开 unsigned char SPI_TransferByte(unsigned char val) { unsigned char BitCounter; for(BitCounter=8; BiCounter!=0; BitCounter--) { CLK=0; DataI=0; // write if(val&0x80) DataI=1; val<<=1; CLK=1; if(DataO)val|=1; // read } CLK=0; return val; } sbit CLK= P1^5; sbit DataI=P1^7; sbit DataO=P1^6; #define SD_Disable() CS=1 //片选关 #define SD_Enable() CS=0 //片选开

Linux下SPI驱动测试程序

Linux下的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 #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",

SPI接口详细说明

SPI 串行外设接口总线,最早由Motorola提出,出现在其M68系列单片机中,由于其简单实用,又不牵涉到专利问题,因此许多厂家的设备都支持该接口,广泛应用于外设控制领域。 SPI接口是一种事实标准,并没有标准协议,大部分厂家都是参照Motorola的SPI接口定义来设计的。但正因为没有确切的版本协议,不同家产品的SPI接口在技术上存在一定的差别,容易引起歧义,有的甚至无法直接互连(需要软件进行必要的修改)。 虽然SPI接口的内容非常简单,但本文仍将就其中的一些容易忽视的问题进行讨论。 SPI ( Serial Peripheral Interface ) SPI接口是Motorola 首先提出的全双工三线同步串行外围接口,采用主从模式(Master Slave)架构;支持多slave模式应用,一般仅支持单Master。 时钟由Master控制,在时钟移位脉冲下,数据按位传输,高位在前,低位在后(MSB first);SPI 接口有2根单向数据线,为全双工通信,目前应用中的数据速率可达几Mbps的水平。 SPI接口信号线 SPI接口共有4根信号线,分别是:设备选择线、时钟线、串行输出数据线、串行输入数据线。 设备选择线SS-(Slave select,或CS-)

SS-线用于选择激活某Slave设备,低有效,由Master驱动输出。只有当SS-信号线为低电平时,对应Slave设备的SPI接口才处于工作状态。 SCLK:同步时钟信号线, SCLK用来同步主从设备的数据传输,由Master驱动输出,Slave设备按SCK的步调接收或发送数据。 串行数据线: SPI接口数据线是单向的,共有两根数据线,分别承担Master到Slave、Slave到Master的数据传输;但是不同厂家的数据线命名有差别。 Motorola的经典命名是MOSI和MISO,这是站在信号线的角度来命名的。 MOSI:When master, out line; when slave, in line MISO:When master, in line; when slave, out line 比如MOSI,该线上数据一定是Master流向Slave的。因此在电路板上,Master的MOSI引脚应与Slave的MOSI引脚连接在一起。双方的MISO也应该连在一起,而不是一方的MOSI连接另一方的MISO。 不过,也有一些产家(比如Microchip)是按照类似SDI,SDO的方式来命名,这是站在器件的角度根据数据流向来定义的。 SDI:串行数据输入 SDO:串行数据输出 这种情况下,当Master与Slave连接时,就应该用一方的SDO连接另一个方的SDI。 由于SPI接口数据线是单向的,故电路设计时,数据线连接一定要正确,必然是一方的输出连接另一方的输入。 其实这个问题本来很简单的,但由于不同厂家产品的命名习惯可能不同,因此还需小心,以免低级出错。 数据传输的时序模式

模拟SPI程序

写程序: void SPIx_WriteByte(u8 TxData) { u8 j=0; SPI_FLASH_CLK_LOW(); //clk=0 if(TxData&0x80) {SPI_FLASH_DI_HIGH();} //mosi=1 else {SPI_FLASH_DI_LOW();} //mosi=0 for(j=0;j<3;j++); //延时 SPI_FLASH_CLK_HIGH(); //clk=1,一个上升沿写入一位for(j=0;j<5;j++); //延时 SPI_FLASH_CLK_LOW(); //clk=0 if(TxData & 0x40) {SPI_FLASH_DI_HIGH();} //mosi=1 else {SPI_FLASH_DI_LOW();} //mosi=0 for(j=0;j<3;j++); //延时 SPI_FLASH_CLK_HIGH(); for(j=0;j<5;j++); SPI_FLASH_CLK_LOW(); if(TxData&0x20) {SPI_FLASH_DI_HIGH();} //mosi=1 else {SPI_FLASH_DI_LOW();} //mosi=0 for(j=0;j<3;j++); //延时 SPI_FLASH_CLK_HIGH(); for(j=0;j<5;j++); SPI_FLASH_CLK_LOW(); if(TxData&0x10) {SPI_FLASH_DI_HIGH();} //mosi=1 else {SPI_FLASH_DI_LOW();} //mosi=0 for(j=0;j<3;j++); //延时 SPI_FLASH_CLK_HIGH(); for(j=0;j<5;j++); SPI_FLASH_CLK_LOW(); if(TxData&0x08) {SPI_FLASH_DI_HIGH();} //mosi=1 else

模拟SPI程序

写程序: voidSPIx_WriteByte(u8 TxData) { u8 j=0; SPI_FLASH_CLK_LOW(); //clk=0 if(TxData&0x80) {SPI_FLASH_DI_HIGH();} //mosi=1 else {SPI_FLASH_DI_LOW();} //mosi=0 for(j=0;j<3;j++); //延时 SPI_FLASH_CLK_HIGH(); //clk=1,一个上升沿写入一位for(j=0;j<5;j++); //延时 SPI_FLASH_CLK_LOW(); //clk=0 if(TxData & 0x40) {SPI_FLASH_DI_HIGH();} //mosi=1 else {SPI_FLASH_DI_LOW();} //mosi=0 for(j=0;j<3;j++); //延时 SPI_FLASH_CLK_HIGH(); for(j=0;j<5;j++); SPI_FLASH_CLK_LOW(); if(TxData&0x20) {SPI_FLASH_DI_HIGH();} //mosi=1 else {SPI_FLASH_DI_LOW();} //mosi=0 for(j=0;j<3;j++); //延时 SPI_FLASH_CLK_HIGH(); for(j=0;j<5;j++); SPI_FLASH_CLK_LOW(); if(TxData&0x10) {SPI_FLASH_DI_HIGH();} //mosi=1 else {SPI_FLASH_DI_LOW();} //mosi=0 for(j=0;j<3;j++); //延时 SPI_FLASH_CLK_HIGH(); for(j=0;j<5;j++); SPI_FLASH_CLK_LOW(); if(TxData&0x08) {SPI_FLASH_DI_HIGH();} //mosi=1 else

SPI总线从机接口实时模拟的实现

SPI总线从机接口实时模拟的实现 收稿日期:2005-09-15 作者简介:郭静华(1976-),女,黑龙江人,硕士研究生,研究方向为电子技术在农业中的应用。zhongguoguojinghua@126.com *通讯作者E-mail:ouyangbl@126.com 郭静华,欧阳斌林* (东北农业大学工程学院,哈尔滨 150030) 摘要:MCS51系列单片机由于不带SPI串行总线接口而限制了其在SPI总线接口器件的使用。文章介绍了 SPI串行总线的特征和时序,并以双CPU通信为例,给出了在51系列单片机上利用中断实现SPI串行总线通信的 方法和软件设计程序,从机在实现SPI接口同时还可以完成其他操作任务。 关键词:SPI总线;中断系统;89C52单片机中图分类号:TP23 文献标识码:A SPI (Serialperipheralinterface)总线是Motorola公司提出的一个同步串行外设接口,用于CPU与各种外围器件进行全双工、同步串行通讯。SPI可以同时发出和接收串行数据,它只需4条线就可以完成MCU与各种外围器件的通讯。这些外围器件可以是简单的TTL移位寄存器,复杂的LCD显示驱动器,A/D、D/A转换子系统或其他的 MCU[1] 。 利用SPI总线可在软件的控制下构成各种系统。如1个主MCU和几个从MCU、几个从MCU相互连接构成多主机系统(分布式系统)、1个主 MCU和1个或几个从I/O设备所构成的各种系统 等。在大多数应用场合,可使用1个MCU作为主控机来控制数据,并向1个或几个从外围器件传送该数据。从器件只有在主机发命令时才能接收或发送数据,其数据的传输格式是高位(MSB)在前,低位(LSB)在后。 一般而言,SPI总线接口主要用于主从分布式的通信网络,只需4根I/O接口线,即可完成主从总线之间的数据通信。这4根接口线分别为:时钟线 (SCLK)、数据输入线(MOSI)(主机输出从机输入)、数据输出线(MOSO)(主机输入从机输出)、片选线 (SS)。根据时钟和触发不同SPI总线可以分为4种,图1是其中一种SPI工作时序。 1系统介绍 MCS51等系列单片机由于不带SPI串行总线接 口而限制了其在SPI总线接口器件的使用,但可以使用软件来模拟SPI的操作,包括串行时钟、数据输入和数据输出[2]。本文所介绍的SPI总线实时系统既是在89C52单片机之间实现SPI总线串行通信的功能。系统结构见图2。重点介绍从机SPI的实现。 主CPU的P3.4(选通线)接到从机的INT0口,P3.5 (时钟线)接到从机的INT1口。选通和时钟都采用中断方式,以提高系统的实时性能。 图1SPI时序 Fig.1TheSPIworksequence 图2系统结构 Fig.2Systemstructure 第38卷第5期东北农业大学学报38(5):669 ̄671 2007年10月JournalofNortheastAgriculturalUniversity Oct.2007 文章编号 1005-9369 (2007)05-0669- 03

SPI驱动介绍

文档编号: SPI驱动使用介绍 文档状态:[ ] 草稿[√] 正式发布[ ] 正在修改 版本0.01 校对版本日期2013-12-18 审核编制孙忠刚批准

版本变更记录 版本日期修改人变更理由/变更内容 孙忠刚初始版本 0.01 2013-12-18

1. SPI驱动提供的通讯方式 Spi提供两种通讯方式,对应的驱动接口分别为SyncTransmit和AsynTransmit。 a)同步通讯方式 当驱动接口Spi_SyncTransmit执行完毕,即完成SPI通讯。当SPI总线传输数据时,CPU处于轮询等待状态,直至SPI通讯结束CPU方继续向下执行。 b)异步通讯方式 当驱动接口Spi_AsynTransmit执行完毕,但SPI通讯不一定完成。CPU将待发送的数据写入SPI数据发送缓冲区,触发SPI通讯,CPU继续向下执行,而不等待 SPI通讯结束。 2. SPI驱动使用方法 a)基础知识介绍 在AutoSar标准中,与SPI通讯相关的三个术语:Channel、Job和Sequence。 1个Channel对应1个发送缓冲区和1接收缓冲区; 1个Job对应着1次SPI通讯发送的内容(既SPI 一次片选过程所传输的内容)。 1个Sequence 对应着1个SPI通讯序列(job序列)。多个Job可以分配给一个Sequence。 关于每个术语的详细解释,参考AutoSar标准。 SPI通讯是基于Sequence触发的,即使发送1个Job也要将该Job分配给1个队列,然后通过触发Sequence来实现Job的传输。 b)两种使用方法 方法1:对应1个SPI外设芯片,分配1个Job、1个Sequence。使用此种方法,触发一次Sequence,只能传输一个Job,当对外设发送多个Job时,需要多次触发Sequence,且在下一次触发Sequence时,必须确保上一次Sequence已经传输完毕,否则下一次Sequence传输会因为上一次Sequence传输占用SPI总线而失败。 方法2:对应1个SPI外设芯片,结合对外设芯片的控制方法,分配多个Job和多个Sequence,有目的分配Job到相应的Sequence中。在不同控制逻辑中,触发不同的Sequence,传输不同个数的Job序列。当对1个Sequence分配多Job时,触发此Sequence就可以完成多个Job的传输,SPI 驱动本身来保证Job序列的传输,不会产生下一个Job传输因为上一个Job占用SPI总线而失败的情况。

单片机模拟SPI程序

时钟相位(CPHA)和时钟极性(CPOL)的不同组合使得SPI传输有了4种方式如果CPOL =0,SCK 引脚在空闲状态保持低电平; 如果CPOL =1,SCK 引脚在空闲状态保持高电平 时序图如下: (一)A VR单片机实现代码 //IO端口定义 #define SPI_SCK PC0 #define SPI_MOSI PC1 #define SPI_MISO PC2 #define SPI_DDR DDRC #define SPI_PORT PROTC #define SPI_PIN PINC //端口操作符定义 #define SCK_SET SPI_PORT|=_BV(SPI_SCK) #define SCK_CLR SPI_PORT&=~_BV(SPI-SCK) #define MOSI_SET SPI_PORT|=_BV(SPI_MOSI) #define MOSI_CLR SPI_PORT&=~_BV(SPI_MOSI)

#define MISO_PIN PINC&_BV(SPI_MISO) #define DELAY_BUS //如需要延时,用延时函数替代此符号 //模式1:CPOL=1 CPHA=1 void spi_init(void) { SCK_SET; SPI_DDR|=_BV(SPI_MOSI)|_BV(SPI_SCK); } uint8_t spi_readwrite_byte(uint8_t data) { uint8_t i,ret=0; for(i=0;i<8;i++) { //下降沿模拟 if(data&0x80)//设置输出 MOSI_SET; else MOSI_CLR; SCK_CRL;//SCK产生下降沿 DELAY_BUS; //上升沿模拟 ret<<=1; if(MISO_PIN)//读数据 ret|=1; SCK_SET; //SCK产生上升沿 data<<=1; DELAY_BUS; } return ret; } //模式2:CPOL=0 CPHA=1 void spi_init(void) { SCK_CLR; SPI_DDR|=_BV(SPI_MOSI)|_BV(SPI_SCK); }

相关文档