OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
楼主: 龙之谷

菜鸟开帖,持续更新90天,顺序学习开发板大部分实验,以此帖作为一个坚持的动力

  [复制链接]

2

主题

14

帖子

0

精华

初级会员

Rank: 2

积分
85
金钱
85
注册时间
2014-2-11
在线时间
13 小时
发表于 2015-9-8 10:08:36 | 显示全部楼层
我在6轴传感器这节卡住了,不知道怎么学习好点,准备暂时先跳过这节,哪位大仙对这章有没有好点的建议,楼主这节怎么学习这么快啊
正点原子逻辑分析仪DL16劲爆上市
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-8 10:20:18 | 显示全部楼层
回复【251楼】beatty102:
---------------------------------
学得快必然学的浅

工作暂时接触不到6轴,也就没花大时间深入学习,只是把原子哥程序简单测试、观察一下实验现象,对基础有个简单认识,同时对程序里面自己感觉经典的点进行一下总结,这就过了,以后有需要再进一步研究(估计很长一段时间使用不到)
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
发表于 2015-9-8 23:06:09 | 显示全部楼层
回复【249楼】龙之谷:
---------------------------------
谢谢分享

回复【250楼】龙之谷:
---------------------------------
好啊。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-8 23:19:07 | 显示全部楼层
第四六天  2015年09月08日  周二     例程:FATFS实验(一)

1.FATFS是一个完全免费开源的FAT文件系统模块,专门为小型嵌入式系统而设计。支持FAT12、FAT16、FAT32。

2.FATFS层次结构







3.FATFS系统移植




其中,必须由用户提供底层IO接口函数一般有六个





4.在完成FATFS移植基础上,还需添加四个辅助文件,配合FATFS的工作和测试




--------------------------------------------------分割线----------------------------------------------------------------------
5.FAT表(File Allocation Table),文件分配表,是Microsoft在FAT文件系统中用于磁盘数据/文件索引和定位引进的一种链式结构。
把磁盘比作一本书,FAT表可以认为相当于书中的目录,而文件就是各个章节的内容。但FAT表示方法却与目录有很大不同,在FAT文件系统中,文件的存储依照FAT表指定的簇链式数据结构来进行,同时,FAT文件系统将组织数据时使用的目录也抽象成文件,以简化对数据的管理。

6.FATFS16使用了16位的空间来表示每个扇区(sector)配置文件的情形,故称之为FAT16。(FAT12、FAT32依此类推)
簇(Cluster),就是磁盘的配置单位,就像图书馆内一格一格的书架一样,每个要存到磁盘的文件就必须配置足够数量的簇,才能存放到磁盘中。簇是由扇区组成,每个簇都是扇区的整数倍。

7.文件占用磁盘空间时,基本单位不是字节而是簇。簇的大小与磁盘的规格有关,一般情况下,软盘每簇是1个扇区,硬盘每簇的扇区数与硬盘的总容量大小有关,可能是4、8、16、32、64......
FAT文件系统下,每一个磁盘被分成固定大小的簇,每个簇至少512字节,其大小可成倍增长,最大32K。FAT16系统中,每个簇都是由唯一的索引号--单个16位二进制数来标识,因为16位二进制最大为65536,所以FAT分区所拥有的簇数量不可能超过65536个,簇的数量和大小的限制,就是FAT16分区不能超过2GB的原因(寻址字节也会限制分区大小,如当FAT32使用2个字节寻址限定其分区不超过4GB)。

8.FAT中的入口连接着一个文件的各个簇,文件的目录入口包含其第一个簇的索引号,而该簇在FAT中的入口又包含下一个簇的索引号,依此类推。一个文件的最后一簇对应的FAT入口则包含一个特殊的文件终止符,未使用的簇和损坏的簇也会用特殊代码标识。

9.FAT12、FAT16、FAT32区分办法:簇数量。FAT12一定小于4096,FAT16一定小于65536,FAT32一定大于65536。

--------------------------------------------------分割线----------------------------------------------------------------------
相关/参考链接
【1】http://www.jb51.net/hardware/yingpan/72332.html
【2】http://baike.baidu.com/link?url=mMuyYvkydRlNOXi357CL0h10CCfFKzG7_oYOWP-mkPrfTs-rnhHU0NnD-0L7BBh0ghK9u5I2PQcmVH7-USLroK
【3】http://blog.csdn.net/reedey/article/details/5450596
【4】http://wenku.baidu.com/link?url=2xSrC86nKroIEALxwWT3LgssIeVJuBzTidHKpkuexezqjX4udVSQVHadPJPyLZk9aZ7NZYkq8515bzjv_Cpeb6_Fu1yTx9dbIkM-UhNo8ee
【5】http://zhidao.baidu.com/link?url=iVtg9h44G6cFTSj-ksG6cBqT-Rqip0H_mW1ycF-wHGc8RE6jKby4vQEI9MTQ-QviOd8pIoK8Fz9hCUf-X4wDTK
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
发表于 2015-9-8 23:24:00 | 显示全部楼层
回复【254楼】龙之谷:
---------------------------------
顶!d=====( ̄▽ ̄*)b
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-9 23:09:42 | 显示全部楼层
第四七天  2015年09月09日  周三     例程:FATFS实验(二)

关于FATFS,简直无从下手,知识点多且有一定难度,故在此只做搬运工吧,按照自己的方式进行排版。接口需实现六函数:

1.disk_initialize



[mw_shl_code=c,true]//初始化磁盘 DSTATUS disk_initialize ( BYTE pdrv /* Physical drive nmuber (0..) */ ) { u8 res=0; switch(pdrv) { case SD_CARD://SD卡 res=SD_Init();//SD卡初始化 break; case EX_FLASH://外部flash W25QXX_Init(); FLASH_SECTOR_COUNT=2048*12;//W25Q1218,前12M字节给FATFS占用 break; default: res=1; } if(res)return STA_NOINIT; else return 0; //初始化成功 } [/mw_shl_code]

2.disk_status



[mw_shl_code=c,true]//获得磁盘状态 DSTATUS disk_status ( BYTE pdrv /* Physical drive nmuber (0..) */ ) { return 0; } [/mw_shl_code]

3.disk_read


[mw_shl_code=c,true]//读扇区 //drv:磁盘编号0~9 //*buff:数据接收缓冲首地址 //sector:扇区地址 //count:需要读取的扇区数 DRESULT disk_read ( BYTE pdrv, /* Physical drive nmuber (0..) */ BYTE *buff, /* Data buffer to store read data */ DWORD sector, /* Sector address (LBA) */ UINT count /* Number of sectors to read (1..128) */ ) { u8 res=0; if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误 switch(pdrv) { case SD_CARD://SD卡 res=SD_ReadDisk(buff,sector,count); while(res)//读出错 { SD_Init(); //重新初始化SD卡 res=SD_ReadDisk(buff,sector,count); //printf("sd rd error:%d\r\n",res); } break; case EX_FLASH://外部flash for(;count>0;count--) { W25QXX_Read(buff,sector*FLASH_SECTOR_SIZE,FLASH_SECTOR_SIZE); sector++; buff+=FLASH_SECTOR_SIZE; } res=0; break; default: res=1; } //处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值 if(res==0x00)return RES_OK; else return RES_ERROR; }[/mw_shl_code]

4.disk_write



[mw_shl_code=c,true]//写扇区 //drv:磁盘编号0~9 //*buff:发送数据首地址 //sector:扇区地址 //count:需要写入的扇区数 #if _USE_WRITE DRESULT disk_write ( BYTE pdrv, /* Physical drive nmuber (0..) */ const BYTE *buff, /* Data to be written */ DWORD sector, /* Sector address (LBA) */ UINT count /* Number of sectors to write (1..128) */ ) { u8 res=0; if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误 switch(pdrv) { case SD_CARD://SD卡 res=SD_WriteDisk((u8*)buff,sector,count); while(res)//写出错 { SD_Init(); //重新初始化SD卡 res=SD_WriteDisk((u8*)buff,sector,count); //printf("sd wr error:%d\r\n",res); } break; case EX_FLASH://外部flash for(;count>0;count--) { W25QXX_Write((u8*)buff,sector*FLASH_SECTOR_SIZE,FLASH_SECTOR_SIZE); sector++; buff+=FLASH_SECTOR_SIZE; } res=0; break; default: res=1; } //处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值 if(res == 0x00)return RES_OK; else return RES_ERROR; } #endif[/mw_shl_code]

5.disk_ioctl



[mw_shl_code=c,true]//其他表参数的获得 //drv:磁盘编号0~9 //ctrl:控制代码 //*buff:发送/接收缓冲区指针 #if _USE_IOCTL DRESULT disk_ioctl ( BYTE pdrv, /* Physical drive nmuber (0..) */ BYTE cmd, /* Control code */ void *buff /* Buffer to send/receive control data */ ) { DRESULT res; if(pdrv==SD_CARD)//SD卡 { switch(cmd) { case CTRL_SYNC: res = RES_OK; break; case GET_SECTOR_SIZE: *(DWORD*)buff = 512; res = RES_OK; break; case GET_BLOCK_SIZE: *(WORD*)buff = SDCardInfo.CardBlockSize; res = RES_OK; break; case GET_SECTOR_COUNT: *(DWORD*)buff = SDCardInfo.CardCapacity/512; res = RES_OK; break; default: res = RES_PARERR; break; } }else if(pdrv==EX_FLASH) //外部FLASH { switch(cmd) { case CTRL_SYNC: res = RES_OK; break; case GET_SECTOR_SIZE: *(WORD*)buff = FLASH_SECTOR_SIZE; res = RES_OK; break; case GET_BLOCK_SIZE: *(WORD*)buff = FLASH_BLOCK_SIZE; res = RES_OK; break; case GET_SECTOR_COUNT: *(DWORD*)buff = FLASH_SECTOR_COUNT; res = RES_OK; break; default: res = RES_PARERR; break; } }else res=RES_ERROR;//其他的不支持 return res; } #endif[/mw_shl_code]

