SylixOS基于Nuc970平台的SD驱动移植

1. 适用范围

本文档为实现Nuc970平台的SD驱动总结,提供一些SylixOS SD驱动移植方法的参考。

2. 原理概述

2.1 控制器类型

SD控制器有两种类型,分为SD标准控制器(SDHCI)SD非标准控制器

SylixOS Base代码中实现了SDHCI的驱动,但Nuc970的SD控制器是非标准控制器,其功能都需要在BSP中单独实现。

2.2  命令、应答、数据

SD传输过程中会有命令、应答和数据三个概念存在。

命令和应答都是在CMD线上传输的,数据在DAT线上进行传输。

2.2.1 命令

命令有四种类型:
   广播无应答命令(bc)    此命令SD总线上的各个设备都会接收到。
   广播有应答命令(bcr)   此命令当SD总线上各个设备的CMD线都是独立时,都会接收到,并且各自独立的在CMD线上产生应答。
   点对点无数据命令(ac)  此命令不会在DAT线上产生数据。
   点对点有数据命令(adtc) 此命令会在DAT线上产生数据。

有的命令是可以带参数的,在SD标准协议中定义了如图 2-1所示的命令,标准协议对每条命令是否可以带参数、所带参数的格式、应答类型以及功能都进行了详细地定义。

图 2-1 SD标准协议中定义的CMD

2.2.2 应答

应答有五种类型:

   (1)R1和R1b

R1和R1b是最常见的应答类型,如图 2-2所示。R1b较R1不同之处为应答产生后R1b会在DAT线上产生busy标志。

图 2-2 R1和R1b应答

(2)R2

R2应答如图 2-3所示,用于读取SD卡中的CID和CSD寄存器,CID和CSD寄存器记录了SD卡的相关信息。

图 2-3 R2应答

    (3)R3

R3应答如图 2-4所示,用于读取SD卡中的OCR寄存器,OCR寄存器记录了SD卡电压支持情况。

图 2-4 R3应答

(4)R6

R6应答如图 2-5所示,用于获取SD卡中的RCA,RCA寄存器用于存储SD卡识别后被设置的16bits卡地址。

图 2-5 R6应答

(5)R7

R7应答如图 2-6所示,用于获取SD卡接口状态,比如电压是否处于可兼容状态。

图 2-6 R7应答

2.2.3 数据

SD的数据传输分为单块传输和多块传输两种。

单块读取的流程如图 2-7所示。

图 2-7单块读取流程

多块读取的流程如图 2-8所示。

图 2-8多块读取流程

2.3 SD卡识别流程

SD卡在被插入SD卡槽后,系统会发送一系列的命令对SD卡进行初始化、获取电压支持等操作。当SD卡被成功识别后,系统会根据SD卡的CID和CSD读取出如所示的SD卡信息,如图 2-9所示。

图 2-9 SD卡信息

SD卡基本识别流程如图 2-10所示,识别之后的SD卡会在/dev/blk下创建设备节点sdcard-0。

图 2-10 SD卡识别流程

2.4 文件系统挂载

SD识别之后,系统会读取SD卡中的首扇区,其中定义了根目录入口簇号、FAT表占用扇区数、文件系统信息扇区号等信息,SD协议栈会根据这些信息读取到目录节点、文件节点等信息,并完成文件系统的挂载。

3. 技术实现

3.1 驱动框架

3.1.1 SD总线适配器创建

SD驱动需要首先创建SD总线适配器,如程序清单 3-1所示,创建适配器时需要注册总线的操作集。

程序清单 3-1 SD总线适配器创建

_G_sdfunc.SDFUNC_pfuncMasterXfer = __sdTransfer;
_G_sdfunc.SDFUNC_pfuncMasterCtl  = __sdIoCtl;
iRet = API_SdAdapterCreate(__SDHOST_NAME,&_G_sdfunc);
if (iRet != ERROR_NONE) {
    printk("__err2\n");
    goto __err2;
}

3.1.2 热插拔检测

