搜索
bottom↓
楼主: xi_liang

百为STM32开发板教程——从LED流水灯到UCGUI手机界面

  [复制链接]

出0入8汤圆

发表于 2013-9-13 16:49:46 | 显示全部楼层
收藏了先啊,谢谢

出0入0汤圆

发表于 2013-9-13 17:22:39 | 显示全部楼层
楼主很给力

出0入12汤圆

发表于 2013-9-14 05:07:07 | 显示全部楼层
学习记号备用

出0入0汤圆

 楼主| 发表于 2013-9-16 05:14:22 | 显示全部楼层
更新程序到网盘
百为STM32_UCOSII邮箱.rar

出0入0汤圆

发表于 2013-9-16 11:53:38 | 显示全部楼层
视频讲的不好。很乱。鼠标,画面不停晃动。明显没有站在学生角度去思考。

出0入0汤圆

发表于 2013-9-16 19:47:12 | 显示全部楼层
路过,标记一下

出0入0汤圆

 楼主| 发表于 2013-9-17 10:12:26 | 显示全部楼层
zzh4933 发表于 2013-9-16 11:53
视频讲的不好。很乱。鼠标,画面不停晃动。明显没有站在学生角度去思考。 ...

初次录视频,经验不足,见晾。我原本的想法是想节省大家时间,所以很多人会觉得讲得快,反复看几次就好了。
另外要说的是,我平均每录一节视频,都准备两天时间的。

出0入0汤圆

发表于 2013-9-18 09:34:54 | 显示全部楼层
最近正在学smt32,收藏了,感觉很有用

出0入0汤圆

 楼主| 发表于 2013-9-22 08:10:36 | 显示全部楼层
百为STM32开发板UCOSII移植步骤


一、准备源码
本例在百为 3.5固件库模版.rar和在ucos官网下载的Micrium_uC-Eval-STM32F107_uCOS-II.zip的基础上进行移植

这里采用Micrium_uC-Eval-STM32F107_uCOS-II.zip,是因为ucos官网上最新的源码ucos2.92在cortex-m3上的移植只提供了这个。

ucos移植到其他CPU只涉及到以下几个文件:
os_cpu.h
os_cpu_a.asm
os_cpu_c.c

我们百为开发板上用的CPU是STM32F103ZET6,和STM32F107上差不多的,所以这三个文件可以直接拿过来用

二、建立工程,并建立组,把相关文件添加到工程的组里,移植、编译工程
1、把Micrium_uC-Eval-STM32F107_uCOS-II.zip里面
Micrium_uC-Eval-STM32F107_uCOS-II\Micrium\Software\下面的uCOS-II目录拷贝到工程目录下,如下图示:



2、添加uCOS-II/Ports,uCOS-II/Source两个组,如下图示:




3、把Ports和Source目录下的文件添加到工程里,如下图示:



4、并把相关头文件路径加入到工程里,如下图示:



5、编译工程,发现还缺少os_cfg.h,includes.h等文件,在目录Micrium_uC-Eval-STM32F107_uCOS-II\Micrium\Software\uC-CPU\ARM-Cortex-M3\RealView下面找到这两个文件,
拷贝到工程目录下面。这里我新建了一个Cfg目录,存放这两个文件。并把这两个头文件所在路径加入到工程。
注释掉uCOS_II.H里的app_cfg.h
//#include <app_cfg.h>

编译出现错误
..\..\uCOS-II\Source\os_tmr.c(899): error:  #20: identifier "OS_TASK_TMR_PRIO" is undefined

这个OS_TASK_TMR_PRIO在app_cfg.h里定义的,我们没加进去,所以报错。把os_cfg.h里的宏开关OS_TMR_EN关掉就可以了:
#define OS_TMR_EN                 1    /* Enable (1) or Disable (0) code generation for TIMERS         */
改为
#define OS_TMR_EN                 0    /* Enable (1) or Disable (0) code generation for TIMERS         */

继续编译出现下面错误:


表示很多xxxHook和OSDebugInit函数没有定义,同样我们把OS_APP_HOOKS_EN和OS_DEBUG_EN宏开关关掉。
#define OS_APP_HOOKS_EN           0    /* Application-defined hooks are called from the uC/OS-II hooks */
#define OS_DEBUG_EN               0    /* Enable(1) debug variables  

然后再编译就可以通过了。

6、给系统增加时钟节拍,这里由STM32的systick定时器提供。
在os_cpu_c.c里面已经给我们提供好了接口,但是它对SysTick的初始化有点烦琐,这里我们用库函数只要一行代码就能实现。

初始化:
SysTick_Config(SystemCoreClock / 1000); //SysTick 1毫秒产生一次中断

systick中断服务函数:
void SysTick_Handler(void)
{
    OS_CPU_SR  cpu_sr;

    OS_ENTER_CRITICAL();                         /* Tell uC/OS-II that we are starting an ISR          */
    OSIntNesting++;
    OS_EXIT_CRITICAL();
    OSTimeTick();                                /* Call uC/OS-II's OSTimeTick()                       */
    OSIntExit();                                 /* Tell uC/OS-II that we are leaving the ISR          */
}

另外,还要在启动代码startup_stm32f10x_hd.s里把PendSV_Handler替换成OS_CPU_PendSVHandler,提供系统调用接口。不然系统启动时会跑到OSStartHang,然后死循环在那里。
这步比较容易忽略,要注意。


至此,UCOSII移植已经完成了,下面开始编写UCOSII应用。

三、编写简单的UCOSII应用程序
1、UCOSII应用程序的组成
(1)必须先调用OSInit()对UCOSII进行初始化。
(2)调用OSTaskCreate或OSTaskCreateExt创建任务。(我们需要增加或修改的只有这部分)
(3)调用OSStart()启动任务调度。

我们来看下OSTaskCreate 函数的原型:
INT8U         OSTaskCreate            (void           (*task)(void *p_arg),
                                       void            *p_arg,
                                       OS_STK          *ptos,
                                       INT8U            prio);
其中:
task是指向任务函数的指针
p_arg传递给任务函数的参数的指针,一般设为0
ptos是分配给任务的堆栈的栈顶指针
prio是任务的优先级

2、编写含有三个简单任务的UCOS应用程序

#define Task1_PRIO       15
#define Task2_PRIO       16
#define Task3_PRIO       17
#define TASK1_STACK_SIZE 64
#define TASK2_STACK_SIZE 64
#define TASK3_STACK_SIZE 64
static OS_STK Task1_Stack[TASK1_STACK_SIZE];
static OS_STK Task2_Stack[TASK2_STACK_SIZE];
static OS_STK Task3_Stack[TASK3_STACK_SIZE];

void Task1_Func(void *p_arg)  //任务1代码
{
while(1)
{
  printf("task1\n\r");
  STM_EVAL_LEDOn(LED1);  //点亮LED1
  STM_EVAL_LEDOff(LED2);
  STM_EVAL_LEDOff(LED3);
  OSTimeDly(200);  //延时200ms
}
}

void Task2_Func(void *p_arg)  //任务2代码
{
while(1)
{
  printf("**********task2\n\r");
  STM_EVAL_LEDOn(LED2);  //点亮LED2
  STM_EVAL_LEDOff(LED1);
  STM_EVAL_LEDOff(LED3);
  OSTimeDly(400);  //延时400ms
}
}
void Task3_Func(void *p_arg)  //任务3代码
{
while(1)
{
  printf("********************task3\n\r");
  STM_EVAL_LEDOn(LED3);  //点亮LED3
  STM_EVAL_LEDOff(LED1);
  STM_EVAL_LEDOff(LED2);
  OSTimeDly(800);   //延时800ms
}
}

void App_TaskStart(void)
{
OSInit();
SysTick_Config(SystemCoreClock / OS_TICKS_PER_SEC);  //初始化SysTick系统时钟
OSTaskCreate(Task1_Func, (void *)0, (OS_STK *)&Task1_Stack[TASK1_STACK_SIZE-1],  Task1_PRIO);  //创建任务1
OSTaskCreate(Task2_Func, (void *)0, (OS_STK *)&Task2_Stack[TASK2_STACK_SIZE-1],  Task2_PRIO);  //创建任务2
OSTaskCreate(Task3_Func, (void *)0, (OS_STK *)&Task3_Stack[TASK3_STACK_SIZE-1],  Task3_PRIO);  //创建任务3
OSStart();
}

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-9-22 09:05:34 | 显示全部楼层
顶楼主,无私。。

出0入0汤圆

 楼主| 发表于 2013-9-23 14:48:09 | 显示全部楼层
顶起,除了官方例程,教程和例程全部都是原创。

出0入0汤圆

发表于 2013-9-23 15:50:07 | 显示全部楼层
感谢楼主的分享,辛苦了。

出0入0汤圆

 楼主| 发表于 2013-9-24 10:20:15 | 显示全部楼层
xckhmf 发表于 2013-9-23 15:50
感谢楼主的分享,辛苦了。

多谢帮顶,多谢支持

出0入0汤圆

 楼主| 发表于 2013-9-29 02:12:21 | 显示全部楼层
SD卡教程和USB教程同时进行中,每天顶一贴

出0入0汤圆

发表于 2013-9-29 08:09:28 | 显示全部楼层
仿的EVAL官方板子啥时候再搞个特价啊, 期待啊。

出0入0汤圆

发表于 2013-9-29 08:24:42 | 显示全部楼层
马克一记,可能会用得着~~

出0入0汤圆

发表于 2013-9-29 08:31:02 | 显示全部楼层
收藏。
买了块百为精简。

出0入0汤圆

 楼主| 发表于 2013-9-29 09:41:19 | 显示全部楼层





百为3.2寸屏电路图:


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-9-29 10:11:24 | 显示全部楼层
好资源,果断顶起来

出0入0汤圆

 楼主| 发表于 2013-9-29 14:39:48 | 显示全部楼层
liujingbin 发表于 2013-9-29 10:11
好资源,果断顶起来

谢谢帮顶

出100入101汤圆

发表于 2013-9-29 14:47:06 | 显示全部楼层
xi_liang 发表于 2013-9-29 14:39
谢谢帮顶

大神,你的手机gui demo还没好么?

出0入0汤圆

 楼主| 发表于 2013-9-30 11:03:36 | 显示全部楼层
fengyunyu 发表于 2013-9-29 14:47
大神,你的手机gui demo还没好么?

还没,现在在弄SD教程

出0入0汤圆

 楼主| 发表于 2013-10-9 02:46:43 | 显示全部楼层
STM32学习任务十七——ADC

实验目的:采集百为STM32开发板上的可调电阻分压AD值,把采集到的AD值显示到LCD上

主要内容:
一、STM32的ADC简介
二、STM32的ADC的使用步骤
三、编程实现把采集到的AD值显示到LCD上(非DMA模式)

一、STM32的ADC简介
STM32的ADC是12位的,是逐次逼近型模数转换器。
其中STM32F103ZET6有3个12位ADC,共有23个通道。可测量21个外部通道和2个内部信号源。



图中的引脚名称标注中出现的ADC12_INx(x表示4~9或14~15之间的整数),表示这个引脚可以是ADC1_INx或ADC2_INx。例如:ADC12_IN9表示这个引脚可以配置为ADC1_IN9,也可以配置为ADC2_IN9。同样,图中的引脚名称标注中出现的ADC123_INx(x表示0~3或10~13之间的整数),表示这个引脚可以是ADC1_INx或ADC2_INx或ADC3_INx。

各通道的A/D转换可以是单次、连续、扫描或间断模式的转换。
ADC转换的结果可以以左对齐或右对齐的方式存储在16位数据寄存器中。
ADC的输入时钟是由PCLK2经分频产生的,不得超过14MHz。

ADC结构框图(部分通道未画出):



1.ADC3的规则转换和注入转换触发与ADC1和ADC2的不同

这里要搞清下面几个概念:
(1)规则通道和注入通道
STM32的每个ADC模块通过内部的模拟多路开关,可以切换到不同的输入通道并进行转换。STM32特别地加入了多种成组转换的模式,可以由程序设置好之后,对多个模拟通道自动地进行逐个地采样转换。

有2种划分转换组的方式:规则通道组和注入通道组。通常规则通道组中可以安排最多16个通道,而注入通道组可以安排最多4个通道。
在执行规则通道组扫描转换时,如有例外处理则可启用注入通道组的转换。一个不太恰当的比喻是:规则通道组的转换好比是程序的正常执行,而注入通道组的转换则好比是程序正常执行之外的一个中断处理程序。

再举一个不一定使用的例子:
假如你在家里的院子内放了5个温度探头,室内放了3个温度探头;你需要时刻监视室外温度即可,但偶尔你想看看室内的温度;因此你可以使用规则通道组循环扫描室外的5个探头并显示AD转换结果,当你想看室内温度时,通过一个按钮启动注入转换组(3个室内探头)并暂时显示室内温度,当你放开这个按钮后,系统又会回到规则通道组继续检测室外温度。

从系统设计上,测量并显示室内温度的过程中断了测量并显示室外温度的过程,但程序设计上可以在初始化阶段分别设置好不同的转换组,系统运行中不必再变更循环转换的配置,从而达到两个任务互不干扰和快速切换的结果。可以设想一下,如果没有规则组和注入组的划分,当你按下按钮后,需要从新配置AD循环扫描的通道,然后在施放按钮后需再次配置AD循环扫描的通道。
——————————————
上面的例子因为速度较慢,不能完全体现这样区分(规则组和注入组)的好处,但在工业应用领域中有很多检测和监视探头需要较快地处理,这样对AD转换的分组将简化事件处理的程序并提高事件处理的速度。

(2)单次转换和连续转换,扫描转换和非扫描转换 (扫描转换其实可以理解为循环转换)
实际应用中可以有4种组合:
单次转换+非扫描转换:转换完一个通道即停止
单次转换+扫描转换:   转换完一个通道后,重复转换
连续转换+非扫描转换:按指定的顺序依次转换每个通道,转换完后停止
连续转换+扫描转换:   按指定的顺序依次转换每个通道,转换完后从头开始转换。


二、STM32的ADC的使用步骤

1、开启ADC时钟和GPIO时钟
  /* 使能 ADC1 和 GPIOC 时钟 */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOC, ENABLE);

2、配置PC.04(ADC通道14)
void GPIO_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  /* Configure PC.04 (ADC Channel14) as analog input -------------------------*/
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  GPIO_Init(GPIOC, &GPIO_InitStructure);
}

3、配置ADC1
  /* ADC1 configuration ------------------------------------------------------*/
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;  //独立模式
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;  //扫描转换关闭
  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;  //连续转换模式
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;  //不用外部事件启动转换
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据对齐为右对齐
  ADC_InitStructure.ADC_NbrOfChannel = 1;  //规则通道序列长度为1
  ADC_Init(ADC1, &ADC_InitStructure);

4、配置ADC采样时间,转换序列
  /* ADC1 regular channels configuration */
  ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 1, ADC_SampleTime_28Cycles5);  //设置ADC采样时间寄存器1(ADC_SMPR1),设置ADC规则序列寄存器3(ADC_SQR3)

5、使能ADC中断(非DMA方式)
    /* Enable ADC1 EOC interupt */
  ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);

6、使能ADC
  /* Enable ADC1 */
  ADC_Cmd(ADC1, ENABLE);

7、校准ADC
  /* Enable ADC1 reset calibaration register */   
  ADC_ResetCalibration(ADC1);
  
  /* Check the end of ADC1 reset calibration register */
  while(ADC_GetResetCalibrationStatus(ADC1));
  /* Start ADC1 calibaration */
  ADC_StartCalibration(ADC1);
  
  /* Check the end of ADC1 calibration */
  while(ADC_GetCalibrationStatus(ADC1));

8、开始ADC转换
  /* Start ADC1 Software Conversion */
  ADC_SoftwareStartConvCmd(ADC1, ENABLE);

9、中断处理
void ADC1_2_IRQHandler(void)
{
  
  /* Clear ADC1 EOC pending interrupt bit */
  ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
  
  /* Get converted value of Channel14 converted by ADC1 */
  ADCConvertedValue = ADC_GetConversionValue(ADC1);
  
}

三、编程实现把采集到的AD值显示到LCD上(非DMA模式)

/* 初始化LCD */
STM3210E_LCD_Init();
   /* Clear the LCD */
  LCD_Clear(LCD_COLOR_WHITE);
  /* Set the LCD Back Color */
  LCD_SetBackColor(LCD_COLOR_BLUE);
  /* Set the LCD Text Color */
  LCD_SetTextColor(LCD_COLOR_WHITE);

/* 配置SysTick定时器为1ms中断 */
if (SysTick_Config(SystemCoreClock / 1000))
  {
    /* Capture error */
    while (1);
  }
  while (1)
  {
  Delay(1000);
  sprintf(buffer,"   adc value = %d   ",ADCConvertedValue);  //AD转换值存放在全局变量ADCConvertedValue 中,把它格式化转换为字符串显示到LCD上
  LCD_DisplayStringLine(LCD_LINE_6, buffer);
  }

程序已更新到网盘
百为STM32_ADC液晶显示.rar

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-10-9 11:22:22 | 显示全部楼层
学习

出0入0汤圆

发表于 2013-10-9 11:22:48 | 显示全部楼层
学习

出0入0汤圆

发表于 2013-10-9 11:33:39 | 显示全部楼层
支持开源!

出0入0汤圆

 楼主| 发表于 2013-10-9 14:34:53 | 显示全部楼层
STM32学习任务十八——SDIO

一、SD卡协议
1、SD卡系统的总线拓扑



SD总线包括以下信号:
CLK:  主机对SD卡发出的时钟信号
CMD:  命令/响应信号
DAT0 - DAT3:  数据信号
VDD, VSS1, VSS2:  电源,地信号

上电初始化时,主机发送命令检测卡,并分配地址给每个卡。这是时候的数据是单独发送给每个卡的。
初始化后,命令是同时发送到所有卡的,在每个命令里包含了卡的地址信息。

SD卡总线允许动态配置数据线的宽度,上电初始化时,SD默认采用DAT0传输数据。
初始化完成后,SD总线可以配置数据线的宽度。


2、SD总线协议
SD总线的通信是基于命令和数据位流的,开始于启动位,结束于停止位。
命令(Command):命令是在CMD线上串行传送的。
数据(Data):数据是通过DAT线传送。
响应(Response):响应是卡对从主机接收到的命令进行回应。响应是在CMD线上串行传送的。




卡寻址是通过一个会话地址来实现的,这个会话地址在初始化阶段分配给卡的。
命令、响应和数据块的结构如上图所示。
SD总线上的通信是命令/响应方式(参考上图)。
这种总线通信是直接在命令和响应里传输信息的,另外,有些操作带有数据。