6.disk_fattime



[mw_shl_code=c,true]//获得时间 //User defined function to give a current time to fatfs module */ //31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */ //15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */ DWORD get_fattime (void) { return 0; } [/mw_shl_code]

微总结:通过一一对照,可发现对接函数也只是将最基础初始化、读、写等函数封装进已固定函数名的函数中,使系统能够对所挂接磁盘进行操作。

--------------------------------------------------分割线---------------------------------------------------------

实验中文件类型识别函数阅读起来难度不是很大,但要自己写估计有一定难度,流程值得反复研究学习。

首先,建立文件类型列表
[mw_shl_code=c,true]#define FILE_MAX_TYPE_NUM 7 //最多FILE_MAX_TYPE_NUM个大类 #define FILE_MAX_SUBT_NUM 4 //最多FILE_MAX_SUBT_NUM个小类 //文件类型列表 u8*const FILE_TYPE_TBL[FILE_MAX_TYPE_NUM][FILE_MAX_SUBT_NUM]= { {"BIN"}, //BIN文件 {"LRC"}, //LRC文件 {"NES"}, //NES文件 {"TXT","C","H"}, //文本文件 {"WAV","MP3","APE","FLAC"},//支持的音乐文件 {"BMP","JPG","JPEG","GIF"},//图片文件 {"AVI"}, //视频文件 };[/mw_shl_code]
然后,编写比对函数,进行识别
[mw_shl_code=c,true]//将小写字母转为大写字母,如果是数字,则保持不变. u8 char_upper(u8 c) { if(c<'A')return c;//数字,保持不变. if(c>='a')return c-0x20;//变为大写. else return c;//大写,保持不变 } //报告文件的类型 //fname:文件名 //返回值:0XFF,表示无法识别的文件类型编号. // 其他,高四位表示所属大类,低四位表示所属小类. u8 f_typetell(u8 *fname) { u8 tbuf[5]; u8 *attr='\0';//后缀名 u8 i=0,j; while(i<250) { i++; if(*fname=='\0')break;//偏移到了最后了. fname++; } if(i==250)return 0XFF;//错误的字符串. for(i=0;i<5;i++)//得到后缀名 { fname--; if(*fname=='.') { fname++; attr=fname; break; } } strcpy((char *)tbuf,(const char*)attr);//copy for(i=0;i<4;i++)tbuf=char_upper(tbuf);//全部变为大写 for(i=0;i<FILE_MAX_TYPE_NUM;i++) //大类对比 { for(j=0;j<FILE_MAX_SUBT_NUM;j++)//子类对比 { if(*FILE_TYPE_TBL[j]==0)break;//此组已经没有可对比的成员了. if(strcmp((const char *)FILE_TYPE_TBL[j],(const char *)tbuf)==0)//找到了 { return (i<<4)|j; } } } return 0XFF;//没找到 }[/mw_shl_code]

以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
发表于 2015-9-9 23:48:24 | 显示全部楼层
回复【256楼】龙之谷:
---------------------------------
楼主快要大成了。。。。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

1

主题

4

帖子

0

精华

新手入门

积分
28
金钱
28
注册时间
2015-8-1
在线时间
0 小时
发表于 2015-9-10 14:30:13 | 显示全部楼层
真是好贴啊!为什么现在才发现。。。我也是醉了,无聊看51的帖子,有个请教arm学习路线的,结果有人给了你帖子的链接,真是巧。。我7月中旬开始学习M4的,一直到今天,学习过程也是挺坎坷的。要是能早点看到楼主的日记也能多一些动力,浪费的时间也能少很多。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-10 23:38:56 | 显示全部楼层
第四八天  2015年09月10日  周四     例程:FATFS实验(三)

一、硬盘的逻辑结构
1.硬盘由很多盘片(Platter)组成,每个盘片的每个面都有一个读写磁头(Head)。N个盘片--2N个面--2N个磁头,取值【0,...】。每个磁盘两个面,每个面都有一个磁头,习惯用磁头号区分不同盘面。
2.盘片表面上以盘片为圆心,不同半径的同心圆称为磁道(类似于树的年轮)。
3.不同盘片相同半径的磁道所组成的圆柱称为柱面(Cylinders),取值【0,...】。
4.每个磁道被划分为几十个扇区(Sector),通常容量为512Byte,取值【1,...】。
5.存储容量 = 磁头数 X 磁道(柱面)数 X 每道扇区数 X 每扇区字节数。

二、磁盘上的数据按照不同特点和作用可以分为5部分:MBR区、DBR区、FAT区、DIR区、DATA区。
1.MBR区(Main Boot Record)主引导扇区,位于整个硬盘0磁道0柱面1扇区。在总共512字节的主引导扇区中,MBR占用了446字节,另外64字节交给了DPT(Disk Partition Table硬盘分区表),最后两个字节“55AA”是分区结束标志。
2.DBR区(Dos Boot Record)操作系统引导记录区,位于硬盘的0磁道1柱面1扇区,是操作系统可以直接访问的第一个扇区,包括一个引导程序和一个BPB(Bios Parameter Block)的本分区参数记录表。其一,引导程序主要任务是当MBR将系统控制权交给它时,判断本分区跟目前两个文件是不是操作系统的引导文件,如果确定存在,就把其读入内存,并把控制权交给该文件。其二,BPB参数块记录本分区起始扇区、结束扇区、文件存储格式、硬盘介质描述符、根目录大小、FAT个数、分配单元大小等重要参数。
3.FAT区(File Allocation Table)文件分配表区。为了实现文件的链式存储,硬盘上必须准确记录哪些簇已经被文件占用,还必须为每个已经占用的簇指明存储后继内容的下一个簇的簇号,对一个文件的最后一簇则要指明本簇无后继簇等这些都是由FAT来保存。由于FAT的重要性,一般在FAT后面再建一个同样的FAT,即备份一下(两者是即时同步的)。
4.DIR区(Directory)根目录区,记录根目录下每个文件(目录)的起始单元、文件属性等。
5.DATA数据区,真正意义上存放数据的地方,占据硬盘上的大部分数据空间。

三、FAT16在根目录中利用两个字节标记一个簇(512 X NByte)来链式管理数据区域。

四、主要结构体(ff.h)
1.FATFS结构,文件系统对象结构体,用来保存独立逻辑驱动器动态工作区域,由应用程序给定,使用f_mount函数注册/注销FATFS模块,在执行f_mount或媒体发生变化后,访问第一个文件时FATFS被初始化,其他应用程序不能改变结构的任何成员变量。
2.FIL结构,文件对象结构,用来保存打开文件的状态,由f_open函数创建,由f_open函数废弃。除cltbl外,其他成员不能被其他应用程序改变。
3.DIR结构,目录对象结构体,被f_opendir,f_readdir函数用来读取工作区目录,其他应用程序不能改变其成员变量。

五、应用程序接口(API)
1.f_mount



例:FATFS fs[2];

f_mount(0, &fs[0]);//注册工作区
f_mount(1, &fs[1]);
...
f_mount(0, NULL);//注销工作区
f_mount(1, NULL);

2.f_open



例:FIL fsrc,fdst;
res = f_open(&fsrc,"1:srcfile.dat", FA_OPEN_EXISTING|FA_READ);//打开驱动器1上的源文件
res = f_open(&fdst, "0:dstfile.dat", FA_CREATE_ALWAYS|FA_WRITE);//在驱动器0上创建目标文件

3.f_close



例:f_close(&fsrc);//关闭打开文件

4.f_read



例:FIL fsrc,fdst;     BYTE buffer[4096];     UINT br,bw;//文件读写字节计数

res = f_read(&fsrc, buffer, sizeof(buffer), &br);//读出数据

5.f_write



例:res = f_write(&fdst, buffer, br, &bw);//写入数据

6.f_lseek



例:FIL *file;
res = f_lseek(file, 5000);//移动文件读、 写指针到相对文件起始偏移5000字节处
res = f_lseek(file, file->fsize);//移动读、写指针到文件结束处
res = f_lseek(file, file->fptr+3000);//向前3000字节
res = f_lseek(file, file->fptr-2000);//向后2000字节(注意溢出)
res = f_lseek(file, PRE_SIZE);//预分配簇

7.f_truncate



例:res = f_truncate(file);

8.f_sync



9.f_opendir



例:DIR dir;     char *path;
res = f_opendir(&dir, path);

10.f_readdir



例:DIR dir;     FILINFO fno;
res = f_readdir(&dir, &fno);

11.f_getfree



例:DWORD fre_clust, fre_sect;     FATFS *fs;
res = f_getfree("/", &fre_clust, &fs);

12.f_stat



13.f_mkdir



例:res = f_mkdir("sub1");
res = f_mkdir("sub1/sub2");

14.f_unlink



15.f_chmod



例:f_chmod("file.txt", AR_RDO, AT_RDO|AR_ARC);//设置只读标志,清除存档标志,其他不变

16.f_utime



17.f_rename



例:f_rename("oldname.txt", "newname.txt");//重命名一个对象
f_rename("oldname.txt", "dir1/newname.txt");//重命名并移动一个对象到另一个目录

18.f_chdir



例:f_chdir("/dir1");//改变当前驱动器的当前目录(根目录下的dir1)
f_chdir("2...");//改变驱动器2的当前目录(父目录)

19.f_chdrive



20.f_getcwd



21.f_forward



例:UINT out_stream(const BYTE *p, UINT btf);     FIL fil;     UINT dmy;
res = f_forward(&fil, out_stream, 1000, &dmy);

22.f_mkfs



23.f_fdisk



24.f_gets



25.f_putc



26.f_puts



27.f_printf





28.f_tell



实现:#define f_tell(fp) ((fp)->fptr)

29.f_eof



实现:#define f_eof(fp) (((fp)->fptr) == ((fp)->fsize)?1:0)