Nuc970的SD控制器在拔插卡之后会产生中断,所以直接在中断中判断SD卡的状态,并通知系统创建SD存储设备节点或移除SD存储设备节点。

3.1.3 SD中断处理

SD中断处理逻辑如图 3-1所示,中断处理主要包括了以下几点内容:

  • 传输CRC校验出错
  • 应答超时出错
  • DMA传输结束置位
  • 热插拔状态检测

图 3-1 SD中断处理流程

3.2 通信流程

通信流程如图 3-2所示。命令是否发送完成和应答是否产生,Nuc970都只提供了可轮询检查的状态位CO和RI;当有数据进行发送时,同时需要对DO位进行置位;当有数据需要读取时,同时需要对DI位进行置位。读写操作由DMA完成,DMA传输结束后会产生中断。

图 3-2通信流程

3.3 代码实现

Nuc970的SD驱动以内核模块的形式提供,整体的代码结构如图 3-3所示。

图 3-3 SD驱动文件结构

3.3.1 发送命令

发送命令的基本代码实现如程序清单 3-2所示。

程序清单 3-2发送命令__mciSendCmd

INT __mciSendCmd (PSDIO_DAT psdio)
{
    ……
    __SD_FUNCTION_ENABLE();
 
    uiRegCtl = readl(REG_SDH_CTL)&
               (~((BIT_CTL_CMD_CODE_MASK)|
               (BIT_CTL_EN_MASK)));
 
    if (psdio->SDIO_bData) {
        /*
         * 有数据发送
         */
        ……
        psdio->SDIO_eCompleteWhat = COMPLETION_XFERFINISH_RSPFIN;
 
    } else if(SD_CMD_TEST_RSP((psdio->SDIO_psdcmd), SD_RSP_PRESENT)) {
        /*
         * 只有应答,没有数据发送
         */
        ……
        psdio->SDIO_eCompleteWhat = COMPLETION_RSPFIN;
 
    } else {
        /*
         * 只发送命令
         */
        psdio->SDIO_eCompleteWhat = COMPLETION_CMDSENT;
    }
 
    writel(psdio->SDIO_psdcmd->SDCMD_uiArg, REG_SDH_CMD);        /* 将参数填入命令寄存器 */
 
    uiRegCtl |= (psdio->SDIO_psdcmd->SDCMD_uiOpcode << 8) |      /* 将Opcode填入寄存器   */
                (BIT_CTL_CO_EN);
    psdio->SDIO_ucEvent |= SD_EVENT_CMD_OUT;                     /* 添加发送命令事件     */
    writel(uiRegCtl, REG_SDH_CTL);
 
    __mciWakeupQueue(psdio->SDIO_hSdioSync);                     /* 通知事件处理线程     */
 
    return(ERROR_NONE);
}

发送命令之后会唤醒等待事件线程,如程序清单 3-3所示,在此线程中会轮询相关状态位,判断发送和应答状态。

程序清单 3-3事件处理线程

#define __WAIT_COMPLETE(ulMsk, x) while (1) {                                       if (!(readl(REG_SDH_CTL) & ulMsk)) {                                           x = 1;                                           break;                                       }                                   }
 
static PVOID __mciSdioThread (PVOID pvArg)
{
    ……
    for (;;) {
        __mciIoWaitQueue(psdio->SDIO_hSdioSync);                  /* 等待事件处理消息  */
        ……
        if (ucEvent & SD_EVENT_CMD_OUT) {
            __WAIT_COMPLETE(BIT_CTL_CO_EN, ucCompleted);          /* 等待命令发送完成  */
        }
        ……
        if (ucCompleted) {
           __sdCompletedCommand(psdio, ucEvent);                  /* 通知事件处理完成  */
        }
    }
 
    return(LW_NULL);
}

3.3.2 接收应答

接收应答需要在调用发送命令之后,只是对于需要产生应答的命令,在发送时同时需要设置应答接收类型和事件,代码实现如程序清单 3-4所示。