传送到SD或从SD卡读出的数据是以块(block)的形式进行的。数据块是否发送成功是由CRC位决定的。
可以进行单块操作或多块操作。要注意的是,对于快速写入操作来说多块操作是更适合的。
当在CMD线上收到一个STOP命令后,多块操作将被终止。
数据传输可以由主机配置为使用单根数据线或多根数据线(只要卡支持)。

单/多块读操作:




单/多块写操作:
写操作在DATA0线使用一个简单的BUSY信号表明写的状态,而不管数据线是单根数据线或多根数据线。如下图示:




命令(Command)格式:
每个命令开始于0,结束于1。总长度是48位。每个命令有CRC校验,所以能检测到传输错误,并有可能会重发。




响应(Response)格式:
响应根据内容有四种不同格式。长度是48位或136位。这里的CRC检验是16为CCITT多项式。




在CMD线上,MSB位先传输,LSB位后传输。
当使用宽总线时,数据是一次传输4位的。起始位、停止位和CRC位,是单独在每根数据线上传输的。
CRC位是在每根数据线上独立计算和校验的。
CRC响应和BUSY忙指示信号是由卡在DAT0线上发送给主机的(此时DAT1~DAT3忽略)。

数据包(Data)格式:




3、SD管脚和寄存器
SD卡形状和接口:




SD卡管脚分配:



数据线(DAT1~DAT3)上电时只作为输入用,在设置了宽总线模式后作为数据线用。

每张卡有一组信息寄存器:




主机可以通过对卡重新上电来使其复位。每张卡有它自己的上电复位检测电路,使它上电后可以处于一个预先定义的状态。而不需要外接的复位信号。
卡也可以被GO_IDLE命令复位。




SD卡协议被设计为MMC卡协议的超集。主要增加的功能是宽总线模式和内容保护功能。
很容易设计主机系统来支持这两种类型的卡。这样做的目的是使应用程序设计者能够利用现有的基于MMC卡的应用程序,
除非系统离不开快速数据传输(宽总线)和卡内容安全。

SD卡和MMC卡的区别:




除了增加的命令,SD和MMC之间唯一不同的是总线拓扑和初始化协议。
所有MMC卡是统一连接到相同的总线上,是使用开漏输出的同步传输进行被识别的。
每张SD卡和主机是单独的一对一的连接,是串行地依次被识别的。

SD卡协议中的初始化程序主要是成功识别目前连接的卡是MMC卡还是SD卡。
检测到卡后,主机执行初始化程序,结束时返回识别的已知类型的卡。

一旦卡被初始化后,应用程序可以通过各个配置寄存器确定卡的功能,并决定是否使用它。

SD卡的外型尺寸比MMC卡厚(2.1mm VS 1.4mm)。但这样定义后,MMC卡可以很容易插到SD卡槽里去。

SD定义了三种不同的卡检测机制。通过WP开关的机械插入,通过DAT3数据线的上拉电阻的电气插入,或周期性地尝试初始化卡。
由于这几种方式在MMC卡不一定相同,所以建议不要依赖于最优先的卡检测方式。
主机应实现一种查询机制或允许请求卡的信息。

SD卡和MMC卡命令比较表:





4、SD卡功能描述
4.1、概述
卡和主机之间的所有通信是由主机控制的。主机发送的命令包括两种类型:广播命令,指定地址命令。

广播命令:
广播命令是面向所有卡的,有些命令要求返回响应。

指定地址命令:
指定地址命令发送到对应地址的卡,并由此卡返回响应。

SD卡系统定义了两种操作模式(主机和卡):
卡识别模式:
上电后或主机是处于卡识别模式,这时它会在总线上检测新卡。
卡上电后处于卡识别模式,直到它接收到SEND_RCA(CMD3)命令。(如果是MMC卡,则是SET_RCA命令)

数据传输模式:
一旦卡的RCA首次公布,卡会进入数据传输模式。
在识别了总线上的所有的卡后,主机会进入数据传输模式。

下面的表表明了卡的状态和操作模式的关系。每一个状态都对应着一个操作模式。




4.2、卡识别模式
在卡识别模式中,主机复位所有处于卡识别模式的卡,验证卡的电压操作范围,识别卡并请求卡返回RCA地址。
这个操作是在每个卡的CMD线上单独操作的。在卡识别模式中,所有数据通信只使用CMD线。

4.2.1、卡复位
GO_IDLE_STATE (CMD0)命令是软件复位命令,它会使每个卡进入空闲(Idle)状态而不管卡当前所处的状态。
处于不活动状态的卡不受此命令的影响。

由主机上电之后,所有卡处于空闲(Idle)状态(包括之前处于不活动状态的卡)。

在上电或接收到CMD0命令之后,所有卡的CMD线处于输入模式,等待下一个命令的起始位。
所有卡被初始化为默认的RCA地址(RCA=0x0000)和默认的驱动层寄存器设置(最低速率,最高电流驱动能力)。

4.2.2、操作电压范围验证
所有卡使用最大允许电压范围内的任何电压与主机建立通信。
所支持的最小和最大电压范围是在OCR(Operation Conditions Register)寄存器中定义,可能不覆盖所有卡的电压范围。
只有支持这些电压范围的卡,其CID和CSD数据才能被访问。这意味着,如果主机和卡具有不兼容的电压范围,那么卡的CID和CSD数据就不能被访问,也不能完成卡的识别过程。

因此,有一个命令SD_SEND_OP_COND (ACMD41)是特意设计来给主机识别和拒绝与主机期望的电压范围不匹配的卡的。
这是通过主机以一个电压范围为这个命令的参数,发送命令来实现的。
不支持此电压范围的卡将放弃总线并进入不活动状态。
卡所支持的电压范围是在OCR寄存器中定义的。见后面OCR详细介绍。
要注意的是ACMD41是应用程序特殊命令,发送ACMD41之前必须先发送APP_CMD(CMD55)。
在空闲状态下,CMD55所使用的RCA是卡的默认值RCA = 0x0000。

MMC卡不会回应ACMD41,实际上它是不回应ACMD41之前的APP_CMD(CMD55)。
MMC卡应依照MMC协议规范被使用SEND_OP_COND (CMD1)命令进行初始化。
主机应忽略MMC卡回应CMD3命令的ILLEGAL_COMMAND状态,因为这个ACMD41残留的状态。(CMD0,1,2不清除状态寄存器)。
实际中,主机将使用ACMD41命令和CMD1命令来识别系统中的SD卡和MMC卡。


卡识别模式流程图:



通过发送忽略电压范围的命令,主机可以查询每个卡,并在使不兼容卡进入非活动状态之前,确定通用电压范围。
主机可以选择一个通用的电压范围,或者期望不使用的卡的应用程序通知时,可以使用此查询。
总之,主机必须选择一个电压操作范围,并在这个条件下重发ACMD41命令,使不兼容卡进入非活动状态。

ACMD41命令的响应中的忙(busy)位,是卡用来告诉主机它正处于上电或复位状态,并且暂时不能通信。
在这种情况下,主机必须重复ACMD41命令,直到忙位被清除。

在初始化过程中,主机不允许改变电压操作范围。此类改变将会被卡忽略。
如果需要改变操作电压范围,主机必须用CMD0命令复位所有卡,并重新开始初始化流程。
并且,如果需要访问所有不活动状态的卡,必须进行硬复位(重新上电)。

GO_INACTIVE_STATE (CMD15) 命令可以用来使一个定址了的卡进入不活动状态。
这个命令可以在主机明确地需要使卡进入非活动状态的时候使用。(例如,主机正在改变电压范围,而已知道卡不在此电压范围内时)

4.2.3、卡的识别过程
主机以时钟速率fOD(后面会提到)开始卡识别流程。在SD卡中,CMD线是开漏输出。

总线启动后,主机要求卡返回它们的有效操作条件(在带参数RCA=0x0000的APP_CMD CMD55命令之后,发送ACMD41 )。
ACMD41命令的响应是OCR(operation condition register)。同样的命令将会发送到系统中所有的新卡。不兼容的卡将被发送进入不活动状态。
主机可以发送ALL_SEND_CID (CMD2)命令到每张卡,来获得卡的CID(unique card identification)。未被识别的卡(如处于就绪状态的卡)将在CMD线上发送其CID作为响应。
在发送了CID之后,卡进入鉴别状态。
然后主机发送SEND_RELATIVE_ADDR(CMD3),要求卡返回其RCA(relative card address)。这个RCA比CID短,并且将在后面的数据传输中用来对卡寻址(尤其是速率比fOD高的时候)。
一旦RCA被接收后,卡将进入待机状态。此时,如果主机需要另外一个RCA号,它会用同样的SEND_RELATIVE_ADDR(CMD3)命令要求卡返回一个新的RCA号码。最后返回的RCA号作为卡的实际RCA号。

主机重复上面的识别过程,反复发送CMD2,CMD3命令来识别每张卡。

在初始化完所有SD卡后,主机将使用MMC协议规范里的CMD2和CMD3来初始化系统中的MMC卡。要注意的是,在SD卡中,所有卡是单独连接的,所以MMC卡将被单独初始化。

4.3数据传输模式
在CSD寄存器的内容被主机知道之前,所有卡都是运行在速率fOD上,因为某些卡可能有操作频率限制。
主机发送SEND_CSD (CMD9) 来获得卡的CSD(Card Specific Data)寄存器内容,如块长度,卡存储容量,最大时钟速率等。

卡数据传输模式流程图:




广播命令SET_DSR (CMD4) 配置已识别卡的驱动层寄存器。它会将应用程序对应的总线长度,总线上卡的数量和卡的传输速率写入到DSR寄存器。
SET_DSR对于卡和主机是个可选的命令。

CMD7用来选中一张卡并使其进入传输状态。同一时刻只有一张卡处于传输状态。如果当前卡正在处于传输状态,它会断开与主机的连接,并进入待机状态。
如果CMD7命令是发送到RCA地址0x0000,则所有卡都进入待机状态。这个可以用在识别新卡之前(在不复位已初始化卡的情况下)。
在这种状态中,已经有RCA的卡,不会回应识别命令(如CMD41, CMD2, CMD3)。

在数据传输模式中,主机和选中卡的所有通信都是点对点的(使用定址命令)。所有定址命令由CMD线上的响应确认。

数据传输模式中的所有关系概括如下:
• 所有数据读命令可以被CMD12命令终止。数据传输将被终止,而卡将返回到传输状态。读命令是:块读(CMD17),多块读(CMD18),发送写保护 (CMD30),发送SCR(ACMD51),和读模式中的通用命令(CMD56)。
• 所有数据写命令可以被CMD12命令终止。写命令必须在CMD7命令(取消选中卡)之前终止。写命令是:块写(CMD24 and CMD25),写CID (CMD26),写CSD (CMD27),
锁定/解锁命令(CMD42),和写模式中的通用命令(CMD56)。
• 一旦传输完成,卡将会退出写状态,并进入编程状态(写成功)或传输状态(写失败)。
• 如果块写操作停止,并且块长度和最后一个块的CRC有效,则数据将被编程写入。
• 卡会为块写提供缓冲,因此当上一个块正在被编程的同时,下一个块的数据可以被发送到卡。如果所有写缓冲满了,只要卡是在编程状态,则DAT0线将保持低电平(BUSY)。
• 对于写CID,写CSD,写保护和擦除,是不支持缓冲的。这意味着,当卡处于忙状态时,这些命令是不能被接收的。只要卡是在编程状态,则DAT0线将保持低点平(BUSY)。
实际上,如果各卡的DAT0线是分开的,则主机可以仍然可以在其中一张卡处于忙时,访问另一张卡。
• 当卡正在编程时,参数设置命令是不允许的。参数设置命令是:设置块程度(CMD16),擦除块开始(CMD32),擦除块结束(CMD33)。
• 当卡正在编程时,读命令是不允许的。
• 使用CMD7命令将卡从待机状态切换到传输状态,不会终止擦除和编程操作。卡将切换到断开连接状态并释放DAT线。
• 在断开连接状态下,使用CMD7命令可以将卡取消选中。此时卡将进入编程状态,并恢复忙指示。
• 使用CMD0或CMD15命令复位卡,将终止任何正在进行的编程操作。这种情况可能会损坏卡的数据内容。主机应避免这样的操作。

4.3.1总线宽度选择
宽总线(4位总线宽度)操作模式可以通过ACMD6选择或取消选择。复位或GO_IDLE(CMD0)命令之后,默认的总线宽度是1bit。
ACMD6命令只在传输状态下有效。这意味着总线宽度只有在卡被CMD7命令选中之后才能被改变。

4.3.2读数据
当无数据传输时,DAT线被拉高为高电平。
数据传输开始于低电平(1或4位低电平),接着是连续的数据流。数据流包含有payload数据(包含错误校正位,如果使用了ECC),数据流以高电平结束(1或4位高电平)。
数据传输是以时钟信号同步的。基于块数据传输的payload是由1或4位CRC校验和保护的。

从SD读数据的操作会被关断电源中断。SD卡在任何情况下都会保护其数据内容不会被损坏(写操作和擦除操作除外),即使在突然断开电源或被移除的时候。

块读
块读是基于块的数据传输。数据传输的基本单位是块,其最大值定义在CSD (READ_BL_LEN)中。
起始地址和结束地址包含在一个物理块中的较小的块也可以被传输。
SD卡能传输512字节的块是个强制性的要求。

为确保数据传输的完整性,CMD17(READ_SINGLE_BLOCK)命令开始块读,并且传输完成后,进入传输状态。
CMD18 (READ_MULTIPLE_BLOCK)命令开始连续的块传输。块将被连续地传送,直到收到STOP_TRANSMISSION(CMD12)命令。
由于是串行的传输,这个停止命令有一个执行的延迟。在接收到停止命令的最后一位时,传输将停止。

如果主机正在使用部分块,其累计长度是不对齐的,并且块错位是不允许的。那么卡应该在第一个错位块的开始检测到错位,并设置状态寄存器中的ADDRESS_ERROR位,
取消传输,并在数据状态中等待停止命令。

4.3.3写数据
写数据的数据传输格式和读数据类似。基于块的数据写传输,CRC校验位被附加到每个数据块中。
在写入数据之前,卡为每个接收到的数据块提供1或4位的CRC奇偶校验。因此,可以防止错误传输数据的写入。

块写
在块写(CMD24 - 27,42,56(w))期间,一个或多个块数据被主机传送到卡,每个块的结尾由主机附加1位或4位的CRC校验。
支持快写的卡总是能接受由WRITE_BL_LEN定义长度的块数据和512字节及其倍数的数据。(例如写1024字节长的数据,那写1024字节的块和写512字节的块都是支持的)。
如果WRITE_BL_PARTIAL = 1,那么写更小的块甚至1字节,也是可以被使用的。
如果CRC错误,那么卡会在DAT线上标记错误;传输的数据将被丢弃而不写入,后面传输的块数据(在多块传输中)将被忽略。

为了使写操作更快速,应该使用多块写命令,而不是使用单块写命令连续写。

如果主机正在使用部分块,其累计长度是不对齐的,并且块错位是不允许的(CSD parameter WRITE_BLK_MISALIGN is not set)。那么卡应该在第一个错位块的开始检测到错位,并设置状态寄存器中的ADDRESS_ERROR位,取消传输,并在数据接收状态中等待停止命令。

如果主机在一个写保护块上写数据,写操作将会被取消。在这种情形下,卡会设置WP_VIOLATION位。

编程写入CID和CSD寄存器之前,不需要进行块长度的设置。这时传输的数据也是CRC保护的。
如果CID和CSD寄存器的一部分是存储在ROM中,那么这个不可改变的部分必须匹配在接收缓冲区中的相应部分。如果这个匹配失败,那么卡将会报告错误,并不改变任何寄存器内容。

某写卡写一个块的数据需要较长的和不确定的时间。接收到一个块数据并完成校验后,如果写缓冲区满,并且不能接受一个新的WRITE_BLOCK命令的数据时,卡将开始写入数据并把DAT0线保持为低电平。
主机可以在任何时刻通过SEND_STATUS(CMD13)命令查询卡的状态,卡将会返回它的状态。状态位READY_FOR_DATA表示卡可接受新数据或它正在写入数据。
主机可以发出CMD7命令取消选择卡(去选择另一个卡),它会使卡进入断开连接状态,并释放DAT线而不会打断写操作的进行。
当重新选择卡时,如果写入仍然在进行并且写缓冲区满,那它会把DAT线拉低恢复忙指示。
实际上,主机会对所有卡同时进行交错的写操作。交错写是通过在其他卡忙时单独地存取每张卡来实现的。这个过程是通过适当的CMD和DAT0~3线的操作来实现的。

多块写操作之前的预擦除
在写操作之前加上(ACMD23)命令设置块写的数量,会比没有加(ACMD23)命令写的速度要块。主机将使用这个命令来定义下一个写操作将要发送多少个块。
如果主机在所有数据被发送到卡之前终止写操作(使用停止命令),那么剩余未写块的内容是未知的(可能被擦除,也可能是旧的数据)。
如果主机要发送的块数量比ACMD23定义的要多,那么卡将会一块一块地擦除(和接收到新数据一样)。
在多块写完成后,这个数量将会复位到默认值(=1)。

建议在CMD25之前使用此命令,对于多块写操作来说速度将会更快。如果主机需要使用预擦除功能,则必须在写命令之前使用ACMD23命令。否则,其他命令执行时预擦除计数将被自动清零。

发送块写的数量
在使用管道机制的数据缓冲区的系统中,在某些情况下,如果在多块写操作的中间发生错误,是无法确定哪个块是最后被写入到闪存中的。卡将会响应ACMD22命令返回已写块的数量。

4.3.4擦除
通过同时擦除很多个要写入的块来提高数据传输量是可行的。
检验这些块写入是否完成用ERASE_WR_BLK_START(CMD32)和ERASE_WR_BLK_END(CMD33)命令。
主机必须按照以下的命令顺序进行:ERASE_WR_BLK_START,ERASE_WR_BLK_END 和ERASE (CMD38)。
如果擦除命令ERASE (CMD38),地址设置命令(CMD32)和(CMD33)不是按照顺序被接收,那么卡将会在状态寄存器中设置ERASE_SEQ_ERROR位,并复位整个顺序。

如果接收到一个未按顺序的命令(SEND_STATUS除外),卡将会设置状态寄存器中的ERASE_RESET位,复位擦除顺序,并执行最后一个命令。

如果擦除范围包括写保护扇区,那这些扇区将保持不变,只有非保护区才被擦除。同时状态寄存器中的WP_ERASE_SKIP位被设置。