30.f_size



实现:#define f_size(fp) ((fp)->size)

31.f_error



实现:#define f_error(fp) (((fp)->flag & FA_ERROR)?1:0)

相关/参考链接
【1】http://wenku.baidu.com/link?url=0q-N7nxPFZDRSzdh1c_NA2pLTOX19bHSYq9CxWHzkGctTZ4aAHvh13wRwyF8rEhZzfk0YVict_HX-6iAIVr68TLLqvn5vNfxS8QwjHTLhF7
【2】http://zhidao.baidu.com/link?url=UqFOE7CvHJFP9Ly_6nK1fjRgV5BkZqKGAq8MRaMbmBDJpDmWxIoK82eZP0fAP6okx19RyWhYFnqzX_zWyzw94q
【3】http://wenku.baidu.com/link?url=yBc7FPYudu8g0pYIGswzFgnxKm4JbHcVvAh6k6vm56DF4B1dkKSIfkCeSjPzu8wLZ4dSx3cNc-Z7I3IlCgU43ceBwpf704y6qveJ9fbhyHO
【4】http://www.doc88.com/p-274608756620.html
【5】http://wenku.baidu.com/link?url=yBc7FPYudu8g0pYIGswzFgnxKm4JbHcVvAh6k6vm56DF4B1dkKSIfkCeSjPzu8wLLVUf-biYvkTN6mxDx3y6lIzqvi-CBf2F66OF7Zz-ipa
【6】http://www.stm8.cn/news/STM32File/1686.html


FatFs通用FAT文件系统模块中文手册.pdf

567.56 KB, 下载次数: 1026

以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
发表于 2015-9-11 00:00:45 | 显示全部楼层
回复【259楼】龙之谷:
---------------------------------
谢谢分享。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-11 00:18:15 | 显示全部楼层
回复【260楼】正点原子:
---------------------------------
总算搬完了,累成狗...
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-11 00:18:48 | 显示全部楼层
回复【258楼】a790439710:
---------------------------------
哈哈,共同努力,共同进步
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-11 00:34:12 | 显示全部楼层
2015年09月11日、12日、13日  无更新

原因:公司安排旅游

没错,没看错,在这阴雨绵绵的时间点上,我们要跟团出去旅游了,前两天报名时候天还好好的,今天下了一天,中雨吧。周五下午六点集合出发,晚上在大巴过夜,然后是长岛蓬莱两日游,周日下午再坐车返回,周一凌晨返回根据地。想想有点累~~~
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

1

主题

4

帖子

0

精华

新手入门

积分
28
金钱
28
注册时间
2015-8-1
在线时间
0 小时
发表于 2015-9-11 10:21:05 | 显示全部楼层
回复【263楼】龙之谷:
---------------------------------
噢耶,旅游好啊,这样我就有时间赶上楼主啦!哈哈
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-11 12:28:41 | 显示全部楼层
回复【264楼】a790439710:
---------------------------------
哈哈,热烈欢迎

同步不同步也没什么太要紧,只要保持每天都有进步就是一种成功

当然啦,如果能够同步一下,有问题方便互相交流、共同进步那是极好的
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

1

主题

4

帖子

0

精华

新手上路

积分
37
金钱
37
注册时间
2015-7-11
在线时间
2 小时
发表于 2015-9-11 16:14:34 | 显示全部楼层
楼主,要坚持呀,你把90天坚持完了才有可能克服你的惰性,不然又会回到以前,我们都看着你呢!!!!以你为动力
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-11 17:05:50 | 显示全部楼层
回复【266楼】pnp333:
---------------------------------
老是有事情打断一下,这次旅游出去玩两天,在车上过两个晚上,实在没时间更新,等回来了接着更

十一如果家里用机器收玉米就不打算回家了,以期尽快完成90天

祝坛友们顺利快乐~~~~~~
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
发表于 2015-9-11 23:17:52 | 显示全部楼层
回复【267楼】龙之谷:
---------------------------------
祝:玩的开心。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

15

主题

786

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3223
金钱
3223
注册时间
2015-7-26
在线时间
811 小时
发表于 2015-9-12 13:03:13 | 显示全部楼层
mark!发现跟楼主一起学下去能学到不少东西,愿一直追随楼主脚步,非常感谢楼主提供的转帖资料和自己对学习内容的理解,感谢!感谢!
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-14 23:14:58 | 显示全部楼层
第四九天  2015年09月14日  周一     例程:汉字显示(一)

一、本章中主要知识点有三:其一使用外部FLASH来存储字库;其二,通过SD卡更新字库;其三,读取存在FLASH里面的字库并显示在LCD上。

二、汉字内码
1.常见汉字内码系统由GB2312、GB1300、GBK、BIG5(繁体)等。其中,GB2312支持的汉字仅有几千个,很多时候不够用;而GBK内码不仅完全兼容GB2312还支持繁体,总汉子两万多个。


2.汉字在液晶上的显示原理与前面显示字符的是一样的,就是将我们需要的对应点显示出来。要显示汉字需要知道汉字的点阵数据,大量(所有)汉字的点阵数据组成汉字库。而汉字在各种文件里面的存储不是以点阵数据格式存储的(占用空间太大),而是以内码如GB2312、GBK、BIG5等内码进行存储,每个汉字对应一个内码,知道内码后再去字库里面查找这个汉字的点阵数据,然后在液晶上显示出来。过程就是:汉字内码(GBK/GB2312
)--查找点阵库--解析--显示。

3.GBK码结构如下



字库定位汉字方法:
当GBKL<0X7F时,Hp = ((GBKH-0X81)*190 + GBKL-0X40)*(SIZE*2);
当GBKL>0X80时,Hp = ((GBKH-0X81)*190 + GBKL-0X41)*(SIZE*2);
其中,GBKH、GBKL分别代表GBK第一个字节和第二个字节,size代表汉字字体大小(如16号、12号字体),Hp则代表对应汉字点阵数据在字库里面的起始地址(假设是从0开始存放)。

三、GBK码与UNICODE码

1.Unicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码。为每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。

2.上一实验中,cc936.c里面包含两个大数组oem2uni和uni2oem分别存放unicode和gbk互转对照表,直接刷在单片机里面太占flash(约192KB)。我们将其转换成bin文件,然后保存到外部FLASH就实现了该数组的转移。


3.大型数组转移及字库制作过程见下图




四、本试验中开发板一开始就显示汉字原因如下


这句话也显示出字库等文件的写入方式:将字库文件通过电脑连接对应读卡器等拷贝到SD卡根目录下,而非直接通过单片机程序的刷新来完成。

五、通过以上分析,我们发现今天第一项中的三个知识点一个也没有具体涉及到,而只是对基础知识进行了简单总结,可以看出本例程确实提升了一些难度,需要逐步去学习。

相关/参考链接
【1】http://baike.haosou.com/doc/4443890-4652181.html

以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-14 23:19:53 | 显示全部楼层
回复【268楼】正点原子:
---------------------------------
感觉蓬莱长岛风景不错,如果没去过,原子哥及坛友有时间可以考虑去放松一下

那蓝蓝的天、壮阔的海,我大河北庄里可是看不见了
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-14 23:20:56 | 显示全部楼层
回复【269楼】229382777@qq.com:
---------------------------------
共同学习,共同进步,共建美好生活,哈哈
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-14 23:43:46 | 显示全部楼层
心情楼层,满满负能量,绕行

工作感觉难度不是很大,但调试结果与预期有较大差别,外加快要考科三,需要分配较多学车时间,向老大请假学车竟然没有全部批准,唉,也是我低估了老大对项目进度的要求了,比较不顺,有点烦...

更帖状态:阻力不是很大了,好像是从第十几天开始喜欢了在论坛编辑发帖,虽然个人感觉编辑框编辑区域某些小地方不够完美,但在论坛直接编辑的过程很舒服,有种写在柔软的纸张上的感觉,所以即使网速慢的时候也是直接写,写完备份到WORD一下上传防止丢失。
但老毛病有点作祟:想利用更帖时间做更接近现在需要做的事情,如模电、工作相关程序调整的,其实自己静下心来想想也明白,假如不更帖,结果不仅完成不了其他事情,还把当前坚持(以后估计没有这样充足的时间和机会了)的计划毁掉,好吧,熬一下,挺过这次项目和科三的重叠时间,这段时间过后应该就不会如此困难了...

坚持、坚持、坚持...
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-14 23:45:22 | 显示全部楼层
自盖一楼刷新心情------
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
发表于 2015-9-15 00:43:42 | 显示全部楼层
回复【270楼】龙之谷:
---------------------------------
图文并茂啊,不错啊
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

15

主题

786

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3223
金钱
3223
注册时间
2015-7-26
在线时间
811 小时
发表于 2015-9-15 20:13:16 | 显示全部楼层
回复【275楼】正点原子:
---------------------------------
其实我有留意到原子哥的回帖时间,我感觉最好不要太晚睡,宁愿早起也不要晚睡啊,毕竟身体是革命的本钱!李开复以前就是睡太少,所以导致后面的身体不好。希望原子哥以健康为第一,也希望原子哥永远健康,带领我们学习更多的知识和惊喜!楼主的毅力也真的很好,给我们一些初学者带来很多没接触过的知识点,希望楼主也注意身体健康!
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-15 23:19:54 | 显示全部楼层
第五十天  2015年09月15日  周二     例程:汉字显示(二)



1.本例程中W25Q64存储空间分配情况如图:




2.如昨天第一点,本例程有三个主要知识点,概览:其一,使用外部FLASH存储字库,如上图,即通过W25Q128分配3.09M空间进行字库存储;其二,通过SD卡更新字库,即在SD卡中存放已经写入字库,通过程序进行字库逐步拷贝到FLASH存储位置;其三,读取FLASH中字库相应汉字并显示在LCD屏,其逆过程是调用显示函数

[mw_shl_code=c,true]Show_Str(30,70,200,16,"GBK字库测试程序",16,0);[/mw_shl_code]
在有字库情况下 此显示函数会调用单个汉字显示函数
[mw_shl_code=c,true]Show_Font(x,y,str,size,mode); //显示这个汉字,空心显示[/mw_shl_code]
此后,单个汉字显示函数调用获取点阵数据函数
[mw_shl_code=c,true]Get_HzMat(font,dzk,size); //得到相应大小的点阵数据 [/mw_shl_code]
得到点阵数据后再通过show_font函数中调用的打点函数(如下)打点显示即完成汉字显示过程。
[mw_shl_code=c,true]LCD_Fast_DrawPoint(x,y,POINT_COLOR);[/mw_shl_code]
其中,“GBK字库...”、str、font三个参数传递的都是字符的GBK地址指针,通过qh=*code;ql=*(++code);得出GBK地址从而进行点阵数据的提取。

3.GB2312码-----中国汉字信息交换用编码,全称《信息交换用汉字编码字符集——基本集》,由国家标准总局发布,通行于大陆,收录简化汉字及符号、字母、日文假名等共7445个图形字符,其中汉字6763个。
BIG5码-----通行于台湾、香港地区的一个繁体字编码方案,虽存在一些瑕疵,但广泛应用于电脑行业,收录13461个汉字和符号,包括:符号408个,常用字5401个,次常用字7652个。
GBK码-----为了解决GB2312收录字符少的缺陷、并配合UNICODE的实施,全国信息技术化技术委员会于1995年12月1日制定《汉字内码扩展规范》,即GBK,共收录21886个汉字和图形符号,包括:GB2312中全部、BIG5中全部汉字、GB13000中其他CJK汉字、其他汉字、部首、符号。
Unicode-----统一码、万国码、单一码,是一种在计算机上使用的字符编码,是为了解决传统的字符编码方案的局限而产生的,为每种语言中的单个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。




例程中涉及到字符串处理函数,有些陌生了,故简单整理回顾一下

1.strcat函数
一般形式:strcat(str1, str2);
作用:连接两个字符数组中的字符串,把字符串2接到字符串1的后面,结果放在字符数组1中,函数调用后得到一个函数值即字符数组1的地址。

2.strcpy函数
一般形式:strcpy(str1, str2);
作用:将字符串2复制到字符数组1中。





3.strcmp函数
一般形式:strcmp(str1, str2);
作用:比较字符串1和字符串2。(依次比较ASCII码值大小,直到出现不同字符或遇到‘\0’为止)





4.strlen函数
一般形式:strlen(字符数组)
作用:测试字符串实际长度(不包括‘\0’)。

5.strlwr函数
一般形式:strlwr(str)
作用:将字符串中大写字母换成小写字母。

6.strupr函数
一般形式:strupr(str)
作用:将字符串中小写字母换成大写字母。

参考/相关链接
【1】http://www.douban.com/group/topic/1174143/
【2】http://baike.haosou.com/doc/5451135-5689505.html
【3】http://baike.baidu.com/link?url=MaO-xI6pixh2tu0MG8sGSz8KTUqm17L_89vvVXft5R1ZYUShT1wew0F-OtwoED0EJq1EgGDcFKOWSXoSTrw1g_

以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-15 23:29:39 | 显示全部楼层
回复【276楼】229382777@qq.com:
---------------------------------
是啊,身体是革命的本钱,希望大家都健健康康的...
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

15

主题

46

帖子

0

精华

初级会员

Rank: 2

积分
151
金钱
151
注册时间
2014-9-29
在线时间
10 小时
发表于 2015-9-16 18:38:23 | 显示全部楼层
回复【278楼】龙之谷:
---------------------------------
楼主真是厉害,速度很快,我去年双十一前买到的开发板,现在还没看完所有的实验。可能个人的基础差,到了后面(SD卡实验开始)就很慢了,反反复复去看,还是有不懂的地方。内心蛮着急的,想尽快将这些内容消化掉。楼主每个实验都是完全理解的吗?
回复 支持 反对

使用道具 举报

3

主题

14

帖子

0

精华

新手上路

积分
47
金钱
47
注册时间
2015-6-30
在线时间
0 小时
发表于 2015-9-16 20:10:54 | 显示全部楼层
回复【207楼】龙之谷:
---------------------------------
可以求教电容屏(GT9147)的下列都是什么意思吗?差值原始值的意思有点搞不懂

0:读坐标状态 1、2:差值原始值
3:基准更新(内部测试) 4:基准校准(内部测试) 5:关屏
6:进入充电模式 7:退出充电模式8:切换手势唤醒固件
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-16 20:36:36 | 显示全部楼层
回复【279楼】aleelau:
---------------------------------
哪能完全理解,原子哥这实验不服不行,内容简直博大精深,例程由浅入深,很多知识不必完全理解,知道大概原理能够使用就可以了,如USMART例程,知道如何使用,对一些自己感觉巧妙知识点进行总结学习即可

如果以前接触STM32少,第一遍走开发板例程个人感觉不必过于精细,先走一遍再说,然后可以跳章节走第二遍深入学习,如果第一遍走的很细致,可能耐心会慢慢消磨以致举步维艰
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-16 20:39:01 | 显示全部楼层
回复【280楼】ken1986:
---------------------------------
没深入学习电容屏,帮不到你哦

可以发帖,把相关内容整理说明一下,论坛有高手会帮助你的
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-16 23:04:40 | 显示全部楼层
第五一天  2015年09月16日  周三     例程:图片显示(一)

一、常用图片格式及特点图



二、BMP由四部分组成



三、JPG/JPEG解码过程(JPEG文件数据分为文件头和图像数据两大部分)



四、GIF文件结构




以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-16 23:10:48 | 显示全部楼层
这几天事略多,科三什么的,晚上没太大精力整理了,所以最近几天可能都是比较简单的基础整理,如果有观看更新的坛友可以跳过这几天...
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-17 23:33:35 | 显示全部楼层
第五二天  2015年09月17日  周四     例程:图片显示(二)

一、在本例程中添加了九个文件,分别是bmp.c、bmp.h、tjpgd.c、tjpgd.h、gif.c、gif.h、integer.h、piclib.c、piclib.h。其中bmp是实现对bmp文件解码的,tjpgd是实现对jpeg/jpg文件解码,gif是实现对gif文件的解码,integer是类型重定义。这7个文件目的性都很明确,在以后使用时直接把文件添加到工程即可,而剩下的piclib.c则是以上其他文件的对外对接文件,需要一定的熟悉了解其实现过程及外部调用方法。

二、关于piclib.c文件函数整理-----共包含九个函数