程序清单 3-4发送命令时设置应答

INT __mciSendCmd (PSDIO_DAT psdio)
{
    ……
    __SD_FUNCTION_ENABLE();
    ……
    if (psdio->SDIO_bData) {
        /*
         * 需要应答
         */
        psdio->SDIO_ucEvent      |= SD_EVENT_RSP_IN;
        psdio->SDIO_eCompleteWhat = COMPLETION_XFERFINISH_RSPFIN;
 
    } else if(SD_CMD_TEST_RSP((psdio->SDIO_psdcmd), SD_RSP_PRESENT)) {
        /*
         * 需要应答
         */
        if ((psdio->SDIO_psdcmd->SDCMD_uiFlag & SD_RSP_R2) == SD_RSP_R2) {
            /*
             * 应答类型为R2
             */
            uiRegCtl            |= BIT_CTL_R2_EN;
            psdio->SDIO_ucEvent |= SD_EVENT_RSP2_IN;
        } else {
            /*
             * 应答类型为R1
             */
            uiRegCtl            |= BIT_CTL_RI_EN;
            psdio->SDIO_ucEvent |= SD_EVENT_RSP_IN;
        }
        psdio->SDIO_eCompleteWhat = COMPLETION_RSPFIN;
 
    } else {
        psdio->SDIO_eCompleteWhat = COMPLETION_CMDSENT;
    }
    ……                                                              /*    发送命令      */
    __mciWakeupQueue(psdio->SDIO_hSdioSync);                          /* 通知事件处理线程 */
 
    return(ERROR_NONE);
}

同样,在事件处理线程中需要对应答状态进行轮询,如程序清单 3-5所示。

程序清单 3-5事件处理线程处理应答

static PVOID __mciSdioThread (PVOID pvArg)
{
    ……
    for (;;) {
        __mciIoWaitQueue(psdio->SDIO_hSdioSync);                      /*  等待事件处理消息 */
        ……
        if (ucEvent & SD_EVENT_RSP_IN) {                              /*  应答类型R1       */
            ……
            while(1) {
               if (!(readl(REG_SDH_CTL) & BIT_CTL_RI_EN)) {           /*  等待应答         */
                   ucCompleted =1;
                   break;
               }
 
               if (readl(REG_SDH_INTSTS) & BIT_INTSTS_RITO_IF) {      /*  传输超时         */
                   ……
                   break;
               }
            }
        }
 
        if (ucEvent & SD_EVENT_RSP2_IN) {                             /* 应答类型R2        */
            __WAIT_COMPLETE(BIT_CTL_R2_EN, ucCompleted);
        }
        ……
        if (ucCompleted) {
            __sdCompletedCommand(psdio, ucEvent);                     /* 通知事件处理完成  */
        }
    }
 
    return(LW_NULL);
}

当应答状态位被置位后,就可以调用如程序清单 3-6所示代码,读取出应答的内容。

程序清单 3-6应答产生后调用

static VOID __sdCompletedCommand(PSDIO_DAT psdio, UINT8 ucEvent)
{
    ……
    UINT32 uiIntSts = readl(REG_SDH_INTSTS);
 
    if (uiIntSts & BIT_INTSTS_RITO_IF) {
        writel(BIT_INTSTS_RITO_IF, REG_SDH_INTSTS);
        ……
    }
 
    if (psdio->SDIO_eCompleteWhat == COMPLETION_RSPFIN ||
        psdio->SDIO_eCompleteWhat == COMPLETION_XFERFINISH_RSPFIN) {
        if (ucEvent & SD_EVENT_RSP_IN) {
            psdio->SDIO_psdcmd->SDCMD_uiResp[0] = (readl(REG_SDH_RESP0) << 8) |
                                                  (readl(REG_SDH_RESP1) & 0xff);
            psdio->SDIO_psdcmd->SDCMD_uiResp[1] = 0;
            psdio->SDIO_psdcmd->SDCMD_uiResp[2] = 0;
            psdio->SDIO_psdcmd->SDCMD_uiResp[3] = 0;
        } else if (ucEvent & SD_EVENT_RSP2_IN) {
            pucPtr = (UINT8 *)REG_SDH_FB0;
            for (i = 0, j = 0; j < 5; i += 4, j++) {
                uiTmp[j] = (*(pucPtr + i)     << 24) |
                           (*(pucPtr + i + 1) << 16) |
                           (*(pucPtr + i + 2) <<  8) |
                           (*(pucPtr + i + 3));
            }
            
            for (i = 0; i < 4; i++) {
                psdio->SDIO_psdcmd->SDCMD_uiResp[i] = ((uiTmp[i]     & 0x00ffffff) <<  8) |
                                                      ((uiTmp[i + 1] & 0xff000000) >> 24);
            }
        }
    }
    ……
 
    psdio->SDIO_eCompleteWhat = COMPLETION_NONE;
    __mciRequestDone(psdio);
}

