spi协议->裸机程序->master驱动程序分析

SPI协议参考:

SPI协议及其工作原理浅析

http://bbs.chinaunix.net/thread-1916003-1-1.html

SPI总线协议及SPI时序图详解

http://blog.163.com/sunshine_linting/blog/static/44893323201181482335951

一、概述

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

SPI 主要应用在 EEPROM, Flash, 实时时钟(RTC), 数模转换器(ADC), 数字信号处理器(DSP) 以及数字信号解码器之间. 它在芯片中只占用四根管脚 (Pin) 用来控制以及数据传输, 节约了芯片的 pin 数目, 同时为 PCB 在布局上节省了空间. 正是出于这种简单易用的特性, 现在越来越多的芯片上都集成了 SPI技术.

二、 特点

1. 采用主-从模式(Master-Slave) 的控制方式

SPI 规定了两个 SPI 设备之间通信必须由主设备 (Master) 来控制次设备 (Slave). 一个 Master 设备可以通过提供 Clock 以及对 Slave 设备进行片选 (Slave Select) 来控制多个 Slave 设备, SPI 协议还规定 Slave 设备的Clock
由 Master 设备通过 SCK 管脚提供给 Slave 设备, Slave 设备本身不能产生或控制 Clock, 没有 Clock 则 Slave 设备不能正常工作.

2. 采用同步方式(Synchronous)传输数据

Master 设备会根据将要交换的数据来产生相应的时钟脉冲(Clock Pulse), 时钟脉冲组成了时钟信号(Clock Signal) , 时钟信号通过时钟极性 (CPOL) 和 时钟相位 (CPHA) 控制着两个 SPI 设备间何时数据交换以及何时对接收到的数据进行采样, 来保证数据在两个设备之间是同步传输的.

3. 数据交换(Data Exchanges)

SPI 设备间的数据传输之所以又被称为数据交换, 是因为 SPI 协议规定一个 SPI 设备不能在数据通信过程中仅仅只充当一个 "发送者(Transmitter)" 或者 "接收者(Receiver)".在每个 Clock 周期内, SPI 设备都会发送并接收一个 bit 大小的数据, 相当于该设备有一个 bit 大小的数据被交换了.

一个 Slave 设备要想能够接收到 Master 发过来的控制信号, 必须在此之前能够被 Master 设备进行访问 (Access). 所以, Master 设备必须首先通过 SS/CS pin 对 Slave 设备进行片选, 把想要访问的 Slave 设备选上.

在数据传输的过程中,  每次接收到的数据必须在下一次数据传输之前被采样. 如果之前接收到的数据没有被读取, 那么这些已经接收完成的数据将有可能会被丢弃,  导致 SPI 物理模块最终失效. 因此, 在程序中一般都会在 SPI 传输完数据后, 去读取 SPI 设备里的数据, 即使这些数据(Dummy Data)在我们的程序里是无用的.

三、 工作机制

  1. 概述

通常情况下, 我们只需要对上图所描述的四个管脚(pin) 进行编程即可控制整个 SPI 设备之间的数据通信:

SCK :Serial Clock主要的作用是 Master 设备往 Slave 设备传输时钟信号, 控制数据交换的时机以及速率;

SS/CS:Slave Select/Chip Select, 用于 Master 设备片选 Slave 设备, 使被选中的 Slave 设备能够被 Master 设备所访问;

SDO/MOSI: Serial Data Output/Master Out Slave In, 在 Master 上面也被称为 Tx-Channel, 作为数据的出口, 主要用于 SPI 设备发送数据;

SDI/MISO: Serial Data Input/Master In Slave Out, 在 Master 上面也被称为 Rx-Channel, 作为数据的入口, 主要用于SPI 设备接收数据;

SPI 设备在进行通信的过程中, Master 设备和 Slave 设备之间会产生一个数据链路回环(Data Loop), 就像上图所画的那样, 通过 SDO 和 SDI 管脚, SSPSR 控制数据移入移出 SSPBUF, Controller 确定 SPI 总线的通信模式, SCK 传输时钟信号.

2.工作模式

CPOL位表示初始时钟极性:

CPOL = 0表示时钟初始为低电平,所以开始的第一(leading)边缘是上升沿,第二边缘(trailing)是下降沿。

CPOL = 1时的时钟启动为高电平,所以第一(leading)的边缘是下降沿。

CPHA的指示用于采样数据的时钟相位(注意是采样数据,采样是指的将数据线上的数据所存起来)

CPHA = 0时表示边沿超前

CPHA = 1表示边沿滞后

以 S3C2440 为例:

模式0:CPOL = 0 CPHA = 0 主机从机的数据均是在下降沿改变,上升沿锁存

模式1:CPOL = 0 CPHA = 1 主机从机的数据均是在上升沿改变,下降沿锁存

模式2:CPOL = 1 CPHA = 0 主机从机的数据均是在下降沿改变,上升沿锁存

模式3:CPOL = 0 CPHA = 1 主机从机的数据均是在上升沿改变,下降沿锁存

以模式0为例,看看具体时序图:

通常情况下,我们并不关心初始时钟电平状态,大多采用上升沿锁存,下降沿改变数据的方式,也就是模式0和模式3,下面看一个具体的spi_flash读数据的时序图

显然,主机和从机都是在上升沿锁存数据,下降沿改变数据。

四、裸机程序

在使用spi控制器的情况下,裸机程序十分简单,以 S3C2440 为例,简单说明一下。

1、配置引脚

1. 设置片选输出低电平

2. 配置 GPE11 SPIMISO 、GPE12 SPIMOSI 、GPE13 SPICLK 为特殊功能引脚

2、配置控制寄存器

SPCONn[6-5]: SPI模式选择,查询 中断 DMA等

SPCONn[4]  : 时钟使能

SPCONn[3]  : 主机模式、从机模式

SPCONn[2]  : CPOL

SPCONn[1]  : CPHA

SPCONn[0]  : 接收数据是是否自动发送,这里选普通模式,我们自己发送就好

3、发送数据

1、检查状态寄存器 传输是否可用 SPSTAn[0] 1表示可用,0表示正在发送或者接收

2、将数据写入 SPTDATn

4、接收数据

1、检查状态寄存器 传输是否可用 SPSTAn[0] 1表示可用,0表示正在发送或者接收

2、写数据 0xff 到 SPTDATn ,这就是普通模式的要求,接收的同时发送,0xff发送完了,数据也就接收完了

3、检查状态寄存器 传输是否可用 SPSTAn[0] 1表示可用,0表示正在发送或者接收

4、从 SPRDATn 取数据

static void SPIFlash_Set_CS(char val)
{
    if (val)
        GPGDAT |= (1<<10);
    else
        GPGDAT &= ~(1<<10);
}
static void SPI_GPIO_Init(void)
{
    /* GPG1 OLED_CSn output
    * GPG10 FLASH_CSn output
    */
    GPGCON &= ~((3<<(1*2)) | (3<<(10*2)));
    GPGCON |= (1<<(1*2)) | (1<<(10*2));
    GPGDAT |= (1<<1) | (1<<10);

    /*
    * GPF3  OLED_DC   output
    * GPE11 SPIMISO
    * GPE12 SPIMOSI
    * GPE13 SPICLK
    */
    GPFCON &= ~(3<<(3*2));
    GPFCON |= (1<<(3*2));    

    GPECON &= ~((3<<(11*2)) | (3<<(12*2)) | (3<<(13*2)));
    GPECON |= ((2<<(11*2)) | (2<<(12*2)) | (2<<(13*2)));
}

void SPISendByte(unsigned char val)
{
    while (!(SPSTA0 & 1));
    SPTDAT0 = val;
}

unsigned char SPIRecvByte(void)
{
    SPTDAT0 = 0xff;
    while (!(SPSTA0 & 1));
    return SPRDAT0;
}

static void SPIControllerInit(void)
{
    /* OLED  : 100ns, 10MHz
    * FLASH : 104MHz
    * 取10MHz
    * 10 = 50 / 2 / (Prescaler value + 1)
    * Prescaler value = 1.5 = 2
    * Baud rate = 50/2/3=8.3MHz
    */
    SPPRE0 = 2;
    SPPRE1 = 2;

    /* [6:5] : 00, polling mode
    * [4]   : 1 = enable
    * [3]   : 1 = master
    * [2]   : 0
    * [1]   : 0 = format A
    * [0]   : 0 = normal mode
    */
    SPCON0 = (1<<4) | (1<<3);
    SPCON1 = (1<<4) | (1<<3);

}

