用GPIO模拟SPI协议的实现
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。
使用MCU的GPIO模拟SPI
使用MCU的GPIO模拟SPI在树莓派等单片机(MCU)上,可以使用GPIO模拟SPI(串行外设接口)来与其他设备进行通信。
SPI是一种同步串行数据传输协议,通常用于连接MCU和传感器、显示器、存储器等外设。
以下是使用MCU的GPIO模拟SPI的详细步骤。
1.了解SPI的基本原理:SPI使用四根信号线进行通信,包括时钟(SCLK)、主机输出从机输入(MOSI)、主机输入从机输出(MISO)和片选(SS)。
-SCLK:时钟信号,由主机产生,用于同步数据传输。
-MOSI:主机输出从机输入,主机将数据发送到从机。
-MISO:主机输入从机输出,从机将数据发送到主机。
-SS:片选信号,用于选择从机。
2.确定所需GPIO引脚:根据所连接的设备的要求,选择合适的GPIO引脚作为SCLK、MOSI、MISO和SS。
3. 配置GPIO引脚:在MCU上,使用相应的编程语言和库函数来配置GPIO引脚。
例如,在树莓派上使用Python编程,可以使用RPi.GPIO库进行配置。
4.编写SPI传输函数:编写一个函数来模拟SPI传输。
该函数应包括以下步骤:a.设置SS为低电平,选中从机设备。
b.发送数据比特串:逐位发送MOSI数据,同时接收并保存MISO数据。
c.设置SS为高电平,取消从机设备的选中。
假设我们要发送8位数据,可以使用以下Python代码实现SPI传输函数:```pythonimport RPi.GPIO as GPIOdef spi_transfer(data):GPIO.output(SS, GPIO.LOW) # 选中从机received_data = 0for bit in range(7, -1, -1): # 逐位传输数据#发送MOSI数据GPIO.output(MOSI, (data >> bit) & 0x01)#接收并保存MISO数据received_bit = GPIO.input(MISO)received_data = (received_data << 1) , received_bit#在SCLK上升沿发送和接收数据GPIO.output(SCLK, GPIO.HIGH)GPIO.output(SCLK, GPIO.LOW)GPIO.output(SS, GPIO.HIGH) # 取消从机选中return received_data```5. 通过调用SPI传输函数与从机通信:在应用程序中,根据需要调用SPI传输函数。
GPIO模拟ST7789V的8080并口通讯
GPIO模拟ST7789V的8080并口通讯——写代码的Tobem 最近要使用ST7789V驱动一个240*240的屏幕,刷新率要越快越好,最好能实现动画播放。
该款屏幕30pin的管脚中,预留有SPI和8080两种通讯方式。
因为刷新率要尽可能快,采用哪种通讯方式,可以先从手册的时序参数中先理论进行计算。
SPI的时序参数如下:可以看出,写时钟最小为66ns,也就是约15MHz的SPI时钟,如果颜色格式采用16bit/pixel,即一个像素点2个字节,则理论刷新一次屏幕需要240*240*2*8*66ns = 60825600ns = 0.0608256ms,也即约1/0.0608256 = 16Hz帧率。
接下来再看看8位总线8080的时序参数如下:可以看出,写时钟最小也为66ns,但由于采用8bit并口通讯方式,所以理论上屏幕刷新率可以达到SPI通讯方式的8倍,也就是约16Hz * 8 = 128Hz。
至于实际刷新率,还要看主控主频大小、任务调度等其他因素。
但不管怎样,为了得到尽可能快的刷新率,8080方式更适合。
由于手里的主控并不支持8080接口,于是采用GPIO进行8080的模拟。
其控制线主要有RESET复位信号、CS片选信号、DC数据/命令信号、WR写信号、RD读信号,此外还有8根数据线DB[7:0],为方便,数据线接到GPIOC[7:0]管脚上。
对GPIO操作进行如下宏定义:#define lcddev_LEDK GPIO_Pins_0#define lcddev_RESET GPIO_Pins_1#define lcddev_CS GPIO_Pins_2#define lcddev_DC GPIO_Pins_3#define lcddev_WR GPIO_Pins_4#define lcddev_RD GPIO_Pins_5#define LCD_TE GPIO_Pins_6#define LCD_LEDK_H GPIO_SetBits(GPIOA, lcddev_LEDK)#define LCD_LEDK_L GPIO_ResetBits(GPIOA, lcddev_LEDK)#define LCD_RESET_H GPIO_SetBits(GPIOA, lcddev_RESET)#define LCD_RESET_L GPIO_ResetBits(GPIOA, lcddev_RESET)#define LCD_CS_H GPIO_SetBits(GPIOA, lcddev_CS)#define LCD_CS_L GPIO_ResetBits(GPIOA, lcddev_CS)#define LCD_DC_H GPIO_SetBits(GPIOA, lcddev_DC)#define LCD_DC_L GPIO_ResetBits(GPIOA, lcddev_DC)#define LCD_WR_H GPIO_SetBits(GPIOA, lcddev_WR)#define LCD_WR_L GPIO_ResetBits(GPIOA, lcddev_WR)#define LCD_RD_H GPIO_SetBits(GPIOA, lcddev_RD)#define LCD_RD_L GPIO_ResetBits(GPIOA, lcddev_RD)#define LCD_TE_In GPIO_ReadInputDataBit(GPIOA, lcddev_TE)首先进行GPIO初始化,全部配置为推挽输出:static void LCD_GPIO_Init(void){GPIO_InitType GPIO_InitStruct;RCC_APB2PeriphClockCmd(RCC_APB2PERIPH_GPIOA|RCC_APB2PERIPH_GPIOC, ENABLE);GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT_PP;GPIO_InitStruct.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz;GPIO_InitStruct.GPIO_Pins = lcddev_LEDK | lcddev_RESET | lcddev_CS | lcddev_DC | lcddev_WR | lcddev_RD;GPIO_Init(GPIOA, &GPIO_InitStruct);GPIO_SetBits(GPIOA, lcddev_LEDK | lcddev_RESET | lcddev_CS | lcddev_DC | lcddev_WR | lcddev_RD);GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT_PP;GPIO_InitStruct.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz;GPIO_InitStruct.GPIO_Pins = GPIO_Pins_0 | GPIO_Pins_1 | GPIO_Pins_2 | GPIO_Pins_3 | GPIO_Pins_4| GPIO_Pins_5 | GPIO_Pins_6 | GPIO_Pins_7;GPIO_Init(GPIOC, &GPIO_InitStruct);GPIO_SetBits(GPIOC, GPIO_Pins_0 | GPIO_Pins_1 | GPIO_Pins_2 | GPIO_Pins_3 | GPIO_Pins_4| GPIO_Pins_5 | GPIO_Pins_6 | GPIO_Pins_7);}然后需要写ST7789V寄存器进行配置。
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芯片的输入输出特性:首先是输入芯片的串行数据输出是否有三态控制端。
单片机IO口模拟SPI四种模式的程序
单⽚机IO⼝模拟SPI四种模式的程序#include "iom8535v.h"#define _CPOL 1#define _CPHA 0#define SCK_IO DDRA|=0X01#define MOSI_IO DDRA|=0X02#define MISO_IO DDRA&=0XFB#define SSEL_IO DDRA|=0X08#define SCK_D(X) (X?(PORTA|=0X01):(PORTA&=0XFE))#define MOSI_D(X) (X?(PORTA|=0X02):(PORTA&=0XFD))#define SSEL_D(X) (X?(PORTA|=0X08):(PORTA&=0XF7))#define MISO_I() (PINA&0X04)void delay(){unsigned char m,n;for(n=0;n<5;n++);for(m=0;m<100;m++);}void SPI_Init(void){SCK_IO ;MOSI_IO ;MISO_IO ;SSEL_IO ;SSEL_D(1);MOSI_D(1);#if _CPOL==0SCK_D(0);#elseSCK_D(1);#endif}#if _CPOL==0&&_CPHA==0 //MODE 0 0void SPI_Send_Dat(unsigned char dat){unsigned char n;for(n=0;n<8;n++){SCK_D(0);if(dat&0x80)MOSI_D(1);else MOSI_D(0);dat<<=1;SCK_D(1);}SCK_D(0);}unsigned char SPI_Receiver_Dat(void){unsigned char n ,dat,bit_t;for(n=0;n<8;n++){SCK_D(0);dat<<=1;if(MISO_I())dat|=0x01;else dat&=0xfe;SCK_D(1);}SCK_D(0);return dat;}#endif#if _CPOL==1&&_CPHA==0 //MODE 1 0 void SPI_Send_Dat(unsigned char dat){unsigned char n;for(n=0;n<8;n++){SCK_D(1);if(dat&0x80)MOSI_D(1);else MOSI_D(0);dat<<=1;SCK_D(0);}SCK_D(1);}unsigned char SPI_Receiver_Dat(void){unsigned char n ,dat,bit_t;for(n=0;n<8;n++){SCK_D(1);dat<<=1;if(MISO_I())dat|=0x01;else dat&=0xfe;SCK_D(0);}SCK_D(1);return dat;}#endif#if _CPOL==0&&_CPHA==1 //MODE 0 1 void SPI_Send_Dat(unsigned char dat){unsigned char n;SCK_D(0);for(n=0;n<8;n++){SCK_D(1);if(dat&0x80)MOSI_D(1);else MOSI_D(0);dat<<=1;SCK_D(0);}}unsigned char SPI_Receiver_Dat(void){unsigned char n ,dat,bit_t;for(n=0;n<8;n++){SCK_D(1);dat<<=1;if(MISO_I())dat|=0x01;else dat&=0xfe;SCK_D(0);}SCK_D(0);return dat;}#endif//////////////////////////////////////////////////////////////////////////////////////////////////////////////#if _CPOL==1&&_CPHA==1 //MODE 1 1 void SPI_Send_Dat(unsigned char dat){unsigned char n;SCK_D(1);for(n=0;n<8;n++){SCK_D(0);if(dat&0x80)MOSI_D(1);else MOSI_D(0);dat<<=1;SCK_D(1);}}unsigned char SPI_Receiver_Dat(void){unsigned char n ,dat,bit_t;SCK_D(0);for(n=0;n<8;n++){ SCK_D(0);dat<<=1;if(MISO_I())dat|=0x01;else dat&=0xfe;SCK_D(1);}SCK_D(1);return dat;}#endifvoid main(){SPI_Init();DDRB = 0XFF;//#if _CPOL//SCK_D(0);//#endifwhile(1){//SSEL_D(0);//SPI_Send_Dat(0x01);//SPI_Send_Dat(0x31);//SSEL_D(1);SSEL_D(0);SPI_Send_Dat(0x81);PORTB =SPI_Receiver_Dat();SSEL_D(1);//delay();}}。
Spi 通信 代码
GPIO模拟SPI总线(4线模式)原作者:heekee 添加时间:2010-05-27 原文发表:2010-05-27 人气:826---------------------------------------------------------------------------------先给大家把源代码贴上来吧,等有时间了好好整理一下。
在大家做之前,先给大家说下模拟常识。
1。
由于GPIO的相应速度有限,所以模拟的SPI速度有限,我这里大概是1.7M。
所以GPIO 模拟SPI只适合用于SPI设备控制和少量低速率数据传输。
一般,SPI可以到26M。
2。
通用性差,需要按照操作的SPI设备提供的SPI时序来模拟,不想专用SPI硬件接口,可以配置多种时序。
3。
一定要那示波器来抓取模拟的读写时序,和SPI设备手册一一对照。
/*************************************************************************//**//* FILE NAME *//* drv_spi.c *//**//* DESCRIPTION *//* This file contains the basic spi function by using GPIO. *//**//*************************************************************************/#include "target.h" // This is the GPIO define of your boardstatic u32 SPI_CS_PIN;static u32 SPI_CLK_PIN;static u32 SPI_DOUT_PIN;static u32 SPI_DIN_PIN;static u32 SPI_BUS;#define USE_SPI_BUSY_PIN 0// 20 ---> 195 KHz// 10 ---> 355 KHz#define DELAY 10void spiDelay(u16 iCount){u16 i;for(i=0;i<iCount;i++);}// SPI Internal Interface API/* -------------------------------------------------------------------------** Function : spiWrBitHigh * * Description: This function is used to transfer a High bit to SPI bus. ** Parameters : None * * Return : None * * -------------------------------------------------------------------------*/void spiWrBitHigh(void){hal_gpio_SetBit(SPI_DOUT_PIN);hal_gpio_ClrBit(SPI_CLK_PIN);spiDelay(10);hal_gpio_SetBit(SPI_CLK_PIN);spiDelay(10);hal_gpio_ClrBit(SPI_CLK_PIN);}/* -------------------------------------------------------------------------** Function : spiWrBitLow * * Description: This function is used to transfer a Low bit to SPI bus. ** Parameters : None * * Return : None * * -------------------------------------------------------------------------*/void spiWrBitLow(void){hal_gpio_ClrBit(SPI_DOUT_PIN);hal_gpio_ClrBit(SPI_CLK_PIN);spiDelay(10);hal_gpio_SetBit(SPI_CLK_PIN);spiDelay(10);hal_gpio_ClrBit(SPI_CLK_PIN);}// I2C Extern Interface API/* -------------------------------------------------------------------------*//* Function : spi_Open */ /* Description: Init SPI port. *//* Parameters : GPIO bit which defined as SPI bus. *//* Return : TRUE if OK. */ /* FALSE if Wrong. */ /* -------------------------------------------------------------------------*/bool spi_Open(u8 spi_cs,u8 spi_dout,u8 spi_din,u8 spi_clk){spi_fprintf((TSTDOUT,"init SPI ports..."));SPI_CS_PIN = 1 << spi_cs;SPI_DOUT_PIN = 1 << spi_dout;SPI_DIN_PIN = 1 << spi_din;SPI_CLK_PIN = 1 << spi_clk;SPI_BUS = SPI_CS_PIN | SPI_DOUT_PIN |SPI_DIN_PIN | SPI_CLK_PIN;//if ((SPI_BUS & USED_GPIO)!= SPI_BUS)//{// spi_fprintf((TSTDOUT, "SPI BUS is 0x%x.",SPI_BUS));// spi_fprintf((TSTDOUT, "SPI BUS is 0x%x.",USED_GPIO));// spi_fprintf((TSTDOUT, "SPI BUS is 0x%x.",(SPI_BUS & USED_GPIO)));// spi_fprintf((TSTDOUT, "SPI GPIO Used Wrong in Board Config."));// return(FALSE);//}// Set the GPIO direction of SPI interfacehal_gpio_SetOut(SPI_CS_PIN);hal_gpio_SetOut(SPI_DOUT_PIN);hal_gpio_SetOut(SPI_CLK_PIN);hal_gpio_SetIn(SPI_DIN_PIN);//hal_gpio_SetIn(SPI_BUSY_PIN);// Set the initialize status of each SPI interfacehal_gpio_ClrBit(SPI_CS_PIN);hal_gpio_ClrBit(SPI_CLK_PIN);hal_gpio_ClrBit(SPI_DOUT_PIN);return(TRUE);}/* -------------------------------------------------------------------------*//* Function : spi_Close *//* Description: Close SPI port. *//* Parameters : None */ /* Return : TRUE if OK. */ /* FALSE if Wrong. */ /* -------------------------------------------------------------------------*/bool spi_Close(void){return(TRUE);}/* -------------------------------------------------------------------------*//* Function : spi_GetData *//* Description: This function is used to Get data from SPI bus. *//* Parameters : iRecvData -- Data pointer to be stored *//* Return : TRUE if OK. */ /* FALSE if Wrong. */ /* -------------------------------------------------------------------------*/bool spi_GetData(u16 * iRecvData){s16 iCount = 0;#if USE_SPI_BUSY_PINu32 i = 0;#endif*iRecvData = 0;// Make sure that the initialize status of SPI CLK is lowhal_gpio_ClrBit(SPI_CLK_PIN);// Make sure that the initialize status of SPI DOUT is lowhal_gpio_ClrBit(SPI_DOUT_PIN);spiDelay(10);#if USE_SPI_BUSY_PINwhile (i < 0xfffff){if(hal_gpio_GetVal(SPI_BUSY_PIN) == 0) break;i++;}if(i == 0xfffff) return (FALSE);#endiffor(iCount = 15;iCount >= 0;iCount --){hal_gpio_SetBit(SPI_CLK_PIN);spiDelay(10);if(hal_gpio_GetVal(SPI_DIN_PIN)) (*iRecvData)|=(1<<iCount);hal_gpio_ClrBit(SPI_CLK_PIN);spiDelay(10);}return(TRUE);}/* -------------------------------------------------------------------------*//* Function : spi_SendData */ /* Description: This function is used to Send data to SPI bus. *//* Parameters : iSendData -- 8 bit data to be sent *//* Return : TRUE if OK. */ /* FALSE if Wrong. */ /* -------------------------------------------------------------------------*/bool spi_SendData(u8 iSendData){s8 iCount = 0;for(iCount=7; iCount>=0; iCount--){if(iSendData & (1<<iCount)) spiWrBitHigh();else spiWrBitLow();}return (TRUE);}用计算机并口模拟SPI通讯的C源程序#define LPT_PORT 0x378#define CLR_WCK(X) {X=X&(~(1<<0)); outportb(LPT_PORT,X); } // data.0 #define SET_WCK(X) {X=X | (1<<0) outportb(LPT_PORT,X); }#define CLR_BCK(X) {X=X&(~(1<<2)); outportb(LPT_PORT,X); } // data.2 #define SET_BCK(X) {X=X | (1<<2) outportb(LPT_PORT,X); }#define CLR_DATA(X) {X=X&(~(1<<3)); outportb(LPT_PORT,X); } // data.3 #define SET_DATA(X) {X=X | (1<<3) outportb(LPT_PORT,X); }#define FALSE 0#define TRUE 1void test_comm(){unsigned char datadata = 0;printf("Please press enter to begin send data\n");getch();printf("Pull down WCK data.0\n");CLR_WCK(data);getch();printf("Pull up WCK data.0\n");SET_WCK(data);getch();printf("Pull down BCK data.2\n");CLR_BCK(data);getch();printf("Pull up BCK data.2\n");SET_BCK(data);getch();printf("Pull down DATA data.3\n");CLR_DATA(data);getch();printf("Pull up DATA data.3\n");SET_DATA(data);getch();}// Note: the size of buffer to send must be dword multiple// size is the number of bytes to sendvoid short_delay(int n){int i;for(i=0;i {int temp =0;}}int send_spi_data(unsigned char *buffer, unsigned long size){unsigned char *buf=buff; unsigned char data;int i,j,k;data =0;if((size%4)!=0) return FALSE; memcpy(buff,buffer,size);do{SET_WCK(data);for(k=0;k<2;k++){for(j=0;j<2;j++){printf(".");for(i=0;i<8;i++){if((*buf)&0x80){SET_DATA(data);}else{CLR_DATA(data);}short_delay(1);// delay(1);SET_BCK(data);short_delay(1);// delay(1);CLR_BCK(data);short_delay(1);// delay(1);*buf<<=1;}buf++;size--;}// buf++;// size--;CLR_WCK(data);}SET_WCK(data);}while(size>0);return TRUE;}/*void main(){int i;tmpdata[0] = 0x34;tmpdata[1] = 0x12;tmpdata[2] = 0x56;tmpdata[3] = 0x78;// for(i=0;i<500;i++)for(i=0;i<50;i++){send_spi_data(tmpdata,4);}// test_comm();}*/运用4个普通I/O口模拟SPI程序源代码/********************************************************************函数名:uchar SpiReadWrite(uchar dat)功能:SPI发送接收一个数据说明:调用:入口参数:出口参数:***********************************************************************/ uchar SpiReadWrite(uchar dat){uchar i,temp;temp=0;SCK=0;_nop_();for(i=0;i<8;i++){if(dat & 0x80) MOSI=1;else MOSI=0;dat<<=1;SCK=1;_nop_();_nop_();_nop_();_nop_();temp<<=1;if(MISO)temp++;SCK=0;_nop_();_nop_();_nop_();_nop_();}return temp;}1、SPI总线速度:波特率可以高达5Mbps,具体速度大小取决于SPI硬件。
IO口模拟SPI主从机例程
IO口模拟spi主从机通讯例程下面这两幅图是,关于SPI数据读取或发送的时序图。
1、主机io口模拟spi通讯例程//**spi io 口初始化**//void SPI_init(void){gpio_configure_fpin(SPI_MISO, IO_TYPE_INPUT);//配置成输入模式gpio_configure_fpin(SPI_MOSI, IO_OUTPUT_1);//配置成输出模式gpio_configure_fpin(SPI_SCK, IO_OUTPUT_1); //配置成输出模式gpio_configure_fpin(SPI_CS, IO_OUTPUT_1); //配置成输出模式clr_spi_GPIO(SPI_SCK);//拉低SPI_SCKset_spi_GPIO(SPI_CS);//拉高SPI_SCKclr_spi_GPIO(SPI_MOSI);//拉低SPI_MOSI}//**主机spi读取一字节api**//unsigned char SPI_ReadByte(void){unsigned char i,rByte=0;clr_spi_GPIO(SPI_CS);for(i=0;i<8;i++){clr_spi_GPIO(SPI_SCK);//clr_spi_sck;delay_us(3);rByte<<=1;if(MISO_is_status())////M16 MISO---PB6rByte|=1;set_spi_GPIO(SPI_SCK);//set_spi_sck;delay_us(3);}clr_spi_GPIO(SPI_SCK);set_spi_GPIO(SPI_CS);return rByte;}//** 读取miso 的电平**//char MISO_is_status(void){if(red_spi_GPIO(SPI_MISO))//return 1;elsereturn 0;}//**主机spi写入一字节api**//void SPI_WriteByte(unsigned char wByte){unsigned char i;clr_spi_GPIO(SPI_CS);for(i=0;i<8;i++){clr_spi_GPIO(SPI_SCK);//delay_us(3);//if(wByte&0x80){set_spi_GPIO(SPI_MOSI);//}else{clr_spi_GPIO(SPI_MOSI);//}wByte=wByte<<1;set_spi_GPIO(SPI_SCK);//set_spi_sck;delay_us(3);//}clr_spi_GPIO(SPI_SCK);set_spi_GPIO(SPI_CS);}////////////////////////////////////////////////////////////////////////////////////注意,我写的主从机的io口对接如下主机io 从机ioSPI_MISO ------------------------- SPI_MISOSPI_MOSI --------------------------- SPI_MOSISPI_SCK --------------------------- SPI_SCKSPI_CS -------------------------- SPI_CS可能有的人对上面的io口对接的方式感到奇怪,请仔细看我对这几个io口做的初始化设置就可以明白。
介绍gpio的八种工作模式,特点及应用场景
介绍gpio的八种工作模式,特点及应用场景【标题】深入介绍GPIO的八种工作模式,揭秘特点及广泛应用场景【导言】GPIO(General Purpose Input/Output)是通用输入输出引脚的简称,它是现代电子设备中非常重要的一个接口,广泛应用于各个领域,如嵌入式系统、电子工程、物联网等。
GPIO的工作模式决定了其功能特性和应用场景的选择。
本文将全面深入地介绍GPIO的八种工作模式,探讨其特点并揭示其广泛应用场景,以帮助读者更好地理解和应用GPIO接口。
【正文】1. 输入模式输入模式是GPIO最基本的工作模式之一,用于读取外部信号的逻辑电平状态。
在输入模式下,GPIO引脚接收外界电平变化,并将其转换成对应的逻辑值,供处理器或者其他IC进行处理。
常见的应用场景包括按键输入、传感器数据采集等。
2. 输出模式输出模式是GPIO的另一个基础工作模式,用于控制外部设备的电平状态。
在输出模式下,GPIO引脚会根据处理器的指令输出相应的电平信号,驱动外部设备进行工作。
控制LED灯、驱动电机等。
3. 开漏输出模式开漏输出模式是GPIO的一种特殊输出模式,它允许多个输出引脚连接到一个电平上拉电阻上,实现共享一个信号线的目的。
开漏输出模式常用于总线通信(I2C、SPI)等场景,同时也可以用于实现外部电平转换。
4. 串行模式(UART)串行模式是GPIO的一种高级工作模式,常用于数据通信。
UART (Universal Asynchronous Receiver/Transmitter)是一种常见的串行通信协议,它通过GPIO的输入和输出引脚实现数据的发送和接收。
串行模式在无线通信、蓝牙、WiFi模块等场景中得到了广泛应用。
5. PWM模式PWM(Pulse Width Modulation)模式是GPIO的一种特殊输出模式,用于控制脉冲宽度的调节。
通过在不同时间段内改变高电平和低电平的占空比,可以控制输出引脚产生不同的平均电压或者电流,从而实现对电机速度、LED亮度等的精确控制。
SPI通信协议详解(四)
SPI通信协议详解(四)1.SPI协议简介板卡内不同芯⽚间通讯最常⽤的三种串⾏协议:UART、I2C、SPI,之前写过串⼝协议及其FPGA实现,今天我们来介绍SPI协议,SPI是Serial Perripheral Interface的简称,是由Motorola公司推出的⼀种⾼速、全双⼯的总线协议。
与IIC类似,SPI也是采⽤主从⽅式⼯作,主机通常为FPGA、MCU或DSP等可编程控制器,从机通常为EPROM、Flash,AD/DA,⾳视频处理芯⽚等设备。
⼀般由SCLK、CS、MOSI,MISO四根线组成,有的地⽅可能是:SCK、SS、SDI、SDO等名称,都是⼀样的含义,当有多个从机存在时,通过CS来选择要控制的从机设备。
和标准SPI类似的协议,还有TI的SSP协议,区别主要在⽚选信号的时序上。
2.4线还是3线?当我们谈到SPI时,默认情况下都是指标准的4线制Motorola SPI协议,即SCLK,MOSI,MISO和CS共4根数据线,标准4线制的好处是可以实现数据的全双⼯传输。
当只有⼀个主机和⼀个从机设备时,只需要⼀个CS,多个从机需要多个CS,各数据线的介绍:SCLK,时钟信号,时钟频率即SPI速率,和SPI模式有关MOSI,主机输出,从机输⼊MISO,主机输⼊,从机输出CS,从机设备选择,低电平有效3线制SPI,根据不同的应⽤场景,主要有以下2种类型:只有3根线:SCLK,CS和DI或DO,适⽤于单⼯通讯,主机只发送或接收数据。
只有3根线:SCLK,SDIO和CS,这⾥的SDIO作为双向端⼝,适⽤于半双⼯通讯,⽐如ADI的多款ADC芯⽚都⽀持双向传输。
在使⽤FPGA操作双向端⼝时,作为输⼊时要设置为⾼阻态z。
还有标准SPI协议的升级版,Dual SPI、Quad SPI和QPI等,这些协议不在本⼩节3线/4线制讨论的范围内,⽂章后⾯会提到。
3.4种⼯作模式既然是进⾏数据传输,双⽅就要明确从机在什么时刻去采样主机发出的数据,主机在什么时刻去读取从机发来的数据。
STM32硬件SPI主从通信教程
STM32硬件SPI主从通信教程一、硬件SPI主从通信原理硬件SPI(Serial Peripheral Interface)是一种支持全双工通信的串行通信协议,常用于连接微控制器与外设。
在SPI通信中,主设备控制整个通信过程,发送和接收数据,从设备则根据主设备的控制进行响应。
1.SCK(时钟信号):用于同步主从设备的数据传输,主设备产生时钟信号控制通信速率。
2.MOSI(主设备发送数据线):主设备向从设备发送数据的线路。
3.MISO(从设备发送数据线):从设备向主设备发送数据的线路。
4.NSS(片选信号):主设备用于选择从设备进行通信的信号。
5.数据寄存器:用于存储传输的数据。
二、硬件SPI主从通信的配置步骤以下是STM32硬件SPI主从通信的配置步骤:1.配置SPI模式和通信速率:选择主从通信模式和时钟速率。
2.配置GPIO引脚:将SPI的SCK、MOSI、MISO和NSS引脚配置为SPI功能。
3.配置SPI控制寄存器:配置SPI的参数,如主从模式、数据大小和时钟相位等。
4.使能SPI和NSS信号:打开SPI和NSS信号,准备开始通信。
5.通过SPI数据寄存器进行数据交换:主设备通过SPI数据寄存器向从设备发送数据,并接收从设备返回的数据。
三、示例代码下面是一个简单的示例代码,展示了如何在STM32中配置和使用硬件SPI主从通信。
```c#include "stm32f4xx.h"void SPI_Init(void)RCC_AHB1PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);SPI_InitTypeDef SPI_InitStructure;SPI_InitStructure.SPI_Direction =SPI_Direction_2Lines_FullDuplex;SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;SPI_InitStructure.SPI_CRCPolynomial = 7;SPI_Init(SPI1, &SPI_InitStructure);GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 , GPIO_Pin_6 ,GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);void SPI_SendData(uint8_t data)while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);SPI_SendData8(SPI1, data);while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);SPI_ReceiveData8(SPI1);int main(void)SPI_Init(;while (1)SPI_SendData(0xAA); // 发送数据}```以上示例代码展示了如何将STM32配置为SPI的从设备,并向主设备发送数据。
STM32之IO口模拟SPI
STM32之IO⼝模拟SPI本⽂介绍如何使⽤STM32标准外设库的GPIO端⼝模拟SPI,本例程使⽤PA5、PA6和PA7模拟⼀路SPI。
SPI有4种⼯作模式,模拟SPI使⽤模式0,即空闲时SCK为低电平,在奇数边沿采样。
本⽂适合对单⽚机及C语⾔有⼀定基础的开发⼈员阅读,MCU使⽤STM32F103VE系列。
1. 简介SPI 协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串⾏外围设备接⼝,是⼀种⾼速全双⼯的通信总线。
它被⼴泛地使⽤在要求通讯速率较⾼的场合。
SPI⽤于多设备之间通讯,分为主机Master和从机Slave,主机只有⼀个,从机可以有多个,通过⽚选信号对从机进⾏选择,⼀次只能选择⼀个从机。
通讯只能由主机发起,⽀持的操作分为读取和写⼊,即主机读取从机的数据,以及向从机写⼊数据。
SPI⼀般有4根线,分别是⽚选线SS、时钟线SCK、主设备输出\从设备输⼊MOSI、主设备输⼊\从设备输出MISO,其中除MISO对于主机为输⼊引脚外,其他引脚对于主机均为输出引脚。
因为有独⽴的输⼊和输出引脚,因此SPI⽀持全双⼯⼯作模式,即可以同时接收和发送。
2. 总线传输信号空闲状态:⽚选信号SS低电平有效,那么空闲状态⽚选信号SS为⾼。
开始信号及结束信号:开始信号需要将⽚选信号SS拉低,结束信号需要将⽚选信号SS拉⾼。
通讯模式:SPI有4种通讯模式,分别为0、1、2、3,根据时钟极性和时钟相位确定,时钟极性分别为空闲低电平和空闲⾼电平,时钟相位分别为SCK奇数边沿采样和偶数边沿采样。
常⽤的模式为模式0和模式3。
SPI模式时钟极性(空闲时SCK时钟)时钟相位(采样时刻)0低电平奇数边沿1低电平偶数边沿2⾼电平奇数边沿3⾼电平偶数边沿3. 时序说明以模式0举例说明:空闲状态:⽚选信号SS为⾼,SCK输出低电平。
开始信号:⽚选信号SS变低,SCK输出低电平。
结束信号:⽚选信号SS变⾼,SCK输出低电平。
MCU的GPIO模拟SPI源代码
MCU的GPIO模拟SPI源代码写程序:void SPIx_WriteByte(u8 TxData){u8 j=0;SPI_FLASH_CLK_LOW(); //clk=0if(TxData&0x80){SPI_FLASH_DI_HIGH();} //mosi=1else{SPI_FLASH_DI_LOW();} //mosi=0for(j=0;j<3;j++); //延时SPI_FLASH_CLK_HIGH(); //clk=1,一个上升沿写入一位for(j=0;j<5;j++); //延时SPI_FLASH_CLK_LOW(); //clk=0if(TxData & 0x40){SPI_FLASH_DI_HIGH();} //mosi=1else{SPI_FLASH_DI_LOW();} //mosi=0for(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_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&0x04){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&0x02){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(); //clk=0if(TxData&0x01){SPI_FLASH_DI_HIGH();}else{SPI_FLASH_DI_LOW();}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_HIGH(); //clk=1for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW(); //clk=0 }读程序0x80==0x80u8 SPIx_ReadByte(void){u8 i=0,j=0;for(j=0;j<3;j++); //延时SPI_FLASH_CLK_HIGH(); //clk=1if(GPIOC->IDR&0x80==0x80){i=i+0x80;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW(); //clk=0,下降沿读数for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x80==0x80){i=i+0x40;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x80==0x80){i=i+0x20;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x80==0x80){i=i+0x10;}for(j=0;j<3;j++); //延时for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x80==0x80) {i=i+0x08;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x80==0x80) {i=i+0x04;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x80==0x80) {i=i+0x02;}for(j=0;j<3;j++); //延时for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x80==0x80){i=i+0x01;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();return i;}读程序0x40==0x40u8 SPIx_ReadByte(void){u8 i=0,j=0;for(j=0;j<3;j++); //延时SPI_FLASH_CLK_HIGH(); //clk=1if(GPIOC->IDR&0x40==0x40){i=i+0x80;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW(); //clk=0,下降沿读数for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x40==0x40) {i=i+0x40;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x40==0x40) {i=i+0x20;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x40==0x40) {i=i+0x10;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x40==0x40) {i=i+0x08;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x40==0x40) {i=i+0x04;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x40==0x40) {i=i+0x02;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x40==0x40){i=i+0x01;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();return i;}读程序0x20==0x20u8 SPIx_ReadByte(void){u8 i=0,j=0;for(j=0;j<3;j++); //延时SPI_FLASH_CLK_HIGH(); //clk=1if(GPIOC->IDR&0x20==0x20){i=i+0x80;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW(); //clk=0,下降沿读数for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();{i=i+0x40;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x20==0x20) {i=i+0x20;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x20==0x20) {i=i+0x10;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();{i=i+0x08;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x20==0x20) {i=i+0x04;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x20==0x20) {i=i+0x02;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x20==0x20){i=i+0x01;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();return i;}读程序0x10==0x10读程序0x08==0x08读程序0x04==0x04读程序0x02==0x02u8 SPIx_ReadByte(void){u8 i=0,j=0;for(j=0;j<3;j++); //延时SPI_FLASH_CLK_HIGH(); //clk=1if(GPIOC->IDR&0x02==0x02)for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW(); //clk=0,下降沿读数for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x02==0x02){i=i+0x40;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x02==0x02){i=i+0x20;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x02==0x02)for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x02==0x02) {i=i+0x08;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x02==0x02) {i=i+0x04;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x02==0x02)for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x02==0x02){i=i+0x01;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();return i;}读程序0x01==0x01u8 SPIx_ReadByte(void){u8 i=0,j=0;for(j=0;j<3;j++); //延时SPI_FLASH_CLK_HIGH(); //clk=1if(GPIOC->IDR&0x01==0x01){i=i+0x80;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW(); //clk=0,下降沿读数for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x01==0x01){i=i+0x40;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x01==0x01){i=i+0x20;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x01==0x01){i=i+0x10;}for(j=0;j<3;j++); //延时for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x01==0x01) {i=i+0x08;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x01==0x01) {i=i+0x04;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x01==0x01) {i=i+0x02;}for(j=0;j<3;j++); //延时for(j=0;j<5;j++);SPI_FLASH_CLK_HIGH();if(GPIOC->IDR&0x01==0x01) {i=i+0x01;}for(j=0;j<3;j++); //延时SPI_FLASH_CLK_LOW();return i;}。
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控制成功。
通过SPI接口协议实现DSP与其它设备的通信
通过SPI接口协议实现DSP与其它设备的通信
通过SPI(Serial Peripheral Interface)接口协议可以实现DSP(Digital Signal Processor)与其他设备的通信。
SPI是一种同步串行通信协议,常用于外围设备与主控制器之间的数据传输。
以下是基本的SPI通信步骤:
1. 确定SPI主设备和从设备的角色:DSP作为主设备,与其他设备进行通信。
2. 配置SPI主设备的工作模式:设置SPI主设备的时钟频率、数据位宽等参数。
3. 选中从设备:设置 SPI 的片选信号,选中需要进行通信的从设备。
4. 主设备发送数据:将需要发送的数据写入 SPI 的数据寄存器。
5. 数据传输:SPI 主设备将数据发送至从设备,从设备接收数据。
6. 从设备发送数据:从设备将需要发送的数据写入 SPI 的数据寄存器。
7. 数据传输:SPI 主设备接收从设备发送的数据,从设备接收数据。
8. 取消片选信号:取消 SPI 的片选信号,完成本次通信。
9. 重复前述步骤进行进一步通信。
具体实施需要根据DSP和其他设备的硬件和软件特性进行配置和实现。
一般可以使用DSP的SPI模块或者通过GPIO口模拟SPI接口来实现通信。
其中包括设置时钟频率,配置数据位宽,设置片选信号,发送和接收数据等操作。
具体的实现可以参考DSP和目标设备的数据手册以及SPI接口的规范。
模拟spiC程序
return rcv;
}
u8 Soft_SPI_ReadWrite_Byte(u8 send_data)
{
u8 i;
u8 rcv;
// Soft_SPI_CLK = 0;
Soft_SPI_MISO_IN(); //设置MISO为输入模式
for(i = 0;i < 8;i++)
/**********************************************************
* @ File name -> soft_spi.c
* @ Version -> V1.0
* @ Date -> 11-15-2013
* @ Brief -> GPIO模拟SPI接口函数
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PA端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; //CLK and MOSI
* 函数功能 ---> 模拟SPI发送数据函数
* 入口参数 ---> send_data: 要发送的数据
* 返回参数 ---> 接收到的数据
* 功能说明 ---> 优先发送MSB,需要先发送LSB的请修改此函数
**********************************************************/
GPIO模拟SPI
用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的设备中,至少有一个主控设备。
gpio 模拟串口通讯的波特率
gpio 模拟串口通讯的波特率
GPIO模拟串口通讯的波特率取决于所使用的硬件和软件设置。
一般来说,如果你使用的是单片机或者嵌入式系统,你需要通过GPIO来模拟串口通讯,这种情况下,波特率是由时钟频率和串口通
讯协议决定的。
首先,你需要确定你的系统时钟频率,然后根据串口通讯协议(如UART)的要求来选择合适的波特率。
常见的波特率包括9600、19200、38400、115200等,当然也可以选择其他非标准波特率。
在
使用GPIO模拟串口通讯时,你需要通过编程来确保GPIO的输入输
出频率符合所选波特率的要求。
在软件层面,你需要编写相应的驱动程序或者使用现有的GPIO
库来配置GPIO引脚,设置输入输出频率,并实现数据的发送和接收。
此外,在使用GPIO模拟串口通讯时,你还需要考虑到时序的稳定性
和数据的可靠性,因为GPIO模拟串口通讯相比硬件串口会更容易受
到外部干扰和时序偏差的影响。
总之,GPIO模拟串口通讯的波特率取决于硬件的时钟频率和串
口通讯协议的要求,同时也需要在软件层面进行相应的配置和编程来实现所需的波特率。
希望这个回答能够全面解答你的问题。
STM32
STM32 SPI通信--OLED⼀、0.96⼨OLED⼆、原理图⼆、GPIO模拟SPI1. 硬件连接通过引脚和模块电路图可以分析出SPI的电路连接OLED STM32GND <----------> GNDVCC <----------> 3.3VD0 <----------> PA4(CLK)D1 <----------> PA3(MOSI)RES <----------> PA2(RET复位)DC <----------> PA1(命令|数据dc)CS <----------> GND2. 软件驱动由OLED模块数据⼿册上SPI的操作时序图写出由GPIO⼝模拟的SPI驱动代码,模块只⽀持向模块写数据不能读数据,所以只需要写SPI发送即可2.1 “SPI.h”#define OLED_CMD 0 //命令声明#define OLED_DATA 1 //数据声明#define OLED_CLK PAout(4) // CLK时钟 d0#define OLED_MOSI PAout(3) // MOSI d1#define OLED_RST PAout(2) // RET复位 ret#define OLED_DC PAout(1) // 命令|数据 dc (0表传输命令1表传输数据)void OLED_SPI_Init(void); //配置MCU的SPIvoid SPI_WriteByte(uint8_t addr,uint8_t data); //向寄存器地址写⼀个byte的数据void WriteCmd(unsigned char cmd); //写命令void WriteDat(unsigned char data); //写数据2.2 “SPI.c”void OLED_SPI_Init(void){GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE);//使能PA端⼝时钟GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4; //端⼝配置GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//IO⼝速度为50MHzGPIO_Init(GPIOA,&GPIO_InitStructure);//根据设定参数初始化GPIOA}void SPI_WriteByte(unsigned char data,unsigned char cmd){unsigned char i=0;OLED_DC =cmd;OLED_CLK=0;for(i=0;i<8;i++){OLED_CLK=0;if(data&0x80)OLED_MOSI=1; //从⾼位到低位else OLED_MOSI=0;OLED_CLK=1;data<<=1;}OLED_CLK=1;OLED_DC=1;}void WriteCmd(unsigned char cmd){SPI_WriteByte(cmd,OLED_CMD);}void WriteData(unsigned char data){SPI_WriteByte(data,OLED_DATA);}三、STM32对OLED模块的控制3.1 指令集3.2 “OLED.h”void OLED_Init(void);//初始化OLEDvoid OLED_ON(void);//唤醒OLEDvoid OLED_OFF(void);//OLED休眠void OLED_Refresh_Gram(void);//更新显存到OLEDvoid OLED_Clear(void);//清屏void OLED_DrawPoint(u8 x,u8 y,u8 t);//画点void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot);//填充void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode);//显⽰字符void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size);//显⽰2个数字void OLED_ShowString(u8 x,u8 y,const u8 *p,u8 size);//显⽰字符串3.3 “OLED.c”//OLED的显存//存放格式如下.//[0]0 1 2 3 (127)//[1]0 1 2 3 (127)//[2]0 1 2 3 (127)//[3]0 1 2 3 (127)//[4]0 1 2 3 (127)//[5]0 1 2 3 (127)//[6]0 1 2 3 (127)//[7]0 1 2 3 (127)u8 OLED_GRAM[128][8];void OLED_DLY_ms(unsigned int ms){unsigned int a;while(ms){a=1335;while(a--);ms--;}}void OLED_Init(void){OLED_SPI_Init();OLED_CLK = 1;OLED_RST = 0;OLED_DLY_ms(100);OLED_RST = 1;//从上电到下⾯开始初始化要有⾜够的时间,即等待RC复位完毕WriteCmd(0xAE); // Display Off (0x00)WriteCmd(0xD5);WriteCmd(0x80); // Set Clock as 100 Frames/SecWriteCmd(0xA8);WriteCmd(0x3F); // 1/64 Duty (0x0F~0x3F)WriteCmd(0xD3);WriteCmd(0x00); // Shift Mapping RAM Counter (0x00~0x3F)WriteCmd(0x40 | 0x00); // Set Mapping RAM Display Start Line (0x00~0x3F) WriteCmd(0x8D);WriteCmd(0x10 | 0x04); // Enable Embedded DC/DC Converter (0x00/0x04) WriteCmd(0x20);WriteCmd(0x02); // Set Page Addressing Mode (0x00/0x01/0x02)WriteCmd(0xA0 | 0x01); // Set SEG/Column MappingWriteCmd(0xC0); // Set COM/x Scan DirectionWriteCmd(0xDA);WriteCmd(0x02 | 0x10); // Set Sequential Configuration (0x00/0x10)WriteCmd(0x81);WriteCmd(0xCF); // Set SEG Output CurrentWriteCmd(0xD9);WriteCmd(0xF1); // Set Pre-Charge as 15 Clocks & Discharge as 1 Clock WriteCmd(0xDB);WriteCmd(0x40); // Set VCOM Deselect LevelWriteCmd(0xA4 | 0x00); // Disable Entire Display On (0x00/0x01)WriteCmd(0xA6 | 0x00); // Disable Inverse Display On (0x00/0x01)WriteCmd(0xAE | 0x01); // Display On (0x01)OLED_Clear(); //初始清屏}void OLED_ON(void){WriteCmd(0X8D); //设置电荷泵WriteCmd(0X14); //开启电荷泵WriteCmd(0XAF); //OLED唤醒}void OLED_OFF(void){WriteCmd(0X8D); //设置电荷泵WriteCmd(0X10); //关闭电荷泵WriteCmd(0XAE); //OLED休眠}void OLED_Refresh_Gram(void){u8 i,n;for(i=0;i<8;i++){WriteCmd(0xb0+i); //设置页地址(0~7)WriteCmd(0x00); //设置显⽰位置—列低地址WriteCmd(0x10); //设置显⽰位置—列⾼地址for(n=0;n<128;n++)WriteData(OLED_GRAM[n][i]);}}void OLED_Clear(void){u8 i,n;for(i=0;i<8;i++)for(n=0;n<128;n++)OLED_GRAM[n][i]=0X00;OLED_Refresh_Gram();//更新显⽰}void OLED_DrawPoint(u8 x,u8 y,u8 t){u8 pos,bx,temp=0;if(x>127||y>63)return;//超出范围了.pos=7-y/8;bx=y%8;temp=1<<(7-bx);if(t)OLED_GRAM[x][pos]|=temp;else OLED_GRAM[x][pos]&=~temp;}void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot){u8 x,y;for(x=x1;x<=x2;x++){for(y=y1;y<=y2;y++)OLED_DrawPoint(x,y,dot);}OLED_Refresh_Gram();//更新显⽰}void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode){u8 temp,t,t1;u8 y0=y;u8 csize=(size/8+((size%8)?1:0))*(size/2); //得到字体⼀个字符对应点阵集所占的字节数 chr=chr-' ';//得到偏移后的值for(t=0;t{if(size==12)temp=asc2_1206[chr][t]; //调⽤1206字体else if(size==16)temp=asc2_1608[chr][t]; //调⽤1608字体else if(size==24)temp=asc2_2412[chr][t]; //调⽤2412字体else return; //没有的字库for(t1=0;t1<8;t1++){if(temp&0x80)OLED_DrawPoint(x,y,mode);else OLED_DrawPoint(x,y,!mode);temp<<=1;y++;if((y-y0)==size){y=y0;x++;break;}}}}//m^n函数u32 mypow(u8 m,u8 n){u32 result=1;while(n--)result*=m;return result;}void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size){u8 t,temp;u8 enshow=0;for(t=0;t{temp=(num/mypow(10,len-t-1));if(enshow==0&&t<(len-1)){if(temp==0){OLED_ShowChar(x+(size/2)*t,y,' ',size,1);continue;}else enshow=1;}OLED_ShowChar(x+(size/2)*t,y,temp+'0',size,1);}}void OLED_ShowString(u8 x,u8 y,const u8 *p,u8 size){while((*p<='~')&&(*p>=' '))//判断是不是⾮法字符!{if(x>(128-(size/2))){x=0;y+=size;}if(y>(64-size)){y=x=0;OLED_Clear();}OLED_ShowChar(x,y,*p,size,1);x+=size/2;p++;}四、⽤STM32的SPI资源也可以不使⽤GPIO⼝模拟,使⽤STM32的SPI资源,驱动代码如下:void OLED_SPI_Init(void){GPIO_InitTypeDef GPIO_InitStructure;SPI_InitTypeDef SPI_InitStructure;RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1, ENABLE ); //①GPIO,SPI 时钟使能GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复⽤推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_7); //①初始化 GPIOGPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2;//端⼝配置GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//IO⼝速度为50MHzGPIO_Init(GPIOA,&GPIO_InitStructure);//根据设定参数初始化GPIOASPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置 SPI 全双⼯ SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置 SPI ⼯作模式:设置为主 SPI SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 8 位帧结构SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//选择了串⾏时钟的稳态:时钟悬空⾼ SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //数据捕获于第⼆个时钟沿SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS 信号由硬件管理SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; //预分频 256 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从 MSB 位开始SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC 值计算的多项式SPI_Init(SPI1, &SPI_InitStructure); //②根据指定的参数初始化外设 SPIx 寄存器SPI_Cmd(SPI1, ENABLE); //③使能 SPI 外设//SPI1_ReadWriteByte(0xff);//④启动传输}void OLED_WB(uint8_t data){while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);SPI_I2S_SendData(SPI1, data);}void SPI_WriteByte(unsigned char data,unsigned char cmd) {unsigned char i=0;OLED_DC =cmd;OLED_WB(data);}。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
一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接口不需要进行寻址操作,且为全双工通信,显得简单高效。
在多个从设备的系统中,每个从设备需要独立的使能信号,硬件上比I2C系统要稍微复杂一些。
最后,SPI接口的一个缺点:没有指定的流控制,没有应答机制确认是否接收到数据。
AT91RM9200的SPI接口主要由4个引脚构成:SPICLK、MOSI、MISO及/SS,其中SPICLK是整个SPI总线的公用时钟,MOSI、MISO作为主机,从机的输入输出的标志,MOSI是主机的输出,从机的输入,MISO 是主机的输入,从机的输出。
/SS是从机的标志管脚,在互相通信的两个SPI总线的器件,/SS管脚的电平低的是从机,相反/SS管脚的电平高的是主机。
在一个SPI通信系统中,必须有主机。
SPI总线可以配置成单主单从,单主多从,互为主从。
SPI的片选可以扩充选择16个外设,这时PCS输出=NPCS,说NPCS0~3接4-16译码器,这个译码器是需要外接4-16译码器,译码器的输入为NPCS0~3,输出用于16个外设的选择。
详细的SPI规范可参考SPI协议。
二GPIO模拟SPI的实现
下面将结合本人项目中的经验来详细描述如何用GPIO来模拟SPI 协议
项目中要求实现一块LCD为ssd1815br1的驱动,它与BB的通信使用SPI协议,由于BB上SPI总线已使用完,因此考虑使用GPIO来模拟实现。
GPIO对应SPI引脚的关系如下:
(1)SDO – GPIO0 (BB到LCD的数据线)
(2)SDI –无,因为暂时不需要BB接收来自LCD的数据
(3)SCLK – GPIO1
(4)CS –接地,使LCD一直处于使能状态。
接下来就是要实现SPI的协议了,SPI有4种传输模式:
开发者可根据具体设备使用的是哪种模式来实现之,我们项目种的这块LCD的模式为CPOL=1, CPHA=1.
具体实现如下:
#define SPI_DATA GPIO0
#define SPI_CLK GPIO1
void spi_write(char data)
{
int8 i = 7;
uint8 mask[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
for(; i >= 0; i--) {
gpio_out(SPI_CLK, GPIO_LOW_VALUE);
gpio_out(SPI_DATA, ((data & mask[i]) >> i));
spi_delay(10);
gpio_out(SPI_CLK, GPIO_HIGH_VALUE);
spi_delay(10);
}
}
实际上模拟SPI是很简单的事情,只要对照SPI传输模式的时序图来模拟就行了。
需要注意的是一定要有个等待时间,以使数据在
数据线上稳定下来,并使设备端有时间取数据。
刚开始调试的时候可以适当把等待时间延长一点,当调通了SPI后在降下等待时间。
我写的等待时间如下:
#define spi_delay(delay) \
{ \
register uint32 i = 0; \
while(i < delay) { \
__asm{ \
NOP; \
NOP; \
NOP; \
NOP; \
}; \
i -= 4; \
} \
}
呵呵,整个过程就是这样简单。