> 文档中心 > 基于STM32数码相册

基于STM32数码相册


基于STM32数码相册

1.硬件平台

  • CPU:STM32F103ZE
  • 屏幕:3.5寸TFTLCD屏
  • 触控:电阻式触摸屏xpt2046
  • SD卡、外扩Sram

2.示例效果

STM32数码相册

 SD卡检测和图片搜索
在这里插入图片描述
 图片加载与显示
在这里插入图片描述

3.软件设计

3.1 遍历目录

  遍历目录,搜索所有的bmp格式图片,以链表方式保存图片名,方便接下来图片切换。

typedef struct FILE_info{char file_name[100];u16 number;//保存第几张图片struct FILE_info *next;struct FILE_info *pre;}FILE_INFO;FILE_INFO *bmp_head=NULL;/*创建链表*/FILE_INFO *List_CreateHead(FILE_INFO *head){if(head!=NULL)return head;head=malloc(sizeof(FILE_INFO));memset(head,0,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));memset(new_node,0,sizeof(FILE_INFO));new_node->pre=phead;phead->next=new_node;new_node->next=NULL;return new_node;}/*遍历目录*/u8 SDCard_PrintDir(const TCHAR* path){DIR dp;u8 res;u8 stat=0;bmp_head=List_CreateHead(bmp_head);//创建链表头res=f_opendir(&dp,path);  FILINFO file_info;  if(res)  {    printf("打开目录失败res=%d\r\n",res);free(bmp_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,".bmp"))//查找bmp图片{temp=List_AddNode(bmp_head);//添加节点if(temp==NULL){stat=2;//动态分配空间失败goto AA;}strcpy(temp->file_name,file_info.fname);//文件名picture_count++;temp->number=picture_count;//第几张图片//printf("文件名:%s\r\n",temp->file_name);}}AA:f_closedir(&dp);//关闭目录return stat;}

3.2 图片解析与显示

  图片通过SD卡保存,SD卡采用SDIO驱动。由于STM32F103ZE主频只有72MHZ,为了提高刷新速度,将主频超频至128MHZ。再通过外扩SRAM建立屏幕缓冲区,借助DMA数据搬运,从而提升屏幕刷新效率。

3.2.1 主时钟超频配置

  超频处理后需要注意串口波特率计算和定时器工作频率

void STM32_Clock_Init(u8 PLL){u8 temp;RCC->CFGR&=0XFFFFFFFC;//修改时钟频率为内部8M   RCC->CR&=~0x01000000;  //PLLOFF  RCC->CFGR&=~(0XF<<18);//清空原来的设置  RCC->CR|=1<<16;//开启HSE时钟while(!(RCC->CR>>17));//等待外部时钟就绪RCC->CFGR|=0x4<<8;//APB1时钟由系统时钟2分频PLL-=2;//实际倍频数和填入参数差2,9倍频写入的数值为7RCC->CFGR|=PLL<<18;//PLL时钟9倍频RCC->CFGR|=1<<16;  //HSE作为PLL时钟输入源FLASH->ACR|=0x32;  //FLASH 2个延时周期RCC->CR|=1<<24;//PLL时钟使能while(!(RCC->CR>>25));//等待PLL锁定RCC->CFGR|=0x2<<0;//PLL输出作为系统时钟while(1){temp=(RCC->CFGR>>2)&0x3;if(temp==0x2)break;}}

3.2.2 DMA配置

  直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。

/******DMA_CH1从存储器到存储器**************形参:u32 cpar -- 外设地址**u32 cmar -- 存储器地址****************************************/void DMA_CH1_Init(void){RCC->AHBENR|=1<<0;//dma1时钟使能DMA1_Channel1->CCR|=1<<14;//存储器到存储器模式DMA1_Channel1->CCR|=0x3<<12;//设置CH1优先级为最高DMA1_Channel1->CCR|=0x1<<10;//存储器数据宽度16位DMA1_Channel1->CCR|=0x1<<8;//外设数据宽度16位DMA1_Channel1->CCR|=1<<7;//存储器地址增量DMA1_Channel1->CCR&=~(1<<6);//外设地址不增量DMA1_Channel1->CCR&=~(1<<5);//不执行循环操作//DMA1_Channel1->CCR|=1<<5;//执行循环操作DMA1_Channel1->CCR&=~(1<<4);//从外设读}/**********开启DMA1_CH1数据传输*******************形参:u16 data_len -- DMA要传输数目***********************************************/void DMA_CH1_Start(u32 cpar,u32 cmar,u16 data_len){DMA1_Channel1->CPAR=cpar;//外设地址DMA1_Channel1->CMAR=cmar;//存储器地址DMA1_Channel1->CCR&=~(1<<0);//关闭通道传输DMA1_Channel1->CNDTR=data_len;//设置传输数量DMA1_Channel1->CCR|=1<<0;//开启通道传输}

3.2.3 图片解析与显示

  本示例主要以BMP图片为例,其他格式图形需要移植第三方库才可实现。
  由于我们常规BMP图片多为24位真彩色,即RGB888;而本次使用的LCD屏是16位真彩色RGB565,因而需要进行颜色格式转换。
  BMP图片处理请参考:https://blog.csdn.net/weixin_44453694/article/details/113428717
  LCD驱动与图片显示请参考:https://blog.csdn.net/weixin_44453694/article/details/123643870

/*颜色转换RGB888转RGB565*/u16 RGB888_Transform_RGB565(u32 rgb){u8 r,g,b;u16 rgb565;r=(rgb>>16)>>3;g=(rgb>>8)>>2;b=(rgb&0xff)>>3;rgb565=(r<<11)|(g<<5)|(b<<0);return rgb565;}u16 picture_count=0;//图片总数量/*BMP图片显示*/static u8 buff_rgb888[320*3];//用来保存读取到的原始数据static u16 buff_rgb565[320*480];//用来保存转换完成的RGB565数据u8 BMP_Display(const char *file,u16 number){FIL fp;FRESULT res;UINT br;u16 w,h;char buff[100];snprintf(buff,sizeof(buff),"0:/photo/%s",file);res=f_open(&fp,buff,FA_READ);if(res!=FR_OK)return 1;BMP_HEADER bmphead;BMP_INFO bmpinfo;memset(&bmphead,0,sizeof(BMP_HEADER));memset(&bmpinfo,0,sizeof(BMP_INFO));res=f_read(&fp,&bmphead,sizeof(BMP_HEADER),&br);if(res!=FR_OK)return 2;res=f_read(&fp,&bmpinfo,sizeof(BMP_INFO),&br);if(res!=FR_OK)return 2;//printf("图片类型:%c%c\r\n",bmphead.bfType>>8,bmphead.bfType);//printf("图片尺寸:%d*%d\r\n",bmpinfo.biWidth,bmpinfo.biHeight);//printf("颜色位数:%d\r\n",bmpinfo.biBitCount);w=bmpinfo.biWidth;h=bmpinfo.biHeight;u32 oneline_size=bmpinfo.biWidth*3;//一行的字节数u32 read_oneline_size=oneline_size;//要读取的一行字节数/*取出有效的rgb颜色值的一行字节数*/  while(oneline_size%4)oneline_size++;/*保存一行字节数为4的倍数*/u32 addr=bmphead.bfOffBits+(bmpinfo.biHeight-1)*oneline_size;/*将指针偏移到最后一行*/u32 i=0,j=0;;u32 rgb888;u32 cnt=0;for(i=0;i<bmpinfo.biHeight;i++){f_lseek(&fp,addr);/*一次读取一行颜色数据*/res=f_read(&fp,buff_rgb888,read_oneline_size,&br);for(j=0;j<br;){rgb888=(buff_rgb888[2+j]<<16)|(buff_rgb888[1+j]<<8)|buff_rgb888[0+j]<<0;//组合RGB颜色数据buff_rgb565[cnt++]=RGB888_Transform_RGB565(rgb888);j+=3;}addr-=oneline_size;}f_close(&fp);LCD_Clear(LIGHTBLUE);LCD_DrawBMP2(0,0,w,h,buff_rgb565);snprintf(buff,sizeof(buff),"第 %d 张,共 %d 张",number,picture_count);LCD_Display_Str(LCD_WIDTH-20-strlen(buff)*12,450,24,(u8 *)buff,BLACK);//字符串显示LCD_Display_Str(LCD_WIDTH/2-strlen(file)*12/2,10,24,(u8 *)file,BLACK);//字符串显示LCD_Refresh();return 0;}

3.3 主函数main.c

  在主函数main.c中主要完成各个外设初始化、SD卡挂载、图片获取、触摸屏坐标和按键值获取,最终实现通过触摸屏滑动或者按下切换图片。

FATFS fs;int main(){u8 key;STM32_Clock_Init(16);Beep_Init();Led_Init();Key_Init();Usartx_Init(USART1,115200,128);TIMx_Init(TIM2,128,20*1000);W25Q64_Init();//W25Q64初始化IIC_Init();//IIC初始化NT35310_Init();//LCD初始化XPT2046_Init();TOUCH_Calibration();//触摸屏校准printf("触摸屏校准完成\r\n");SRAM_Init();u8 res;AA:res=f_mount(&fs,"0",1);if(res){LCD_Clear(LIGHTBLUE);LCD_Display_Str(LCD_WIDTH/2-strlen("请检查SD卡是否插好!")*12/2,210,24,(u8 *)"请检查SD卡是否插好!",RED);LCD_Display_Str(LCD_WIDTH/2-strlen("注意文件系统格式须为FAT32!")*12/2,240,24,(u8 *)"注意文件系统格式须为FAT32!",RED);LCD_Refresh();//更新显示Delay_Ms(1000);goto AA;}BB:LCD_Clear(LIGHTBLUE);res=SDCard_PrintDir("photo");//遍历目录if(res){LCD_Refresh();//更新显示LCD_Display_Str(LCD_WIDTH/2-strlen("BMP图片不存在!")*12/2,210,24,(u8 *)"BMP图片不存在!",RED);LCD_Display_Str(LCD_WIDTH/2-strlen("请将图片存储在/photo下")*12/2,240,24,(u8 *)"请将图片存储在/photo下",RED);LCD_Refresh();//更新显示Delay_Ms(1000);goto BB;}LCD_Clear(LIGHTBLUE);LCD_Display_Str(LCD_WIDTH/2-strlen("正在加载图片。。")*12/2,210,24,(u8 *)"正在加载图片。。",RED);LCD_Refresh();//更新显示Delay_Ms(1000);FILE_INFO *bmp_temp=bmp_head;if(bmp_temp->next!=NULL){bmp_temp=bmp_temp->next;BMP_Display(bmp_temp->file_name,bmp_temp->number);}u16 x1,x2;int stat=0;while(1){res=XPT2046_ReadXY();if(res){x1=touch_info.x;while(T_PEN==0)//等待松开{XPT2046_ReadXY();x2=touch_info.x;}if(x1-x2>50)stat=1;else if(x2-x1>50)stat=2;}key=Key_Scan();if(key==1 || stat==1){stat=0;BEEP=1;Delay_Ms(50);BEEP=0;if(bmp_temp->next!=NULL){bmp_temp=bmp_temp->next;BMP_Display(bmp_temp->file_name,bmp_temp->number);}}else if(key==2 || stat==2){stat=0;BEEP=1;Delay_Ms(50);BEEP=0;if(bmp_temp->pre!=NULL && bmp_temp->pre->file_name[0]!=0){bmp_temp=bmp_temp->pre;BMP_Display(bmp_temp->file_name,bmp_temp->number);}//printf("%s\r\n",bmp_temp->file_name);}}}

4.完整示例

https://download.csdn.net/download/weixin_44453694/85029028

影搜视频