首页-达尔闻    全部    毕业设计| STM32F4电子阅读器制作教程

毕业设计| STM32F4电子阅读器制作教程

在电子阅读器中,STM32F4作为整个项目的核心主控器件,用来处理各种数据。
收藏
  • 最近,有小伙伴问我们能否分享电子书相关的设计。今天我们就找到了化作尘同学刚刚完成的项目,借花献佛分享为大家。

    化作尘同学还是个在校学生,平时会捣鼓自己喜欢的项目,并且特别愿意与大家分享,想要看他的作品可以关注他的B站(ID:化作尘my),之后达尔闻也会继续分享他的作品。

    话不多说,来看看他的设计过程吧。

     

    实现功能

    开机logo

    电子书列表扫描

    电子书列表显示及小说选择菜单

    阅读功能:

    1)能够记录每本电子书的退出时处于什么阅读位置,下次进入从该位置继续阅读

    2)字体选择

    3)字体大小选择

    4)字体颜色设置

    5)阅读背景设置

    6)书签设置

    7)目录生成功能(字符匹配)

    8)章节跳转功能(基于目录生成功能)

    9)阅读翻页设置(滚动/切换)

     

    项目功能要求

    项目偏软件,但是要依赖于自己对硬件的熟悉和驱动才能完成,用到的主要技术:

    1)SD卡驱动(难–不过可移植 SD卡驱动细节可在用完再了解其驱动协议)

    2)FatFs文件系统移植使用

    3)LCD屏驱动(加载字库文件 做字库在LCD上的显示)

    材料准备

    ✔ STM32F4开发板(本设计用的是普中STM32F4ZGT6最小系统板)

    作为毕设的话,最好自己硬件设计一块开发板,这样可以体现硬件的工作量。淡然不论是购买,还是自己做,注意一定要具备LCD屏接口。在电子阅读器中,STM32F4作为整个项目的核心主控器件,用来处理各种数据。

    ✔ 800*480 4.3寸电容触摸LCD屏(项目中用的正点原子的屏)

    作用:显示与触摸控制

    ✔ 一张32G以下的SD卡和一个读卡器

    作用:存储小说文件与图片文件图片硬件连接非常简单,把屏幕插到开发板上即可。

     

    相关知识点

    ✦ txt文件数据的读取

    1)谈谈txt文件

    学过C语言的文件IO都知道,用open函数打开后,用read函数读取的第一个字节开始就是文本内容,txt文件是文档文件,对于我们程序员来说文档文件是最容易操作的文件类型之一,因为它不像图片那样还需要考虑头部的一大堆信息。

    2)读取txt文件数据

    利用FATFS文件系统提供的函数f_open 、f_read即可对数据进行读取出来,当然读取出来的都是文字的编码,可以先用串口函数打印出来看看数据对不对 printf("%s\r\n",buf)。

    ✦ 汉字的显示(具体代码参考正点原子“汉字显示实验”)

    1)汉字的显示原理

    学习过屏幕驱动的应该都知道,屏幕的显示无非就是在屏幕上打相应颜色的点,比如要显示一个字符 ‘A’ 那么我只需要知道 ‘A’ 相应的点阵数据就可以把它显示出来,汉字也一样。

    2)汉字库的存储

    但是汉字与字符不同的是,汉字有1万多个,字符却只有100多个,并且汉字的显示明显比字符的点阵复杂程度高,比如说字体是12的字符占用点阵是12x6,每个字符需要12x6/8=9个字节的存储空间。字体是12 的汉字占用12x12的点阵,每个汉字需要12x12/8=18个字节存储空间,这还是最小字体,大的字体所需要的存储空间只会成倍增加。

    3)一般12号字体的汉字库需要500K字节、16号字体700K、24号字体1.6M、32号字体3M,这对于单片机来说数据量太大了,内部FLASH512K~1M表示压力很大。

    4)需要把汉字库通过SD卡再保存到外部FILASH才行,w25q128无疑是个好选择,它拥有16M的存储空间,SPI通信协议,读写速度也是相当快。

     

    ✦ txt文件数据显示到MCU屏

    有关显示汉字就必然会涉及编码格式的问题,不同的编码格式对应的汉字编码是不同的,比如UTF-8编码的文件用GB2312编码打开就会乱码。

    同理,比如文档的格式是UTF-8编码,但是我的字库是GBK编码,读出来的数据直接显示到屏幕就必然会乱码。

     

    下载调试

    步骤一:SD卡根目录文件拷贝

    阅读器的源码分享给各位,可以在“达尔闻说”的微信回复:电子阅读器。下载好配套资料,把下面两个文件夹拷贝到SD卡根目录下:

    注意:一定要是根目录不然不能正常运行。

    步骤二:初始化外部FLASH -W25Q128

    用keil打开代码工程,编译通过后下载。下载完成等待外部FLASH -W25Q128初始化完成,一开始会黑屏一段时间,是在擦除FLASH,然后会提示字库加载进度,直到LCD显示阅读界面为止。

    如果下载代码后运行不正常:

    1)注释掉这些代码,编译下载图片

    2)不要断电,取消注释这段代码,编译下载,下载完成后W25Q128初始化完成,应该就可以运行

    步骤三:动态logo显示(化作尘)

    步骤四:进入主界面

    啥都没有,先导入图书。图书要从SD卡中导入,txt文件在资料里已经打包好了,直接导入。

    找到0:/TXT目录下的.txt文件,选择导入:

    导入“斗罗大陆.txt”,主界面开始显示。图片是系统自动匹配,检测是否有与小说同名图片文件,比如“斗罗大陆.txt” 对应 “斗罗大陆.jpg”。如果没有匹配图片会显示默认图片。

    点击左下角删除图书,再点击 X 即可移除图书操作,只是移除,并不是真正删除。

    阅读界面都是根据页数匹配相应文字,因为读写速度问题加上需要算法识别加载,翻页加载过程都会有一段时间

    目录功能:自动检测目录,通过检测“第” 与 “章”加上相应算法就可以检测显示目录,并记录偏移,可以跳转,上划可以显示下一页目录:

    设置字体、背景:可以设置显示背景颜色,字体颜色,字体大小(4种字号)来适应自己喜欢的阅读方式:

    程序设计

    代码过长,只展示部分代码,想要完整的代码在“达尔闻说”微信回复:电子阅读器。
    #include "sys.h"
    #include "delay.h"
    #include "usart.h"
    #include "led.h"
    #include "lcd.h"
    #include "key.h"  
    #include "sram.h"   
    #include "malloc.h" 
    #include "usmart.h"  
    #include "sdio_sdcard.h"    
    #include "malloc.h" 
    #include "w25qxx.h"    
    #include "ff.h"  
    #include "exfuns.h"    
    #include "fontupd.h"
    #include "text.h"   
    #include "piclib.h"  
    #include "string.h"  
    #include "math.h"  
    #include "touch.h"

    u8 menu_num;//当前主菜单页数
    u32 page;//当前页数
    //char chapter_name[50];//当前章节名
    u32 page_offset[1000];//保存页数偏移
    //u32 page_off_mult;//页数偏移倍数
    u8 txt_num;//当前书架图书数量
    u8 txt_path_name[30][60];//导入图书路径
    u32 bookmark[30];        //保存对应书签 也就是页数 下次进入直接偏移页数
    char txt_name[4][30]={"","","",""};//显示4本图书名字
    u8 pname[60];      //正在打开的文件名路径
    int SIZE=24;      //初始化字体
    char buff[2500];//一页数据 缓冲文档
    int page_mul_cnt=0;//存取次数 一次1000页
    int now_page_mul;//当前1000页的倍数
    u32 data_offset;//读取偏移  读写flash用
    //显示主界面
    void menu(void);
    //获取触摸键值
    u16 get_ts_val(int key);
    //读取文件数据到LCD
    int read_to_lcd(void);
    //更新小说名
    u8 lcd_dis_files(u8 * path);
    //阅读模式
    void read_mode(void);
    //设置字体格式
    int setting(void);
    //设置界面
    void show_setting(void);
    //目录功能
    u32 txt_dir_scan(void);
    //文件系统功能 导入小说
    void fatfs_choose(void);
    //读取数据到flash
    void file_data_to_w25q(void);
    //根据页码得到页数
    u32 ident_get_page(u32 page_offset);
    //遍历文件 显示到屏幕
    u8 mf_scan_file(u8 * path);
    //刷新显示书架名字
    void re_dis_txtname(int i);
    //带路径的文件名 转 不带路径文件名
    char* path_change(char *oldpath);
    //根据页数得到页码
    u32 ident_get_offset(u32 page);
    //更新内存1000页页码
    void update_page_offset(void);
    //读出书签数据
    void read_bookmark(void);
    //保存书签数据到flash
    void save_bookmark(void);
    //保存书名数据到flash
    void save_bookinfo(void);
    //删除小说
    void del_txt(u8 *path);
    //删除小说控制界面
    void del_txt_ctrl(void);
      
    int main(void)
    {        
      DIR picdir;       //图片目录
       
      u8 key;
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
      delay_init(168);  //初始化延时函数
      uart_init(115200);    //初始化串口波特率为115200
      LED_Init();          //初始化LED  
      LCD_Init();          //LCD初始化  
      KEY_Init();          //按键初始化 
      tp_dev.init();      //触摸初始化  
      W25QXX_Init();        //初始化W25Q128
      usmart_dev.init(168);    //初始化USMART
      my_mem_init(SRAMIN);    //初始化内部内存池 
      my_mem_init(SRAMCCM);    //初始化CCM内存池 
      exfuns_init();        //为fatfs相关变量申请内存  
      f_mount(fs[0],"0:",1);     //挂载SD卡 
      f_mount(fs[1],"1:",1);     //挂载FLASH.

      while(font_init());      //检查字库
      
      delay_ms(150);
      piclib_init();                    //初始化画图    
      delay_ms(150);
      LCD_Clear(GBLUE);//清屏
      ai_load_picfile("0:/SYSTEM/PICTURE/logo.gif",0,0,lcddev.width,lcddev.height,1);//显示图
      read_bookmark();//读出图书 书签数据
      memset(page_offset,0,1000*4);//清空页码数组
      menu_num=1;//显示第一页
      //lcd_dis_files("0:/TXT");//更新系统书名
      menu();//显示主界面
      while(1)
      {
        key = get_ts_val(1);
        if(key!=0)
        {
          printf("key=%d\n",key);
          switch(key)//选择要打开的图书
          {
            case 1: if(txt_num-4*(menu_num-1)>=1){
                      strcpy((char *)pname,(char *)txt_path_name[0+(menu_num-1)*4]);//把要打开的书籍名称赋值
              
                      page = bookmark[0+(menu_num-1)*4];//根据flash保存的书签初始化进入的页数
                      if(page >= 0xffff)page=0;
                        read_mode();//进入阅读模式
                      bookmark[0+(menu_num-1)*4] = page;//退出保存书签
                      save_bookmark();
                      }break;
            
            case 2:  if(txt_num-4*(menu_num-1)>=2){
                      strcpy((char *)pname,(char *)txt_path_name[1+(menu_num-1)*4]);
              
                      page = bookmark[1+(menu_num-1)*4];
                      if(page >= 0xffff)page=0;
                      read_mode();//进入阅读模式
                      bookmark[1+(menu_num-1)*4] = page;
                      save_bookmark();
                      }break;
                      
                      
            case 3: if(txt_num-4*(menu_num-1)>=3){
                      strcpy((char *)pname,(char *)txt_path_name[2+(menu_num-1)*4]);
              
                      page = bookmark[2+(menu_num-1)*4];
                      if(page >= 0xffff)page=0;
                      read_mode();//进入阅读模式
                      bookmark[2+(menu_num-1)*4] = page;
                      save_bookmark();
                      }break;
                      
            case 4: if(txt_num-4*(menu_num-1)>=4){
                      strcpy((char *)pname,(char *)txt_path_name[3+(menu_num-1)*4]);
              
                      page = bookmark[3+(menu_num-1)*4];
                      if(page >= 0xffff)page=0;
                      read_mode();//进入阅读模式
                      bookmark[3+(menu_num-1)*4] = page;
                      save_bookmark();
                      }break;

            case 5:    //导入图书
                      fatfs_choose();//文件系统选择文件导入
                      save_bookinfo();//保存书籍数据到flash
                      printf("当前图书数量:%d\r",txt_num);
                      break;
            
            case 6:    del_txt_ctrl();//删除图书
                      break;
            
            case 111: if((txt_num-1)/4+1>menu_num)menu_num++;
                        printf("menu_num = %d\n",menu_num);//上一页
                      break;
            
            case 222: if(1<menu_num)menu_num--;
                        printf("menu_num = %d\n",menu_num);//下一页
                      break;  
          }
          
          menu();//显示主界面
          data_offset=0;//偏移清零
          page_mul_cnt=0;//1000页倍数清零
        }
        delay_ms(10);
      }
    }

    每周四,达尔闻的毕设分享系列为大家分享过往的毕设项目,如果你有项目愿意分享,可以联系妮姐(微信:459888529),所有热心分享的小伙伴将会获得达尔闻礼品一份。