智能车制作

 找回密码
 注册

扫一扫,访问微社区

查看: 29812|回复: 78
打印 上一主题 下一主题

xs128接sd卡调试的一点资料

  [复制链接]

0

主题

7

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
322
威望
238
贡献
62
兑换币
40
注册时间
2009-3-10
在线时间
11 小时
跳转到指定楼层
1#
发表于 2009-5-20 20:22:45 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 cfanfrank 于 2009-5-20 20:26 编辑


呵呵,网上没有现成资料,自己动手做的一点东西,共享一下。希望高手指正批评啊。

硬件:
S125v)可以跟sd卡模块(3.3v)直接连接。这个是冒着俺手机里的sd卡被烧的危险试的!
sd卡模块是在淘宝上买的,icdev网站出的那种,20多块。

Sd <-> S12
MISO <-> MISO
MOSI <-> MOSI
3.3V <-> 任意一个PWM,这个也可以直接接5V,接PWM的目的是可以通过程序控制sd卡重新启动。但是不能接3.3V!因为试过接3.3V,不大正常!
GND <-> GND
SCK <-> SCK
CS <-> 任意一个通用io

软件:

Spi读取和写入函数:

  1. #define spi_write(x) \
  2. {   \
  3.     while (!SPI0SR_SPTEF);   \
  4.     SPI0DRL = (x);    \
  5.     while (!SPI0SR_SPIF);    \
  6.     spi_tmp_var = SPI0DRL;   \
  7. }   

  8. #define spi_read(x)  \
  9. {   \
  10.     while (!SPI0SR_SPTEF);   \
  11.     SPI0DRL = 0xff;   \
  12.     while (!SPI0SR_SPIF);    \
  13.     (x) = SPI0DRL;    \
  14. }
复制代码




这样写是为了避免函数调用,浪费时间。

0

主题

7

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
322
威望
238
贡献
62
兑换币
40
注册时间
2009-3-10
在线时间
11 小时
2#
 楼主| 发表于 2009-5-20 20:23:05 | 只看该作者
本帖最后由 cfanfrank 于 2009-5-20 20:28 编辑

Sd卡发送命令函数:




  1. byte sd_send_cmd(byte cmd, dword arg, byte crc)
  2. {
  3.     unsigned char r1;
  4.     unsigned char Retry = 0;

  5.     //????????
  6.     spi_write(0xff);
  7.     //片选端置低,选中SD卡
  8.     SD_CS = 0;

  9.     //发送
  10.     spi_write(cmd | 0x40);                         //分别写入命令
  11.     spi_write(arg >> 24);
  12.     spi_write(arg >> 16);
  13.     spi_write(arg >> 8);
  14.     spi_write(arg);
  15.     spi_write(crc);

  16.     //等待响应,或超时退出
  17.     do {
  18.        Retry++;
  19.        if(Retry > 200)
  20.        {
  21.            break;
  22.        }
  23.        spi_read(r1);
  24.     } while(r1 == 0xFF);

  25.     //关闭片选
  26.     SD_CS = 1;

  27.     //在总线上额外增加个时钟,让SD卡完成剩下的工作
  28.     spi_write(0xFF);

  29.     //返回状态值
  30.     return r1;
  31. }
复制代码


Sd卡初始化:

1.首先是要把连sd卡电源的那个pwm端口调成5v,也就是占空比100%,要让它先0v一会儿,再调成5v,这样sd卡就会重启了。这方法有点笨,占用了一个pwm口,谁有的比较好的方法呢?



  1.     // power down
  2.     PWMDTY6 = 100;
  3.     delay(200);

  4.     // power up
  5.     PWMDTY6 = 0;
  6.     delay(200);
复制代码
回复 支持 反对

使用道具 举报

0

主题

7

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
322
威望
238
贡献
62
兑换币
40
注册时间
2009-3-10
在线时间
11 小时
3#
 楼主| 发表于 2009-5-20 20:23:25 | 只看该作者
本帖最后由 cfanfrank 于 2009-5-20 20:29 编辑

2.然后就是设置spi参数了,发些0xff,给一些时间让sd卡初始化。注意,初始化的时候spi时钟是很慢的。别人说,最好350K以下。


  1. // set low speed
  2. SPI0CR1 = 0x5e; //CPOL=1,CPHA=1
  3. SPI0CR2 = 0x10;
  4. SPI0BR  = 0x07;  // 80M / 256 = 312.5k
  5.                     
  6. // > 74 clock cycle init
  7. for (i = 0; i < 10; i++)
  8.    spi_write(0xff);