1.piclib_draw_hline,划横线函数
[mw_shl_code=c,true]//lcd.h没有提供划横线函数,需要自己实现 void piclib_draw_hline(u16 x0,u16 y0,u16 len,u16 color) { if((len==0)||(x0>lcddev.width)||(y0>lcddev.height))return; LCD_Fill(x0,y0,x0+len-1,y0,color); }[/mw_shl_code]
2.piclib_fill_color,填充颜色
[mw_shl_code=c,true]//填充颜色 //x,y:起始坐标 //width,height:宽度和高度。 //*color:颜色数组 void piclib_fill_color(u16 x,u16 y,u16 width,u16 height,u16 *color) { LCD_Color_Fill(x,y,x+width-1,y+height-1,color); }[/mw_shl_code]
3.piclib_init,画图初始化函数,用于初始化图片解码的相关信息,主要初始化了自定义的两个结构体
[mw_shl_code=c,true]//画图初始化,在画图之前,必须先调用此函数 //指定画点/读点 void piclib_init(void) { pic_phy.read_point=LCD_ReadPoint; //读点函数实现,仅BMP需要 pic_phy.draw_point=LCD_Fast_DrawPoint; //画点函数实现 pic_phy.fill=LCD_Fill; //填充函数实现,仅GIF需要 pic_phy.draw_hline=piclib_draw_hline; //画线函数实现,仅GIF需要 pic_phy.fillcolor=piclib_fill_color; //颜色填充函数实现,仅TJPGD需要 picinfo.lcdwidth=lcddev.width; //得到LCD的宽度像素 picinfo.lcdheight=lcddev.height;//得到LCD的高度像素 picinfo.ImgWidth=0; //初始化宽度为0 picinfo.ImgHeight=0;//初始化高度为0 picinfo.Div_Fac=0; //初始化缩放系数为0 picinfo.S_Height=0; //初始化设定的高度为0 picinfo.S_Width=0; //初始化设定的宽度为0 picinfo.S_XOFF=0; //初始化x轴的偏移量为0 picinfo.S_YOFF=0; //初始化y轴的偏移量为0 picinfo.staticx=0; //初始化当前显示到的x坐标为0 picinfo.staticy=0; //初始化当前显示到的y坐标为0 }[/mw_shl_code]
4.piclib_alpha_blend,用于实现半透明效果,在小格式(图片分辨率小于LCD分辨率)bmp解码的时候可能被用到
[mw_shl_code=c,true]//快速ALPHA BLENDING算法. //src:源颜色 //dst:目标颜色 //alpha:透明程度(0~32) //返回值:混合后的颜色. u16 piclib_alpha_blend(u16 src,u16 dst,u8 alpha) { u32 src2; u32 dst2; //Convert to 32bit |-----GGGGGG-----RRRRR------BBBBB| src2=((src<<16)|src)&0x07E0F81F; dst2=((dst<<16)|dst)&0x07E0F81F; //Perform blending R:G:B with alpha in range 0..32 //Note that the reason that alpha may not exceed 32 is that there are only //5bits of space between each R:G:B value, any higher value will overflow //into the next component and deliver ugly result. dst2=((((dst2-src2)*alpha)>>5)+src2)&0x07E0F81F; return (dst2>>16)|dst2; }[/mw_shl_code]
5.ai_draw_init,用于实现图片在显示区域的居中显示初始化,就是根据图片大小选择缩放比例和坐标偏移值
[mw_shl_code=c,true]//初始化智能画点 //内部调用 void ai_draw_init(void) { float temp,temp1; temp=(float)picinfo.S_Width/picinfo.ImgWidth; temp1=(float)picinfo.S_Height/picinfo.ImgHeight; if(temp<temp1)temp1=temp;//取较小的那个 if(temp1>1)temp1=1; //使图片处于所给区域的中间 picinfo.S_XOFF+=(picinfo.S_Width-temp1*picinfo.ImgWidth)/2; picinfo.S_YOFF+=(picinfo.S_Height-temp1*picinfo.ImgHeight)/2; temp1*=8192;//扩大8192倍 picinfo.Div_Fac=temp1; picinfo.staticx=0xffff; picinfo.staticy=0xffff;//放到一个不可能的值上面 } [/mw_shl_code]
6.is_element_ok,用于判断一个点是不是应该显示出来,在图片缩放的时候必须用到
[mw_shl_code=c,true]//判断这个像素是否可以显示 //(x,y) :像素原始坐标 //chg :功能变量. //返回值:0,不需要显示.1,需要显示 u8 is_element_ok(u16 x,u16 y,u8 chg) { if(x!=picinfo.staticx||y!=picinfo.staticy) { if(chg==1) { picinfo.staticx=x; picinfo.staticy=y; } return 1; }else return 0; }[/mw_shl_code]
7.ai_load_picfile,整个图片显示的对外接口,外部程序通过调用该函数实现对bmp、jpg/jpeg、gif的显示,其通过输入文件的后缀名,判断文件格式,然后交给响应的解码程序执行解码,完成图片操作(其中用到一个f_typetell函数,在exfuns.c中实现)
[mw_shl_code=c,true]//智能画图 //FileName:要显示的图片文件 BMP/JPG/JPEG/GIF //x,y,width,height:坐标及显示区域尺寸 //fast:使能jpeg/jpg小图片(图片尺寸小于等于液晶分辨率)快速解码,0,不使能;1,使能. //图片在开始和结束的坐标点范围内显示 u8 ai_load_picfile(const u8 *filename,u16 x,u16 y,u16 width,u16 height,u8 fast) { u8 res;//返回值 u8 temp; if((x+width)>picinfo.lcdwidth)return PIC_WINDOW_ERR; //x坐标超范围了. if((y+height)>picinfo.lcdheight)return PIC_WINDOW_ERR; //y坐标超范围了. //得到显示方框大小 if(width==0||height==0)return PIC_WINDOW_ERR; //窗口设定错误 picinfo.S_Height=height; picinfo.S_Width=width; //显示区域无效 if(picinfo.S_Height==0||picinfo.S_Width==0) { picinfo.S_Height=lcddev.height; picinfo.S_Width=lcddev.width; return FALSE; } if(pic_phy.fillcolor==NULL)fast=0;//颜色填充函数未实现,不能快速显示 //显示的开始坐标点 picinfo.S_YOFF=y; picinfo.S_XOFF=x; //文件名传递 temp=f_typetell((u8*)filename); //得到文件的类型 switch(temp) { case T_BMP: res=stdbmp_decode(filename); //解码bmp break; case T_JPG: case T_JPEG: res=jpg_decode(filename,fast); //解码JPG/JPEG break; case T_GIF: res=gif_decode(filename,x,y,width,height); //解码gif break; default: res=PIC_FORMAT_ERR; //非图片格式!!! break; } return res; }[/mw_shl_code]
8.pic_memalloc,图片解码时需要用到的内存申请
[mw_shl_code=c,true]//动态分配内存 void *pic_memalloc (u32 size) { return (void*)mymalloc(SRAMIN,size); }[/mw_shl_code]
9.pic_memfree,图片解码时申请内存的释放
[mw_shl_code=c,true]//释放内存 void pic_memfree (void* mf) { myfree(SRAMIN,mf); }[/mw_shl_code]

以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-18 22:39:39 | 显示全部楼层
第五三天  2015年09月18日  周五     例程:照相机实验(一)

一、BMP文件保存过程如下



二、JPEG

1.实验采用摄像头模块直接可以输出压缩后的JPEG数据,所以实现JPEG拍照的关键,在于准确接收摄像头模块发送过来的编码数据,然后将这些数据保存为.JPG文件,就可以实现JPEG拍照了。

2.DCMI接口使用DMA直接传输JPEG数据到外部SRAM会出现数据丢失,所以DMA接收JPEG数据只能用内部SRAM,故使用内存管理。


3.使用DMA的双缓冲机制来读取,其读取过程如下





4.将存储在外部SRAM的JPEG数据,保存为.JPG/.JPEG存放在SD卡,就完成了一次JPEG拍照。
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
发表于 2015-9-18 22:45:05 | 显示全部楼层
虽然在休假,但是还是要来顶顶楼主的。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-19 10:51:16 | 显示全部楼层
回复【287楼】正点原子:
---------------------------------
谢原子哥鼓励,祝原子哥休假轻松愉快~~~~~
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-19 11:29:46 | 显示全部楼层
心情楼层:

这周略有不顺,请假练车四个半天,老大只批准两个半天,还批评哪个公司都不会允许员工上班时间请假练车,好吧,我工作生活可能分的不够清楚...

周三周四下班还要练会车,练完七点多,吃完饭回来八点左右,累成狗,寄希望于周五考试,结果没有考过,除了一些规则可能涉及敏..感就不说了,总结一下自己原因:第一次是转向时前边有车,稍等了一会,安全员就拿对讲机向上级说车停在转向位置无动作,我心里有点慌了,当然,相对于前两个人的待遇(第一个人的不要太明显)其中原因就不说了;第二次直接蒙圈了,上车启动后直接挂档,而没有等待指令,属于抢指令直接不过,夜考,考完八点半,结果这么个结果,心理因素较大,抗压能力比较差...

---------------------------------------------------------------------------------------------------------------------------------
这几天也是由于事多、比较浮躁,更新内容比较薄弱,希望接下来静下心来踏踏实实的继续更新.....

而在随后的几章更新内容,包括正在更新的照相机实验、音乐播放器实验、录音机实验、视频播放器实验,简单看了一下章节内容,感觉这几个实验都是对数据按照对应规则进行编码解码,由于现在工作一时半会用不到,所以对编码解码相关内容不会花费太多时间,而试图去寻找一些感兴趣的点进行一到两天的学习整理分析;而后的FPU测试实验、DSP测试实验,都是STM32F4的一个功能的实现,看了一下代码,量不大,学习整理起来应该工作量也不大。

接下来就到了第五十三章手写识别实验,没了解框架,感觉略叼,学习整理时长到时再定,其后的T9拼音输入法由于工作原因移植过,所以整理起来心理比较放心,IAP实验等会再说,USB实验感觉内容较多,到时候看情况,工作上暂时用不到,不会花费太多时间学习整理。

IAP、UCOS、UCGUI(STemWIN)是很早很早以前(早于开帖时间)的目标,也是本帖的主要方向,其中IAP按开发指南抄写过程序,但没有静下心来细细分析,现在忘得差不多了,UCOS没有实际运用或实验过,但看过邵贝贝和任哲的书(超过一遍),UCGUI大致浏览了一遍中文手册,没有实际移植过。所以这三项会是学习重点,根据内容量分配学习整理时间。

网络通信实验感觉内容可能会比较多,也会一带而过,待完成最后UCGUI实验后回过头来二遍跳跃学习会有此实验的进一步学习。

以上是以后一段时间的学习整理计划,有时可能计划赶不上变化,但有一个不脱离实际的计划应该不是什么坏事.....
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-19 23:36:35 | 显示全部楼层
第五四天  2015年09月19日  周六     例程:照相机实验(二)

一、DMA双缓冲模式
1.本章新增加了一个参数:memladdr,根据该值是否非0来判断是否需要使用双缓冲机制,只有在双缓冲机制下,才开启DMA传输完成中断。



2.DMA双缓冲数据最终存储位置(SD卡)追踪
DMA2_Stream1_IRQHandler函数,是DMA传输完成中断



里面通过dcmi_rx_callback回调函数(函数指针,指向jpeg_dcmi_rx_callback函数



而dcmi_rx_callback进行了回调,如下
[mw_shl_code=c,true] dcmi_rx_callback=jpeg_dcmi_rx_callback;//回调函数,位于u8 ov2640_jpg_photo(u8 *pname)函数中[/mw_shl_code]
jpeg_dcmi_rx_callback函数负责将满了的内存(M0AR或M1AR)数据读取到外部SRAM,如下
[mw_shl_code=c,true]//jpeg数据接收回调函数 void jpeg_dcmi_rx_callback(void) { u16 i; u32 *pbuf; pbuf=jpeg_data_buf+jpeg_data_len;//偏移到有效数据末尾 if(DMA2_Stream1->CR&(1<<19))//buf0已满,正常处理buf1 { for(i=0;i<jpeg_dma_bufsize;i++)pbuf=jpeg_buf0;//读取buf0里面的数据 jpeg_data_len+=jpeg_dma_bufsize;//偏移 }else //buf1已满,正常处理buf0 { for(i=0;i<jpeg_dma_bufsize;i++)pbuf=jpeg_buf1;//读取buf1里面的数据 jpeg_data_len+=jpeg_dma_bufsize;//偏移 } }[/mw_shl_code]
由上可知,进入中断后判断是哪个内存满引起的,然后进行存储,存入pbuf【】中,由pbuf=jpeg_data_buf+jpeg_data_len;(其中jpeg_data_len随着数据存入在变化),所以数据存入的地址与jpeg_data_buf相关,其在test.c定义
[mw_shl_code=c,true]u32 *jpeg_data_buf; //JPEG数据缓存buf,通过malloc申请内存[/mw_shl_code]
在main函数中,有
[mw_shl_code=c,true]jpeg_data_buf=mymalloc(SRAMEX,300*1024); //为jpeg文件申请内存(最大300KB)[/mw_shl_code]
当数据采集后,会通过FATFS文件系统进行存储,在ov2640_jpg_photo()函数中
[mw_shl_code=c,true]res=f_open(f_jpg,(const TCHAR*)pname,FA_WRITE|FA_CREATE_NEW);//模式0,或者尝试打开失败,则创建新文件 if(res==0) { printf("jpeg data size:%d\r\n",jpeg_data_len*4);//串口打印JPEG文件大小 pbuf=(u8*)jpeg_data_buf;//将JPEG暂时存储的数据全部进行存储SD卡中,见下操作 for(i=0;i<jpeg_data_len*4;i++)//查找0XFF,0XD8 { if((pbuf==0XFF)&&(pbuf[i+1]==0XD8))break; } if(i==jpeg_data_len*4)res=0XFD;//没找到0XFF,0XD8 else//找到了 { pbuf+=i;//偏移到0XFF,0XD8处 res=f_write(f_jpg,pbuf,jpeg_data_len*4-i,&bwr); if(bwr!=(jpeg_data_len*4-i))res=0XFE; } } jpeg_data_len=0; f_close(f_jpg); [/mw_shl_code]
至此,完成了数据的存储。(其中f_jpg为FIL*结构体,定义于ff.h中)


3.DMA双缓冲内存分配追踪
DCMI_DMA_Init()函数部分配置如下
[mw_shl_code=c,true]//DCMI DMA配置 //mem0addr:存储器地址0 将要存储摄像头数据的内存地址(也可以是外设地址) //mem1addr:存储器地址1 当只使用mem0addr的时候,该值必须为0 //memsize:存储器长度 0~65535 //memblen:存储器位宽 0,8位,1,16位,2,32位 //meminc:存储器增长方式,0,不增长;1,增长 void DCMI_DMA_Init(u32 mem0addr,u32 mem1addr,u16 memsize,u8 memblen,u8 meminc) { RCC->AHB1ENR|=1<<22; //DMA2时钟使能 while(DMA2_Stream1->CR&0X01);//等待DMA2_Stream1可配置 DMA2->LIFCR|=0X3D<<6*1; //清空通道1上所有中断标志 DMA2_Stream1->FCR=0X0000021;//设置为默认值 DMA2_Stream1->AR=(u32)&DCMI->DR;//外设地址为CMI->DR DMA2_Stream1->M0AR=mem0addr; //mem0addr作为目标地址0 DMA2_Stream1->M1AR=mem1addr; //mem1addr作为目标地址1 DMA2_Stream1->NDTR=memsize; //传输长度为memsize DMA2_Stream1->CR=0; //先全部复位CR寄存器值 DMA2_Stream1->CR|=0<<6; //外设到存储器模式 DMA2_Stream1->CR|=1<<8; //循环模式 DMA2_Stream1->CR|=0<<9; //外设非增量模式 DMA2_Stream1->CR|=meminc<<10; //存储器增量模式 DMA2_Stream1->CR|=2<<11; //外设数据长度:32位 DMA2_Stream1->CR|=memblen<<13; //存储器位宽,8/16/32bit DMA2_Stream1->CR|=2<<16; //高优先级 DMA2_Stream1->CR|=0<<21; //外设突发单次传输 DMA2_Stream1->CR|=0<<23; //存储器突发单次传输 DMA2_Stream1->CR|=1<<25; //通道1 DCMI通道 if(mem1addr)//双缓冲的时候,才需要开启[/mw_shl_code] [mw_shl_code=c,true]        ...... [/mw_shl_code]
在ov2640_jpg_photo()函数中进行了调用
[mw_shl_code=c,true]DCMI_DMA_Init((u32)jpeg_buf0,(u32)jpeg_buf1,jpeg_dma_bufsize,2,1);;//DCMI DMA配置(双缓冲模式)[/mw_shl_code]
其中jpeg_buf0,jpeg_buf1在test.c定义如下
[mw_shl_code=c,true]u32 *jpeg_buf0; //JPEG数据缓存buf,通过malloc申请内存 u32 *jpeg_buf1; //JPEG数据缓存buf,通过malloc申请内存[/mw_shl_code]
在main函数中有
[mw_shl_code=c,true]jpeg_buf0=mymalloc(SRAMIN,jpeg_dma_bufsize*4); //为jpeg dma接收申请内存 jpeg_buf1=mymalloc(SRAMIN,jpeg_dma_bufsize*4); //为jpeg dma接收申请内存 [/mw_shl_code]
其中,jpeg_dma_bufsize在test.c中为
[mw_shl_code=c,true]#define jpeg_dma_bufsize 5*1024 //定义JPEG DMA接收时数据缓存jpeg_buf0/1的大小(*4字节)[/mw_shl_code]
二、test.c中函数简介
1.jpeg_data_process函数,当jpeg拍照时,用于将最后接收到的JPEG数据拷贝到SRAM(jpeg_data_buf)里面,并标记JPEG数据采集完成,同时也可以启动下一次JPEG采集
[mw_shl_code=c,true]//处理JPEG数据 //当采集完一帧JPEG数据后,调用此函数,切换JPEG BUF.开始下一帧采集. void jpeg_data_process(void) { u16 i; u16 rlen;//剩余数据长度 u32 *pbuf; if(ov2640_mode)//只有在JPEG格式下,才需要做处理. { if(jpeg_data_ok==0) //jpeg数据还未采集完? { DMA2_Stream1->CR&=~(1<<0); //停止当前传输 while(DMA2_Stream1->CR&0X01); //等待DMA2_Stream1可配置 rlen=jpeg_dma_bufsize-DMA2_Stream1->NDTR;//得到剩余数据长度 pbuf=jpeg_data_buf+jpeg_data_len;//偏移到有效数据末尾,继续添加 if(DMA2_Stream1->CR&(1<<19))for(i=0;i<rlen;i++)pbuf=jpeg_buf1;//读取buf1里面的剩余数据 else for(i=0;i<rlen;i++)pbuf=jpeg_buf0;//读取buf0里面的剩余数据 jpeg_data_len+=rlen; //加上剩余长度 jpeg_data_ok=1; //标记JPEG数据采集完按成,等待其他函数处理 } if(jpeg_data_ok==2) //上一次的jpeg数据已经被处理了 { DMA2_Stream1->NDTR=jpeg_dma_bufsize;//传输长度为jpeg_buf_size*4字节 DMA2_Stream1->CR|=1<<0; //重新传输 jpeg_data_ok=0; //标记数据未采集 jpeg_data_len=0; //数据重新开始 } } }[/mw_shl_code]
在DCMI帧中断里面被调用
[mw_shl_code=c,true]//DCMI中断服务函数 void DCMI_IRQHandler(void) { if(DCMI->MISR&0X01)//捕获到一帧图像 { jpeg_data_process();//jpeg数据处理 DCMI->ICR|=1<<0; //清除帧中断 LED1=!LED1; ov_frame++; } } [/mw_shl_code]
2.jpeg_dcmi_rx_callback函数,是DCMI的DMA传输完成中断,循环读取M0AR和M1AR的数据,存放到外部SRAM(jpeg_data_buf)里面
[mw_shl_code=c,true]//jpeg数据接收回调函数 void jpeg_dcmi_rx_callback(void) { u16 i; u32 *pbuf; pbuf=jpeg_data_buf+jpeg_data_len;//偏移到有效数据末尾 if(DMA2_Stream1->CR&(1<<19))//buf0已满,正常处理buf1 { for(i=0;i<jpeg_dma_bufsize;i++)pbuf=jpeg_buf0;//读取buf0里面的数据 jpeg_data_len+=jpeg_dma_bufsize;//偏移 }else //buf1已满,正常处理buf0 { for(i=0;i<jpeg_dma_bufsize;i++)pbuf=jpeg_buf1;//读取buf1里面的数据 jpeg_data_len+=jpeg_dma_bufsize;//偏移 } }[/mw_shl_code]
3.sw_ov2640_mode用于设置SDIO和DCMI共用的IO为DCMI模式
[mw_shl_code=c,true]//切换为OV2640模式 void sw_ov2640_mode(void) { OV2640_PWDN=0;//OV2640 Power Up //GPIOC8/9/11切换为 DCMI接口 GPIO_AF_Set(GPIOC,8,13); //PC8,AF13 DCMI_D2 GPIO_AF_Set(GPIOC,9,13); //PC9,AF13 DCMI_D3 GPIO_AF_Set(GPIOC,11,13); //PC11,AF13 DCMI_D4 } [/mw_shl_code]
4.sw_sdcard_mode用于设置SDIO和DCMI共用的IO为SD模式
[mw_shl_code=c,true]//切换为SD卡模式 void sw_sdcard_mode(void) { OV2640_PWDN=1;//OV2640 Power Down //GPIOC8/9/11切换为 SDIO接口 GPIO_AF_Set(GPIOC,8,12); //PC8,AF12 GPIO_AF_Set(GPIOC,9,12); //PC9,AF12 GPIO_AF_Set(GPIOC,11,12); //PC11,AF12 }[/mw_shl_code]
5.camera_new_pathname函数,根据拍照类型(bmp/jpg)创建新文件名,保证文件名和SD卡原有照片不重复(避免覆盖)
[mw_shl_code=c,true]//文件名自增(避免覆盖) //mode:0,创建.bmp文件;1,创建.jpg文件. //bmp组合成:形如"0HOTO/PIC13141.bmp"的文件名 //jpg组合成:形如"0HOTO/PIC13141.jpg"的文件名 void camera_new_pathname(u8 *pname,u8 mode) { u8 res; u16 index=0; while(index<0XFFFF) { if(mode==0)sprintf((char*)pname,"0HOTO/PIC%05d.bmp",index); else sprintf((char*)pname,"0HOTO/PIC%05d.jpg",index); res=f_open(ftemp,(const TCHAR*)pname,FA_READ);//尝试打开这个文件 if(res==FR_NO_FILE)break; //该文件名不存在=正是我们需要的. index++; } } [/mw_shl_code]
6.ov2640_jpg_photo函数,用于jpg拍照,该函数设置OV2640为JPEG输出,然后设置分辨率1600*1200,为了确保数据完整,jpg拍照选择的是第二帧数据(第一帧可能不完整,丢弃)。拍照完成后,偏移到JPEG数据起始标识(0xff,0xd8),然后保存到SD卡
[mw_shl_code=c,true]//OV2640拍照jpg图片 //返回值:0,成功 // 其他,错误代码 u8 ov2640_jpg_photo(u8 *pname) { FIL* f_jpg; u8 res=0; u32 bwr; u16 i; u8* pbuf; f_jpg=(FIL *)mymalloc(SRAMIN,sizeof(FIL)); //开辟FIL字节的内存区域 if(f_jpg==NULL)return 0XFF; //内存申请失败. ov2640_mode=1; sw_ov2640_mode(); //切换为OV2640模式 dcmi_rx_callback=jpeg_dcmi_rx_callback;//回调函数 DCMI_DMA_Init((u32)jpeg_buf0,(u32)jpeg_buf1,jpeg_dma_bufsize,2,1);;//DCMI DMA配置(双缓冲模式) OV2640_JPEG_Mode(); //切换为JPEG模式 OV2640_ImageWin_Set(0,0,1600,1200); OV2640_OutSize_Set(1600,1200);//拍照尺寸为1600*1200 DCMI_Start(); //启动传输 while(jpeg_data_ok!=1); //等待第一帧图片采集完 jpeg_data_ok=2; //忽略本帧图片,启动下一帧采集 while(jpeg_data_ok!=1); //等待第二帧图片采集完,第二帧,才保存到SD卡去. DCMI_Stop(); //停止DMA搬运 ov2640_mode=0; sw_sdcard_mode(); //切换为SD卡模式 res=f_open(f_jpg,(const TCHAR*)pname,FA_WRITE|FA_CREATE_NEW);//模式0,或者尝试打开失败,则创建新文件 if(res==0) { printf("jpeg data size:%d\r\n",jpeg_data_len*4);//串口打印JPEG文件大小 pbuf=(u8*)jpeg_data_buf; for(i=0;i<jpeg_data_len*4;i++)//查找0XFF,0XD8 { if((pbuf==0XFF)&&(pbuf[i+1]==0XD8))break; } if(i==jpeg_data_len*4)res=0XFD;//没找到0XFF,0XD8 else//找到了 { pbuf+=i;//偏移到0XFF,0XD8处 res=f_write(f_jpg,pbuf,jpeg_data_len*4-i,&bwr); if(bwr!=(jpeg_data_len*4-i))res=0XFE; } } jpeg_data_len=0; f_close(f_jpg); sw_ov2640_mode(); //切换为OV2640模式 OV2640_RGB565_Mode(); //RGB565模式 DCMI_DMA_Init((u32)&LCD->LCD_RAM,0,1,1,0);//DCMI DMA配置 myfree(SRAMIN,f_jpg); return res; } [/mw_shl_code]
三、DCMI简单介绍(后补,2015年09月20日 11:24:32)
1.DCMI,数字摄像头接口。是一个同步并行接口,能够接收外部8位、10位、12位、或14位CMOS摄像头模块发出的高速(可达54MB/s)数据流。可支持不同数据格式:YCbCr4:2:2/RGB565逐行视屏和压缩数据(JPEG)。
2.此接口适用于黑白摄像头、X24和X5摄像头,并假定所有预处理(如调整大小)都在摄像头中执行。
3.该接口包含多达14条数据线(D13-D0)和一条像素时钟线(PIXCLK)。像素时钟线的极性可以编程,因此可以在像素时钟的上升沿或下降沿捕获数据。这些数据被放在32位数据寄存器(DCMI_DR)中,然后通过通用DMA进行传输,图像缓冲区由DMA管理,而不是由摄像头接口管理。
4.从摄像头接收的数据可以按行/帧来组织(原始YUB/RGB/拜尔模式),也可以是一系列JPEG图像。
5.数据流可由可选的HSYNC(水平同步)信号和VSYNC(垂直同步)信号硬件同步,或者通过数据流中嵌入的同步码同步。
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-20 13:10:06 | 显示全部楼层
第五五天  2015年09月20日  周日     例程:音乐播放器实验(一)

一、开发板拥有全双工I2S,且外扩了一颗HIFI级CODEC芯片:WM8978G,支持最高192K 24BIT的音频播放,并且支持录音。本例程利用探索者开发板实现一个简单的仅支持WAV播放的音乐播放器。【注:HIFI,High-Fidelity,即高保真,其定义是,与原来的声音高度相似的重放声音

二、WAV

1.WAV即WAVE文件,是微软公司专门位Windows系统定义的 波形文件格式(Waveform Audio),用于保存Windows平台的音频信息资源,支持MSADPCM、CCITT A LAW等多种压缩算法,支持多种音频数字、取道频率和声道,标准格式化WAV文件和CD格式一样,也是44.1K的取样频率,16位量化数字,因此在声音文件质量和CD相差无几。WAV一般采用线型PCM(脉冲编码调制)编码,这也是本章讨论编码方式。
2.WAV文件由若干个Chunk组成



每个Chunk由三部分组成



3.RIFF块(RIFF WAVE Chunk),以“RIFF”作为标示,紧跟wav文件大小(wav文件总大小-8),数据段为"WAVE",表示是wav文件



4.Format块(Format Chunk),以“fmt ”作为标识,一般情况下,该段大小为16字节,但有些软件生成的wav格式,该部分可能由18字节(含2个字节的附加信息)




5.Fact块(Fact Chunk),可选块,以“fact”作为标示,非PCM格式文件中,一般会在Format结果后加入一个Fact块



(其中,DataFactSize是此Chunk最重要数据,如果这是某种压缩格式的声音文件,那么从这里就可以知道他解压缩后的大小,对于解压时的计算会有很大好处

6.Data块(Data Chunk),以“data”作为标示,该块是真正保存wav数据的地方。


ChunkSize后紧接着就是wav数据,根据Format Chunk中断声道数以及采样bit数,wav数据的bit位置会有几种形式(详见相关资料)。

7.在得到以上wav数据以后,通过I2S丢给WM8978,就可以欣赏音乐了。

三、WM8978
1.WM8978是欧胜(Wolfson)推出的一款全功能音频处理器,集成了对麦克风的支持,以及用于一个强悍的扬声器功放,可提供高达900mW的高质量音响效果扬声器功率。
2.WM8978的控制通过I2S接口(数字音频接口)同MCU进行音频数据传输,通过两线(MODE=0,即IIC接口)或三线(MODE=1)接口进行配置。WM8978的I2S接口,由四个引脚组成:ADCDAT--ADC数据输出,DACDAT--DAC数据输出,LRC--数据左/右对齐时钟,BCLK--位时钟,用于同步。
3.WM8978可作为I2S主机,输出LRC和BLCK时钟,不够一般使用WM8978作为从机,接收LRC和BLCK。
4.WM8978的I2S接口支持5种不同的音频数据格式:左(MSB)对齐标准、右(LSB)对齐标准、飞利浦(I2S)标准、DSP模式A、DSP模式B。

四、I2S

1.I2S(Inter IC Sound)总线,又称集成电路内置音频总线,是飞利浦公司为数字音频设备之间的音频数据传输而指定的一种总线标准,专负责音频设备之间的数据传输,广泛用于各种多媒体系统。
2.I2S采用了沿独立的导线传输时钟与数据信号的设计,通过将数据和时钟信号分离,避免了因时差诱发的失真,为用户节省了购买抵抗音频抖动的专业设备的费用。
3.I2S支持主时钟输出,固定为256fs,其中fs即音频采样率。常用的音频采样率由:22.05Khz、44.1Khz、48Khz、96Khz、196Khz等。
4.STM32F4的I2S是与SPI部分共用的,通过设置SPI_I2SCFGR寄存器的I2SMOD位即可开启I2S功能,I2S接口使用了几乎与SPI相同的引脚、标志和中断。I2S用到的信号由:
其一,SD--串行数据(映射到MOSI引脚),用于发送或接收两个时分复用的数据通道上的数据(仅半双工模式
其二,WS--字选择(映射到NSS引脚),即帧时钟,用于切换左右声道的数据。WS频率等于音频信号采样率(fs)
其三,CK--串行时钟(映射到SCK引脚),即位时钟,是主模式下的串行时钟输出以及从模式下的串行时钟输入
其四,I2S2ext_SD和I2S3ext_SD--用于控制I2S全双工模式的附加引脚(映射到MISO引脚
其五,MCK--即主时钟输出,当I2S配置为主模式(且SPI_I2SPR中的MCKOE位置一)时,使用此时钟,该时钟输出频率256fs



相关/参考链接
【1】http://baike.baidu.com/link?url=5ug1_pSaBAFfKCruVQ-4PyzN-aJIfVCm2BF7KNn-VdrgBd72KnWUUk_AXWwYVUjXPEOYic0W3TMmoHKJcPHlFdGIg57TEyFrAoJ7BXtkCXIBhSrC0NpxUg-Tac3zPFkj2TmNofF899DhrtmeH_jimK

以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

0

主题

5

帖子

0

精华

新手上路

积分
25
金钱
25
注册时间
2015-8-21
在线时间
0 小时
发表于 2015-9-20 22:12:39 | 显示全部楼层
好久没来看了,加油啊楼主,这么久了还在坚持
回复 支持 反对

使用道具 举报

0

主题

5

帖子

0

精华

新手上路

积分
25
金钱
25
注册时间
2015-8-21
在线时间
0 小时
发表于 2015-9-20 22:21:10 | 显示全部楼层
不知道楼主科目三之后怎么安排,其实考试没通过没什么大不了的,下次考过就是了,我当初科目二第一次没过,特别难过,因为同一个考场的通过率非常高。
看到一起学得人都通过了,自己却在坡起折戟了,感觉特别难过
对我来讲,当时不仅是运气不好,更重要的是当时没有好好学,上课的时候有一种应付的心态,觉得把课时上完了,再去参加考试就ok了。
然而考试并不是那么简单的事情,平时的糊弄,导致基本功不扎实,考试就挂了。
之后又花钱买圈学3小时,把不会的地方好好补齐,科目二才过了。
所以学科目三的时候,每节课每分钟都非常认真,用考试的标准严格要求自己,最后科目三也是轻松通过。
做什么事情都是一样的,楼主有这种认真的态度,还能坚持下去,平时打的基础一定差不了,真正需要做什么东西的时候,领导也一会放心地委以重任。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-20 23:22:57 | 显示全部楼层
回复【293楼】neary:
---------------------------------
谢谢鼓励...

最后那句话自己还做不到,基础有点薄弱,所以虽然在公司需要担起技术的工作,但还不足以去很好的完成,希望今年能坚持一下,明年能够让领导放心的分配工作吧...

加油~~~~~
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-21 22:37:16 | 显示全部楼层
第五六天  2015年09月21日  周一     例程:音乐播放器实验(二)

一、回调函数使用:定义如下
[mw_shl_code=c,true]//I2S DMA回调函数指针 void (*i2s_tx_callback)(void); //TX回调函数 [/mw_shl_code]
函数i2s_tx_callback是函数指针,使用前需指向特定函数,如
[mw_shl_code=c,true]i2s_tx_callback=wav_i2s_dma_tx_callback; //回调函数指wav_i2s_dma_callback[/mw_shl_code]
其中
[mw_shl_code=c,true]//WAV播放时,I2S DMA传输回调函数 void wav_i2s_dma_tx_callback(void) { u16 i; if(DMA1_Stream4->CR&(1<<19)) { wavwitchbuf=0; if((audiodev.status&0X01)==0) { for(i=0;i<WAV_I2S_TX_DMA_BUFSIZE;i++)//暂停 { audiodev.i2sbuf1=0;//填充0 } } }else { wavwitchbuf=1; if((audiodev.status&0X01)==0) { for(i=0;i<WAV_I2S_TX_DMA_BUFSIZE;i++)//暂停 { audiodev.i2sbuf2=0;//填充0 } } } wavtransferend=1; } [/mw_shl_code]
二、当寄存器不支持读操作,而某些操作需要知道寄存器当前值时,如何进行处理?
[mw_shl_code=c,true]//WM8978寄存器值缓存区(总共58个寄存器,0~57),占用116字节内存 //因为WM8978的IIC操作不支持读操作,所以在本地保存所有寄存器值 //写WM8978寄存器时,同步更新到本地寄存器值,读寄存器时,直接返回本地保存的寄存器值. //注意:WM8978的寄存器值是9位的,所以要用u16来存储. static u16 WM8978_REGVAL_TBL[58]= { 0X0000,0X0000,0X0000,0X0000,0X0050,0X0000,0X0140,0X0000, 0X0000,0X0000,0X0000,0X00FF,0X00FF,0X0000,0X0100,0X00FF, 0X00FF,0X0000,0X012C,0X002C,0X002C,0X002C,0X002C,0X0000, 0X0032,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000,0X0000, 0X0038,0X000B,0X0032,0X0000,0X0008,0X000C,0X0093,0X00E9, 0X0000,0X0000,0X0000,0X0000,0X0003,0X0010,0X0010,0X0100, 0X0100,0X0002,0X0001,0X0001,0X0039,0X0039,0X0039,0X0039, 0X0001,0X0001 }; [/mw_shl_code]
[mw_shl_code=c,true]//WM8978读寄存器 //就是读取本地寄存器值缓冲区内的对应值 //reg:寄存器地址 //返回值:寄存器值 u16 WM8978_Read_Reg(u8 reg) { return WM8978_REGVAL_TBL[reg]; } [/mw_shl_code]

以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-21 22:38:55 | 显示全部楼层
静不下心来

玩玩玩

三国杀起来嗨
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
发表于 2015-9-21 23:12:05 | 显示全部楼层
回复【296楼】龙之谷:
---------------------------------
呵呵
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-22 22:32:42 | 显示全部楼层
第五七天  2015年09月22日  周二     例程:录音机实验

一、本章需要想WM8978提供WS、CK和MCK等时钟,同时又要录音,所以只能使用全双工模式。主I2S循环发送数据0X0000,给WM8978,以产生CK、WS和MCK等信号,从I2S_ext,则接收来自WM8978的ADC数据(I2Sxext_SD),并保存到SD卡,实现录音。

二、本章定义了几个结构体,通过这几个结构体清晰地体现出WAV音频的组成
[mw_shl_code=c,true]//RIFF块 typedef __packed struct { u32 ChunkID; //chunk id;这里固定为"RIFF",即0X46464952 u32 ChunkSize ; //集合大小;文件总大小-8 u32 Format; //格式;WAVE,即0X45564157 }ChunkRIFF ; //fmt块 typedef __packed struct { u32 ChunkID; //chunk id;这里固定为"fmt ",即0X20746D66 u32 ChunkSize ; //子集合大小(不包括ID和Size);这里为:20. u16 AudioFormat; //音频格式;0X01,表示线性PCM;0X11表示IMA ADPCM u16 NumOfChannels; //通道数量;1,表示单声道;2,表示双声道; u32 SampleRate; //采样率;0X1F40,表示8Khz u32 ByteRate; //字节速率; u16 BlockAlign; //块对齐(字节); u16 BitsPerSample; //单个采样数据大小;4位ADPCM,设置为4 // u16 ByteExtraData; //附加的数据字节;2个; 线性PCM,没有这个参数 }ChunkFMT; //fact块 typedef __packed struct { u32 ChunkID; //chunk id;这里固定为"fact",即0X74636166; u32 ChunkSize ; //子集合大小(不包括ID和Size);这里为:4. u32 NumOfSamples; //采样的数量; }ChunkFACT; //LIST块 typedef __packed struct { u32 ChunkID; //chunk id;这里固定为"LIST",即0X74636166; u32 ChunkSize ; //子集合大小(不包括ID和Size);这里为:4. }ChunkLIST; //data块 typedef __packed struct { u32 ChunkID; //chunk id;这里固定为"data",即0X5453494C u32 ChunkSize ; //子集合大小(不包括ID和Size) }ChunkDATA; //wav头 typedef __packed struct { ChunkRIFF riff; //riff块 ChunkFMT fmt; //fmt块 // ChunkFACT fact; //fact块 线性PCM,没有这个结构体 ChunkDATA data; //data块 }__WaveHeader; [/mw_shl_code]
先把每个CHUNK根据各自结构进行单独定义,最后放在一个大的结构体同一管理,对_WaveHeader的跟踪如下
首先,定义一个指针变量指向_WaveHeader,
[mw_shl_code=c,true]__WaveHeader *wavhead=0; [/mw_shl_code]
对其进行动态内存申请处理
[mw_shl_code=c,true]wavhead=(__WaveHeader*)mymalloc(SRAMIN,sizeof(__WaveHeader));//开辟__WaveHeader字节的内存区域 [/mw_shl_code]
录音前先进行初始化
[mw_shl_code=c,true]recoder_wav_init(wavhead); //初始化wav数据 [/mw_shl_code]
其中,初始化函数
[mw_shl_code=c,true]//初始化WAV头. void recoder_wav_init(__WaveHeader* wavhead) //初始化WAV头 { wavhead->riff.ChunkID=0X46464952; //"RIFF" wavhead->riff.ChunkSize=0; //还未确定,最后需要计算 wavhead->riff.Format=0X45564157; //"WAVE" wavhead->fmt.ChunkID=0X20746D66; //"fmt " wavhead->fmt.ChunkSize=16; //大小为16个字节 wavhead->fmt.AudioFormat=0X01; //0X01,表示PCM;0X01,表示IMA ADPCM wavhead->fmt.NumOfChannels=2; //双声道 wavhead->fmt.SampleRate=16000; //16Khz采样率 采样速率 wavhead->fmt.ByteRate=wavhead->fmt.SampleRate*4;//字节速率=采样率*通道数*(ADC位数/8) wavhead->fmt.BlockAlign=4; //块大小=通道数*(ADC位数/8) wavhead->fmt.BitsPerSample=16; //16位PCM wavhead->data.ChunkID=0X61746164; //"data" wavhead->data.ChunkSize=0; //数据大小,还需要计算 } [/mw_shl_code]
然后将WaveHeader数据写入WAV文件头(未确定数据会在录音完成确定后再次写入),其后,通过DMA双缓冲模式进行录音,将数据存入SD卡。
录音完成后,再次写入WaveHeader到SD卡,将数据更新完整

[mw_shl_code=c,true] wavhead->riff.ChunkSize=wavsize+36; //整个文件的大小-8; wavhead->data.ChunkSize=wavsize; //数据大小 f_lseek(f_rec,0); //偏移到文件头. f_write(f_rec,(const void*)wavhead,sizeof(__WaveHeader),&bw);//写入头数据 f_close(f_rec);[/mw_shl_code]
(其中,wavesize为DMA存入SD卡数据量),至此,完成一个录音流程。
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

1

主题

10

帖子

0

精华

初级会员

Rank: 2

积分
68
金钱
68
注册时间
2015-9-22
在线时间
16 小时
发表于 2015-9-23 00:57:17 | 显示全部楼层
哥们你写的这些都记得么,下次再写 是不是很轻松的写出来啊
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-23 21:03:42 | 显示全部楼层
回复【299楼】qq991463507:
---------------------------------
不记得,我只是把思路捋一下
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

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

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-22 11:33

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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