地址设置命令(CMD32和CMD33)中的地址域是一个以字节为单位的块地址。卡将会忽略所有LSB小于WRITE_BLK_LEN大小的数据。

如之前对块写的描述,当擦除正在进行时卡会将DAT0线拉低。实际擦除的时间可能会很长,主机可能会发出CMD7取消选择卡或将卡断开连接,如之前对块写的描述一样。

4.3.5应用程序特定命令
SD卡协议是定义为向前兼容MMC卡协议的。
SD卡是被设计为各种应用提供各种各样的接口的。
为了保持MMC卡标准和新SD卡专用命令的兼容性,SD卡使用应用程序特定命令来实现它的专用命令。
以下是APP_CMD 和GEN_CMD命令的定义。

应用程序特定命令 -  APP_CMD (CMD55)
当卡接收到此命令时,会导致卡将紧接着的下一个命令解析为应用程序特定命令,ACMD。
ACMD和标准的MMC卡命令一样具有一样的结构,并具有相同的CMD号。
只要这个命令是出现在APP_CMD之后,卡会将它标记为ACMD。

APP_CMD的效果是,如果紧接着的命令是有ACMD定义的,那么非标准的命令被使用(ACMD)。例如,卡有ACMD13定义,而没有ACMD7定义,
那么紧跟在APP_CMD命令后,命令13将认作为ACMD13非标准命令,而命令7将被认作为CMD7标准命令。
为了使用一个厂商特定ACMD,主机将会:
• 发送APP_CMD命令。响应将会有APP_CMD位设置返回给主机,表示卡期望接收ACMD命令。
• 发送期望的ACMD命令。响应将会有APP_CMD位设置,表示命令被看作为ACMD。如果一个非ACMD的命令被发送,那么它将会被认作为标准的SD卡命令,并且卡将会清除状态寄存器中的APP_CMD位。

如果一个非有效命令(既不是ACMD,也不是CMD)被发送,那么它将会被当作为标准SD卡非法命令错误处理。

从SD卡协议的角度来看,ACMD号被厂商定义是有一定限制的。以下的ACMD号是被SD卡协议保留的,而不能被厂商定义的:
ACMD6,ACMD13, ACMD17-25, ACMD38-49, ACMD51。

通用命令 - GEN_CMD (CMD56)
GEN_CMD命令的总线传输和块读或块写(CMD24 或 CMD17)命令是一样的。不同的是它的参数表示数据传输的方向而不是地址。数据块不是一个内存负载型的数据,但是有厂商定义的格式和意义。在发送CMD56命令之前,卡应被设置为传输状态。传输的数据块大小是由CMD16定义的BLOCK_LEN大小。 响应CMD56的是R1响应。

4.5错误条件
4.5.1 CRC和非法命令
所有命令是受CRC位保护的。如果定址卡的CRC校验失败,则卡不会返回响应并且命令不会被执行。卡不会改变其状态,状态寄存器中的COM_CRC_ERROR位被设置。
类似地,如果卡接收到非法命令,卡不会改变其状态,不会返回响应,并且状态寄存器中的ILLEGAL_COMMAND位将被设置。
有各种不同的非法命令:
• 命令属于类,但卡不支持(如只读卡中的写命令)。
• 命令在当前状态中不允许(如传输状态中的CMD2)。
• 命令未定义(如CMD5)。

4.5.2读、写和擦除超时条件
读、写和擦除操作的超时时间是正常访问时间的100倍。卡必须在这个时间内完成命令或者取消并返回错误信息。如果主机在指定的时间内没有得到任何响应,则它会假设卡将不再返回响应,并尝试恢复(例如复位卡,电源周期,拒绝卡等)。正常访问时间定义如下:
• 读
正常读访问的时间是由CSD参数TAAC和NSAC给定的两个时间之和定义的。这些参数定义了读命令的结束位和块数据的开始位之间的延时。这些参数是因卡而异的,应被主机用来计算流读
的吞吐量和最大频率。
• 写
CSD中的R2W_FACTOR是用来计算正常的块编程时间的,通过将正常读访问时间乘以这个系数获得。它适用于所有的写/擦除命令(如SET(CLEAR)_WRITE_PROTECT, PROGRAM_CSD(CID)和块写命令)
• 擦除
擦除命令的持续时间是等于要擦除的块数量乘以块写的时间。

4.6命令
4.6.1命令类型
共定义有4种命令来控制SD卡的:
• 广播命令,无响应(bc)
• 广播命令,有响应(bcr)
• 定址命令,无数据在DAT上传输(ac)
• 定址命令,有数据在DAT上传输(adtc)
所有命令和响应都是在SD卡的CMD线上传输。

4.6.2命令格式
所有命令都有个固定的长度——48位。



一个命令总是以起始位'0'开始,接着一个方向位(主机='1'),然后6位表示命令的序号,这个值是一个二进制码(从0到63)。有些命令需要一个参数(如一个地址),这个是32位的。
所有命令都是受CRC保护的。每个命令都是终止位'1'结束。

4.6.3命令类集
SD卡的命令集被分为各种类集。每一个类支持一组卡功能。
命令类集0, 2, 4, 5 和 8是强制性的,必须被所有SD卡支持。其他类集是可选的。




4.6.4详细命令描述

基本命令(class 0):




基于块的读命令(class 2):




①  数据传输不能跨越物理块边界除非CSD中的READ_BLK_MISALIGN被设置。

基于块的写命令(class 4):



①  数据传输不能跨越物理块边界除非CSD中的WRITE_BLK_MISALIGN被设置。

基于块的写保护命令(class 6):



擦除命令(class 5):
  


锁卡命令(class 7):



应用程序特定命令(class 8):



以下表格描述SD卡支持/保留的所有应用程序特殊命令。所有ACMD命令都必须紧跟在APP_CMD (CMD55)命令之后。



4.7响应
所有响应都是在CMD线上被发送。响应的编码长度取决于响应的类型。
所有响应开始于起始位(通常为‘0’),接着一位表示传输的方向(card = ‘0’),所有响应(除开响应类型R3)是受CRC保护的。每个响应的编码结束于停止位(通常为‘1’)。
共有4种类型的响应,格式定义如下:
R1响应 正常的响应命令




R2响应 (CID, CSD 寄存器)



R3响应 (OCR寄存器)



R6响应 (发布RCA地址响应)



4.8 SD卡状态
SD卡支持两种卡状态如下:
Card Status:兼容MMC卡协议
SD_Status: 扩展512位状态域,支持SD专用特性和未来应用特性。

4.8.1 Card Status
响应格式R1包含有一个称为Card Status的32位域。这个域是用来传输卡的状态信息的(卡状态是存放在本地状态寄存器中的)。
如果没有指定,状态通常是对应之前发生的命令的。

各种不同的状态定义如下表
类型和清除条件域缩写如下:
类型
E:错误位
S:起始位
R:实际命令的响应的测检和设置
X:命令执行时的检测和设置。主机必须通过状态命令查询卡来读这些位。

清除条件
A:根据卡当前状态
B:通常与之前的命令相关。接收到一个有效的命令将会清除它。
C:读时清除






4.8.2 SD Status
SD Status包含有SD专有特性的状态位,并可以在将来的应用中使用。SD Status的大小是一个数据块512bit的大小。这个寄存器的内容是在DAT线上被发送到主机的,并附有16位CRC校验。
ACMD13只可以在传输状态(卡被选中)中被发送到卡。SD Status的结构定义如下:



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-10-9 14:38:14 | 显示全部楼层
谢谢分享

出0入0汤圆

 楼主| 发表于 2013-10-9 14:40:25 | 显示全部楼层
SDIO程序分析
/*
  * @file    stm32_eval_sdio_sd.c
  * @author  MCD Application Team
  * @version V4.4.0
*/

主要函数:
void SD_DeInit(void);
SD_Error SD_Init(void);
SDTransferState SD_GetStatus(void);
SDCardState SD_GetState(void);
uint8_t SD_Detect(void);
SD_Error SD_PowerON(void);
SD_Error SD_PowerOFF(void);
SD_Error SD_InitializeCards(void);
SD_Error SD_GetCardInfo(SD_CardInfo *cardinfo);
SD_Error SD_EnableWideBusOperation(uint32_t WideMode);
SD_Error SD_SetDeviceMode(uint32_t Mode);
SD_Error SD_SelectDeselect(uint32_t addr);
SD_Error SD_ReadBlock(uint8_t *readbuff, uint32_t ReadAddr, uint16_t BlockSize);
SD_Error SD_ReadMultiBlocks(uint8_t *readbuff, uint32_t ReadAddr, uint16_t BlockSize, uint32_t NumberOfBlocks);
SD_Error SD_WriteBlock(uint8_t *writebuff, uint32_t WriteAddr, uint16_t BlockSize);
SD_Error SD_WriteMultiBlocks(uint8_t *writebuff, uint32_t WriteAddr, uint16_t BlockSize, uint32_t NumberOfBlocks);
SDTransferState SD_GetTransferState(void);
SD_Error SD_StopTransfer(void);
SD_Error SD_Erase(uint32_t startaddr, uint32_t endaddr);
SD_Error SD_SendStatus(uint32_t *pcardstatus);
SD_Error SD_SendSDStatus(uint32_t *psdstatus);
SD_Error SD_ProcessIRQSrc(void);

出0入0汤圆

 楼主| 发表于 2013-10-9 14:40:49 | 显示全部楼层
下面对所有函数进行一一分析,首先是SD_Init函数。
这个函数用来初始化SD卡。
首先调用SD_LowLevel_Init函数初始化STM32的SDIO所使用的GPIO端口,SD卡插入检测端口,开启SDIO时钟,开启DMA2时钟。
然后调用SD_PowerON函数开启SDIO电源、时钟,复位卡到空闲状态,发送CMD55命令识别是MMC卡或SD卡,如果是SD卡,则发送ACMD41识别和拒绝与主机期望的电压范围不匹配的SD卡。
再调用SD_InitializeCards函数发送CMD2命令,获取卡的CID数据;发送CMD3命令,要求卡返回其RCA地址;发送CMD9命令,获取卡的CSD数据。
读取CSD,CID数据保存到结构体指针cardinfo中,选中卡,设置总线宽度,设置传输模式为DMA模式。
/**
  * @brief  Initializes the SD Card and put it into StandBy State (Ready for data
  *         transfer).
  * @param  None
  * @retval SD_Error: SD Card Error code.
  */
SD_Error SD_Init(void)
{
  SD_Error errorstatus = SD_OK;
  
  /* SDIO外设底层初始化 */
  SD_LowLevel_Init();
  SDIO_DeInit();
  errorstatus = SD_PowerON();
  if (errorstatus != SD_OK)
  {
    /* CMD 响应超时 (等待CMDSENT标志) */
    return(errorstatus);
  }
  errorstatus = SD_InitializeCards();
  if (errorstatus != SD_OK)
  {
    /* CMD 响应超时 (等待CMDSENT标志) */
    return(errorstatus);
  }
  /* 配置SDIO外设 */
  /* SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_TRANSFER_CLK_DIV) */
  SDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV;
  SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
  SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;
  SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;
  SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;
  SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;
  SDIO_Init(&SDIO_InitStructure);
  
  if (errorstatus == SD_OK)
  {
    /*----------------- 读 CSD/CID 寄存器 ------------------*/
    errorstatus = SD_GetCardInfo(&SDCardInfo);
  }
  if (errorstatus == SD_OK)
  {
    /*----------------- 选中 Card --------------------------------*/
    errorstatus = SD_SelectDeselect((uint32_t) (SDCardInfo.RCA << 16));
  }
  if (errorstatus == SD_OK)
  {
    errorstatus = SD_EnableWideBusOperation(SDIO_BusWide_4b);
  }  
  /* 设置器件传输模式为DMA */
  if (errorstatus == SD_OK)
  {  
    errorstatus = SD_SetDeviceMode(SD_DMA_MODE);
  }
  
  return(errorstatus);
}

出0入0汤圆

 楼主| 发表于 2013-10-9 14:41:31 | 显示全部楼层
SD_LowLevel_Init函数初始化STM32的SDIO所使用的GPIO端口,SD卡插入检测端口,开启SDIO时钟,开启DMA2时钟
/**
  * @brief  Initializes the SD Card and put it into StandBy State (Ready for
  *         data transfer).
  * @param  None
  * @retval None
  */
void SD_LowLevel_Init(void)
{
  GPIO_InitTypeDef  GPIO_InitStructure;
  /* 使能 GPIOC 和 GPIOD */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | SD_DETECT_GPIO_CLK, ENABLE);
  /* 配置 PC.08, PC.09, PC.10, PC.11, PC.12 管脚: D0, D1, D2, D3, CLK 管脚 */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(GPIOC, &GPIO_InitStructure);
  /* 配置 PD.02 CMD线 */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
  GPIO_Init(GPIOD, &GPIO_InitStructure);
  /* 配置 SD_SPI_DETECT_PIN 管脚: SD卡检测管脚 */
  GPIO_InitStructure.GPIO_Pin = SD_DETECT_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  GPIO_Init(SD_DETECT_GPIO_PORT, &GPIO_InitStructure);
  
  /* 使能 SDIO AHB 时钟 */
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_SDIO, ENABLE);
  /* 使能 DMA2 时钟 */
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
}

出0入0汤圆

 楼主| 发表于 2013-10-9 14:42:08 | 显示全部楼层
SD_PowerON函数开启SDIO电源、时钟,复位卡到空闲状态,发送APP_CMD CMD55命令识别是MMC卡还是SD卡,
如果是SD卡,则发送ACMD41识别和拒绝与主机期望的电压范围不匹配的SD卡。
/**
  * @brief  Enquires cards about their operating voltage and configures
  *   clock controls.
  * @param  None
  * @retval SD_Error: SD Card Error code.
  */
SD_Error SD_PowerON(void)
{
  SD_Error errorstatus = SD_OK;
  uint32_t response = 0, count = 0, validvoltage = 0;
  uint32_t SDType = SD_STD_CAPACITY;
  /*!< Power ON Sequence -----------------------------------------------------*/
  /* 配置SDIO外设 */
  /* SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_INIT_CLK_DIV) */
  /* 初始化的SDIO_CK 不能超过 400 KHz */  
  SDIO_InitStructure.SDIO_ClockDiv = SDIO_INIT_CLK_DIV;
  SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
  SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;
  SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;
  SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;
  SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;
  SDIO_Init(&SDIO_InitStructure);
  /* 开启SDIO电源 */
  SDIO_SetPowerState(SDIO_PowerState_ON);  //设置SDIO电源控制寄存器(SDIO_POWER)的值为SDIO_PowerState_ON(0x00000003),即上电状态。
  /* 使能SDIO时钟 */
  SDIO_ClockCmd(ENABLE);  //设置SDIO时钟控制寄存器(SDIO_CLKCR)第8位的值为1,即SDIO_CK使能。
  /* CMD0: 复位卡到空闲状态 ---------------------------------------------------*/
  /* No CMD response required */
  SDIO_CmdInitStructure.SDIO_Argument = 0x0;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_GO_IDLE_STATE;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_No;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);
  errorstatus = CmdError();
  if (errorstatus != SD_OK)
  {
    /* CMD 响应超时 (等待 CMDSENT 标志) */
    return(errorstatus);
  }
  /* CMD8: SEND_IF_COND ----------------------------------------------------*/
  /* 发送 CMD8来检验卡接口操作条件*/
  /* 参数: - [31:12]: 保留 (应设为 '0')
               - [11:8]: 电源电压 (VHS) 0x1 (范围: 2.7-3.6 V)
               - [7:0]: 检查模式 (要求为 0xAA) */
  /* CMD 响应: R7 */
  SDIO_CmdInitStructure.SDIO_Argument = SD_CHECK_PATTERN;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_SEND_IF_COND;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);
  errorstatus = CmdResp7Error();
  if (errorstatus == SD_OK)
  {
    CardType = SDIO_STD_CAPACITY_SD_CARD_V2_0; /* SD Card 2.0 */
    SDType = SD_HIGH_CAPACITY;
  }
  else
  {
    /* APP_CMD CMD55应用程序特殊命令 */
    SDIO_CmdInitStructure.SDIO_Argument = 0x00;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);
    errorstatus = CmdResp1Error(SD_CMD_APP_CMD);
  }
  /* APP_CMD CMD55应用程序特殊命令 */
  SDIO_CmdInitStructure.SDIO_Argument = 0x00;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);
  errorstatus = CmdResp1Error(SD_CMD_APP_CMD);
  /* 如果 errorstatus 是 Command TimeOut,那么这个是 MMC 卡,因为MMC卡不会回应APP_CMD CMD55命令 */
  /* 如果 errorstatus 是 SD_OK ,那这个是SD卡: SD card 2.0 (电压范围缺失)或者 SD card 1.x */
  if (errorstatus == SD_OK)
  {
    /* SD CARD */
    /* 发送 ACMD41 SD_APP_OP_COND,参数为0x80100000 */
    while ((!validvoltage) && (count < SD_MAX_VOLT_TRIAL))
    {
      /* 发送 CMD55 APP_CMD ,参数 RCA 为 0 */
      SDIO_CmdInitStructure.SDIO_Argument = 0x00;
      SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
      SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
      SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
      SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
      SDIO_SendCommand(&SDIO_CmdInitStructure);
      errorstatus = CmdResp1Error(SD_CMD_APP_CMD);
      if (errorstatus != SD_OK)
      {
        return(errorstatus);
      }
      SDIO_CmdInitStructure.SDIO_Argument = SD_VOLTAGE_WINDOW_SD | SDType;
      SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_APP_OP_COND;
      SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
      SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
      SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
      SDIO_SendCommand(&SDIO_CmdInitStructure);
      errorstatus = CmdResp3Error();
      if (errorstatus != SD_OK)
      {
        return(errorstatus);
      }
      response = SDIO_GetResponse(SDIO_RESP1);
      validvoltage = (((response >> 31) == 1) ? 1 : 0);
      count++;
    }
    if (count >= SD_MAX_VOLT_TRIAL)
    {
      errorstatus = SD_INVALID_VOLTRANGE;
      return(errorstatus);
    }
    if (response &= SD_HIGH_CAPACITY)
    {
      CardType = SDIO_HIGH_CAPACITY_SD_CARD;
    }
  }/*!< else MMC Card */
  return(errorstatus);
}

出0入0汤圆

 楼主| 发表于 2013-10-9 14:45:19 | 显示全部楼层
SD_InitializeCards函数发送CMD2命令,获取卡的CID数据;发送CMD3命令,请求卡返回RCA地址;发送CMD9命令,获取卡的CSD数据。
/**
  * @brief  Intialises all cards or single card as the case may be Card(s) come
  *         into standby state.
  * @param  None
  * @retval SD_Error: SD Card Error code.
  */