复制代码


3.发送cmd0cmd55acmd41,让卡初始化。这俺也不懂,网上搬过来的。



  1.     retry = 0;
  2.     do
  3.     {
  4.        //发送CMD0,让SD卡进入IDLE状态
  5.        r1 = sd_send_cmd(0, 0, 0x95);
  6.        retry++;
  7.     } while ((r1 != 0x01) && (retry < 200));

  8.     //发卡初始化指令CMD55+ACMD41
  9.     do
  10.     {
  11.        r1 = sd_send_cmd(55, 0, 0);
  12.        if (r1 != 0x01) {
  13.            uart_printf("cmd55 failed %x\n\r", r1);
  14.            return;
  15.        }
  16.        r1 = sd_send_cmd(41, 0x40000000, 0);
  17.        if (retry > 200) {
  18.            uart_printf("cmd41 failed %x\n\r", r1);
  19.            return;  //超时则返回r1状态
  20.        }
  21.     } while (r1 != 0);
复制代码

4.现在就要把spi时钟调到最快了。初始化的时候spi时钟是很慢的,但是传输的时候,当然越快越好。这里spi的频率已经是最快了,40M。手册上说最多25m的,但是这里调40m也没事,不知道是nokiasd卡质量好还是咋的。




  1.     // set high speed
  2.     SPI0BR  = 0x00;  // 80M / 2 = 40M
复制代码
回复 支持 反对

使用道具 举报

0

主题

7

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
322
威望
238
贡献
62
兑换币
40
注册时间
2009-3-10
在线时间
11 小时
4#
 楼主| 发表于 2009-5-20 20:24:11 | 只看该作者
本帖最后由 cfanfrank 于 2009-5-20 20:30 编辑

5.读数据。Cmd17是读单块命令,一次读512字节(默认是512,如果你调了块大小,这里就可能不止512了)。

sd_send_cmd(17, 0x00, 0)就是说,命令sd卡读从地址0x00开始的512字节,第二个参数是地址,需要512字节对齐(如果你的是sdhc卡,传进去的是块的编号,编号1代表地址512,编号4代表地址2048。。。)。

接下来的sd_recv_data(buf, 512);就是说,接受字节流,把它们放到buf里。

还有一个命令是多块一起读的,有需要的话,查下手册自己写吧。
一块被格式化好的sd卡,上面一般是fat文件系统,所以buf[510]buf[511]里的值是0x55, 0xaa。这个是拿来测试的。




  1. byte sd_recv_data(byte *data, word len)
  2. {
  3.     static word retry;
  4.     static byte r1;

  5.     // 启动一次传输
  6.     SD_CS = 0;
  7.     //等待SD卡发回数据起始令牌xFE
  8.     retry = 0;
  9.     do
  10.     {
  11.        spi_read(r1);
  12.        retry++;
  13.        if(retry>2000)  //2000次等待后没有应答,退出报错
  14.        {
  15.            SD_CS = 1;
  16.            return 1;
  17.        }
  18.     } while (r1 != 0xFE);

  19.     //开始接收数据
  20.     while (len--) {
  21.        spi_read(*data);
  22.        data++;
  23.     }
  24.     //下面是个伪CRC(dummy CRC)
  25.     spi_read(r1);
  26.     spi_read(r1);

  27.     //传输结束
  28.     SD_CS = 1;
  29.     spi_write(0xFF);

  30.     return 0;
  31. }

  32.     // 测试读
  33.     r1 = sd_send_cmd(17, 0x00, 0);//读命令
  34.     if (r1 != 0x00) {
  35.        uart_printf("cmd17 failed %x\n\r", r1);
  36.        return;
  37.     }
  38.     r1 = sd_recv_data(buf, 512);
  39.     if(r1 != 0)   {
  40.        uart_printf("recv data failed %x\n\r", r1);
  41.        return;
  42.     }
  43.     uart_printf("%x %x\n\r", buf[510], buf[511]);
复制代码
回复 支持 反对

使用道具 举报

0

主题

7

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
322
威望
238
贡献
62
兑换币
40
注册时间
2009-3-10
在线时间
11 小时
5#
 楼主| 发表于 2009-5-20 20:24:31 | 只看该作者
本帖最后由 cfanfrank 于 2009-5-20 20:31 编辑

