基于STM32的小说阅读器

发布者:EuphoricMelody最新更新时间:2024-02-18 来源: elecfans关键字:STM32 手机看文章 扫描二维码
随时随地手机看文章

1.硬件平台

  • CPU:STM32F103ZE

  • 屏幕:3.5寸TFTLCD屏

  • 触控:电阻式触摸屏xpt2046

  • SD卡

2.实现功能

1.SD卡设备检测,文件系统移植,用户存储小说和字库文件;

2.字库信息加载检测,自动完成字库信息加载与更新。本次采用GBK字库,字体大小有16*16、24*24、32*32三个字库;


3.触摸屏校准,上电检测触摸屏校准信息;手动进入触摸屏校准模式

4.小说文件索引,小说文本切换;

5.小说翻页,字体大小选择,颜色选择,返回主目录;

3.示例效果

 SD卡检测和触摸屏校准

poYBAGKWuTuAJKEyAAhpr0Axaa8252.png

 字库检测与更新

pYYBAGKWuVOAB3ZwAAXo7AKeGlA674.png

目录和小说显示界面

poYBAGKWuW6AHwsRAAiD7y7IHy8664.png

颜色切换

poYBAGKWuYOAN8MJAAfxcsfMwv4966.png

字体选择

在这里插入图片描述

4.软件设计

 1.SD卡字库更新加载


/***********************SD卡字库更新***********************/

u8 SDcard_DownFont(const TCHAR* path,u32 addr,u16 font_size)

{

  FIL fp;

  u8 res;

  UINT br;

  u32 size;

  u32 cnt=0;

u16 y=0;

float load=0,load2=0;

  u8 buff[1024];

char buff2[20];

  /*1.读取文件大小*/

  FILINFO file_info;

  f_stat(path,&file_info);

  size=file_info.fsize;

  if(size==0)return 1;

  //printf("文件大小:%u bytern",size);

  /*2.打开文件*/

  res=f_open(&fp,path,FA_READ);

  if(res)

  {

   // printf("文件打开失败res=%drn",res);

    return 2;

  }

W25Q64_WriteData(addr-10,(u8 *)"        ",9);//清除标志位

if(font_size==16)y=100;

else if(font_size==24)y=140;

else if(font_size==32)y=180;

LCD_Display_Str2(20,y,16,(u8 *)"更新进度:",BLACK,WHITE);

LCD_Refresh();

  while(1)

  {

    f_read(&fp,buff,1024,&br);

    W25Q64_WriteData(addr+cnt,buff,br);

    cnt+=br;

load=(cnt*1.0/size)*100;

if(load!=load2)

{

load2=load;

LCD_Display_Str2(30+strlen("更新进度")/2*16,y,16,(u8 *)"        ",WHITE,WHITE);

snprintf(buff2,sizeof(buff2),"%.1f %%",load2);

LCD_Display_Str2(30+strlen("更新进度")/2*16,y,16,(u8 *)buff2,RED,WHITE);

LCD_Refresh();

}

    //printf("更新进度:%drn",cnt);

    if(br!=1024)break;

  }

/*写入标志位*/

if(font_size==16)

{

strcpy((char *)buff,"GBK16_OK");

}

else if(font_size==24)

{

strcpy((char *)buff,"GBK24_OK");

}

else if(font_size==32)

{

strcpy((char *)buff,"GBK32_OK");

}

W25Q64_WriteData(addr-10,buff,9);//GBK16_OK

  f_close(&fp);//关闭文件

return 0;

}

2.获取小说文件信息


/*SD卡操作函数*/

typedef struct FILE_info

{

char file_name[100];

u32 file_size;

u16 y;/*在屏幕位置*/

struct FILE_info *next;

struct FILE_info *pre;

}FILE_INFO;

FILE_INFO *story_head=NULL;

/*创建链表*/

FILE_INFO *List_CreateHead(FILE_INFO *head)