SD_Error SD_InitializeCards(void)
{
  SD_Error errorstatus = SD_OK;
  uint16_t rca = 0x01;
  if (SDIO_GetPowerState() == SDIO_PowerState_OFF)
  {
    errorstatus = SD_REQUEST_NOT_APPLICABLE;
    return(errorstatus);
  }
  if (SDIO_SECURE_DIGITAL_IO_CARD != CardType)
  {
    /*发送 CMD2(ALL_SEND_CID)命令,获取卡的CID数据 */
    SDIO_CmdInitStructure.SDIO_Argument = 0x0;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ALL_SEND_CID;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);
    errorstatus = CmdResp2Error();
    if (SD_OK != errorstatus)
    {
      return(errorstatus);
    }
    CID_Tab[0] = SDIO_GetResponse(SDIO_RESP1);
    CID_Tab[1] = SDIO_GetResponse(SDIO_RESP2);
    CID_Tab[2] = SDIO_GetResponse(SDIO_RESP3);
    CID_Tab[3] = SDIO_GetResponse(SDIO_RESP4);
  }
  if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) ||  (SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType) ||  (SDIO_SECURE_DIGITAL_IO_COMBO_CARD == CardType)
      ||  (SDIO_HIGH_CAPACITY_SD_CARD == CardType))
  {
    /* 发送 CMD3(SET_REL_ADDR)命令,参数为 0 */
    /* SD卡返回其RCA地址. */
    SDIO_CmdInitStructure.SDIO_Argument = 0x00;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_REL_ADDR;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);
    errorstatus = CmdResp6Error(SD_CMD_SET_REL_ADDR, &rca);
    if (SD_OK != errorstatus)
    {
      return(errorstatus);
    }
  }
  if (SDIO_SECURE_DIGITAL_IO_CARD != CardType)
  {
    RCA = rca;
    /* 发送 CMD9(SEND_CSD)命令,获取卡的CSD数据,参数为卡的RCA地址 */
    SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)(rca << 16);
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_CSD;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);
    errorstatus = CmdResp2Error();
    if (SD_OK != errorstatus)
    {
      return(errorstatus);
    }
    CSD_Tab[0] = SDIO_GetResponse(SDIO_RESP1);
    CSD_Tab[1] = SDIO_GetResponse(SDIO_RESP2);
    CSD_Tab[2] = SDIO_GetResponse(SDIO_RESP3);
    CSD_Tab[3] = SDIO_GetResponse(SDIO_RESP4);
  }
  errorstatus = SD_OK; /* 所有卡被初始化完毕 */
  return(errorstatus);
}

出0入0汤圆

 楼主| 发表于 2013-10-9 14:45:47 | 显示全部楼层
下面这几个函数只作简要介绍
SD_Error SD_GetCardInfo(SD_CardInfo *cardinfo); //这个函数是把获得的卡的所有信息存放到SD_CardInfo结构体指针cardinfo中。
SD_Error SD_EnableWideBusOperation(uint32_t WideMode);  //设置总线数据宽度为4位或1位(不支持8位),MMC卡不支持此特性。
SD_Error SD_SetDeviceMode(uint32_t Mode); //设置数据传输模式为DMA模式,中断模式或查询模式

出0入0汤圆

 楼主| 发表于 2013-10-9 14:46:01 | 显示全部楼层
SD_SelectDeselect函数选中卡,函数的参数为卡的地址
/**
  * @brief  Selects od Deselects the corresponding card.
  * @param  addr: Address of the Card to be selected.
  * @retval SD_Error: SD Card Error code.
  */
SD_Error SD_SelectDeselect(uint32_t addr)
{
  SD_Error errorstatus = SD_OK;

  /* 发送 CMD7(SDIO_SEL_DESEL_CARD)命令 */
  SDIO_CmdInitStructure.SDIO_Argument =  addr;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEL_DESEL_CARD;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);

  errorstatus = CmdResp1Error(SD_CMD_SEL_DESEL_CARD);

  return(errorstatus);
}

出0入0汤圆

 楼主| 发表于 2013-10-9 14:46:47 | 显示全部楼层
/**
  * @摘要:  允许从卡中的指定地址读一个块的数据
  * @参数  readbuff: 指向存放接收到数据的buffer
  * @参数  ReadAddr: 将要读数据的地址
  * @参数  BlockSize: SD卡数据块的大小
  * @retval SD_Error: SD卡错误代码
  */
SD_Error SD_ReadBlock(uint8_t *readbuff, uint32_t ReadAddr, uint16_t BlockSize)
{
  SD_Error errorstatus = SD_OK;
  uint32_t count = 0, *tempbuff = (uint32_t *)readbuff;
  uint8_t power = 0;
  if (NULL == readbuff)
  {
    errorstatus = SD_INVALID_PARAMETER;
    return(errorstatus);
  }
  TransferError = SD_OK;
  TransferEnd = 0;
  TotalNumberOfBytes = 0;
  /* 清除所有DPSM 配置 */
  SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
  SDIO_DataInitStructure.SDIO_DataLength = 0;
  SDIO_DataInitStructure.SDIO_DataBlockSize = SDIO_DataBlockSize_1b;
  SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToCard;
  SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
  SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Disable;
  SDIO_DataConfig(&SDIO_DataInitStructure);
  SDIO_DMACmd(DISABLE);
  if (SDIO_GetResponse(SDIO_RESP1) & SD_CARD_LOCKED)
  {
    errorstatus = SD_LOCK_UNLOCK_FAILED;
    return(errorstatus);
  }
  
  if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
  {
    BlockSize = 512;
    ReadAddr /= 512;
  }
  if ((BlockSize > 0) && (BlockSize <= 2048) && ((BlockSize & (BlockSize - 1)) == 0))
  {
    power = convert_from_bytes_to_power_of_two(BlockSize);
    /*设置SD卡的块大小 */
    SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) BlockSize;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);
    errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);
    if (SD_OK != errorstatus)
    {
      return(errorstatus);
    }
  }
  else
  {
    errorstatus = SD_INVALID_PARAMETER;
    return(errorstatus);
  }
  SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
  SDIO_DataInitStructure.SDIO_DataLength = BlockSize;
  SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) power << 4;
  SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToSDIO;
  SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
  SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
  SDIO_DataConfig(&SDIO_DataInitStructure);
  TotalNumberOfBytes = BlockSize;
  StopCondition = 0;
  DestBuffer = (uint32_t *)readbuff;
  /*发送 CMD17(READ_SINGLE_BLOCK)命令 */
  SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)ReadAddr;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_READ_SINGLE_BLOCK;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);
  errorstatus = CmdResp1Error(SD_CMD_READ_SINGLE_BLOCK);
  if (errorstatus != SD_OK)
  {
    return(errorstatus);
  }
  /*如果是单块传输,不需要手动去停止传输*/
  if (DeviceMode == SD_POLLING_MODE)
  {
    /*查询模式 */
    while (!(SDIO->STA &(SDIO_FLAG_RXOVERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_DBCKEND | SDIO_FLAG_STBITERR)))
    {
      if (SDIO_GetFlagStatus(SDIO_FLAG_RXFIFOHF) != RESET)
      {
        for (count = 0; count < 8; count++)
        {
          *(tempbuff + count) = SDIO_ReadData();
        }
        tempbuff += 8;
      }
    }
    if (SDIO_GetFlagStatus(SDIO_FLAG_DTIMEOUT) != RESET)
    {
      SDIO_ClearFlag(SDIO_FLAG_DTIMEOUT);
      errorstatus = SD_DATA_TIMEOUT;
      return(errorstatus);
    }
    else if (SDIO_GetFlagStatus(SDIO_FLAG_DCRCFAIL) != RESET)
    {
      SDIO_ClearFlag(SDIO_FLAG_DCRCFAIL);
      errorstatus = SD_DATA_CRC_FAIL;
      return(errorstatus);
    }
    else if (SDIO_GetFlagStatus(SDIO_FLAG_RXOVERR) != RESET)
    {
      SDIO_ClearFlag(SDIO_FLAG_RXOVERR);
      errorstatus = SD_RX_OVERRUN;
      return(errorstatus);
    }
    else if (SDIO_GetFlagStatus(SDIO_FLAG_STBITERR) != RESET)
    {
      SDIO_ClearFlag(SDIO_FLAG_STBITERR);
      errorstatus = SD_START_BIT_ERR;
      return(errorstatus);
    }
    while (SDIO_GetFlagStatus(SDIO_FLAG_RXDAVL) != RESET)
    {
      *tempbuff = SDIO_ReadData();
      tempbuff++;
    }
    /*!< Clear all the static flags */
    SDIO_ClearFlag(SDIO_STATIC_FLAGS);
  }
  else if (DeviceMode == SD_INTERRUPT_MODE)
  {
    SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND | SDIO_IT_RXOVERR | SDIO_IT_RXFIFOHF | SDIO_IT_STBITERR, ENABLE);
    while ((TransferEnd == 0) && (TransferError == SD_OK))
    {}
    if (TransferError != SD_OK)
    {
      return(TransferError);
    }
  }
  else if (DeviceMode == SD_DMA_MODE)
  {
    SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND | SDIO_IT_RXOVERR | SDIO_IT_STBITERR, ENABLE);
    SDIO_DMACmd(ENABLE);
    SD_LowLevel_DMA_RxConfig((uint32_t *)readbuff, BlockSize);
    while ((SD_DMAEndOfTransferStatus() == RESET) && (TransferEnd == 0) && (TransferError == SD_OK))
    {}
    if (TransferError != SD_OK)
    {
      return(TransferError);
    }
  }
  return(errorstatus);
}

出0入0汤圆

 楼主| 发表于 2013-10-9 14:47:19 | 显示全部楼层
/**
  * @摘要:  允许从卡中的指定地址读多个块的数据
  * @参数  readbuff: 指向存放接收到数据的buffer
  * @参数  ReadAddr: 将要读数据的地址
  * @参数  BlockSize: SD卡数据块的大小
  * @参数  NumberOfBlocks: 要读的块的数量
  * @返回值 SD_Error: SD卡错误代码
  */
SD_Error SD_ReadMultiBlocks(uint8_t *readbuff, uint32_t ReadAddr, uint16_t BlockSize, uint32_t NumberOfBlocks)
{
  SD_Error errorstatus = SD_OK;
  uint32_t count = 0, *tempbuff = (uint32_t *)readbuff;
  uint8_t power = 0;
  if (NULL == readbuff)
  {
    errorstatus = SD_INVALID_PARAMETER;
    return(errorstatus);
  }
  TransferError = SD_OK;
  TransferEnd = 0;
  TotalNumberOfBytes = 0;
  /* 清除所有DPSM 配置 */
  SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
  SDIO_DataInitStructure.SDIO_DataLength = 0;
  SDIO_DataInitStructure.SDIO_DataBlockSize = SDIO_DataBlockSize_1b;
  SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToCard;
  SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
  SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Disable;
  SDIO_DataConfig(&SDIO_DataInitStructure);
  SDIO_DMACmd(DISABLE);
  if (SDIO_GetResponse(SDIO_RESP1) & SD_CARD_LOCKED)
  {
    errorstatus = SD_LOCK_UNLOCK_FAILED;
    return(errorstatus);
  }
  if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
  {
    BlockSize = 512;
    ReadAddr /= 512;
  }
  
  if ((BlockSize > 0) && (BlockSize <= 2048) && (0 == (BlockSize & (BlockSize - 1))))
  {
    power = convert_from_bytes_to_power_of_two(BlockSize);
    /*设置SD卡块的大小 */
    SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) BlockSize;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);
    errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);
    if (SD_OK != errorstatus)
    {
      return(errorstatus);
    }
  }
  else
  {
    errorstatus = SD_INVALID_PARAMETER;
    return(errorstatus);
  }
  if (NumberOfBlocks > 1)
  {
    /*!< Common to all modes */
    if (NumberOfBlocks * BlockSize > SD_MAX_DATA_LENGTH)
    {
      errorstatus = SD_INVALID_PARAMETER;
      return(errorstatus);
    }
    TotalNumberOfBytes = NumberOfBlocks * BlockSize;
    StopCondition = 1;
    DestBuffer = (uint32_t *)readbuff;
    SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
    SDIO_DataInitStructure.SDIO_DataLength = NumberOfBlocks * BlockSize;
    SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) power << 4;
    SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToSDIO;
    SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
    SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
    SDIO_DataConfig(&SDIO_DataInitStructure);
    /* 发送 CMD18(READ_MULT_BLOCK)命令,参数为要读数据的地址 */
    SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)ReadAddr;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_READ_MULT_BLOCK;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);
    errorstatus = CmdResp1Error(SD_CMD_READ_MULT_BLOCK);
    if (errorstatus != SD_OK)
    {
      return(errorstatus);
    }
    if (DeviceMode == SD_POLLING_MODE)
    {
      /* 查询模式 */
      while (!(SDIO->STA &(SDIO_FLAG_RXOVERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DATAEND | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_STBITERR)))
      {
        if (SDIO_GetFlagStatus(SDIO_FLAG_RXFIFOHF) != RESET)
        {
          for (count = 0; count < SD_HALFFIFO; count++)
          {
            *(tempbuff + count) = SDIO_ReadData();
          }
          tempbuff += SD_HALFFIFO;
        }
      }
      if (SDIO_GetFlagStatus(SDIO_FLAG_DTIMEOUT) != RESET)
      {
        SDIO_ClearFlag(SDIO_FLAG_DTIMEOUT);
        errorstatus = SD_DATA_TIMEOUT;
        return(errorstatus);
      }
      else if (SDIO_GetFlagStatus(SDIO_FLAG_DCRCFAIL) != RESET)
      {
        SDIO_ClearFlag(SDIO_FLAG_DCRCFAIL);
        errorstatus = SD_DATA_CRC_FAIL;
        return(errorstatus);
      }
      else if (SDIO_GetFlagStatus(SDIO_FLAG_RXOVERR) != RESET)
      {
        SDIO_ClearFlag(SDIO_FLAG_RXOVERR);
        errorstatus = SD_RX_OVERRUN;
        return(errorstatus);
      }
      else if (SDIO_GetFlagStatus(SDIO_FLAG_STBITERR) != RESET)
      {
        SDIO_ClearFlag(SDIO_FLAG_STBITERR);
        errorstatus = SD_START_BIT_ERR;
        return(errorstatus);
      }
      while (SDIO_GetFlagStatus(SDIO_FLAG_RXDAVL) != RESET)
      {
        *tempbuff = SDIO_ReadData();
        tempbuff++;
      }
      if (SDIO_GetFlagStatus(SDIO_FLAG_DATAEND) != RESET)
      {
        /*如果发送STOP_TRANSMISSION命令 */
        if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) || (SDIO_HIGH_CAPACITY_SD_CARD == CardType) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType))
        {
          /*发送 CMD12(STOP_TRANSMISSION)命令 */
          SDIO_CmdInitStructure.SDIO_Argument = 0x0;
          SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_STOP_TRANSMISSION;
          SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
          SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
          SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
          SDIO_SendCommand(&SDIO_CmdInitStructure);
          errorstatus = CmdResp1Error(SD_CMD_STOP_TRANSMISSION);
          if (errorstatus != SD_OK)
          {
            return(errorstatus);
          }
        }
      }
      /*!< Clear all the static flags */
      SDIO_ClearFlag(SDIO_STATIC_FLAGS);
    }
    else if (DeviceMode == SD_INTERRUPT_MODE)
    {
      /* 中断模式 */
      SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND | SDIO_IT_RXOVERR | SDIO_IT_RXFIFOHF | SDIO_IT_STBITERR, ENABLE);
      while ((TransferEnd == 0) && (TransferError == SD_OK))
      {}
      if (TransferError != SD_OK)
      {
        return(TransferError);
      }
    }
    else if (DeviceMode == SD_DMA_MODE)
    {
      /* DMA模式 */
      SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND | SDIO_IT_RXOVERR | SDIO_IT_STBITERR, ENABLE);
      SDIO_DMACmd(ENABLE);
      SD_LowLevel_DMA_RxConfig((uint32_t *)readbuff, (NumberOfBlocks * BlockSize));
      while ((SD_DMAEndOfTransferStatus() == RESET) && (TransferEnd == 0) && (TransferError == SD_OK))
      {}
      if (TransferError != SD_OK)
      {
        return(TransferError);
      }
    }
  }
  return(errorstatus);
}

出0入0汤圆

 楼主| 发表于 2013-10-9 14:47:43 | 显示全部楼层
/**
  * @摘要:  允许从卡中的指定地址写一个块的数据
  * @参数  writebuff: 指向存放将要发送数据的buffer
  * @参数  WriteAddr: 将要写数据的地址
  * @参数  BlockSize: SD卡数据块的大小
  * @返回值 SD_Error: SD卡错误代码
  */
SD_Error SD_WriteBlock(uint8_t *writebuff, uint32_t WriteAddr, uint16_t BlockSize)
{
  SD_Error errorstatus = SD_OK;
  uint8_t  power = 0, cardstate = 0;
  uint32_t timeout = 0, bytestransferred = 0;
  uint32_t cardstatus = 0, count = 0, restwords = 0;
  uint32_t *tempbuff = (uint32_t *)writebuff;
  if (writebuff == NULL)
  {
    errorstatus = SD_INVALID_PARAMETER;
    return(errorstatus);
  }
  TransferError = SD_OK;
  TransferEnd = 0;
  TotalNumberOfBytes = 0;
  SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
  SDIO_DataInitStructure.SDIO_DataLength = 0;
  SDIO_DataInitStructure.SDIO_DataBlockSize = SDIO_DataBlockSize_1b;
  SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToCard;
  SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
  SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Disable;
  SDIO_DataConfig(&SDIO_DataInitStructure);
  SDIO_DMACmd(DISABLE);
  if (SDIO_GetResponse(SDIO_RESP1) & SD_CARD_LOCKED)
  {
    errorstatus = SD_LOCK_UNLOCK_FAILED;
    return(errorstatus);
  }
  if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
  {
    BlockSize = 512;
    WriteAddr /= 512;
  }
  
  /*设置主机和卡的块的大小 */
  if ((BlockSize > 0) && (BlockSize <= 2048) && ((BlockSize & (BlockSize - 1)) == 0))
  {
    power = convert_from_bytes_to_power_of_two(BlockSize);
    SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) BlockSize;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);
    errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);
    if (errorstatus != SD_OK)
    {
      return(errorstatus);
    }
  }
  else
  {
    errorstatus = SD_INVALID_PARAMETER;
    return(errorstatus);
  }
  /*等待卡准备好接收数据 */
  SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) (RCA << 16);
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_STATUS;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);
  errorstatus = CmdResp1Error(SD_CMD_SEND_STATUS);
  if (errorstatus != SD_OK)
  {
    return(errorstatus);
  }
  cardstatus = SDIO_GetResponse(SDIO_RESP1);
  timeout = SD_DATATIMEOUT;
  while (((cardstatus & 0x00000100) == 0) && (timeout > 0))
  {
    timeout--;
    SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) (RCA << 16);
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_STATUS;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);
    errorstatus = CmdResp1Error(SD_CMD_SEND_STATUS);
    if (errorstatus != SD_OK)
    {
      return(errorstatus);
    }
    cardstatus = SDIO_GetResponse(SDIO_RESP1);
  }
  if (timeout == 0)
  {
    return(SD_ERROR);
  }
  /*发送 CMD24(WRITE_SINGLE_BLOCK)命令 */
  SDIO_CmdInitStructure.SDIO_Argument = WriteAddr;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_WRITE_SINGLE_BLOCK;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);
  errorstatus = CmdResp1Error(SD_CMD_WRITE_SINGLE_BLOCK);
  if (errorstatus != SD_OK)
  {
    return(errorstatus);
  }
  TotalNumberOfBytes = BlockSize;
  StopCondition = 0;
  SrcBuffer = (uint32_t *)writebuff;
  SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
  SDIO_DataInitStructure.SDIO_DataLength = BlockSize;
  SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) power << 4;
  SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToCard;
  SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
  SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
  SDIO_DataConfig(&SDIO_DataInitStructure);
  /*如果是单块传输不需要停止命令*/
  if (DeviceMode == SD_POLLING_MODE)
  {
    /* 查询模式 */
    while (!(SDIO->STA & (SDIO_FLAG_DBCKEND | SDIO_FLAG_TXUNDERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_STBITERR)))
    {
      if (SDIO_GetFlagStatus(SDIO_FLAG_TXFIFOHE) != RESET)
      {
        if ((TotalNumberOfBytes - bytestransferred) < 32)
        {
          restwords = ((TotalNumberOfBytes - bytestransferred) % 4 == 0) ? ((TotalNumberOfBytes - bytestransferred) / 4) : (( TotalNumberOfBytes -  bytestransferred) / 4 + 1);
          for (count = 0; count < restwords; count++, tempbuff++, bytestransferred += 4)
          {
            SDIO_WriteData(*tempbuff);
          }
        }
        else
        {
          for (count = 0; count < 8; count++)
          {
            SDIO_WriteData(*(tempbuff + count));
          }
          tempbuff += 8;
          bytestransferred += 32;
        }
      }
    }
    if (SDIO_GetFlagStatus(SDIO_FLAG_DTIMEOUT) != RESET)
    {
      SDIO_ClearFlag(SDIO_FLAG_DTIMEOUT);
      errorstatus = SD_DATA_TIMEOUT;
      return(errorstatus);
    }
    else if (SDIO_GetFlagStatus(SDIO_FLAG_DCRCFAIL) != RESET)
    {
      SDIO_ClearFlag(SDIO_FLAG_DCRCFAIL);
      errorstatus = SD_DATA_CRC_FAIL;
      return(errorstatus);
    }
    else if (SDIO_GetFlagStatus(SDIO_FLAG_TXUNDERR) != RESET)
    {
      SDIO_ClearFlag(SDIO_FLAG_TXUNDERR);
      errorstatus = SD_TX_UNDERRUN;
      return(errorstatus);
    }
    else if (SDIO_GetFlagStatus(SDIO_FLAG_STBITERR) != RESET)
    {
      SDIO_ClearFlag(SDIO_FLAG_STBITERR);
      errorstatus = SD_START_BIT_ERR;
      return(errorstatus);
    }
  }
  else if (DeviceMode == SD_INTERRUPT_MODE)
  {
    /* 中断模式 */
    SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND | SDIO_FLAG_TXFIFOHE | SDIO_IT_TXUNDERR | SDIO_IT_STBITERR, ENABLE);
    while ((TransferEnd == 0) && (TransferError == SD_OK))
    {}
    if (TransferError != SD_OK)
    {
      return(TransferError);
    }
  }
  else if (DeviceMode == SD_DMA_MODE)
  {
    /* DMA模式 */
    SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND | SDIO_IT_TXUNDERR | SDIO_IT_STBITERR, ENABLE);
    SD_LowLevel_DMA_TxConfig((uint32_t *)writebuff, BlockSize);
    SDIO_DMACmd(ENABLE);
    while ((SD_DMAEndOfTransferStatus() == RESET) && (TransferEnd == 0) && (TransferError == SD_OK))
    {}
    if (TransferError != SD_OK)
    {
      return(TransferError);
    }
  }
  /*!< Clear all the static flags */
  SDIO_ClearFlag(SDIO_STATIC_FLAGS);
  /*等待卡退出编程状态 */
  errorstatus = IsCardProgramming(&cardstate);
  while ((errorstatus == SD_OK) && ((cardstate == SD_CARD_PROGRAMMING) || (cardstate == SD_CARD_RECEIVING)))
  {
    errorstatus = IsCardProgramming(&cardstate);
  }
  return(errorstatus);
}

出0入0汤圆

 楼主| 发表于 2013-10-9 14:48:04 | 显示全部楼层
/**
  * @摘要  允许从卡中的指定地址写多个块的数据
  * @参数  WriteAddr: 将要写数据的地址
  * @参数  writebuff: 指向存放将要发送数据的buffer
  * @参数  BlockSize: SD卡数据块的大小
  * @参数  NumberOfBlocks: 要写的块的数量
  * @返回值l SD_Error: SD卡错误代码
  */
SD_Error SD_WriteMultiBlocks(uint8_t *writebuff, uint32_t WriteAddr, uint16_t BlockSize, uint32_t NumberOfBlocks)
{
  SD_Error errorstatus = SD_OK;
  uint8_t  power = 0, cardstate = 0;
  uint32_t bytestransferred = 0;
  uint32_t restwords = 0;
  uint32_t *tempbuff = (uint32_t *)writebuff;
  __IO uint32_t count = 0;
  
  if (writebuff == NULL)
  {
    errorstatus = SD_INVALID_PARAMETER;
    return(errorstatus);
  }
  TransferError = SD_OK;
  TransferEnd = 0;
  TotalNumberOfBytes = 0;
  SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
  SDIO_DataInitStructure.SDIO_DataLength = 0;
  SDIO_DataInitStructure.SDIO_DataBlockSize = SDIO_DataBlockSize_1b;
  SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToCard;
  SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
  SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Disable;
  SDIO_DataConfig(&SDIO_DataInitStructure);
  SDIO_DMACmd(DISABLE);
  if (SDIO_GetResponse(SDIO_RESP1) & SD_CARD_LOCKED)
  {
    errorstatus = SD_LOCK_UNLOCK_FAILED;
    return(errorstatus);
  }
  if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
  {
    BlockSize = 512;
    WriteAddr /= 512;
  }
  
  /*设置主机和卡的块的大小 */
  if ((BlockSize > 0) && (BlockSize <= 2048) && ((BlockSize & (BlockSize - 1)) == 0))
  {
    power = convert_from_bytes_to_power_of_two(BlockSize);
    SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) BlockSize;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);
    errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);
    if (errorstatus != SD_OK)
    {
      return(errorstatus);
    }
  }
  else
  {
    errorstatus = SD_INVALID_PARAMETER;
    return(errorstatus);
  }
  /*等待卡准备好接收数据 */
  SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) (RCA << 16);
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_STATUS;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);
  errorstatus = CmdResp1Error(SD_CMD_SEND_STATUS);
  if (errorstatus != SD_OK)
  {
    return(errorstatus);
  }
  if (NumberOfBlocks > 1)
  {
    /*!< Common to all modes */
    if (NumberOfBlocks * BlockSize > SD_MAX_DATA_LENGTH)
    {
      errorstatus = SD_INVALID_PARAMETER;
      return(errorstatus);
    }
    if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType) || (SDIO_HIGH_CAPACITY_SD_CARD == CardType))
    {
      /*!< To improve performance */
      SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) (RCA << 16);
      SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
      SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
      SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
      SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
      SDIO_SendCommand(&SDIO_CmdInitStructure);

      errorstatus = CmdResp1Error(SD_CMD_APP_CMD);
      if (errorstatus != SD_OK)
      {
        return(errorstatus);
      }
      /*!< To improve performance */
      SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)NumberOfBlocks;
      SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCK_COUNT;
      SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
      SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
      SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
      SDIO_SendCommand(&SDIO_CmdInitStructure);
      errorstatus = CmdResp1Error(SD_CMD_SET_BLOCK_COUNT);
      if (errorstatus != SD_OK)
      {
        return(errorstatus);
      }
    }
    /*发送 CMD25(WRITE_MULT_BLOCK)命令, 参数为要写数据的地址 */
    SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)WriteAddr;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_WRITE_MULT_BLOCK;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);
    errorstatus = CmdResp1Error(SD_CMD_WRITE_MULT_BLOCK);
    if (SD_OK != errorstatus)
    {
      return(errorstatus);
    }
    TotalNumberOfBytes = NumberOfBlocks * BlockSize;
    StopCondition = 1;
    SrcBuffer = (uint32_t *)writebuff;
    SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
    SDIO_DataInitStructure.SDIO_DataLength = NumberOfBlocks * BlockSize;
    SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) power << 4;
    SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToCard;
    SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
    SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
    SDIO_DataConfig(&SDIO_DataInitStructure);
    if (DeviceMode == SD_POLLING_MODE)
    {
      /* 查询模式 */
      while (!(SDIO->STA & (SDIO_FLAG_TXUNDERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DATAEND | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_STBITERR)))
      {
        if (SDIO_GetFlagStatus(SDIO_FLAG_TXFIFOHE) != RESET)
        {
          if (!((TotalNumberOfBytes - bytestransferred) < SD_HALFFIFOBYTES))
          {
            for (count = 0; count < SD_HALFFIFO; count++)
            {
              SDIO_WriteData(*(tempbuff + count));
            }
            tempbuff += SD_HALFFIFO;
            bytestransferred += SD_HALFFIFOBYTES;
          }
          else
          {
            restwords = ((TotalNumberOfBytes - bytestransferred) % 4 == 0) ? ((TotalNumberOfBytes - bytestransferred) / 4) :
                        ((TotalNumberOfBytes - bytestransferred) / 4 + 1);
            for (count = 0; count < restwords; count++, tempbuff++, bytestransferred += 4)
            {
              SDIO_WriteData(*tempbuff);
            }
          }
        }
      }
      if (SDIO_GetFlagStatus(SDIO_FLAG_DTIMEOUT) != RESET)
      {
        SDIO_ClearFlag(SDIO_FLAG_DTIMEOUT);
        errorstatus = SD_DATA_TIMEOUT;
        return(errorstatus);
      }
      else if (SDIO_GetFlagStatus(SDIO_FLAG_DCRCFAIL) != RESET)
      {
        SDIO_ClearFlag(SDIO_FLAG_DCRCFAIL);
        errorstatus = SD_DATA_CRC_FAIL;
        return(errorstatus);
      }
      else if (SDIO_GetFlagStatus(SDIO_FLAG_TXUNDERR) != RESET)
      {
        SDIO_ClearFlag(SDIO_FLAG_TXUNDERR);
        errorstatus = SD_TX_UNDERRUN;
        return(errorstatus);
      }
      else if (SDIO_GetFlagStatus(SDIO_FLAG_STBITERR) != RESET)
      {
        SDIO_ClearFlag(SDIO_FLAG_STBITERR);
        errorstatus = SD_START_BIT_ERR;
        return(errorstatus);
      }
      if (SDIO_GetFlagStatus(SDIO_FLAG_DATAEND) != RESET)
      {
       if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType) || (SDIO_HIGH_CAPACITY_SD_CARD == CardType))
        {
          /*发送 CMD12(STOP_TRANSMISSION)命令 */
          SDIO_CmdInitStructure.SDIO_Argument = 0x0;
          SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_STOP_TRANSMISSION;
          SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
          SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
          SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
          SDIO_SendCommand(&SDIO_CmdInitStructure);

          errorstatus = CmdResp1Error(SD_CMD_STOP_TRANSMISSION);
          if (errorstatus != SD_OK)
          {
            return(errorstatus);
          }
        }
      }
    }
    else if (DeviceMode == SD_INTERRUPT_MODE)
    {
      /* 中断模式 */
      SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND | SDIO_IT_TXFIFOHE | SDIO_IT_TXUNDERR | SDIO_IT_STBITERR, ENABLE);
      while ((TransferEnd == 0) && (TransferError == SD_OK))
      {}
      if (TransferError != SD_OK)
      {
        return(TransferError);
      }
    }
    else if (DeviceMode == SD_DMA_MODE)
    {
     /* DMA模式 */
      SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND | SDIO_IT_TXUNDERR | SDIO_IT_STBITERR, ENABLE);
      SDIO_DMACmd(ENABLE);
      SD_LowLevel_DMA_TxConfig((uint32_t *)writebuff, (NumberOfBlocks * BlockSize));
      while ((SD_DMAEndOfTransferStatus() == RESET) && (TransferEnd == 0) && (TransferError == SD_OK))
      {}
      if (TransferError != SD_OK)
      {
        return(TransferError);
      }
    }
  }
  /*!< Clear all the static flags */
  SDIO_ClearFlag(SDIO_STATIC_FLAGS);
  
  /* 查询卡状态之前先延时一下 */
  for(count = 0; count < 0xFFFF; count++)
  {
  }
  /* 查询卡状态,等待卡退出编程状态 */
  errorstatus = IsCardProgramming(&cardstate);
  while ((errorstatus == SD_OK) && ((cardstate == SD_CARD_PROGRAMMING) || (cardstate == SD_CARD_RECEIVING)))
  {
    errorstatus = IsCardProgramming(&cardstate);
  }
  return(errorstatus);
}

出0入0汤圆

 楼主| 发表于 2013-10-9 14:48:23 | 显示全部楼层
下面两个函数只作简单介绍
SDTransferState SD_GetTransferState(void);  //获取SDIO是否正在传输状态,这个函数没有被调用到
SD_Error SD_StopTransfer(void);  //发送CMD12(STOP_TRANSMISSION)命令停止传输

出0入0汤圆

 楼主| 发表于 2013-10-9 14:48:39 | 显示全部楼层
/**
  * @摘要  允许擦除卡指定的内存区域
  * @参数  startaddr: 起始地址
  * @参数  endaddr: 结束地址
  * @返回值 SD_Error: SD卡错误代码
  */
SD_Error SD_Erase(uint32_t startaddr, uint32_t endaddr)
{
  SD_Error errorstatus = SD_OK;
  uint32_t delay = 0;
  __IO uint32_t maxdelay = 0;
  uint8_t cardstate = 0;
  /* 检查卡命令集是否支持擦除命令 */
  if (((CSD_Tab[1] >> 20) & SD_CCCC_ERASE) == 0)
  {
    errorstatus = SD_REQUEST_NOT_APPLICABLE;
    return(errorstatus);
  }
  maxdelay = 120000 / ((SDIO->CLKCR & 0xFF) + 2);
  if (SDIO_GetResponse(SDIO_RESP1) & SD_CARD_LOCKED)
  {
    errorstatus = SD_LOCK_UNLOCK_FAILED;
    return(errorstatus);
  }
  if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
  {
    startaddr /= 512;
    endaddr /= 512;
  }
  
  /* 根据 sd-card spec 1.0,发送ERASE_GROUP_START (CMD32) 和 erase_group_end(CMD33)命令 */
  if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType) || (SDIO_HIGH_CAPACITY_SD_CARD == CardType))
  {
    /* 发送CMD32(SD_ERASE_GRP_START)命令,参数为 startaddr */
    SDIO_CmdInitStructure.SDIO_Argument = startaddr;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_START;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);
    errorstatus = CmdResp1Error(SD_CMD_SD_ERASE_GRP_START);
    if (errorstatus != SD_OK)
    {
      return(errorstatus);
    }
    /*发送CMD33 (SD_ERASE_GRP_END)命令,参数为endaddr  */
    SDIO_CmdInitStructure.SDIO_Argument = endaddr;
    SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_END;
    SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
    SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
    SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
    SDIO_SendCommand(&SDIO_CmdInitStructure);
    errorstatus = CmdResp1Error(SD_CMD_SD_ERASE_GRP_END);
    if (errorstatus != SD_OK)
    {
      return(errorstatus);
    }
  }
  /*发送 CMD38 (ERASE)命令 */
  SDIO_CmdInitStructure.SDIO_Argument = 0;
  SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ERASE;
  SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
  SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
  SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
  SDIO_SendCommand(&SDIO_CmdInitStructure);
  errorstatus = CmdResp1Error(SD_CMD_ERASE);
  if (errorstatus != SD_OK)
  {
    return(errorstatus);
  }
  for (delay = 0; delay < maxdelay; delay++)
  {}
  /*等待卡退出编程状态 */
  errorstatus = IsCardProgramming(&cardstate);
  while ((errorstatus == SD_OK) && ((SD_CARD_PROGRAMMING == cardstate) || (SD_CARD_RECEIVING == cardstate)))
  {
    errorstatus = IsCardProgramming(&cardstate);
  }
  return(errorstatus);
}

出0入0汤圆

 楼主| 发表于 2013-10-9 14:49:08 | 显示全部楼层
SD_Error SD_SendStatus(uint32_t *pcardstatus) //返回卡的状态寄存器
SD_Error SD_SendSDStatus(uint32_t *psdstatus) //这个函数没有被调用到

出0入0汤圆

 楼主| 发表于 2013-10-9 14:49:28 | 显示全部楼层
/**
  * @摘要  处理SD卡中断
  * @参数  无
  * @返回值 SD_Error:SD卡错误代码
  */
SD_Error SD_ProcessIRQSrc(void)
{
  uint32_t count = 0, restwords = 0;
  if (DeviceMode == SD_INTERRUPT_MODE)
  {
    /* 中断模式 */
    if (SDIO_GetITStatus(SDIO_IT_RXFIFOHF) != RESET)
    {
      for (count = 0; count < SD_HALFFIFO; count++)
      {
        *(DestBuffer + count) = SDIO_ReadData();
      }
      DestBuffer += SD_HALFFIFO;
      NumberOfBytes += SD_HALFFIFOBYTES;
    }
    else if (SDIO_GetITStatus(SDIO_IT_TXFIFOHE) != RESET)
    {
      if ((TotalNumberOfBytes - NumberOfBytes) < SD_HALFFIFOBYTES)
      {
        restwords = ((TotalNumberOfBytes - NumberOfBytes) %  4 == 0) ?
                    ((TotalNumberOfBytes - NumberOfBytes) / 4) :
                    ((TotalNumberOfBytes - NumberOfBytes) / 4 + 1);
        for (count = 0; count < restwords;  count++, SrcBuffer++, NumberOfBytes += 4)
        {
          SDIO_WriteData(*SrcBuffer);
        }
      }
      else
      {
        for (count = 0; count < SD_HALFFIFO; count++)
        {
          SDIO_WriteData(*(SrcBuffer + count));
        }
        SrcBuffer += SD_HALFFIFO;
        NumberOfBytes += SD_HALFFIFOBYTES;
      }
    }
  }
  if (SDIO_GetITStatus(SDIO_IT_DATAEND) != RESET)
  {
    if (DeviceMode != SD_DMA_MODE)
    {
      while ((SDIO_GetFlagStatus(SDIO_FLAG_RXDAVL) != RESET)  &&  (NumberOfBytes < TotalNumberOfBytes))
      {
        *DestBuffer = SDIO_ReadData();
        DestBuffer++;
        NumberOfBytes += 4;
      }
    }
    if (StopCondition == 1)
    {
      TransferError = SD_StopTransfer();
    }
    else
    {
      TransferError = SD_OK;
    }
    SDIO_ClearITPendingBit(SDIO_IT_DATAEND);
    SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND |
                  SDIO_IT_TXFIFOHE | SDIO_IT_RXFIFOHF | SDIO_IT_TXUNDERR |
                  SDIO_IT_RXOVERR | SDIO_IT_STBITERR, DISABLE);
    TransferEnd = 1;
    NumberOfBytes = 0;
    return(TransferError);
  }
  if (SDIO_GetITStatus(SDIO_IT_DCRCFAIL) != RESET)
  {
    SDIO_ClearITPendingBit(SDIO_IT_DCRCFAIL);
    SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND |
                  SDIO_IT_TXFIFOHE | SDIO_IT_RXFIFOHF | SDIO_IT_TXUNDERR |
                  SDIO_IT_RXOVERR | SDIO_IT_STBITERR, DISABLE);
    NumberOfBytes = 0;
    TransferError = SD_DATA_CRC_FAIL;
    return(SD_DATA_CRC_FAIL);
  }
  if (SDIO_GetITStatus(SDIO_IT_DTIMEOUT) != RESET)
  {
    SDIO_ClearITPendingBit(SDIO_IT_DTIMEOUT);
    SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND |
                  SDIO_IT_TXFIFOHE | SDIO_IT_RXFIFOHF | SDIO_IT_TXUNDERR |
                  SDIO_IT_RXOVERR | SDIO_IT_STBITERR, DISABLE);
    NumberOfBytes = 0;
    TransferError = SD_DATA_TIMEOUT;
    return(SD_DATA_TIMEOUT);
  }
  if (SDIO_GetITStatus(SDIO_IT_RXOVERR) != RESET)
  {
    SDIO_ClearITPendingBit(SDIO_IT_RXOVERR);
    SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND |
                  SDIO_IT_TXFIFOHE | SDIO_IT_RXFIFOHF | SDIO_IT_TXUNDERR |
                  SDIO_IT_RXOVERR | SDIO_IT_STBITERR, DISABLE);
    NumberOfBytes = 0;
    TransferError = SD_RX_OVERRUN;
    return(SD_RX_OVERRUN);
  }
  if (SDIO_GetITStatus(SDIO_IT_TXUNDERR) != RESET)
  {
    SDIO_ClearITPendingBit(SDIO_IT_TXUNDERR);
    SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND |
                  SDIO_IT_TXFIFOHE | SDIO_IT_RXFIFOHF | SDIO_IT_TXUNDERR |
                  SDIO_IT_RXOVERR | SDIO_IT_STBITERR, DISABLE);
    NumberOfBytes = 0;
    TransferError = SD_TX_UNDERRUN;
    return(SD_TX_UNDERRUN);
  }
  if (SDIO_GetITStatus(SDIO_IT_STBITERR) != RESET)
  {
    SDIO_ClearITPendingBit(SDIO_IT_STBITERR);
    SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND |
                  SDIO_IT_TXFIFOHE | SDIO_IT_RXFIFOHF | SDIO_IT_TXUNDERR |
                  SDIO_IT_RXOVERR | SDIO_IT_STBITERR, DISABLE);
    NumberOfBytes = 0;
    TransferError = SD_START_BIT_ERR;
    return(SD_START_BIT_ERR);
  }
  return(SD_OK);
}

出0入0汤圆

 楼主| 发表于 2013-10-9 17:29:47 | 显示全部楼层
3.5固件库SDIO代码修改:
3.5固件库例程\STM32F10x_StdPeriph_Examples\SDIO\uSDCard

把V4.5.0的下面两个文件替换成V4.4.0的(可以从官方USB库里拷贝过来,USB库里的SDIO是没有问题的)
stm32_eval_sdio_sd.c
stm32_eval_sdio_sd.h

然后把用到下面两个函数的地方注释掉,因为V4.4.0没有这两个函数,也不需要用这两个函数。
//    /* Check if the Transfer is finished */
//    Status = SD_WaitReadOperation();
//    while(SD_GetStatus() != SD_TRANSFER_OK);

//    /* Check if the Transfer is finished */
//    Status = SD_WaitWriteOperation();
//    while(SD_GetStatus() != SD_TRANSFER_OK);

还要把时钟修改:
/* SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_TRANSFER_CLK_DIV) */
这里HCLK = 72Mhz,SDIO时钟SDIO_CK = 72Mhz/2 = 36Mhz太高了,改为72Mhz/3 = 24Mhz
/**
  * @brief  SDIO Data Transfer Frequency (25MHz max)
  */

#define SDIO_TRANSFER_CLK_DIV            ((uint8_t)0x00)
改为
#define SDIO_TRANSFER_CLK_DIV            ((uint8_t)0x01)

出0入0汤圆

发表于 2013-10-9 20:16:13 | 显示全部楼层
好东西  支持了

出0入0汤圆

 楼主| 发表于 2013-10-9 21:31:05 | 显示全部楼层
乐_vip 发表于 2013-10-9 20:16
好东西  支持了

多谢支持

出0入0汤圆

发表于 2013-10-9 21:46:24 | 显示全部楼层
MRAK..................

出0入0汤圆

发表于 2013-10-9 22:47:12 | 显示全部楼层
支持一下,等忙过这一阵子买块开发板学习一下。

出0入0汤圆

发表于 2013-10-9 22:49:36 | 显示全部楼层
开源到这种程度,只能说是牛人一个

出0入0汤圆

 楼主| 发表于 2013-10-10 12:31:52 | 显示全部楼层
cooleryou 发表于 2013-10-9 22:47
支持一下,等忙过这一阵子买块开发板学习一下。

多谢支持

出0入0汤圆

发表于 2013-10-10 13:05:50 | 显示全部楼层
对初学者很有用,赞一个!顶起来,可以学到不少内容

出0入0汤圆

发表于 2013-10-10 13:10:54 | 显示全部楼层
开源好,帮顶一下

出0入0汤圆

 楼主| 发表于 2013-10-10 14:27:08 | 显示全部楼层
落叶随风 发表于 2013-10-10 13:05
对初学者很有用,赞一个!顶起来,可以学到不少内容

多谢帮顶,这应该是市面最详细的SDIO教程了,呵呵

出0入0汤圆

 楼主| 发表于 2013-10-10 14:27:51 | 显示全部楼层
csb1030 发表于 2013-10-10 13:10
开源好,帮顶一下

多谢帮顶,会继续更新的

出0入0汤圆

发表于 2013-10-10 22:54:01 | 显示全部楼层
楼主好人,顶一个!

出0入0汤圆

发表于 2013-10-11 09:03:29 | 显示全部楼层
顶一个,支持一下吧!

出0入0汤圆

发表于 2013-10-11 09:26:15 | 显示全部楼层
下来先学习学习,资料很详细

出0入0汤圆

发表于 2013-10-11 12:02:11 | 显示全部楼层
必须得支持下!啊

出0入0汤圆

 楼主| 发表于 2013-10-12 13:51:48 | 显示全部楼层
STM32学习任务十九——SDIO+FATFS

实验目的:在STM32学习任务十八——SDIO里我们已经介绍SD卡协议和SD卡读写测试,但是还没有增加文件系统,没法读SD卡内的文件。
在这节里我们将增加最常用的文件系统FATFS,并编程程序读写SD卡内的文件。

一、FATFS简介
二、FATFS移植
三、编程实现读写SD卡的文件

一、FATFS简介
FATFS官网主页:
http://elm-chan.org/fsw/ff/00index_e.html

FATFS是用C写的一个FAT文件系统。它的应用层和驱动层是完全分开的,所以很容易移植到其他处理器,如AVR, 8051, PIC, ARM等等。

应用层接口(Application Interface):

f_open - Open/Create a file
f_close - Close an open file
f_read - Read file
f_write - Write file
f_lseek - Move read/write pointer, Expand file size
f_truncate - Truncate file size
f_sync - Flush cached data
f_forward - Forward file data to the stream
f_stat - Check existance of a file or sub-directory
f_opendir - Open a directory
f_closedir - Close an open directory
f_readdir - Read a directory item
f_mkdir - Create a sub-directory
f_unlink - Remove a file or sub-directory
f_chmod - Change attribute
f_utime - Change timestamp
f_rename - Rename/Move a file or sub-directory
f_chdir - Change current directory
f_chdrive - Change current drive
f_getcwd - Retrieve the current directory
f_getfree - Get free clusters
f_getlabel - Get volume label
f_setlabel - Set volume label
f_mount - Register/Unregister a work area
f_mkfs - Create a file system on the drive
f_fdisk - Divide a physical drive
f_gets - Read a string
f_putc - Write a character
f_puts - Write a string
f_printf - Write a formatted string
f_tell - Get current read/write pointer
f_eof - Test for end-of-file on a file
f_size - Get size of a file
f_error - Test for an error on a file



驱动层接口(Disk I/O Interface):

disk_initialize - Initialize disk drive
disk_status - Get disk status
disk_read - Read sector(s)
disk_write - Write sector(s)
disk_ioctl - Control device dependent features
get_fattime - Get current time




二、FATFS移植
我们移植主要是要实现驱动层接口的函数。
SDIO的移植参看"STM32学习任务十八——SDIO"

首先到FATFS官网下载FATFS最新版R0.10
下载地址: http://elm-chan.org/fsw/ff/ff10.zip

然后解压把ff10/src里面的内容拷贝到工程目录Utilities\FatFS_R0.10目录下

然后在工程里添加组FatFS,添加Utilities\FatFS_R0.10目录下的ff.c diskio.c文件。
添加Utilities\FatFS_R0.10\option目录下的.c文件。



然后实现diskio.c里面的几个函数:
DSTATUS disk_initialize (BYTE pdrv);
DSTATUS disk_status (BYTE pdrv);
DRESULT disk_read (BYTE pdrv, BYTE*buff, DWORD sector, UINT count);
DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);

修改ffconf.h,使系统支持长文件名

#define _USE_LFN 0
改为
#define _USE_LFN 1


#define _CODE_PAGE 932
改为
#define _CODE_PAGE 936 //Simplified Chinese GBK (DBCS, OEM, Windows)

编译之后还会报错,这里还有个get_fattime函数要实现,我们在这里先实现为空,在diskio.c的最后面加入如下代码:
DWORD get_fattime (void)
{
}

修改ffconf.h,使系统支持格式化

#define _USE_MKFS  0
改为
#define _USE_MKFS  1



三、编程实现读写SD卡的文件
status = SD_Init();  //初始化SD卡
if(status != SD_OK)
{
  printf("sd card init error.\n\r");
}
res = f_mount(&fs ,"0:", 0);  //加载文件系统
if(res != FR_OK)
{
  printf("f_mount error = %d\n\r",res);
}
res = f_mkfs("0:", 0, 0);  //格式化SD卡
if(res != FR_OK)
{
  printf("f_mkfs error = %d\n\r",res);
}
res = f_open (&F, "1.txt", FA_CREATE_ALWAYS | FA_READ | FA_WRITE);  //创建读写文件1.txt
if(res != FR_OK)
{
  printf("f_open error = %d\n\r",res);
}
strcpy(Buffer, "hello world");
res = f_write (&F, Buffer, strlen(Buffer), &BytesWritten); //把字符串"hello world"写入文件
if(res != FR_OK)
{
  printf("f_write error = %d\n\r",res);
}
res = f_read (&F, Buffer, _MAX_SS, &BytesRead); //读出文件内的内容
if(res != FR_OK)
{
  printf("f_read error = %d\n\r",res);
}
else
{
  printf("f_read = %s\n\r", Buffer);
}

程序已上传到网盘:
百为STM32_SDIO_FATFS程序.rar

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-10-12 22:16:19 | 显示全部楼层
前面教程都比较枯燥,后面来图片显示了,要显示美女什么的,做界面啊,都可以用了

出0入0汤圆

 楼主| 发表于 2013-10-13 00:59:38 | 显示全部楼层
STM32学习任务二十——BMP图片显示

实验目的:从FATFS文件系统的SD卡内读取BMP图片显示到LCD上

主要内容:
一、BMP文件格式
二、用程序定义BMP文件结构
三、把SD卡内(FATFS文件系统)的图片显示到LCD上

参考文档:
微软官方bitmap相关文档(英文):
http://msdn.microsoft.com/en-us/library/dd183377(v=vs.85).aspx

BMP格式文档(英文)——Microsoft Windows Bitmap Format
http://www.fileformat.info/forma ... 789c886d9c/view.htm


BMP文件格式
一个.bmp文件包括以下四个部分,位图文件头,位图文件信息头,颜色表,点阵字节数据:


BITMAPFILEHEADER bmfh;   //位图文件头
BITMAPINFOHEADER bmih;  //位图文件信息头
RGBQUAD          aColors[];   //颜色表
BYTE             aBitmapBits[];  //点阵字节数据

BMP文件结构具体内容:




二、用程序定义BMP文件结构

1、位图文件头
BITMAPFILEHEADER结构定义如下:
typedef struct tagBITMAPFILEHEADER {
  WORD  bfType;  //位图文件的类型
  DWORD bfSize;  //位图文件的大小,以字节为单位
  WORD  bfReserved1;  //位图文件保留字
  WORD  bfReserved2;  //位图文件保留字
  DWORD bfOffBits;  //位图数据的起始位置,以相对于位图文件头的偏移量表示,以字节为单位
} BITMAPFILEHEADER, *PBITMAPFILEHEADER;

2、位图文件信息头
BITMAPINFOHEADER结构定义如下:
typedef struct tagBITMAPINFOHEADER{
  DWORD biSize;   //本结构所占用字节数
  LONG biWidth;  //位图的宽度,以像素为单位
  LONG biHeight;  //位图的高度,以像素为单位
  WORD biPlanes;  //Specifies the number of planes for the target device. This value must be set to 1.
  WORD biBitCount;  //每个像素所占的位数,1(黑白),4(16色),8(256色),16(65535色),24(真彩色),32(真彩色)
  DWORD biCompression;  //位图压缩类型,0(不压缩),1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)
  DWORD biSizeImage;  //位图的大小,以字节为单位
  LONG biXPelsPerMeter;  //位图水平分辨率,用像素/米表示
  LONG biYPelsPerMeter;  //位图垂直分辨率,用像素/米表示
  DWORD biClrUsed;  //位图实际使用的颜色表中的颜色数目
  DWORD biClrImportant;  //位图显示所需要的颜色数目,若为0,则所有颜色被使用
} BITMAPINFOHEADER;

3、颜色表
RGBQUAD结构定义如下:
typedef struct tagRGBQUAD {
  BYTE rgbBlue;  //蓝色的强度
  BYTE rgbGreen;  //绿色的强度
  BYTE rgbRed;  //红色的强度
  BYTE rgbReserved;
} RGBQUAD;

三、把SD卡内(FATFS文件系统)的图片显示到LCD上

/* draw_bmp.c */
/**
  * @摘要     从SD卡读出一个BMP图片显示到LCD上.
  * @参数  Xpos: X坐标
  * @参数  Ypos: Y坐标
  * @参数  BmpName: SD卡内的BMP文件名(可包含路径)
  * @返回值 无
  */
void GUI_DrawBMP(uint32_t Xpos, uint32_t Ypos, const char* BmpName)
{
  uint32_t size = 0, offset = 0;
uint32_t width = 0, height = 0;
uint32_t compression, clrUsed;
uint16_t type, bitCount;
uint32_t BmpAddress;
uint32_t x, y;
uint32_t Color, BkColor, MixColor;
  FIL F;
  f_open (&F, BmpName, FA_READ);  //打开BMP文件
  f_read (&F, Buffer, 54, &BytesRead);  //读出54字节数据,取出BMP文件信息
BmpAddress = (uint32_t)Buffer;
// type = *(uint16_t *) (BmpAddress);  //位图文件的类型
  
  size = *(uint16_t *) (BmpAddress + 2);  //位图文件的大小
  size |= (*(uint16_t *) (BmpAddress + 4)) << 16;

  offset = *(uint16_t *) (BmpAddress + 10);  //位图数据的起始位置
  offset |= (*(uint16_t *) (BmpAddress + 12)) << 16;

  width = *(uint16_t *) (BmpAddress + 18);  //位图的宽度
  width |= (*(uint16_t *) (BmpAddress + 20)) << 16;

  height = *(uint16_t *) (BmpAddress + 22);  //位图的高度
  height |= (*(uint16_t *) (BmpAddress + 24)) << 16;

bitCount = *(uint16_t *) (BmpAddress + 28);  //每个像素所占的位数
compression = *(uint16_t *) (BmpAddress + 30);  //位图压缩类型
compression |= *(uint16_t *) (BmpAddress + 32) << 16;
clrUsed = *(uint16_t *) (BmpAddress + 46);  //位图实际使用的颜色表中的颜色数目
clrUsed |= *(uint16_t *) (BmpAddress + 48) << 16;
  size = (size - offset)/2;
  f_read (&F, Buffer, offset - 54, &BytesRead);  //把读指针指向位图数据的起始位置
switch (bitCount)
{
    case 1:
    case 4:
    case 8:
   break;
  case 16:  //这里用的是16位565格式的BMP
   for (y = height - 1; (y < height) && (y >= 0); y -= 1)
   {
    if(width*2>512)  //如果显示一行的数据超过512,分两次读
    {
     f_read (&F, Buffer, width, &BytesRead);
     f_read (&F, Buffer + width, width, &BytesRead);
    }
    else
    {
     f_read (&F, Buffer, width*2, &BytesRead);
    }
    for(x = 0; x < width; x++)  //显示一行图象数据
    {
     Color = *(uint16_t*)(Buffer + 2*x); //16位565格式,一个像素数据占两个字节
      LCD_SetCursor(Xpos + x, Ypos + y);
      LCD_WriteRAM_Prepare(); /* Prepare to write GRAM */
      LCD_WriteRAM(Color);
    }
   }   
   f_close (&F); //显示完,关闭文件
   break;
  case 24:
  case 32:
   break;   
}
}