3.3.3 读取和写入数据

在数据读写之前需要配置DMA传输的块大小,如程序清单 3-7所示。

程序清单 3-7数据传输前准备

static INT __sendDataPrepare (PSDIO_DAT psdio)
{
    ……
    /*
     * 设置块传输的块大小
     */
    uiBlkSiz = psdio->SDIO_psddata->SDDAT_uiBlkSize;
    uiBlkLen = psdio->SDIO_psddata->SDDAT_uiBlkNum;
    writel(uiBlkSiz - 1, REG_SDH_BLEN);
 
    if ((uiBlkSiz > 512) || (uiBlkLen >= 256)) {
        printk("ERROR: don‘t support read/write 256 blocks in on CMD\n");
    } else {
        uiRegCtl  = readl(REG_SDH_CTL) & ~0x00ff0000;
        uiRegCtl |= (uiBlkLen << 16);
        writel(uiRegCtl, REG_SDH_CTL);
    }
    /*
     * 设置DMA传输基址
     */
    writel((UINT32)psdio->SDIO_puiBuf, REG_SDH_DMASA);
 
    return(ERROR_NONE);
}

4. 总结

本文章结合Nuc970说明了SylixOS中非标SD驱动移植的方法,标准SD驱动实现方法可能和本文介绍内容差异较大。

时间: 2024-11-03 21:54:25

SylixOS基于Nuc970平台的SD驱动移植的相关文章

基于FS4412平台的Linux系统移植

实验一 交叉工具链的安装 [实验目的] 了解交叉工具链的编译过程及其使用. 说明:在实验中命令行提示符 为"$"表示在主机上运行,"#"表示在目标板上运行 [实验环境] 1.  ubuntu 12.04发行版 2.  FS4412平台 [实验步骤] 1. 如果要自己编译工具链,从以下链接下载源码 crosstools-ng下载地址 http://ymorin.is-a-geek.org/download/crosstool-ng/ 同时对每一个版本都有相应的补丁我们

SylixOS的imx1050平台PWM捕获驱动

概述本文档是对IMXRT1050平台上的SylixOS PWM波的产生和捕获功能的详细分析.代码在IMXRT1050的板级支持包的"bsp_rt1050/SylixOs/driver/pwm/"目录下的pwm.c文件中,该文件会依赖于bsp_rt1050/SylixOs/driver/lib目录,这个目录是NXP官方提供的库文件.本文档描述的应用场景是这样的:PWM需要根据用户自己设置的参数,达到控制输出波形的频率和精确的周期个数,以达到控制3D打印机的目的. PWM波的输出IMXRT

基于335X平台的UBOOT中交换芯片驱动移植

基于335X平台的UBOOT中交换芯片驱动移植 一.软硬件平台资料 1.开发板:创龙AM3359核心板,网口采用RMII形式. 2.UBOOT版本:U-Boot-2016.05,采用FDT和DM. 3.交换芯片MARVELL的88E6321. 4.参考文章:本博客基于335X的UBOOT网口驱动分析. 二.移植主要步骤 1.准备工作: (1).必须熟悉U-Boot-2016.05中的网口驱动构架,熟悉其中各个网口设备结构体的意义,网口初始化流程.重点详细分析常规基于phydev的驱动初始化的过程

SylixOS音频驱动移植

1. 适用范围 本文档为实现Nuc970平台音频驱动的方法总结,以此提供一些SylixOS音频驱动移植方法的参考. 2. 原理概述 2.1 Codec编解码芯片 声音信号分为模拟信号和数字信号,Codec编解码芯片主要功能就是实现模拟信号与数字信号的互相转换. 本文调试的Codec型号为NAU8822L,其结构如图 2-1所示. 图 2-1 NAU8822L编解码芯片 其中主要使用到的是以下三个部分: Mixer  混音器设备,它的作用是将多个信号组合或者叠加在一起. 由输入混音器(input

SylixOS中SD驱动实现流程

1.概述 本文档以imx6实验平台为例,介绍SD设备驱动实现流程. 2.SylixOS中SD系统框架 SylixOS中SD协议栈(以下称作SD Stack)结构如图 2.1所示. 图 2.1 SD 协议栈结构 1)Host层:硬件控制器抽象层,SD控制器在不同的硬件平台上可能有不同的实现,因此需要实现具体的传输处理操作.所有的控制器驱动都向上(Core层)提供统一的操作接口.SD Stack已经提供了符合SD规范的标准控制器SDHCI驱动,在此情况下,控制器驱动的编写将更加简单.当然也可使用SP

基于fs210平台的dm9000原理及移植

dm9000的原理图如下: 网卡的移植工作很简单,首先是需要添加目标版的平台设备信息,就可以实现网卡的移植工作,平台设备信息如何添加很重要 添加平台信息的第一步是添加一个platform_device设备信息 首先在arch/arm/mach-s5pv210/mach-smdkv210.c 添加头文件: #include <linux/dm9000.h>              #include <linux/irq.h>              添加platform_devi

基于335X平台Linux交换芯片驱动开发

基于335X平台Linux交换芯片驱动开发   一.软硬件平台资料 1.开发板:创龙AM3359核心板,网口采用RMII形式. 2.Kernel版本:4.4.12,采用FDT 3.交换芯片MARVELL的88E6321. 二.移植准备工作 1.熟悉88E6321的datasheet及Functional_Specification_Rev.0.05 2.熟悉设备树相关理论和用法 3.熟悉Linux网络驱动MDIO.PHY部分的软件流程 三.DTS文件修改 本工程的DTS文件以am335x-ice

基于Zynq平台的EtherCAT主站方案实现

作者:陈秋苑 谢晓锋 陈海焕 广州虹科电子科技有限公司 摘 要:EtherCAT 是开放的实时以太网通讯协议,由德国倍福自动化有限公司研发.EtherCAT 具有高性能.低成本.容易使用等特点,目前在工业自动化领域有着广泛的应用.Zynq-7000 是赛灵思公司(Xilinx)推出的行业第一个全可编程 SoC 产品, 它将双核 ARM Cortex-A9 处理器,低功耗可编程逻辑以及常用的外设紧密集成在一起.ZedBoard 是基于 XC7Z020 器件的低成本开发板,此板可以运行基于 Linu

基于Android平台的i-jetty网站智能农业监控系统

基于android平台i-jetty网站的智能农业监控系统 摘要:传统的监控系统,一般是基于PC的有线通信传输,其有很多不足之处,如功耗较高.布线成本高.难度大,适应性差,可扩展性不强,增加新的通信线路需要再次布线施工,而且维护起来也比较麻烦,一旦线路出问题,需要繁琐的检查.而嵌入式Web监控系统是基于物联网技术,其无线通信技术具有成本低廉.适应性强.扩展性强.信息安全.使用维护简单等优点. 智能农业中,种植大棚是通过大棚内安装温湿度以及光照传感器,来对农作物的环境参数进行实时采集,由Web监控