{

if(head!=NULL)return head;

head=malloc(sizeof(FILE_INFO));

head->next=NULL;

head->pre=NULL;

return head;

}

/*添加节点*/

FILE_INFO *List_AddNode(FILE_INFO *head)

{

if(head==NULL)return NULL;//链表头不存在

FILE_INFO *phead=head;

while(phead->next!=NULL)

{

phead=phead->next;

}

FILE_INFO *new_node=malloc(sizeof(FILE_INFO));

new_node->pre=phead;

phead->next=new_node;

new_node->next=NULL;

return new_node;

}

/*遍历节点*/

void List_PrintNode(FILE_INFO *head)

{

u16 x=20,y=50;

LCD_Display_Str2(LCD_WIDTH/2-24,10,16,(u8 *)"书 架",BLACK,WHITE);

if(head==NULL)return ;//链表头不存在

FILE_INFO *phead=head;

while(phead->next!=NULL)

{

phead=phead->next;

phead->y=y;

LCD_Display_Str2(x,phead->y,16,(u8 *)phead->file_name,DARKBLUE,WHITE);//显示字符串

y+=35;

//printf("%s,%drn",phead->file_name,phead->file_size);

}

LCD_Refresh();

}

u16 List_CheckNode(FILE_INFO *head,u16 y,u8 *file_name)

{

if(head==NULL)return 0;//链表头不存在

FILE_INFO *phead=head;

while(phead->next!=NULL)

{

phead=phead->next;

if(y<=phead->y+26 && y>=phead->y-5)

{

strcpy((char *)file_name,phead->file_name);

return phead->y;

}

}

return 0;//未找到 

}

/*************************************目录遍历(读取小说文件信息)************************/

u8 FATFS_printDir(const TCHAR* path)

{

  DIR dp;

  u8 res;

u8 stat=0;

story_head=List_CreateHead(story_head);//创建链表头

  res=f_opendir(&dp,path);

  FILINFO file_info;

  if(res)

  {

    printf("打开目录失败res=%drn",res);

free(story_head);//释放链表头

    return 1;

  }

FILE_INFO *temp=NULL;

  while(1)

  {

    res=f_readdir(&dp,&file_info);

    if(res!=FR_OK || file_info.fname[0]==0)break;

if(strstr(file_info.fname,".txt"))

{

temp=List_AddNode(story_head);

if(temp==NULL)

{

stat=2;//动态分配空间失败

goto AA;

}

//printf("文件名:%s,",file_info.fname);

strcpy(temp->file_name,file_info.fname);//文件名

if(file_info.fattrib == AM_ARC)//普通文件

{

temp->file_size=file_info.fsize;//文件大小

//printf("文件大小:%u bytern",file_info.fsize);

}

}

  }

AA:

f_closedir(&dp);//关闭目录

List_PrintNode(story_head);

return stat;

}

3.读取小说内容,翻页,字体选择、颜色切换


/*打开小说*/

u8 buff_read[4098];

const u16 font_corlour[]={BLACK,BLUE,RED,LIGHTGREEN};

void FATFS_ReadFile(const char *file_name)