/* main.c */
调用GUI_DrawBMP函数在坐标(0,0)显示图像
GUI_DrawBMP(0,0,"STFILES/STLogo.bmp");

程序源码已上传到网盘:
百为STM32_SDIO_FATFS_BMP程序.rar

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-10-13 15:50:00 | 显示全部楼层
百为STM32_SDIO_FATFS_BMP程序.rar
这个程序包含了画点画任意直线,矩形,圆形,显示汉字,显示BMP图片(16位565格式,用photoshop可以转换)等函数。大家可以自由DIY界面了

还差个触摸,迟点把触摸画板程序整合过来

出0入0汤圆

 楼主| 发表于 2013-10-14 17:46:59 | 显示全部楼层
目前已把 STM32 3.5固件库 + UCOS2.92 + UCGUI3.90 + USB库3.30 + FATFS R0.10 整合在一起

源码上传网盘
百为STM32_stdlib3.5_ucosii2.92_ucgui3.90.rar

出0入0汤圆

 楼主| 发表于 2013-10-14 22:07:32 | 显示全部楼层
百为STM32开发板UCGUI移植步骤

一、UCOS移植
这个参考110楼“百为STM32开发板UCOSII移植步骤”
源码已上传到网盘——百为STM32_3.5固件库_uCOS2.92源码.rar
工程目录图:


二、UCGUI移植
1、下载UCGUI3.90源码
首先下载UCGUI3.90源码,注意网上流传的很多UCGUI代码有问题。具体可以参考这个帖子:

完整源码版uCOSII2.86+uCGUI3.90在stm32上的移植,含uCGUI3.9源码(不是.lib)
http://www.amobbs.com/thread-4787247-1-1.html

2、然后在工程里add groud创建组,把UCGUI代码加进工程


把创建的New Group组改名为uCGUI/Config
用同样的方法创建uCGUI/GUI_X,uCGUI/LCDDriver等组,
然后把UCGUI390a\Start目录下的对应目录C文件添加到对应的组上

建立组uCGUI,然后把UCGUI390a\Start\GUI目录下的所有目录C文件添加到该组上
这里创建组是为了阅读方便,不是必需的。建立好后的目录结构如下:


3、增加LCD驱动接口LCDDriver.c
实现UCGUI的LCD接口,主要是实现下面LCD_L0_XXXX这些函数,因为上层画点画线画图形最终调用的都是这些函数:
   LCD_L0_Init,
    (tLCDDEV_DrawBitmap*)LCD_L0_DrawBitmap,
    LCD_L0_DrawHLine,
    LCD_L0_DrawVLine,
    LCD_L0_FillRect,
    LCD_L0_GetPixelIndex,
    LCD_L0_SetPixelIndex,
    LCD_L0_XorPixel,
    LCD_L0_SetLUTEntry,

但最简单的方法不是每个函数重新去编写,按照LCDWin.c来修改比较快,
在LCD_L0_Init初始化时增加调用9320 LCD初始化代码STM3210E_LCD_Init();
把LCDSIM_SetPixelIndex改名为ili9320_SetPixelIndex
把LCDSIM_GetPixelIndex改名为ili9320_GetPixelIndex

然后在底层ili9320_lcd.c里增加以下三个函数
void ili9320_SetPixelIndex(u16 Xpos, u16 Ypos, u16 c)
{

  LCD_SetCursor(Xpos, Ypos);
  LCD_WriteRAM_Prepare(); /* Prepare to write GRAM */
  LCD_WriteRAM(c);

}

u16 ili9320_GetPixelIndex(u16 Xpos, u16 Ypos)
{
  u16 c;
  LCD_SetCursor(Xpos, Ypos);
  c = LCD_ReadRAM();  //在这个函数里,注意9320 LCD的读GRAM需要读两次,第一次为无效数据,第二次才是有效数据,具体可以看9320数据手册
  return(ili9320_BGR2RGB(c));
}

u16 ili9320_BGR2RGB(u16 c)
{
  u16 r,g,b;
  u16 rgb;
  b = (c>>0) & 0x1f;
  g = (c>>5) & 0x3f;
  r = (c>>11) & 0x1f;
  rgb = (b<<11) + (g<<5) + (r<<0);
  return( rgb );
}

4、在GUI_X_Touch.c里增加触摸驱动接口
主要是实现读触摸坐标的两个函数:
int  GUI_TOUCH_X_MeasureX(void) {
  U32 i,j=0;
  for(i=0;i<10;i++)
  {
    j+=SPI_TOUCH_Read_X();
  }
        
  return j/10;
}

int  GUI_TOUCH_X_MeasureY(void) {
  U32 i,j=0;
  for(i=0;i<10;i++)
  {
    j+=SPI_TOUCH_Read_Y();
  }
        
  return j/10;
}

#define TOUCH_X_MeasureX   GUI_TOUCH_X_MeasureX
#define TOUCH_X_MeasureY   GUI_TOUCH_X_MeasureY

在GUI_TOUCH_Exec里面会默认调用上面两个函数采样坐标并转换,然后调用_StoreUnstable -> GUI_TOUCH_StoreUnstable ……
-> GUI_PID_StoreState存储触摸按下状态。

然后在WM_Exec -> WM_Exec1里会调用 WM_pfHandlePID -> WM_HandlePID - > GUI_PID_GetState获取出触摸按下状态并发送到相应窗口处理

在app.c里增加一个UCOS任务,在里面不断地调用GUI_TOUCH_Exec处理触摸消息
static  void  Task2_Func (void *p_arg)  //Task2_Func这个是用户自己创建的任务名,可以改为任意名字
{
    ……

    while (DEF_TRUE) {
    ……
    GUI_TOUCH_Exec();
    OSTimeDly(20);
    }
}

5、在GUI_X_uCOS.c里增加UCOS和UCGUI之间的接口:

void GUI_X_ExecIdle (void)
{
    OS_X_Delay(1);
}
改为
void GUI_X_ExecIdle (void)
{
    GUI_X_Delay(1);
}

实现下面几个空函数,作为打印调试信息等用途
void GUI_X_Log(const char *s)
{

}

void GUI_X_Warn(const char *s)
{

}

void GUI_X_ErrorOut(const char *s)
{

}

6、增加UCOS任务,在里面调用UCGUI DEMO任务MainTask:
static  void  User_Task_Func (void *p_arg)  //User_Task_Func这个是用户自己创建的任务名,可以改为任意名字
{
    ……
   
    while(1)
    {
        MainTask();
    }
}

MainTask任务如下,使用UCGUI前先调用 GUI_Init初始化UCGUI,然后运行UCGUI DEMO:

void MainTask(void) {
  GUI_Init();
  while(1) {
    GUIDEMO_main();  //这个函数可以改为自己的应用程序,这样就是最简单的UCGUI应用程序了
  }
}

但要注意的是UCOS的任务,都必须要有OSTimeDly等延时函数,这样其他任务才能被调度
所以这里的
  while(1) {
    GUIDEMO_main();  //这个函数可以改为自己的应用程序,这样就是最简单的UCGUI应用程序了
  }
并不是简单的while死循环,在GUIDEMO_main函数里是有简接调用OSTimeDly的,比如GUI_Delay,GUI_X_Delay等

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-10-15 08:53:47 | 显示全部楼层
UCGUI教程一——HelloWorld

实验目的:在LCD屏幕上显示一行字符helloworld

这是一个最简单的UCGUI的应用程序,
我们把这个应用作为UCOS的一个任务来跑
首先要使用UCGUI,必须先调用GUI_Init()函数进行初始化,这个函数会调用LCD_L0_Init -> STM3210E_LCD_Init()对LCD进行初始化,
另外还初始化UCGUI的一些全局变量(调用_InitContext()函数),比如默认字体,画笔大小,前景背景颜色等等。

/* BASIC_Hello1.c */

#include "GUI.h"

void MainTask(void) {
  int i=0;
  GUI_Init();  //初始化UCGUI
  GUI_DispString("Hello world!");  //在初始化位置(0,0)显示helloworld字符
  while(1) {
    GUI_DispDecAt( i++, 20,20,4);  //显示十进制数值
    if (i>9999) i=0;
  }
}

程序已上传到网盘目录(百为STM3210E-EVAL开发板例程更新 -> 百为STM32_UCGUI基础例程)
百为STM32_ucgui_helloworld.rar

运行图片:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-10-15 08:58:58 | 显示全部楼层
UCGUI教程二——Button控制LED

实验目的:调用UCGUI函数创建两个BUTTON按纽,按下其中一个按纽LED2亮,按另一个LED2灭

主要内容:
一、了解UCGUI父窗口
二、了解UCGUI窗口回调函数
三、编写UCGUI程序实现button控制LED


一、了解UCGUI父窗口
这里处理button的触摸消息是采用窗口回调方式,当触摸按下时,在触摸处理任务里读取触摸坐标并发给UCGUI内核处理,UCGUI内核会把相关消息分发给对应的窗口或控件。

下面来了解下父窗口

UCGUI默认在初始化的时候创建了一个父窗口(GUI_Init->WM_Init->WM_CreateWindow),句柄为WM_HBKWIN(句柄是一个short型整数,用来标识窗口和控件的)。

#define WM_HBKWIN      WM_GetDesktopWindow()                /* Handle of background window */

WM_HWIN WM_GetDesktopWindow(void) {
  return WM__ahDesktopWin[0];
}
在WM_Init函数里面调用WM_CreateWindow创建父窗口:
WM__ahDesktopWin[0] = WM_CreateWindow(0, 0, GUI_XMAX, GUI_YMAX, WM_CF_SHOW, cbBackWin, 0);

二、了解UCGUI窗口回调函数
设置窗口的回调函数是通过WM_SetCallback函数设置,第一个参数为窗口句柄,第二个参数是回调函数名。
例如要设置父窗口的回调函数为_cbBkWindow可以这样设置:
WM_SetCallback(WM_HBKWIN, _cbBkWindow);

那回调函数是怎么回事呢
先看下WM_SetCallback这个函数内容:
WM_CALLBACK* WM_SetCallback (WM_HWIN hWin, WM_CALLBACK* cb) {
  WM_CALLBACK* r = NULL;  
  if (hWin) {
    WM_Obj* pWin;
    WM_LOCK();
    pWin = WM_H2P(hWin);
    r = pWin->cb;
    pWin->cb = cb;
    WM_InvalidateWindow(hWin);
    WM_UNLOCK();
  }
  return r;
}

这里的窗口回调函数名是赋给每个窗口的pWin->cb,然后GUI_Exec函数通过层层调用,最终调用WM__SendMessage函数,再调用pWin->cb回调函数的:
GUI_Exec -> GUI_Exec1 -> WM_Exec -> WM_Exec1 -> WM_pfHandlePID -> WM_HandlePID -> WM__SendTouchMessage ->
WM__SendPIDMessage -> WM__SendMessageIfEnabled -> WM__SendMessage

void WM__SendMessage(WM_HWIN hWin, WM_MESSAGE* pMsg) {
  WM_Obj* pWin = WM_HANDLE2PTR(hWin);
  pMsg->hWin = hWin;
  if (pWin->cb != NULL) {
    (*pWin->cb)(pMsg);
  } else {
    WM_DefaultProc(pMsg);
  }
}

GUI_Exec一般是在while循环里调用来处理消息的。
这么看来,回调函数也就是在while循环里不断地被查询调用的函数(有对应消息到来时就被调用)。

三、编写UCGUI程序实现button控制LED

/* 父窗口的回调函数 */
static void _cbBkWindow(WM_MESSAGE* pMsg) {
  int NCode, Id;
  int x, y;
  switch (pMsg->MsgId) {
  case WM_PAINT: //处理WM_PAINT消息
    GUI_SetBkColor(GUI_GREEN);  //设置背景颜色
    GUI_Clear(); //用背景颜色清除屏幕
    GUI_SetColor(0x505000);  //设置颜色
    GUI_SetFont(&GUI_Font24_ASCII); //设置字体
  GUI_SetTextMode(GUI_TM_TRANS);  //设置字体透明显示
  GUI_DispStringHCenterAt("baiwei stm32", 160, 40);  //以坐标160为中点,显示字符
    break;
case WM_NOTIFY_PARENT:  //子窗口或控件消息
  Id    = WM_GetId(pMsg->hWinSrc);    /* Id of widget */
  NCode = pMsg->Data.v;               /* Notification code */
  switch (NCode) {
   case WM_NOTIFICATION_CLICKED:  //按下消息
    if (Id == GUI_ID_OK) {          /* OK Button */
     STM_EVAL_LEDOn(LED2);  //点亮LED2
    }
    if (Id == GUI_ID_CANCEL) {      /* Cancel Button */
     STM_EVAL_LEDOff(LED2);  //熄灭LED2
    }
    break;
   case WM_NOTIFICATION_RELEASED:  //松开消息
    if (Id == GUI_ID_OK) {          /* OK Button */
    }
    if (Id == GUI_ID_CANCEL) {      /* Cancel Button */
    }
    break;
  }
  break;
  default:
    WM_DefaultProc(pMsg);
  }
}

void MainTask(void)
{
GUI_Init();
WM_SetCallback(WM_HBKWIN, _cbBkWindow);  //设置父窗口的回调函数为_cbBkWindow
WM_SetDesktopColor(GUI_BLUE); //设置背景颜色
_ahButton[0] = BUTTON_CreateAsChild(170, 180, 60, 40, WM_HBKWIN, GUI_ID_OK , WM_CF_SHOW | WM_CF_STAYONTOP | WM_CF_MEMDEV); //创建按纽
_ahButton[1] = BUTTON_CreateAsChild(240, 180, 60, 40, WM_HBKWIN, GUI_ID_CANCEL , WM_CF_SHOW | WM_CF_STAYONTOP | WM_CF_MEMDEV); //创建按纽
BUTTON_SetText(_ahButton[0], "ON"); //按纽显示的字符为“ON”
BUTTON_SetText(_ahButton[1], "OFF");  //按纽显示的字符为“OFF”
while(1)
{
  GUI_Exec();  //处理消息
  GUI_X_ExecIdle();  //UCOS任务延时
}
}

程序已上传到网盘目录(百为STM3210E-EVAL开发板例程更新 -> 百为STM32_UCGUI基础例程)
百为STM32_ucgui_button控制LED.rar

运行图片:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-10-17 14:45:03 | 显示全部楼层
更新UCGUI例程,UCGUI里的DEMO有不少例子可以借鉴
百为STM32_ucgui_helloworld.rar
百为STM32_ucgui_button控制LED.rar
百为STM32_ucgui_2dgl_drawscale.rar
百为STM32_ucgui_aa_hiresantialiasing.rar
百为STM32_ucgui_aa_hirespixels.rar
百为STM32_ucgui_color_showcolorbar.rar
百为STM32_ucgui_cursor_sample.rar


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2013-10-17 15:54:15 | 显示全部楼层
顶起。。。。。。。。。。。。。。

出0入0汤圆

 楼主| 发表于 2013-10-19 06:20:00 | 显示全部楼层
UCGUI Button 3D增强效果


程序已更新到网盘
百为STM32_ucgui_button_3D_Enhance.rar

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-10-21 10:42:44 | 显示全部楼层
自己顶下

出0入0汤圆

 楼主| 发表于 2013-10-21 10:47:00 | 显示全部楼层
后面将会加入外部字库,SRAM刷屏,NAND FLASH + FATFS

出0入0汤圆

发表于 2013-10-21 11:05:42 | 显示全部楼层
楼主真心不错!顶了

出0入0汤圆

 楼主| 发表于 2013-10-22 10:30:51 | 显示全部楼层
UCGUI教程三——UCGUI外部字库

实验目的:增加UCGUI外部汉字库,实现汉字显示

主要内容:
一、了解UCGUI字库相关函数和数据结构
二、了解UCGUI字库显示调用过程
三、增加汉字显示代码,实现汉字显示


一、了解UCGUI字库相关函数和数据结构
1、GUI_FONTTYPE_PROP宏定义
首先了解下UCGUI字库显示的相关函数,在GUIType.h里有以下一段代码。
/* PROP: Proportional fonts */
DECLARE_FONT(PROP);  //声明一组函数
#define GUI_FONTTYPE_PROP       \
  GUIPROP_DispChar,             \
         GUIPROP_GetCharDistX,         \
         GUIPROP_GetFontInfo,          \
         GUIPROP_IsInFont,             \
  (tGUI_ENC_APIList*)0  //以上是宏GUI_FONTTYPE_PROP的定义,宏展开后为上面几个函数名。

实际使用时GUI_FONTTYPE_PROP会被插入到字库的GUI_FONT 结构体里,如GUI_Font24B_ASCII的定义如下:
GUI_CONST_STORAGE GUI_FONT GUI_Font24B_ASCII = {
   GUI_FONTTYPE_PROP        /* type of font    */
  ,24                       /* height of font  */
  ,24                       /* space of font y */
  ,1                        /* magnification x */
  ,1                        /* magnification y */
  ,{&GUI_Font24B_ASCII_Prop1}
  ,19, 11, 15
};
也就说GUI_FONTTYPE_PROP是字库对应的一组关于显示的函数。

2、DECLARE_FONT宏定义
上面还有个DECLARE_FONT(PROP),这个DECLARE_FONT宏在GUIType.h里定义如下
#define DECLARE_FONT(Type)                                     \
void GUI##Type##_DispChar    (U16P c);                         \
int  GUI##Type##_GetCharDistX(U16P c);                         \
void GUI##Type##_GetFontInfo (const GUI_FONT GUI_UNI_PTR * pFont, GUI_FONTINFO * pfi); \
char GUI##Type##_IsInFont    (const GUI_FONT GUI_UNI_PTR * pFont, U16 c)

这里主要是要弄明白##的含义:
##是C语言宏定义中的连接符,用来把前后两个参数连接起来。
例如DECLARE_FONT(PROP);
宏展开后为以下一段代码:
void GUIPROP_DispChar    (U16P c);                         \
int  GUIPROP_GetCharDistX(U16P c);                         \
void GUIPROP_GetFontInfo (const GUI_FONT GUI_UNI_PTR * pFont, GUI_FONTINFO * pfi); \
char GUIPROP_IsInFont    (const GUI_FONT GUI_UNI_PTR * pFont, U16 c);
那么DECLARE_FONT(PROP);是相当于声明上面几个字库显示相关函数。