6.写数据
写数据用的是批量写,也一定要用这个,不然你写完512字节又要发命令,很慢。那把块大小调大点行不行呢?我试过,我的sd卡是不行的,最大512,手册里也没说明最大多少。你可以自己试试,如果能调大,最好调大。



  1.     //发多块写入指令
  2.     r1 = sd_send_cmd(25, 0x00, 0x00);
  3.     if (r1 != 0x00) {
  4.        uart_printf("cmd25 failed %x\n\r", r1);
  5.        return;  //应答不正确,直接返回
  6.     }

  7.     //开始准备数据传输
  8.     SD_CS = 0;

  9.     //先放个空数据,等待SD卡准备好
  10.     spi_write(0xff);
  11.     spi_write(0xff);
复制代码


发送完命令以后,就可以写了。下面的代码是每次写512字节,你可以放到你的主循环里。



  1.            spi_write(0xFC);

  2.            //放一个sector的数据
  3.            i = 512;
  4.            while (i--)
  5.               spi_write(*ptr++);

  6.            //发个Byte的dummy CRC
  7.            spi_write(0xff);
  8.            spi_write(0xff);
  9.    
  10.            spi_read(r1);
  11.            if ((r1 & 0x0F) != 0x05) {
  12.               // 如果数据出错,死机..
  13.               PORTB = 0xff;
  14.               while (1);
  15.            }
复制代码
回复 支持 反对

使用道具 举报

0

主题

7

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
322
威望
238
贡献
62
兑换币
40
注册时间
2009-3-10
在线时间
11 小时
6#
 楼主| 发表于 2009-5-20 20:24:47 | 只看该作者
本帖最后由 cfanfrank 于 2009-5-20 20:50 编辑

经测试,40mspi频率下,连续spi_write六次,大概是256个总线时钟,还是能接受的。

在执行算法的同时,写6*512B的数据,周期是40ms,摄像头周期是20ms
光执行算法,周期是20ms,跟摄像头一样。
所以这跟上海大学那份技术报告里说得一样,如果加上sd调试,就只能是40ms周期了。

这也有些不爽。我也想过用一块arm同时读取摄像头数据,存在ram里,但是那样成本高,又麻烦。
北京科技大学的技术报告提到用无线影音模块的方法,可行是可行,不过估计数据处理又够麻烦,毕竟是模拟传输。如果用spi接口无线模块,估计速度也上不去。

如果有谁有好的方法,不用另外一块mcu,能在20ms内完成6*512B数据的读写,希望兄弟都能贴出来共享下!

补充几点:

sd卡的里的数据怎么组织,可以比较随便,可以没有文件系统,可以省很多事的。

VC里,CreateFile可以直接打开盘符。路径名为“\\.\X”,“X”就是你想要的盘符了。mfc里的CFile也行。


  1. CFile fp;

  2. if (!fp.Open("\\\\.\\H:", CFile::modeRead))
  3.   return;
复制代码


s12跟x86平台存在“大端小端”的问题,8bit以上的字段,要转换。给个宏:
  1. #define be16_to_cpu(x) ((x) = (((x) & 0xff00) >> 8) | (((x) & 0x00ff) << 8))
复制代码


回复 支持 反对

使用道具 举报

0

主题

242

帖子

0

精华

高级会员

Rank: 4

积分
925
QQ
威望
451
贡献
382
兑换币
0
注册时间
2008-11-28
在线时间
46 小时
7#
发表于 2009-5-21 09:21:35 | 只看该作者
不错,虽然现在没有时间做。不过还是要谢谢楼主这么大方哦
回复 支持 反对

使用道具 举报

0

主题

33

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
468
威望
335
贡献
71
兑换币
18
注册时间
2008-12-6
在线时间
31 小时
8#
发表于 2009-5-21 18:14:35 | 只看该作者
谢谢楼主奉献啊
回复 支持 反对

使用道具 举报

1

主题

51

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
310
威望
228
贡献
26
兑换币
0
注册时间
2009-3-21
在线时间
28 小时
9#
发表于 2009-5-24 19:15:42 | 只看该作者
谢谢楼主奉献啊
回复 支持 反对

使用道具 举报

22

主题

518

帖子

0

精华

版主

Rank: 9Rank: 9Rank: 9

积分
6442
QQ
威望
943
贡献
5041
兑换币
99
注册时间
2008-2-26
在线时间
229 小时
10#
发表于 2009-5-24 23:33:11 | 只看该作者
谢谢楼主奉献啊
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

关于我们|联系我们|小黑屋|智能车制作 ( 黑ICP备2022002344号

GMT+8, 2024-12-25 13:35 , Processed in 0.168980 second(s), 32 queries , Gzip On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

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