下面的文章主要是转载的,先记录下自己的经验。
MMC/SD驱动有两种模式:FIFO和DMA。在代码中两种方式都予以了实现,在make menuconfig时候,可以选择是使用fifo方式还是DMA方式。其中FIFO方式就是 向FIFO寄存器写入和读取。FIFO模式刚开始不太明白,FIFO就是起到缓存的作用,可以提高响应速度。具体在代码的操作中,写入时就是将要发送的数据用while循环依次写入FIFO寄存器的地址(只是一个寄存器),硬件上的实现使得我们虽然写入时都是写入了FIFO寄存器(它是FIFO的起始),但是多个数据在FIFO中依次排列的,这里应该用到了移位器和锁存器。 读取时也是相同的,只读FIFO寄存器,就可以将FIFO中的寄存器都读出来。
另外,FIFO是配合中断来使用的,中断可以进行配置,当FIFO empty、half full、full的时候都可以产生中断。在有些硬件上面,可以直接设置FIFO的trigger level,就是指收到多少字节之后产生中断。
此外,MMC对应的FIFO是64bytes的。接收和发送各有一个FIFO,可以分开配置。另外,串口也有FIFO。
最后,代码中实现了的DMA方式不能正常工作,目前我还不知道怎么修改。DMA 发送发送data时候,会打印错误:buffer load timeout
一、开发环境
主 机:VMWare--Fedora 9
开发板:Mini2440--64MB Nand, Kernel:2.6.30.4
编译器:arm-linux-gcc-4.3.2
上接:S3C2440上MMC/SD卡驱动实例开发讲解(一)
6. s3cmci_ops SDI主机控制器操作接口函数功能分析:
static struct mmc_host_ops s3cmci_ops =
{
.request = s3cmci_request,//实现host的请求处理(即:命令和数据的发送和接收)
.set_ios = s3cmci_set_ios,//通过核心层传递过来的ios,配置host寄存器(使能时钟、总线带宽等)
.get_ro = s3cmci_get_ro,//通过读取GPIO端口来判断卡是否写有保护
.get_cd = s3cmci_card_present,//通过读取GPIO端口来判断卡是否存在
};
mmc_host_ops结构体定义了对host主机进行操作的各种方法,其定义在Core核心层的host.h中,也就是Core核心层对Host主机层提供的接口函数。这里各种方法的函数原型如下:
void (*request)(struct mmc_host *host, struct mmc_request *req);
void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios);
int (*get_ro)(struct mmc_host *host);
int (*get_cd)(struct mmc_host *host);
从各函数原型上看,他们都将mmc_host结构体作为参数,所以我在刚开始的时候就说过mmc_host结构体是MMC/SD卡驱动中比较重要的数据结构。 可以这样说,他是Core层与Host层进行数据交换的载体。那么,这些接口函数何时会被调用呢?答案可以在Core层的core.c和sd.c中找到,我们可以看到如下部分代码:
static void mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
......
host->ops->request(host, mrq);//导致s3cmci_request被调用
}
static inline void mmc_set_ios(struct mmc_host *host)
{
......
host->ops->set_ios(host, ios);//导致s3cmci_set_ios被调用
}
void mmc_rescan(struct work_struct *work)
{
......//导致s3cmci_card_present被调用
if (host->ops->get_cd && host->ops->get_cd(host) == 0)
goto out;
......
}
static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
struct mmc_card *oldcard)
{
......
/* Check if read-only switch is active.*/
if (!oldcard)
{ //导致s3cmci_get_ro被调用
if (!host->ops->get_ro || host->ops->get_ro(host) < 0)
{
printk(KERN_WARNING "%s: host does not "
"support reading read-only "
"switch. assuming write-enable.n",
mmc_hostname(host));
}
else
{
if (host->ops->get_ro(host) > 0)
mmc_card_set_readonly(card);
}
}
......
}
好了,我们开始分析每个接口函数的具体实现吧,从简单的开始吧。 判断卡是否存在,如下代码:
static int s3cmci_card_present(struct mmc_host *mmc)
{
//从mmc_host的对象中获取出s3cmci_host结构体的数据,在s3cmci_probe函数中进行关联的
struct s3cmci_host *host = mmc_priv(mmc);
struct s3c24xx_mci_pdata *pdata = host->pdata;
int ret;
//判断有无设置卡检测引脚端口,引脚在s3cmci_probe函数中已设置
if (pdata->gpio_detect == 0)
return -ENOSYS;
//从设置的卡检测引脚中读出当前的电平值,来判断卡是插入存在的还是被拔出不存在的
ret = s3c2410_gpio_getpin(pdata->gpio_detect) ? 0 : 1;
return ret ^ pdata->detect_invert;
}
获取卡是否写有保护,其实实现跟卡检查类似,代码如下:
static int s3cmci_get_ro(struct mmc_host *mmc)
{
//从mmc_host的对象中获取出s3cmci_host结构体的数据,在s3cmci_probe函数中进行关联的
struct s3cmci_host *host = mmc_priv(mmc);
struct s3c24xx_mci_pdata *pdata = host->pdata;
int ret;
//判断有无设置卡写保护引脚端口,引脚在s3cmci_probe函数中已设置
if (pdata->gpio_wprotect == 0)
return 0;
//从设置的卡写保护引脚中读出当前的电平值,来判断卡是否写有保护
ret = s3c2410_gpio_getpin(pdata->gpio_wprotect);
if (pdata->wprotect_invert)
ret = !ret;
return ret;
}
配置host寄存器的时钟和总线宽度,代码如下:
static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
//从mmc_host的对象中获取出s3cmci_host结构体的数据,在s3cmci_probe函数中进行关联的
struct s3cmci_host *host = mmc_priv(mmc);
u32 mci_con;
//读取SDI控制寄存器的值
mci_con = readl(host->base + S3C2410_SDICON);
//ios结构体参数从Core层传递过来,根据不同的电源状态来配置SDI各寄存器
switch (ios->power_mode)
{
case MMC_POWER_ON:
case MMC_POWER_UP:
//根据开发板引脚连接情况配置SDI控制器的各信号线,包括:时钟线、命令线和四条数据线
s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_SDCLK);
s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2410_GPE6_SDCMD);
s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2410_GPE7_SDDAT0);
s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_SDDAT1);
s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2);
s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3);
if (host->pdata->set_power)
host->pdata->set_power(ios->power_mode, ios->vdd);
break;
case MMC_POWER_OFF:
default:
//如果电源状态为关闭或者默认情况下,关闭SDI的时钟信号
s3c2410_gpio_setpin(S3C2410_GPE5, 0);
s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_OUTP);
//根据数据手册的SDICON寄存器位的介绍,此处是将整个sdmmc时钟复位
mci_con |= S3C2440_SDICON_SDRESET;
if (host->pdata->set_power)
host->pdata->set_power(ios->power_mode, ios->vdd);
break;
}
//设置SDI波特率预定标器寄存器以确定时钟,看其定义部分
s3cmci_set_clk(host, ios);
//根据SDI当前的时钟频率来设置寄存器的使能时钟位
if (ios->clock)
mci_con |= S3C2410_SDICON_CLOCKTYPE;
else
mci_con &= ~S3C2410_SDICON_CLOCKTYPE;
//将计算好的值写回SDI控制寄存器
writel(mci_con, host->base + S3C2410_SDICON);
//下面只是一些调试信息,可以不要
if ((ios->power_mode == MMC_POWER_ON) || (ios->power_mode == MMC_POWER_UP))
{
dbg(host, dbg_conf, "running at %lukHz (requested: %ukHz).n",
host->real_rate/1000, ios->clock/1000);
}
else
{
dbg(host, dbg_conf, "powered down.n");
}
//设置总线宽度
host->bus_width = ios->bus_width;
}
//设置SDI波特率预定标器寄存器以确定时钟
static void s3cmci_set_clk(struct s3cmci_host *host, struct mmc_ios *ios)
{
u32 mci_psc;
//根据SDI工作时钟频率范围来确定时钟预分频器值
for (mci_psc = 0; mci_psc < 255; mci_psc++)
{
host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1));
if (host->real_rate <= ios->clock)
break;
}
//根据数据手册描述,SDI波特率预定标器寄存器只有8个位,所以最大值为255
if (mci_psc > 255)
mci_psc = 255;
host->prescaler = mci_psc;//确定的预分频器值
//将预分频器值写于SDI波特率预定标器寄存器中
writel(host->prescaler, host->base + S3C2410_SDIPRE);
if (ios->clock == 0)
host->real_rate = 0;
}
MMC/SD请求处理,这是Host驱动中比较重要的一部分。请求处理的整个流程请参考(一)中的流程图,他很好的描述了一个请求是怎样从Host层发出,通过Core层提交到Card层被块设备处理的。下面看代码:
static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
//从mmc_host的对象中获取出s3cmci_host结构体的数据,在s3cmci_probe函数中进行关联的
struct s3cmci_host *host = mmc_priv(mmc);
//s3cmci_host结构体定义的status主要是记录请求过程所处的阶段及状态,方便调试时使用
host->status = "mmc request";
//请求处理主要包括MMC/SD命令和数据处理,所以定义cmd_is_stop来区分是哪种请求
host->cmd_is_stop = 0;
//将Core层的mmc_request对象保存到Host层中以备使用
host->mrq = mrq;
//在开始发出一个请求前先要检测一下卡是否还存在,否则提交到了块设备层而没有请求处理的对象发生错误
if (s3cmci_card_present(mmc) == 0)
{
dbg(host, dbg_err, "%s: no medium presentn", __func__);
host->mrq->cmd->error = -ENOMEDIUM;
mmc_request_done(mmc, mrq);//如果卡不存在则马上结束这次请求
}
else
{
s3cmci_send_request(mmc);//如果卡还存在则发出请求
}
}
//发送请求
static void s3cmci_send_request(struct mmc_host *mmc)
{
//从mmc_host的对象中获取出s3cmci_host结构体的数据,在s3cmci_probe函数中进行关联的
struct s3cmci_host *host = mmc_priv(mmc);
//取出在s3cmci_request函数中保存的mmc_request对象以使用
struct mmc_request *mrq = host->mrq;
上一篇:mini2440上dm9000驱动分析(一)
下一篇:【ARM学习笔记】ARM中断嵌套模式理解
推荐阅读
史海拾趣
德力西非常重视品牌建设,通过全方位的品牌建设及宣传,不断提高品牌知名度和美誉度。同时,公司也加快了国际化步伐。2007年,德力西低压电器业务迈向企业国际化的新阶段,在全国范围内进行资本大重组、产品大联合、市场大拓展和技术大提高。这些努力使德力西在国际市场上也取得了一席之地。
在半导体测试领域取得成功后,东沅并没有停止前进的步伐。他们开始关注新兴的电子行业趋势,并决定拓展新的业务领域。例如,他们开始研发和生产与电源管理相关的Power Mosfet产品,这些产品具有高效能、高可靠度和价格优势等特点,迅速在市场上占据了一席之地。此外,东沅还积极拓展射频和模拟芯片领域的研发和销售,为客户提供更全面的解决方案。
为了更好地服务全球客户,美高测积极寻求与国际知名企业的合作。通过与半导体制造巨头台积电、英特尔等公司的合作,美高测不仅获得了先进的制造技术和市场洞察能力,还成功将其高压测试产品打入了这些巨头的供应链体系。同时,公司还与多个国家的科研机构建立了长期合作关系,共同开展前沿技术的研发和应用,进一步提升了公司的国际影响力。
近年来,Fabrimex AG在品质管理和环境保护方面取得了显著的成绩。2016年,包括Fabrimex在内的整个WERAP集团均通过了ISO 9001:2015和ISO 14001:2015认证。这些认证不仅证明了公司在品质管理和环境保护方面的实力,还为公司赢得了更多客户的信任和认可。此外,公司还积极参与行业标准的制定和修订工作,推动行业的技术进步和发展。
这些故事只是Fabrimex AG在电子行业发展历程中的一部分,但它们足以展示公司在技术创新、市场拓展和品质提升方面的不懈努力。通过这些努力,Fabrimex AG在电子行业中逐渐崭露头角,成为了一家备受瞩目的企业。
随着业务的不断发展,Crouzet公司于1989年和1992年分别成功兼并了法国Syreles公司及墨西哥Gordos公司。这一系列的兼并活动不仅增强了公司的实力,还进一步扩大了公司的业务范围。此后,Crouzet开始在全球范围内布局,陆续在美国、德国、英国、荷兰、比利时、瑞士、瑞典等国设立分公司,形成了一个覆盖全球的销售网络。
随着市场竞争的加剧,Crouzet开始注重提供定制化的解决方案,以满足客户不断变化的需求。公司凭借强大的研发能力和丰富的行业经验,能够根据客户的具体需求,提供量身定制的产品和服务。这种以客户需求为导向的经营理念,使得Crouzet在市场上赢得了更多的客户和合作伙伴。
本人刚接触wince,现在要用EVC做应用层的开发,现在我的目的是要把一些图片放到rom里,然后再把这些图片解码出来,我有两个问题要问大家: 1.这些图片怎么放到rom里? 2.我用模拟器开发时,把这些图片放到哪个目录下才能访问的到这些图片?也就是 ...… 查看全部问答∨ |
|
大家好,我现在用S3C2440A,数据手册上写了:SD Host interface version 1.0 & MMC Protocol version 2.11 compatible,这个说明它支持4G的SD卡吗?要怎么样才能使WINCE支持这4G的SD卡呀?有哪位兄弟成功过呀? !… 查看全部问答∨ |
大家好,我在xp下安装WINCE,当进行到输入product key时,输入正确的key,总是提示不正确,要求重新输入, key和安装文件都是正确的,这种问题怎么解决啊? … 查看全部问答∨ |
学了一年的VC老用不上,于是想在公司的设备基础上做个工控啥的。 思想是这样,利用串口通讯实现电脑与单片机相连,并且可以在电脑上实时监控和操作。 我想把这些工作分成通讯、数据处理、参数设定、实时监控等模块,然后编一个总界面来调这些模块 ...… 查看全部问答∨ |
从外形上看相信你已经对它十分感兴趣了,一种设计十分新颖的灯具——LED 菊花台灯。它的设计足够让人惊叹了,不点亮时形如洁白无瑕的菊花环绕的菊花台,是一个完美的家居装饰。当夜幕降临,华灯初上,菊花台灯一触即开,宛如千瓣菊花于黑暗中同时怒放,光 ...… 查看全部问答∨ |
在StellarisWare的软件包中都附带了aes的加解密例程,本人很好奇也研究了一下。程序中只对一个字符串进行了加解密并显示。但是不是很清除AES在嵌入式中有何具体的应用?… 查看全部问答∨ |
STM32库函数配置stm32 固件库V3.0以上的版本,main等源文件中不再直接包含stm32f10x_conf.h,而是stm32f10x.h,stm32f10x.h则定义了启动设置,以及所有寄存器宏定义,此文件中需要注意的有: 使用V3.0以上版本固件库的方法如下: 1.选择device( ...… 查看全部问答∨ |
请教CAN控制器是否有带有DMA接口的? 也就是说CAN数据帧收发使用DMA模式,burst模式/single模式~~~ 各位有见过这样的产品么?… 查看全部问答∨ |