``APM32F411V Tiny开发板上有一个USB接口,可以配置为HOST MSC,再加上FAT_Fs就可以读取U盘中的文件。官方有这方面的例程位于APM32F4xx_DAL_SDK_V1.1.1\Examples\Board_APM32F411_Tiny\USB_OTG\Host_Examples\OTGH_MSC\Fat_Fs。
Fat_Fs的作者还开发有TJpgDec用于显示JPG图片。
本文在读取U盘内容基础上,利用TJpgDec显示U盘中的JPG图片在LCD上。
1、void DAL_DeviceConfig(void)中增加:
/* Configure USB host */
USB_HostInitalize();
2、USB_HostInitalize
void USB_HostInitalize(void)
{
/* USB host and class init */
if(gUsbHostType==USBH_CLASS_HID)
{
USBH_Init(&gUsbHostFS, USBH_SPEED_FS, &USBH_HID_CLASS, USB_HostUserHandler);
}else if(gUsbHostType==USBH_CLASS_MSC)
{
USBH_Init(&gUsbHostFS, USBH_SPEED_FS, &USBH_MSC_CLASS, USB_HostUserHandler);
}
}
HID设置和MSC设备一次只能设置一个生效,这里USBH_CLASS_MSC有效。
3、main中
while (1)
{
USB_HostProcess();
USB_HostUserApplication();
}
4、USB_HostUserApplication
void USB_HostUserApplication(void)
{
static USBH_APP_STA_T preAppStatus = USBH_APP_IDLE;
uint8_t status = gUsbHostAppStatus;
if (preAppStatus != gUsbHostAppStatus)
{
switch (status)
{
case USBH_APP_CONNECTION:
USBH_USR_LOG("USB 连接成功!\r\n");
Gui_DrawFont_GBK16_DMA(5,105,WHITE,RED,"USB connected ");
break;
case USBH_APP_DISCONNECTION:
USBH_USR_LOG("USB 失去连接\r\n");
Gui_DrawFont_GBK16_DMA(5,105,WHITE,RED,"USB disconnected ");
break;
case USBH_APP_READY:
USBH_USR_LOG("USB 准备就绪\r\n");
Gui_DrawFont_GBK16_DMA(5,105,WHITE,RED,"USB ready ");
//MSC
if ((gUsbHostType==USBH_CLASS_MSC)&&(gUsbHostFS.devInfo.connectedStatus == ENABLE))
{
if (f_mount(&fatfs, "0:", 0) == FR_OK)
{
printf("f_mount fatfs\r\n");
piclib_ai_load_picfile("0:1.jpg",0,0,240,320,0);
}
}
break;
default:
break;
}
preAppStatus = gUsbHostAppStatus;
}
}
当接入U盘,gUsbHostAppStatus为USBH_APP_READY时,执行:f_mount文件系统、piclib_ai_load_picfile打开1.jpg文件并显示在LCD上。
uint8_t piclib_ai_load_picfile(char *filename, uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t fast)
{
uint8_t res;
res = jpg_decode(filename, fast); /* 统一采用软件解码JPG/JPEG */
printf("piclib_ai_load_picfile:%d\r\n",res);
return res;
}
jpg_decode是jpg解码函数
5、解码函数jpg_decode
uint8_t jpg_decode(const char *filename, uint8_t fast)
{
uint8_t res = 0; /* 返回值 */
uint8_t scale; /* 图像输出比例 0,1/2,1/4,1/8 */
int (*outfun)(JDEC *, void *, JRECT *);
#if JPEG_USE_MALLOC == 1 /* 使用malloc */
//res = jpeg_mallocall();
#endif
if (res == 0)
{
/* 得到JPEG/JPG图片的开始信息 */
res = f_open(f_jpeg, (const TCHAR *)filename, FA_OPEN_EXISTING | FA_READ); /* 打开文件 */
if (res == FR_OK) /* 打开文件成功 */
{
printf("open file :%s success\r\n",filename);
res = jd_prepare(jpeg_dev, in_func, jpg_buffer, JPEG_WBUF_SIZE, f_jpeg); /* 执行解码的准备工作,调用TjpgDec模块的jd_prepare函数 */
outfun = out_func; /* 默认采用画点的方式显示 */
if (res == JDR_OK) /* 准备解码成功 */
{
printf("jd_prepare ok\r\n");
for (scale = 0; scale < 4; scale++) /* 确定输出图像的比例因子 */
{
if ((jpeg_dev->width >> scale) <= picinfo.S_Width && (jpeg_dev->height >> scale) <= picinfo.S_Height) /* 在目标区域内 */
{
if (((jpeg_dev->width >> scale) != picinfo.S_Width) && ((jpeg_dev->height >> scale) != picinfo.S_Height && scale))
{
scale = 0; /* 不能贴边,则不缩放 */
}
else
{
outfun = out_func; /* 在显示尺寸以内,可以采用填充的方式显示 */
}
break;
}
}
if (scale == 4)scale = 0; /* 错误 */
if (fast == 0) /* 不需要快速解码 */
{
outfun = jpeg_out_func_point; /* 默认采用画点的方式显示 */
}
picinfo.ImgHeight = jpeg_dev->height >> scale; /* 缩放后的图片尺寸 */
picinfo.ImgWidth = jpeg_dev->width >> scale; /* 缩放后的图片尺寸 */
piclib_ai_draw_init(); /* 初始化智能画图 */
/* 执行解码工作,调用TjpgDec模块的jd_decomp函数 */
res = jd_decomp(jpeg_dev, outfun, scale);
res = (res == JDR_OK) ? 0 : res;
if(res==JDR_OK) printf("jd_decomp ok\r\n");
else printf("jd_decomp fail\r\n");
}
else {
printf("jd_prepare fail\r\n");
}
}
else {
printf("open file :%s fail\r\n",filename);
}
f_close(f_jpeg); /* 解码工作执行成功,返回0 */
}
#if JPEG_USE_MALLOC == 1 /* 使用malloc */
jpeg_freeall(); /* 释放内存 */
#endif
return res;
}
这里主要是调用了TJpgDec提供的jd_prepare、jd_decomp函数完成jpg解码。
6、TJgpDec初始化
需要一个画点的函数,Gui_DrawPoint在LCD驱动中实现。
void lcd_draw_point(uint16_t x, uint16_t y, uint32_t color)
{
Gui_DrawPoint(x,y,color);
}
void piclib_init(void)
{
pic_phy.draw_point = lcd_draw_point; /* 画点函数实现 */
picinfo.lcdwidth = 220; /* 得到LCD的宽度像素 */
picinfo.lcdheight = 320; /* 得到LCD的高度像素 */
picinfo.ImgWidth = 240; /* 初始化宽度为0 */
picinfo.ImgHeight = 320; /* 初始化高度为0 */
picinfo.Div_Fac = 0; /* 初始化缩放系数为0 */
picinfo.S_Height = 320; /* 初始化设定的高度为0 */
picinfo.S_Width = 220; /* 初始化设定的宽度为0 */
picinfo.S_XOFF = 0; /* 初始化x轴的偏移量为0 */
picinfo.S_YOFF = 0; /* 初始化y轴的偏移量为0 */
picinfo.staticx = 0; /* 初始化当前显示到的x坐标为0 */
picinfo.staticy = 0; /* 初始化当前显示到的y坐标为0 */
}
TJgpDec需要的in函数:
unsigned int in_func(JDEC *jd, uint8_t *buf, size_t num)
{
uint16_t rb; /* 读取到的字节数 */
FIL *dev = (FIL *)jd->device; /* 待解码的文件的信息,使用FATFS中的FIL结构类型进行定义 */
if (buf) /* 读取数据有效,开始读取数据 */
{
f_read(dev, buf, num, (UINT *)&rb); /* 调用FATFS的f_read函数,用于把jpeg文件的数据读取出来 */
return rb; /* 返回读取到的字节数目 */
}
else
{
return (f_lseek(dev, f_tell(dev) + num) == FR_OK) ? num : 0; /* 重新定位数据点,相当于删除之前的n字节数据 */
}
}
TJgpDec需要的out函数:
int jpeg_out_func_point(JDEC* jd,void* rgbbuf,JRECT* rect)
{
uint16_t i,j;
uint16_t realx=rect->left,realy=0;
uint16_t *pencolor=rgbbuf;
uint16_t width=rect->right-rect->left+1; //图片的宽度
uint16_t height=rect->bottom-rect->top+1; //图片的高度
for(i=0;i<height;i++)//y坐标
{
realy=(picinfo.Div_Fac*(rect->top+i))>>13;//实际Y坐标
//在这里不改变picinfo.staticx和picinfo.staticy的值 ,如果在这里改变,则会造成每块的第一个点不显示!!!
if(!is_element_ok(realx,realy,0))//行值是否满足条件? 寻找满足条件的行
{
pencolor+=width;
continue;
}
for(j=0;j<width;j++)//x坐标
{
realx=(picinfo.Div_Fac*(rect->left+j))>>13;//实际X坐标
//在这里改变picinfo.staticx和picinfo.staticy的值
if(!is_element_ok(realx,realy,1))//列值是否满足条件? 寻找满足条件的列
{
pencolor++;
continue;
}
pic_phy.draw_point(realx+picinfo.S_XOFF,realy+picinfo.S_YOFF,*pencolor);//显示图片
pencolor++;
}
}
return 1; //返回1,使得解码工作继续执行
}
7、效果
板上USB是TYPE C 接口,接U盘需要一个TYPE C转TYPE A的转换器