{

FIL fp;//文件指针

u8 res=0;

UINT br;

u32 font_buff[20];//保存每页字节数据

u16 font_cnt=0;

u16 font_len=0;

u16 font_size=16;//字体大小

u16 x=0,y=20;

u8 corlour=0;

char name[100];

snprintf(name,sizeof(name),"0:/Text/%s",file_name);

res=f_open(&fp,name,FA_READ);//打开文件

if(res)

{

printf("文件打开失败res=%drn",res);

return ;

}

u8 *p;

u16 x1,y1;

u8 stat=0;

while(1)

{

if(stat)

{

buff_read[0]=stat;

stat=0;

res=f_read(&fp,&buff_read[1],4095,&br);

br++;

buff_read[br]='';

}

else

{

res=f_read(&fp,buff_read,4096,&br);

buff_read[br]='';

}

p=buff_read;

while(*p)

{

font_len=LCD_Display_Str(x,y,font_size,p,font_corlour[corlour]);//显示字符串

LCD_Refresh();

while(1)

{

res=XPT2046_ReadXY();//触摸屏检测

if(res)

{

x1=xpt2046_info.x;

y1=xpt2046_info.y;

while(T_PEN==0){}//等待松开

//printf("x1=%d,y1=%drn",x1,y1);

if((x1>=212 && x1<=320) && (y1>=416 && y1<=480))//返回

{

LcdFill(0,0,320,480,WHITE);

List_PrintNode(story_head);

return ;

}

else if((x1>=106 && x1<=210) && (y1>=416 && y1<=480))//字体颜色

{

corlour++;

if(corlour>=4)corlour=0;

LcdFill(0,0,320,410,WHITE);

break;

}

else if((x1>=1 && x1<=105) && (y1>=416 && y1<=480))//字体大小调节

{

if(font_size==16)font_size=24;

else if(font_size==24)font_size=32;

else if(font_size==32)font_size=16;

LcdFill(0,0,320,410,WHITE);

break;

}

else if(x1>160 && y1<=380)

{

p+=font_len&0x7fff;

if(*p!='')

{

font_buff[font_cnt++]=font_len&0x7fff;

LcdFill(0,0,320,410,WHITE);

break;

}

else 

{

if(font_len&0x8000)

{

stat=*(p-1);

}

LcdFill(0,0,320,410,WHITE);

break;

}

}

else if(x1<160 && y1<=380)

{

if(p!=buff_read && font_cnt>0)

{

font_cnt--;

p-=font_buff[font_cnt];

LcdFill(0,0,320,410,WHITE);

break;

}

}

}

}

}


font_cnt=0;

memset(buff_read,0,sizeof(buff_read));

if(br!=4096)break;

}

}

4.主函数main.c,硬件初始化,触摸屏校准,字体检测与更新,主界面显示


#include "stm32f10x.h"

#include "beep.h"

#include "led.h"

#include "key.h"

#include "delay.h"

#include "usart.h"

#include "w25q64.h"

#include "at24c08.h"

#include "timer.h"

#include 

#include "xpt2046.h"

#include "nt35310.h"

#include "sram.h"

#include 

#include "ff.h"//文件系统头文件

#include "sdcard.h"

u8 SDcard_DownFont(const TCHAR* path,u32 addr,u16 font_size);//字库更新

u8 FATFS_printDir(const TCHAR* path);//遍历目录

u16 List_CheckNode(FILE_INFO *head,u16 y,u8 *file_name);//查找文件

void FATFS_ReadFile(const char *file_name);//读文件

FATFS fs;

int main()