3、GUI_FONT结构体的定义

GUI_FONT结构体的定义如下:
struct GUI_FONT {
  GUI_DISPCHAR*     pfDispChar;  //函数指针,显示字符函数
  GUI_GETCHARDISTX* pfGetCharDistX;   //函数指针,获得字符宽度
  GUI_GETFONTINFO*  pfGetFontInfo;  //函数指针,获得字符信息
  GUI_ISINFONT*     pfIsInFont;  //函数指针,判断字符是否在字库编码内
  const tGUI_ENC_APIList* pafEncode;  //结构体指针,一组字符编码相关函数。若为空,则默认用GUI_UC_EncodeNone.c里的函数
  U8 YSize;  //字库的高度
  U8 YDist;  //字库的Y间距
  U8 XMag; //X放大倍数
  U8 YMag;  //Y放大倍数
  union {
    const void          GUI_UNI_PTR * pFontData;
    const GUI_FONT_MONO GUI_UNI_PTR * pMono;
    const GUI_FONT_PROP GUI_UNI_PTR * pProp;  //GUI_FONT_PROP结构体指针
  } p;
  U8 Baseline;
  U8 LHeight;     /* height of a small lower case character (a,x) */
  U8 CHeight;     /* height of a small upper case character (A,X) */
};

4、GUI_FONT_PROP结构体
typedef struct GUI_FONT_PROP {
  U16P First;                                /* first character               */  //字库里的第一个字符的编码
  U16P Last;                                 /* last character                */  //字库里的最后一个字符的编码
  const GUI_CHARINFO GUI_UNI_PTR * paCharInfo;            /* address of first character    */  //第一个字符的GUI_CHARINFO结构信息
  const struct GUI_FONT_PROP GUI_UNI_PTR * pNext;        /* pointer to next */  //结构体指针,指向下一个GUI_FONT_PROP结构
} GUI_FONT_PROP;

5、GUI_CHARINFO结构体
typedef struct {
  U8 XSize;  //字库的宽度
  U8 XDist;  //字库的X间距
  U8 BytesPerLine;  //一行点阵数据占用的字节数。如16x16点阵,一行占用2个字节
  const unsigned char GUI_UNI_PTR * pData;  //指向字库点阵表首地址
} GUI_CHARINFO;

二、了解UCGUI字库显示调用过程
如在程序里调用GUI_DispStringAt("abc", 0, 0); 会调用下面的一系列函数,最终调用字库对应的pfDispChar进行显示:
GUI_DispStringAt -> GUI_DispString -> GUI__DispLine -> _DispLine -> GUI_Context.pAFont->pfDispChar(Char)

pfDispChar即是字库对应的显示函数,很多字库是调用GUIPROP_DispChar来显示,GUIPROP_DispChar定义如下:
void GUIPROP_DispChar(U16P c) {
  int BytesPerLine;  //一行点阵数据占用的字节数
  GUI_DRAWMODE DrawMode = GUI_Context.TextMode;  //字符显示模式,是否透明显示等
  const GUI_FONT_PROP GUI_UNI_PTR * pProp = GUIPROP_FindChar(GUI_Context.pAFont->p.pProp, c);  //查找字符对应的GUI_FONT_PROP结构体指针
  if (pProp) {
    GUI_DRAWMODE OldDrawMode;
    const GUI_CHARINFO GUI_UNI_PTR * pCharInfo = pProp->paCharInfo+(c-pProp->First);  //获得字符的pCharInfo信息。其中c-pProp->First是字符的paCharInfo结构体相对第一个字符paCharInfo的偏移
    BytesPerLine = pCharInfo->BytesPerLine;  //获得字符的一行点阵数据占用的字节数
    OldDrawMode  = LCD_SetDrawMode(DrawMode);  //获得字符显示模式
    LCD_DrawBitmap( GUI_Context.DispPosX, GUI_Context.DispPosY,  //把字符点阵数据扫描到LCD上
                       pCharInfo->XSize,
            GUI_Context.pAFont->YSize,
                       GUI_Context.pAFont->XMag,
            GUI_Context.pAFont->YMag,
                       1,     /* Bits per Pixel */
                       BytesPerLine,
                       pCharInfo->pData,  //pCharInfo->pData是字符的点阵数据
                       &LCD_BKCOLORINDEX
                       );
    /* Fill empty pixel lines */
    if (GUI_Context.pAFont->YDist > GUI_Context.pAFont->YSize) {
      int YMag = GUI_Context.pAFont->YMag;
      int YDist = GUI_Context.pAFont->YDist * YMag;
      int YSize = GUI_Context.pAFont->YSize * YMag;
      if (DrawMode != LCD_DRAWMODE_TRANS) {  //非透明模式下,需要画字符背景
        LCD_COLOR OldColor = GUI_GetColor();
        GUI_SetColor(GUI_GetBkColor());
        LCD_FillRect(GUI_Context.DispPosX,
                     GUI_Context.DispPosY + YSize,
                     GUI_Context.DispPosX + pCharInfo->XSize,
                     GUI_Context.DispPosY + YDist);
        GUI_SetColor(OldColor);
      }
    }
    LCD_SetDrawMode(OldDrawMode); /* Restore draw mode */
    GUI_Context.DispPosX += pCharInfo->XDist * GUI_Context.pAFont->XMag;
  }
}

三、增加汉字显示代码,实现汉字显示
1、首先定义汉字字库
首先增加文件hzk16.c,定义汉字字库GUI_CHARINFO,GUI_FONT_PROP,GUI_FONT 等结构体信息:
#include "..\core\GUI.H"
#ifndef GUI_FLASH
   #define GUI_FLASH
#endif
extern GUI_FLASH const GUI_FONT GUI_FontHZ16;
GUI_FLASH   const GUI_CHARINFO GUI_FontHZ16_CharInfo[] = {
    {  8, 8,  1, (void GUI_FLASH *)"0:/font/ASC16" },
    {  16, 16, 2, (void GUI_FLASH *)"0:/font/HZK16" },
};
GUI_FLASH  const GUI_FONT_PROP GUI_FontHZ16_PropHZ= {
      0xa1a1,
      0xf7fe,
      &GUI_FontHZ16_CharInfo[1],
      (void *)0,
};
GUI_FLASH const GUI_FONT_PROP GUI_FontHZ16_PropASC= {
      0x0020,
      0x007f,
      &GUI_FontHZ16_CharInfo[0],
      (void GUI_FLASH *)&GUI_FontHZ16_PropHZ ,
};
GUI_FLASH const GUI_FONT GUI_FontHZ16 = {
      GUI_FONTTYPE_PROP_XBF,
      16,
      16,
      1,  
      1,  
      (void GUI_FLASH *)&GUI_FontHZ16_PropASC
};

然后在GUI.h里加入声明:
extern GUI_CONST_STORAGE GUI_FONT GUI_FontHZ16;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2、定义外部字库相关宏
在GUIType.h里增加以下宏定义,定义字库相关显示函数:
/* PROP: Proportional fonts */
DECLARE_FONT(PROP_XBF);
#define GUI_FONTTYPE_PROP_XBF   \
  GUIPROP_XBF_DispChar,             \
GUIPROP_XBF_GetCharDistX,         \
GUIPROP_XBF_GetFontInfo,          \
GUIPROP_XBF_IsInFont,             \
  (tGUI_ENC_APIList*)0

3、修改GUI_UC_EncodeNone.c里面的代码,使系统支持GB2312汉字编码。
上面的(tGUI_ENC_APIList*)0表示用默认编码,当字体显示函数调用涉及到编码时,如果字体没有定义编码,则默认采用GUI_UC_EncodeNone.c里面的函数:
const GUI_UC_ENC_APILIST GUI__API_TableNone = {
  _GetCharCode,     /*  return character code as U16 */
  _GetCharSize,     /*  return size of character: 1 */
  _CalcSizeOfChar,  /*  return size of character: 1 */
  _Encode           /*  Encode character */
};

这里默认都是ASCII编码的,我们要增加汉字显示,所以要对_GetCharCode这几个函数进行修改。
/* File        : GUI_UC_EncodeNone.c */
#include "GUI_Protected.h"
/**********************************************************************/
static U16 _GetCharCode(const char GUI_UNI_PTR * s) {
if(*s > 0xA0)  //增加对汉字编码判断
  return *(const U16 GUI_UNI_PTR *)s;
else
   return *(const U8 GUI_UNI_PTR *)s;
}
/**********************************************************************/
static int _GetCharSize(const char GUI_UNI_PTR * s) {
  GUI_USE_PARA(s);
if(*s > 0xA0)  //增加对汉字编码判断
{
   return 2;
}
else
{
   return 1;
}
}
/**********************************************************************/
static int _CalcSizeOfChar(U16 Char) {
  GUI_USE_PARA(Char);
if(Char > 0xA0)  //增加对汉字编码判断
{
   return 2;
}
else
{
   return 1;
}
}
/**********************************************************************/
static int _Encode(char *s, U16 Char) {
if(Char > 0xA0)  //增加对汉字编码判断
{
  *(U16*)s = (U16)(Char);
   return 2;
}
else
{
   *s = (U8)(Char);
   return 1;
}
}
/**********************************************************************/
const GUI_UC_ENC_APILIST GUI__API_TableNone = {
  _GetCharCode,     /*  return character code as U16 */
  _GetCharSize,     /*  return size of character: 1 */
  _CalcSizeOfChar,  /*  return size of character: 1 */
  _Encode           /*  Encode character */
};

/**********************************************************************/
void GUI_UC_SetEncodeNone(void) {
  GUI_LOCK();
  GUI_Context.pUC_API = &GUI__API_TableNone;
  GUI_UNLOCK();
}

4、增加GUI_XBF_Prop.c,实现外部汉字库显示相关函数。
/*File        : GUI_XBF_Prop.c */
#include <stddef.h>           /* needed for definition of NULL */
#include "GUI_Private.h"
#include "ff.h"
/*********************************************************************
*
*       GUIPROP_FindChar
*/
static const GUI_FONT_PROP GUI_UNI_PTR * GUIPROP_FindChar(const GUI_FONT_PROP GUI_UNI_PTR* pProp, U16P c) {
  for (; pProp; pProp = pProp->pNext) {
    if ((c>=pProp->First) && (c<=pProp->Last))
      break;
  }
  return pProp;
}

/*********************************************************************
*
*       _GetpCharData
*/
static const U8 * _GetpCharData(const GUI_FONT_PROP GUI_UNI_PTR * pProp, U16P c, U8 * pData) {
FIL fl;
U32 len;
U32 offset;
  U8 byteOfDots;
U8 *fileName;
const GUI_CHARINFO GUI_UNI_PTR * pCharInfo = pProp->paCharInfo;
byteOfDots = GUI_Context.pAFont->YSize * pCharInfo->BytesPerLine;
fileName = (char *)pCharInfo->pData;
if (c < 0xA1)                                                              
{
  offset = c * byteOfDots;
}
else                                                                          
{
  offset = 94 * ((c & 0xFF) - 0xA1) * byteOfDots + ((c >> 8) - 0xA1) * byteOfDots;                                          
}
  f_open (&fl, fileName, FA_READ);
f_lseek(&fl, offset);
f_read (&fl, pData, byteOfDots, &len);

f_close(&fl);
return 0;
}
/*********************************************************************
*
*       Exported code
*
**********************************************************************
*/
/*********************************************************************
*
*       GUI_XBF_DispChar
*/
void GUIPROP_XBF_DispChar(U16P c) {
  U8 Data[128];
  int BytesPerLine;
  GUI_DRAWMODE DrawMode = GUI_Context.TextMode;
  const GUI_FONT_PROP GUI_UNI_PTR * pProp = GUIPROP_FindChar(GUI_Context.pAFont->p.pProp, c);
  if (pProp) {
    GUI_DRAWMODE OldDrawMode;
    const GUI_CHARINFO GUI_UNI_PTR * pCharInfo = pProp->paCharInfo;
    BytesPerLine = pCharInfo->BytesPerLine;
    _GetpCharData(pProp, c, Data);
    OldDrawMode  = LCD_SetDrawMode(DrawMode);
    LCD_DrawBitmap(GUI_Context.DispPosX, GUI_Context.DispPosY,
                       pCharInfo->XSize,
            GUI_Context.pAFont->YSize,
                       GUI_Context.pAFont->XMag,
            GUI_Context.pAFont->YMag,
                       1,     /* Bits per Pixel */
                       BytesPerLine,
                       Data,
                       &LCD_BKCOLORINDEX
                       );
    /* Fill empty pixel lines */
    if (GUI_Context.pAFont->YDist > GUI_Context.pAFont->YSize) {
      int YMag = GUI_Context.pAFont->YMag;
      int YDist = GUI_Context.pAFont->YDist * YMag;
      int YSize = GUI_Context.pAFont->YSize * YMag;
      if (DrawMode != LCD_DRAWMODE_TRANS) {
        LCD_COLOR OldColor = GUI_GetColor();
        GUI_SetColor(GUI_GetBkColor());
        LCD_FillRect(GUI_Context.DispPosX,
                     GUI_Context.DispPosY + YSize,
                     GUI_Context.DispPosX + pCharInfo->XSize,
                     GUI_Context.DispPosY + YDist);
        GUI_SetColor(OldColor);
      }
    }
    LCD_SetDrawMode(OldDrawMode); /* Restore draw mode */
    GUI_Context.DispPosX += pCharInfo->XDist * GUI_Context.pAFont->XMag;
  }
}
/*********************************************************************
*
*       GUI_XBF_GetCharDistX
*/
int GUIPROP_XBF_GetCharDistX(U16P c) {
  const GUI_FONT_PROP GUI_UNI_PTR * pProp = GUIPROP_FindChar(GUI_Context.pAFont->p.pProp, c);
  return (pProp) ? (pProp->paCharInfo)->XSize * GUI_Context.pAFont->XMag : 0;
}
/*********************************************************************
*
*       GUI_XBF_GetFontInfo
*/
void GUIPROP_XBF_GetFontInfo(const GUI_FONT GUI_UNI_PTR * pFont, GUI_FONTINFO * pfi) {
  GUI_USE_PARA(pFont);
  pfi->Flags = GUI_FONTINFO_FLAG_PROP;
}
/*********************************************************************
*
*       GUI_XBF_IsInFont
*/
char GUIPROP_XBF_IsInFont(const GUI_FONT GUI_UNI_PTR * pFont, U16 c) {
  const GUI_FONT_PROP GUI_UNI_PTR * pProp = GUIPROP_FindChar(pFont->p.pProp, c);
  return (pProp==NULL) ? 0 : 1;
}

///////////////////////////////////////////////////////////////////////////////////////////////////


程序已更新到网盘
百为STM32_UCGUI汉字显示.rar

运行图片

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-10-23 12:06:04 | 显示全部楼层
在做的项目的界面

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-10-25 15:59:15 | 显示全部楼层
更新PDF版教程

包括目前市面最详尽的SDIO教程,长达65页。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2013-10-29 11:31:59 | 显示全部楼层
好东西都在这里,还有好多没写。顶下

出0入0汤圆

发表于 2013-11-13 23:37:52 | 显示全部楼层
顶一下,资料内容确实很多,谢谢!

出0入0汤圆

发表于 2013-11-14 12:52:02 | 显示全部楼层
这么丰富的内容,楼主不容易啊!

出0入0汤圆

发表于 2013-11-14 15:36:21 | 显示全部楼层
到此一游

出0入0汤圆

 楼主| 发表于 2013-11-21 12:46:41 | 显示全部楼层
老灰兔 发表于 2013-11-14 12:52
这么丰富的内容,楼主不容易啊!

还有更丰富的内容的,看顶到多少楼了

出0入0汤圆

发表于 2013-11-21 13:52:38 | 显示全部楼层

谢谢楼主,写这么多

出0入36汤圆

发表于 2013-11-21 14:46:37 | 显示全部楼层
支持楼主,好好的学学

出0入0汤圆

 楼主| 发表于 2013-11-21 17:49:00 | 显示全部楼层
matmat 发表于 2013-11-21 13:52
谢谢楼主,写这么多

呵呵,还有很多可以写的,像USB那些写起来可以出一本书了

出0入0汤圆

 楼主| 发表于 2013-11-21 17:56:42 | 显示全部楼层
GZZXB 发表于 2013-11-21 14:46
支持楼主,好好的学学

多谢支持

出0入0汤圆

发表于 2013-11-21 19:44:41 | 显示全部楼层
开元好厉害啊     

出0入0汤圆

发表于 2013-11-22 00:09:03 | 显示全部楼层
楼主也开源了?

出0入0汤圆

发表于 2013-11-22 09:18:48 | 显示全部楼层
mark一下

出0入0汤圆

发表于 2013-11-22 11:43:35 | 显示全部楼层
LZ给力,LZ辛苦,谢谢分享~

出0入0汤圆

发表于 2013-11-22 11:45:37 来自手机 | 显示全部楼层
正在学stm32  标记

出0入0汤圆

发表于 2013-11-22 14:11:50 | 显示全部楼层
mark,学习中!!!!!!

出0入0汤圆

 楼主| 发表于 2013-11-23 12:49:01 | 显示全部楼层
百为STM32_enc28j60测试程序.rar
已更新到网盘

出0入0汤圆

发表于 2013-11-23 13:28:55 | 显示全部楼层
支持一下。。

出0入0汤圆

发表于 2013-11-24 13:45:18 | 显示全部楼层
越来越感觉自己需要学习!!!谢谢分享~

出0入0汤圆

 楼主| 发表于 2013-11-25 10:36:48 | 显示全部楼层
百为STM32_4通道ADC液晶显示.rar
程序已上传到网盘

出0入0汤圆

发表于 2013-11-25 12:05:38 | 显示全部楼层
支持!!!!!!!!!!!!!!!!!

出0入0汤圆

发表于 2013-11-25 12:10:57 | 显示全部楼层
mark

出100入101汤圆

发表于 2013-11-25 12:32:07 | 显示全部楼层
手机界面还是遥遥无期

出0入0汤圆

发表于 2013-11-25 12:33:12 | 显示全部楼层
开源到这程度,确实很很难见

出0入0汤圆

发表于 2013-12-2 13:11:51 | 显示全部楼层
很强大的板子,不知道现在还有没有199的?

出0入4汤圆

发表于 2013-12-2 13:41:09 | 显示全部楼层
不错,楼主加油!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-6-18 09:02

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表