void SPIInit(void)
{
    /* 初始化引脚 */
    SPI_GPIO_Init();

    SPIControllerInit();
}

实际的SPI通信中,是由片选函数、字节读、字节写三部分组合而成~例如:

void SPIFlashRead(unsigned int addr, unsigned char *buf, int len)
{
    int i;

    SPIFlash_Set_CS(0);
    SPISendByte(0x03);
    SPISendByte(addr >> 16);
    SPISendByte(addr >> 8);
    SPISendByte(addr & 0xff);
    for (i = 0; i < len; i++)
        buf[i] = SPIRecvByte();

    SPIFlash_Set_CS(1);
}

五、master 驱动实例详细分析

上一篇文章中,分析了 master 和 spi 设备驱动的框架,我们知道 master 是在 atmel_spi_probe 函数中分配、设置、注册。其实在哪不重要,主要是设置的那些东西,我们再来看一下都设置了哪些东西

master = spi_alloc_master(&pdev->dev, sizeof *as);

/* 设置 master  */

master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;   // 所支持的模式

master->bus_num = pdev->id;   // 控制器编号,用于分辨外围spi设备是连接在哪一个控制器上

master->num_chipselect = 4;  // 片选最大值+1,spi设备的片选值要小于它

master->setup = atmel_spi_setup; // 一个控制器上可能接有多个spi设备,它们的频率和模式是不一样的,用于设备之间切换时设置这些信息。

master->transfer = atmel_spi_transfer;   // 最重要的发送函数

ret = request_irq(irq, atmel_spi_interrupt, 0,  dev_name(&pdev->dev), master);

master->transfer 函数,就跟I2c里的adapter->xxx_i2c_xfer函数一样,是最底层的发送接收函数,在spi中,设备驱动层调用到master层transfer函数的过程是这样的spi_sync(spi, &m)->spi_async(spi,&m)->master->transfer(spi, &m),也就是说,设备驱动层传递给我们一个spi_device 和 一个 spi_message ,还有一点值得注意的。

int spi_sync(struct spi_device *spi, struct spi_message *message)
{
	DECLARE_COMPLETION_ONSTACK(done);
	int status;

	message->complete = spi_complete;
	message->context = &done;
	status = spi_async(spi, message);
	if (status == 0) {
		wait_for_completion(&done);
		status = message->status;
	}
	message->context = NULL;
	return status;
}

在 spi_sync 中,调用 spi_async(spi, message) 之后,会 wait_for_completion(&done)休眠,那么我们在master的发送函数中,发送完成,自然要 mesg->complete(mesg->context),还要设置 mesg->status = 0

在 spi_new_device 我们根据 Board_list 里的信息创建了 spi_device,它含有一下信息

struct spi_device *spi_new_device(struct spi_master *master,
                  struct spi_board_info *chip)
{
    struct spi_device   *proxy;
    int         status;  

    proxy = spi_alloc_device(master);  

    proxy->chip_select = chip->chip_select;
    proxy->max_speed_hz = chip->max_speed_hz;
    proxy->mode = chip->mode;
    proxy->irq = chip->irq;
    strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));
    proxy->dev.platform_data = (void *) chip->platform_data;
    proxy->controller_data = chip->controller_data;
    proxy->controller_state = NULL;  

    status = spi_add_device(proxy);  

    return proxy;
} 

对于 spi_message,它对应于一个不可打断的spi传输过程,可以理解为片选选中直到取消选中的过程(特殊情况下,一个spi_message里面是可以取消片选再选中的),而 spi_message 由 spi_transfer 组成,根据 tx_buf  rx_buf 是否为空来判断是 写还是读 操作。

那么,我们master->transfer函数需要做什么?

1、传输前,根据 spi_device 里的信息,对 master 进行设置(调用master->setup),因为一个spi控制器可能接有多个spi设备,它们的频率 模式可能是不一样的

2、取出 spi_message 里的 spi_transfer 进行寄存器操作,发送或接收,这个过程和 I2C 的传输过程是类似的。

下面开始分析代码,先看setup函数。代码参考:韦东山老师的spi教程

static int s3c2440_spi_setup(struct spi_device *spi)
{
    struct s3c_spi_info *info;
    struct clk *clk;

    int cpol = 0;
    int cpha = 0;

    int div;

    info = spi_master_get_devdata(spi->master);
    clk = clk_get(NULL, "plck");

    /* 设置传输模式 : mode
     * 传输频率 : max_speed_hz
     * bits_per_word : 不用管
     */

    /* spi_mode: CPOL,CPHA组成一个值,0,1,2,3
     */

    if (spi->mode & 1)
    {
        cpha = 1;
    }
    if (spi->mode & 2)
    {
        cpol = 1;
    }

    /* 写入SPCON0,1 */
    /* [6:5] : 01, polling mode
     * [4]   : 1 = enable
     * [3]   : 1 = master
     * [2]   : CPOL
     * [1]   : CPHA
     * [0]   : 0 = normal mode
     */
	writeb((1<<5) | (1<<4) | (1<<3) | (cpol << 2) | (cpha << 1), info->reg_base + S3C2410_SPCON);

    /*
     * Baud rate = PCLK / 2 / (Prescaler value + 1)
     * Prescaler value = PCLK/(2*Baud rate) - 1
     * Prescaler Value : 0,1,...,255
     */
	div = DIV_ROUND_UP(clk_get_rate(clk), spi->max_speed_hz * 2) - 1;
    clk_put(clk);

	if (div > 255)
		div = 255;

	writeb(div, info->reg_base + S3C2410_SPPRE);

    return 0;
}

info 是我们用 spi_alloc_master 申请内存时多申请出来的那个自定义结构体,韦东山老师在里面放了 寄存器基地址等信息,后面完整代码会看到,setup 函数很简单,就是根据 spi_device 里的模式,设置master的模式,根据 spi_device 的最大频率,设置 master 的工作频率。

下面来看transfer函数

思路:

1、首先一个 spi_message 对应一个 片选过程,我们要先片选

2、master->setup

3、取出 spi_message 中的 第一个 spi_transfer

如果是读:跟裸机一样,先写0xff,等待中断

如果是写:那么将 spi_transfer->tx_buf[0]里的数据写到寄存器,等待休眠

何时唤醒?

在中断服务程序里,判断当前 spi_transfer 的发送接收完毕了,唤醒,取出下一个 spi_transfer
,如果有所得 spi_transfer 传输完了,则唤醒设备驱动层进程

中断服务程序:

读:先把上次读的取出来,判断是否还有数据要读?如果有则继续发送0xff,等待中断,没有则唤醒,进入下一个spi_transfer

写:判断当前 spi_transfer 的发送是否完成,没有完成则把下一个Buf里的数据发送或接收。完成了,则唤醒,进入下一个spi_transfer。

附上韦东山老师完整源码:

#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/slab.h>

#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/spi/s3c24xx.h>
#include <linux/module.h>

#include <plat/regs-spi.h>
#include <mach/regs-gpio.h>

/* 构造注册spi_master */

static struct spi_master *spi0_controller;
static struct spi_master *spi1_controller;

struct s3c_spi_info {
    int irq;
    unsigned int reg_base;
    struct completion	 done;
    struct spi_transfer *cur_t;
    int cur_cnt;
    struct platform_device *pdev;
};

static int s3c2440_spi_setup(struct spi_device *spi)
{
    struct s3c_spi_info *info;
    struct clk *clk;

    int cpol = 0;
    int cpha = 0;

    int div;

    info = spi_master_get_devdata(spi->master);
    clk = clk_get(NULL, "plck");

    /* 设置传输模式 : mode
     * 传输频率 : max_speed_hz
     * bits_per_word : 不用管
     */

    /* spi_mode: CPOL,CPHA组成一个值,0,1,2,3
     */

    if (spi->mode & 1)
    {
        cpha = 1;
    }
    if (spi->mode & 2)
    {
        cpol = 1;
    }

    /* 写入SPCON0,1 */
    /* [6:5] : 01, polling mode
     * [4]   : 1 = enable
     * [3]   : 1 = master
     * [2]   : CPOL
     * [1]   : CPHA
     * [0]   : 0 = normal mode
     */
	writeb((1<<5) | (1<<4) | (1<<3) | (cpol << 2) | (cpha << 1), info->reg_base + S3C2410_SPCON);

    /*
     * Baud rate = PCLK / 2 / (Prescaler value + 1)
     * Prescaler value = PCLK/(2*Baud rate) - 1
     * Prescaler Value : 0,1,...,255
     */
	div = DIV_ROUND_UP(clk_get_rate(clk), spi->max_speed_hz * 2) - 1;
    clk_put(clk);

	if (div > 255)
		div = 255;

	writeb(div, info->reg_base + S3C2410_SPPRE);

    return 0;
}

static int s3c2440_spi_transfer(struct spi_device *spi, struct spi_message *mesg)
{
    struct spi_master *master = spi->master;
    struct s3c_spi_info *info;
	struct spi_transfer	*t = NULL;

    info = spi_master_get_devdata(master);

    /* 1. 选中芯片 */
    s3c2410_gpio_setpin(spi->chip_select, 0);  /* 默认为低电平选中 */

    /* 2. 发数据 */

    /* 2.1 发送第1个spi_transfer之前setup */
    master->setup(spi);

    /* 2.2 从spi_message中逐个取出spi_transfer,执行它 */
	list_for_each_entry (t, &mesg->transfers, transfer_list) {
	    /* 处理这个spi_transfer */
        info->cur_t = t;
        info->cur_cnt = 0;
    	init_completion(&info->done);

        /* 如果该spi_transfer的speed_hz或bits_per_word
         * 不是0, 则调用setup来设置
         */

        if (t->tx_buf)
        {
            /* 发送 */
            writeb(((unsigned char *)t->tx_buf)[0], info->reg_base + S3C2410_SPTDAT);

            /* 它会触发中断 */

            /* 休眠 等待当前transfer 发送完毕 */
            wait_for_completion(&info->done);
        }
        else if(t->rx_buf)
        {
            /* 接收 */
            writeb(0xff, info->reg_base + S3C2410_SPTDAT);
            /* 它会触发中断 */

            /* 休眠 等待当前 transfer 接收完毕 */
            wait_for_completion(&info->done);
        }

        /* 如果该spi_transfer的cs_change为1
         * 则取消片选
         */
    }

    /* 2.3 唤醒等待的进程 */
	mesg->status = 0;
	mesg->complete(mesg->context);    

    /* 3. 取消片选 */
    s3c2410_gpio_setpin(spi->chip_select, 1);  /* 默认为低电平选中 */

    return 0;
}

static irqreturn_t s3c2440_spi_irq(int irq, void *dev_id)
{
    struct spi_master *master = (struct spi_master *)dev_id;
    struct s3c_spi_info *info = spi_master_get_devdata(master);
    struct spi_transfer *t = info->cur_t;

    if (!t)
    {
        /* 误触发 */
    	return IRQ_HANDLED;
    }

    /* 处理 spi_transfer */

    if (t->tx_buf) /* 是发送 */
    {
        info->cur_cnt++;

        if (info->cur_cnt < t->len)/* 没发完? */
            writeb(((unsigned char *)t->tx_buf)[info->cur_cnt], info->reg_base + S3C2410_SPTDAT);
        else
            complete(&info->done); /* 唤醒 */
    }
    else /* 接收 */
    {
        /* 读/存数据 */
        ((unsigned char *)t->rx_buf)[info->cur_cnt] = readb(info->reg_base + S3C2410_SPRDAT);
        info->cur_cnt++;

        if (info->cur_cnt < t->len)/* 没收完? */
            writeb(0xff, info->reg_base + S3C2410_SPTDAT);
        else
            complete(&info->done); /* 唤醒 */
    }
	return IRQ_HANDLED;
}

static void s3c2440_spi_controler_init(int which)
{
	struct clk *clk = clk_get(NULL, "spi");

    /* 使能spi controller 0/1的时钟 */
    clk_enable(clk);
    clk_put(clk);

    /* GPIO */
    if (which == 0)
    {
        /* SPI controller 0 */
        /*
         * GPE11 SPIMISO
         * GPE12 SPIMOSI
         * GPE13 SPICLK
         */
        s3c2410_gpio_cfgpin(S3C2410_GPE(11), S3C2410_GPE11_SPIMISO0);
        s3c2410_gpio_cfgpin(S3C2410_GPE(12), S3C2410_GPE12_SPIMOSI0);
        s3c2410_gpio_cfgpin(S3C2410_GPE(13), S3C2410_GPE13_SPICLK0);        

    }
    else if (which == 1)
    {
        /* SPI controller 1 */
        /*
         * GPG5 SPIMISO
         * GPG6 SPIMOSI
         * GPG7 SPICLK
         */
        s3c2410_gpio_cfgpin(S3C2410_GPG(5), S3C2410_GPG5_SPIMISO1);
        s3c2410_gpio_cfgpin(S3C2410_GPG(6), S3C2410_GPG6_SPIMOSI1);
        s3c2410_gpio_cfgpin(S3C2410_GPG(7), S3C2410_GPG7_SPICLK1);        

        /* 使能spi controller 1的时钟 */
    }
}

static struct spi_master *create_spi_master_s3c2440(int bus_num, unsigned int reg_base_phy, int irq)
{
    int ret;
    struct spi_master *master;
    struct s3c_spi_info *info;
    struct platform_device *pdev;

	pdev = platform_device_alloc("s3c2440_spi", bus_num);
    platform_device_add(pdev);

    master = spi_alloc_master(&pdev->dev, sizeof(struct s3c_spi_info));
    master->bus_num = bus_num;
    master->num_chipselect = 0xffff;
	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;

    master->setup    = s3c2440_spi_setup;
    master->transfer = s3c2440_spi_transfer;

    info = spi_master_get_devdata(master);
    info->reg_base = (unsigned int)ioremap(reg_base_phy, 0x18);
    info->irq = irq;
    info->pdev = pdev;

    /* 硬件初始化 */
    s3c2440_spi_controler_init(bus_num);

    ret = request_irq(irq, s3c2440_spi_irq, 0, "s3c2440_spi", master);

    spi_register_master(master);

    return master;
}

static void destroy_spi_master_s3c2440(struct spi_master *master)
{
    struct s3c_spi_info *info = spi_master_get_devdata(master);;

    spi_unregister_master(master);
    platform_device_del(info->pdev);
    platform_device_put(info->pdev);
    free_irq(info->irq, master);
    iounmap((void *)info->reg_base);
    //kfree(master);
}

static int spi_s3c2440_init(void)
{
    spi0_controller = create_spi_master_s3c2440(0, 0x59000000, IRQ_SPI0);
    spi1_controller = create_spi_master_s3c2440(1, 0x59000020, IRQ_SPI1);

    return 0;
}

static void spi_s3c2440_exit(void)
{
    destroy_spi_master_s3c2440(spi0_controller);
    destroy_spi_master_s3c2440(spi1_controller);
}

module_init(spi_s3c2440_init);
module_exit(spi_s3c2440_exit);
MODULE_DESCRIPTION("SPI Controller Driver");
MODULE_AUTHOR("[email protected],www.100ask.net");
MODULE_LICENSE("GPL");
时间: 2024-10-14 09:36:59

spi协议->裸机程序->master驱动程序分析的相关文章

I2C协议-&gt;裸机程序-&gt;adapter驱动程序分析

开发板:mini2440 内核  :linux2.6.32.2 参考  :韦东山毕业班I2C视频教程 1.i2c协议简要分析 i2c中线是一种由 PHILIPS 公司开发的串行总线,用于连接微控制器及其外围设备,它具有以下特点. 1.只有两条总线线路:一条串行数据线SDA,一条串行时钟线SCL. 2.每个连接到总线的器件都可以使用软件根据它的唯一的地址来确定. 3.传输数据的设备之间是简单的主从关系. 4.主机可以用作主机发送器或者主机接收器. 5.它是一个真正的多主机总线,两个或多个主机同时发

spi协议及工作原理分析

转自----http://blog.csdn.net/skyflying2012/article/details/11710801 一.概述. SPI, Serial Perripheral Interface, 串行外围设备接口, 是 Motorola 公司推出的一种同步串行接口技术. SPI 总线在物理上是通过接在外围设备微控制器(PICmicro) 上面的微处理控制单元 (MCU) 上叫作同步串行端口(Synchronous Serial Port) 的模块(Module)来实现的, 它允

nandflash裸机程序分析

原文: nandflash裸机程序分析 它包含7个文件: head.S init.c main.c Makefile nand.c nand.lds 我们之前的程序都是在nandflash的前4k放代码,上电后自动拷贝到SRAM中,之后将SRAM中的代码拷贝到SDRAM中.可是当我们的程序太大超过4k的时候就不行了,因为无法将nandflash的代码完全拷贝到SRAM中去,这时就需要从nandflash中拷贝代码了. 本程序里面我们要实现的就是:将一部分代码放在nandflash的4096之后,

基于S5PC100裸机程序之SPI(上)

作者:杨老师,华清远见嵌入式学院讲师. SPI作为应用最为广泛的通信总线协议之一,开发人员应当掌握,本章将介绍SPI总线协议的基本理论,以及S5PC100的SPI总线控制器的操作方法. 1. SPI总线协议理论 1.1  协议简介 SPI是英文Serial Peripheral Interface的缩写,该协议是由美国摩托罗拉公司推出的一种同步串行传输规范,首先由摩托罗拉公司在其MC68HCXX系列处理器上定义,后主要应 用在 EEPROM.FLASH.实时时钟.AD转换器,还有数字信号处理器和

TQ2440按键点亮LED灯的裸机程序

一,说到做ARM的裸机程序,很多人马上就会联想到一个名为ADS的开发工具,但是我们在linux下同样也可以做ARM的裸机程序,下面来说说其具体实施过程: 步骤一:编辑代码,这个没什么好说的. 步骤二:编译代码,编译代码分为三个方面的内容:1.链接脚本 2.用命令行确定链接时的文件顺序 3.用命令行编译.这三部分的内容可以全部写成一个Makefile文件,编译的时候执行make命令就可以了. 步骤三:把编译后的".bin"文件烧写进开发板,重新上电观察效果. 二,TQ2440按键点亮LE

基于Linux C的socket抓包程序和Package分析 (一)

 测试运行平台:CentOS 6.5发行版,内核版本3.11 1. Linux抓包源程序 在OSI七层模型中,网卡工作在物理层和数据链路层的MAC子层. 进行网络通信时,源主机通过socket(或其它)应用程序产生IP报文,经过各个OSI层层封装,数据包以Ethernet帧的形式进入物理层.Ethernet帧包含源主机地址.IP报文.目标地址(IP地址.端口号或映射的6字节MAC地址)和需要传送到目标主机的其它信息. 目标的MAC地址是哪里来的呢?这牵扯到一个ARP协议(介乎于网络层和数据链

[SPI]SPI协议详解

转自:https://my.oschina.net/freeblues/blog/67400 1.SPI协议简介 1.1.SPI协议概括 SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口.是Motorola首先在其MC68HCXX系列处理器上定义的.SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间.SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了

SPI协议及其工作原理浅析

转载自:http://bbs.chinaunix.net/thread-1916003-1-1.html一.概述. SPI, Serial Perripheral Interface, 串行外围设备接口, 是 Motorola 公司推出的一种同步串行接口技术. SPI 总线在物理上是通过接在外围设备微控制器(PICmicro) 上面的微处理控制单元 (MCU) 上叫作同步串行端口(Synchronous Serial Port) 的模块(Module)来实现的, 它允许 MCU 以全双工的同步串

python程序之profile分析

操作系统 : CentOS7.3.1611_x64 python版本:2.7.5 问题描述 1.Python开发的程序在使用过程中很慢,想确定下是哪段代码比较慢: 2.Python开发的程序在使用过程中占用内存很大,想确定下是哪段代码引起的: 解决方案 使用profile分析分析cpu使用情况 profile介绍: https://docs.python.org/2/library/profile.html 可以使用profile和cProfile对python程序进行分析,这里主要记录下cPr