{

u16 y=0;

char buff[30];

  u8 res=0;

  Beep_Init();

  Led_Init();

  Key_Init();

  Usartx_Init(USART1,115200,72);

  printf("串口初始化完成rn");

  W25Q64_Init();

  IIC_Init();

  LCD_Init();

  Sram_Init();

  XPT2046_Init();//初始化

Touch_Calibration();

AA:

LCD_Clear(WHITE);//清屏函数

LCD_Refresh();

LCD_Display_Str2(20,20,16,(u8 *)"SD卡状态",RED,WHITE);

  res=f_mount(&fs,"0:",1);//磁盘挂载

  if(res)

{

printf("SD卡挂载失败ERR=%drn",res);

snprintf(buff,sizeof(buff),"err%d",res);

LCD_Display_Str2(20+strlen("SD卡状态")*12+20,20,16,(u8 *)buff,RED,WHITE);

LCD_Display_Str2(20,50,16,(u8 *)"请检查SD卡是否插入!",RED,WHITE);

LCD_Refresh();

Delay_Ms(500);

goto AA;

}

else LCD_Display_Str2(20+strlen("SD卡状态")*12+20,20,16,(u8 *)"OK",RED,WHITE);

/*字库检测*/

LCD_Display_Str2(LCD_WIDTH/2-strlen("字库检测")/2*16,40,16,(u8 *)"字库检测",RED,WHITE);

LCD_Refresh();


GBK_16:

W25Q64_ReadData(GBK_16_ADDR-10,(u8*)buff,9);//GBK16_OK

if(strstr(buff,"GBK16_OK"))

{

LCD_Display_Str2(20,60,16,(u8 *)"GBK16    OK",RED,WHITE);

LCD_Refresh();

}

else 

{

LCD_Display_Str2(20,60,16,(u8 *)"GBK16    NO",RED,WHITE);

LCD_Display_Str2(LCD_WIDTH/2-strlen("更新GBK16字库")/2*16,80,16,(u8 *)"更新GBK16字库",RED,WHITE);

LCD_Refresh();

if(SDcard_DownFont("0:/font/GBK_16.DZK",GBK_16_ADDR,16))//字库更新

{

LCD_Display_Str2(LCD_WIDTH/2-strlen("                 ")/2*16,80,16,(u8 *)"                 ",WHITE,WHITE);

LCD_Display_Str2(10,80,16,(u8 *)"请将GBK_16.DZK放到/font/目录下,重启!",BLACK,WHITE);

LCD_Refresh();

}

else 

{

LCD_Display_Str2(LCD_WIDTH/2-strlen("                 ")/2*16,80,16,(u8 *)"                 ",WHITE,WHITE);

LCD_Display_Str2(20,100,16,(u8 *)"                   ",WHITE,WHITE);

LCD_Refresh();

goto GBK_16;

}

}

/*GBK24_OK*/

GBK_24:

W25Q64_ReadData(GBK_24_ADDR-10,(u8*)buff,9);

if(strstr(buff,"GBK24_OK"))

{

LCD_Display_Str2(20,100,16,(u8 *)"GBK24    OK",RED,WHITE);

LCD_Refresh();

}

else 

{

LCD_Display_Str2(20,100,16,(u8 *)"GBK24    NO",RED,WHITE);

[1] [2]
关键字:STM32 引用地址:基于STM32的小说阅读器

上一篇:STM32硬件错误的调试技巧
下一篇:STM32操作 I/O 口的步骤

推荐阅读最新更新时间:2024-11-13 12:21

STM32的 位寻址(地址映射)
STM32的寄存器的分布情况: STM比较特色的就是 位带操作: 如下解释: (寄存器的地址是固定好的,我们想要进行位操作的话 就需要用到地址映射,将位带区寄存器的 各位 映射到 位带别名区的地址。) 通过 位带别名区 的 4个字节(32个位)用来存放 位带区的 位地址。 如将 0x2200 0000 映射到 0x2000 000 的第一个位! 具体的映射C程序是这样的: 算法公式: (addr & 0xF0000000)+0x2000000+((addr &0xFFFFF) 5)+(bitnum 2) addr & 0xF0000000: 取0x40
[单片机]
【stm32f0】stm32 总中断的打开与关闭
问题: 对于基于ARM Cortex M0内核的STM32芯片各类应用开发时,有的时候需要进行总的中断的开、关处理。那就究竟有没有开、关总的中断的函数或者指令呢? 回答: 随着Corte Mn各种内核的MCU的芯片越来越多和相关编译工具的升级换代,编译工具在有关内核指令操作的文档安排以及函数书写等方面可能发生了细微的变化。所以即使用过STM32 F1系列产品的工程师,在使用晚推出的STM32 F0芯片开发时,发现那些跟内核操作有关的指令或函数不知道哪里去找了。以STM32各系列的标准固件库为例,与内核相关的指令及函数都可以在...librariescmsis...后面目录的相关文件里找到。于不同系列的标准库中对应的子目录以及相
[单片机]
【菜鸟入门】stm32的第一个程序--LED
经过今天一天的努力终于完成了我的stm32第一个程序;我也是今天才开始接触stm32。 由于苦于没有资料,木有例程,找到的例程都是带有库的,这样对stm32基础的管脚配置就不容易懂了,主要是没有一个具体的轮廓。 经过对库文件的研究,和看了好几节视频,又根据自己以前搞430和arm9的经验,就按着以前的思路进行研究,终于开发出自己的第一个LED程序; 1、创建工程 (1)Project -- New uvision Project (2)选择工程要保存的地方 (3)选择CPU (4)选择“是”(如果你使用从STM下载的库的话,就选“否”) (5)修改一下代码(如果不注视掉红色部分,会出现错误)
[单片机]
STM32-串口通信printf重定向
前言:平时我们进行c语言编程的时候会经常用到printf函数进行打印输出,来调试代码。可是这个printf函数C库已经帮我们实现好了,通常只需要直接调用即可,但是如果在一个新的开发平台,如果库没有帮我们实现好,比如STM32开发板,那么我们怎么实现printf打印输出呢? 首先我们来了解一下串口通信! 1、什么是串口通信? 串口通信(Serial Communication),是指外设和计算机间,通过数据信号线、地线等,按位进行传输数据的一种通讯方式。 串口是一种接口标准,它规定了接口的电气标准,没有规定接口插件电缆以及使用的协议。 2、串口通信协议 在串口通信中,常用的协议包括RS-232、RS-422和RS-485
[单片机]
STM32 RTC 读写不正确
调试RTC的时候发现读写不正确。 读写是调用的库函数,年月日和时分秒是分开操作的 读写时发现年月日写完之后读出来要等个好几秒中才能正确的读出来,否则读出来的就一直是设置之前的日期(这里读写都是先操作的日期,然后操作时间) 然后我把写的顺序调换了下,先写时间,再写日期。杯具了,读日期都出现错误了 然后我再把读和写的顺序都调换了下,写的时候是先写日期,再写时间;读的时候是先读时间,再读日期,这样才正确的读写RTC的日期和时间了。
[单片机]
STM32蜂鸣器pwm控制频率播放音乐
通过修改输出的pwm波的频率来达到不同的音频 单片机源程序如下: #include stm32f10x.h #include led.h #include delay.h #include sys.h #include timer.h #define proport 10000 //Tclk/(psc+1)=72000000/(7199+1) #define L1 ((proport/131)-1)//低调 do 的周期根据Tout= ((arr+1)*(psc+1))/Tclk推出arr值就是本句define定义的值,Tout为音调频率131Hz的倒数,Tclk=72MHz #define L2
[单片机]
Systick滴答定时器讲解
Systick定时器基础知识讲解: Systick定时器,是一个简单的定时器,对于CM3,CM4内核芯片,都有- Systick定时器。 Systick定时器常用来做延时,或者实时系统的心跳时钟。这样可以节省MCU资源,不用浪费一个定时器。比如UCOS中,分时复用,需要一个最小的时间戳,一般在STM32+UCOS系统中,都采用Systick做UCOS心跳时钟。 Systick定时器就是系统滴答定时器,一个24位的倒计数(从大到小)定时器,计到0时,将从RELOAD寄存器中自动重装载定时初值。只要不把它在SysTick控制及状态寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作。 SysTick定时器被捆绑在
[单片机]
Systick滴答定时器讲解
Moser现货供应STMicroelectronics Nucleo 开发板
贸泽电子(Mouser Electronics)现库存并供应 STMicroelectronics 新型 Nucleo 开发板。此类开发板专为欲使用 ST 的 STM32 产品系列(具有 ARM Cortex M0、Cortex M3 及 Cortex M4 微控制器)进行开发的人士而设计,并与多种扩展板兼容。 Mouser提供的 STMicroelectronics 新型 Nucleo 开发板 支持开发及评估 ST 的 32 位 STM32 微控制器。此新型开发板具有其他微控制器开发生态系统所不具有的多项高级特性。除了惯有的按钮、LED 和一个 USB 调试接口的组合外,Nucleo 板还独具特色地配有两组扩展接口。第一
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
更多往期活动

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved