文章目录
前言
本项目主要功能是商品界面简易购买功能,登陆界面,登陆成功后进入管理员界面,在管理员界面可以对商品数量进行补货,上架产品,下架产品等操作。以下是各个.c文件和.h文件源码
一、字库
wrod_stock.h文件
#ifndef __wrod_stock_h__
#define __word_stock_h__
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/mman.h>
#include<string.h>
#define color u32
#define getColor(a, b, c, d) (a|b<<8|c<<16|d<<24)
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef char s8;
typedef short s16;
typedef int s32;
typedef long long s64;
typedef struct stbtt_fontinfo
{
void * userdata;
unsigned char * data; // pointer to .ttf file
int fontstart; // offset of start of font
int numGlyphs; // number of glyphs, needed for range checking
int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf
int index_map; // a cmap mapping for our chosen character encoding
int indexToLocFormat; // format needed to map from glyph index to glyph
} stbtt_fontinfo;
typedef struct{
u32 height;
u32 width;
u32 byteperpixel;
u8 *map;
}bitmap;
typedef struct{
stbtt_fontinfo *info;
u8 *buffer;
float scale;
}font;
//lcd设备结构体
struct LcdDevice
{
int fd;
unsigned int *mp; //保存映射首地址
};
//1.初始化字库
font *fontLoad(char *fontPath);
//2.设置字体的大小
void fontSetSize(font *f, s32 pixels);
//3.设置字体输出框的大小
bitmap *createBitmap(u32 width, u32 height, u32 byteperpixel);
//可以指定输出框的颜色
bitmap *createBitmapWithInit(u32 width, u32 height, u32 byteperpixel, color c);
//4.把字体输出到输出框中
void fontPrint(font *f, bitmap *screen, s32 x, s32 y, char *text, color c, s32 maxWidth);
//5.把输出框的所有信息显示到LCD屏幕中
void show_font_to_lcd(unsigned int *p,int px,int py,bitmap *bm);
// 关闭字体库
void fontUnload(font *f);
// 关闭bitmap
void destroyBitmap(bitmap *bm);
//函数声明
extern int word_stock_show();
extern int word_show();
extern int buyloggo_show(int x);
extern int whiteboard_show();
extern int storenum_show(int number);
extern int uesrname_show();
extern int uesrpassword_show();
extern int userword_show();
extern int word_store_show(char *name,int x,int y);
#endif
这是word_stock.c文件,上面的.h文件开头两行
#ifndef __wrod_stock_h__
#define __word_stock_h__
可改成word_stock_h,小编在写的时候可能手误了,不改也没问题
下面的函数基本上都是有一个函数原型,小编将原型参数或者内容稍微修改了一下封装成另一个函数,所以代码显得冗余了。
#include "word_stock.h"
// 初始化Lcd
struct LcdDevice *init_lcd(const char *device)
{
// 申请空间
struct LcdDevice *lcd = malloc(sizeof(struct LcdDevice));
if (lcd == NULL)
{
return NULL;
}
// 1打开设备
lcd->fd = open(device, O_RDWR);
if (lcd->fd < 0)
{
perror("open lcd fail");
free(lcd);
return NULL;
}
// 映射
lcd->mp = mmap(NULL, 800 * 480 * 4, PROT_READ | PROT_WRITE, MAP_SHARED, lcd->fd, 0);
return lcd;
}
int word_stock_show(char *name,int x,int y)//商品信息函数
{
// 初始化Lcd
struct LcdDevice *lcd = init_lcd("/dev/fb0");
// 打开字体
font *f = fontLoad("/usr/share/fonts/DroidSansFallback.ttf");
// 字体大小的设置
fontSetSize(f, 25); // 字体大小20,可以人为修改
// 创建一个画板(点阵图)宽是200个像素,高是50个像素
bitmap *bm = createBitmapWithInit(90, 70, 4, getColor(0, 255, 255, 255)); // 也可使用createBitmapWithInit函数,改变画板颜色
// bitmap *bm = createBitmap(288, 100, 4);
char buf[256];
strcpy(buf,name);
// 将字体写到点阵图上 0,0表示汉字在画板的左上角显示
fontPrint(f, bm, 0, 0, buf, getColor(0, 0, 0, 0), 0);
// A B G R
// 把字体框输出到LCD屏幕上 参数0,0表示画板显示的坐标位置
show_font_to_lcd(lcd->mp, x, y, bm);
// 关闭字体,关闭画板
fontUnload(f);
destroyBitmap(bm);
}
int word_store_show(char *name,int x,int y)//上架商品
{
// 初始化Lcd
struct LcdDevice *lcd = init_lcd("/dev/fb0");
// 打开字体
font *f = fontLoad("/usr/share/fonts/DroidSansFallback.ttf");
// 字体大小的设置
fontSetSize(f, 25); // 字体大小20,可以人为修改
// 创建一个画板(点阵图)宽是200个像素,高是50个像素
bitmap *bm = createBitmapWithInit(100, 25, 4, getColor(0, 255, 255, 255)); // 也可使用createBitmapWithInit函数,改变画板颜色
// bitmap *bm = createBitmap(288, 100, 4);
char buf[256];
strcpy(buf,name);
// 将字体写到点阵图上 0,0表示汉字在画板的左上角显示
fontPrint(f, bm, 0, 0, buf, getColor(0, 0, 0, 0), 0);
// A B G R
// 把字体框输出到LCD屏幕上 参数0,0表示画板显示的坐标位置
show_font_to_lcd(lcd->mp, x, y, bm);
// 关闭字体,关闭画板
fontUnload(f);
destroyBitmap(bm);
}
int word_show()//字体信息
{
// 初始化Lcd
struct LcdDevice *lcd = init_lcd("/dev/fb0");
// 打开字体
font *f = fontLoad("/usr/share/fonts/DroidSansFallback.ttf");
// 字体大小的设置
fontSetSize(f, 30); // 字体大小20,可以人为修改
// 创建一个画板(点阵图)宽是200个像素,高是50个像素
bitmap *bm = createBitmapWithInit(600, 60, 4, getColor(0, 255, 255, 255)); // 也可使用createBitmapWithInit函数,改变画板颜色
// bitmap *bm = createBitmap(288, 100, 4);
char buf[]="智 能 售 货 机";
// 将字体写到点阵图上 0,0表示汉字在画板的左上角显示
fontPrint(f, bm, 330, 0, buf, getColor(0, 0, 0, 0), 0);
// A B G R
// 把字体框输出到LCD屏幕上 参数0,0表示画板显示的坐标位置
show_font_to_lcd(lcd->mp, 0, 0, bm);
// 关闭字体,关闭画板
fontUnload(f);
destroyBitmap(bm);
}
int userword_show()//管理员模式
{
// 初始化Lcd
struct LcdDevice *lcd = init_lcd("/dev/fb0");
// 打开字体
font *f = fontLoad("/usr/share/fonts/DroidSansFallback.ttf");
// 字体大小的设置
fontSetSize(f, 30); // 字体大小20,可以人为修改
// 创建一个画板(点阵图)宽是200个像素,高是50个像素
bitmap *bm = createBitmapWithInit(600, 60, 4, getColor(0, 255, 255, 255)); // 也可使用createBitmapWithInit函数,改变画板颜色
// bitmap *bm = createBitmap(288, 100, 4);
char buf[]="管 理 员 模 式";
// 将字体写到点阵图上 0,0表示汉字在画板的左上角显示
fontPrint(f, bm, 330, 0, buf, getColor(0, 0, 0, 0), 0);
// A B G R
// 把字体框输出到LCD屏幕上 参数0,0表示画板显示的坐标位置
show_font_to_lcd(lcd->mp, 0, 0, bm);
// 关闭字体,关闭画板
fontUnload(f);
destroyBitmap(bm);
}
int buyloggo_show(int x)
{
// 初始化Lcd
struct LcdDevice *lcd = init_lcd("/dev/fb0");
// 打开字体
font *f = fontLoad("/usr/share/fonts/DroidSansFallback.ttf");
// 字体大小的设置
fontSetSize(f, 30); // 字体大小20,可以人为修改
// 创建一个画板(点阵图)宽是200个像素,高是50个像素
bitmap *bm = createBitmapWithInit(30, 30, 4, getColor(0, 255, 255, 255)); // 也可使用createBitmapWithInit函数,改变画板颜色
// bitmap *bm = createBitmap(288, 100, 4);
char buf[]="^";
// 将字体写到点阵图上 0,0表示汉字在画板的左上角显示
fontPrint(f, bm, 0, 0, buf, getColor(0, 0, 0, 255), 0);
// A B G R
// 把字体框输出到LCD屏幕上 参数0,0表示画板显示的坐标位置
show_font_to_lcd(lcd->mp, x, 250, bm);
// 关闭字体,关闭画板
fontUnload(f);
destroyBitmap(bm);
}
int whiteboard_show()
{
// 初始化Lcd
struct LcdDevice *lcd = init_lcd("/dev/fb0");
// 打开字体
font *f = fontLoad("/usr/share/fonts/DroidSansFallback.ttf");
// 字体大小的设置
fontSetSize(f, 30); // 字体大小20,可以人为修改
// 创建一个画板(点阵图)宽是200个像素,高是50个像素
bitmap *bm = createBitmapWithInit(800, 30, 4, getColor(0, 255, 255, 255)); // 也可使用createBitmapWithInit函数,改变画板颜色
// bitmap *bm = createBitmap(288, 100, 4);
char buf[]=" ";
// 将字体写到点阵图上 0,0表示汉字在画板的左上角显示
fontPrint(f, bm, 0, 0, buf, getColor(0, 255, 255, 255), 0);
// A B G R
// 把字体框输出到LCD屏幕上 参数0,0表示画板显示的坐标位置
show_font_to_lcd(lcd->mp, 0, 250, bm);
// 关闭字体,关闭画板
fontUnload(f);
destroyBitmap(bm);
}
int storenum_show(int number)
{
// 初始化Lcd
struct LcdDevice *lcd = init_lcd("/dev/fb0");
// 打开字体
font *f = fontLoad("/usr/share/fonts/DroidSansFallback.ttf");
// 字体大小的设置
fontSetSize(f, 40); // 字体大小20,可以人为修改
// 创建一个画板(点阵图)宽是200个像素,高是50个像素
bitmap *bm = createBitmapWithInit(60, 60, 4, getColor(0, 255, 255, 255)); // 也可使用createBitmapWithInit函数,改变画板颜色
// bitmap *bm = createBitmap(288, 100, 4);
char buf[10];
snprintf(buf, sizeof(buf), "%d", number);
// 将字体写到点阵图上 0,0表示汉字在画板的左上角显示
fontPrint(f, bm, 12, 10, buf, getColor(0, 0, 0, 0), 0);
// A B G R
// 把字体框输出到LCD屏幕上 参数0,0表示画板显示的坐标位置
show_font_to_lcd(lcd->mp, 260, 390, bm);
// 关闭字体,关闭画板
fontUnload(f);
destroyBitmap(bm);
}
int uesrname_show()//用户名显示
{
// 初始化Lcd
struct LcdDevice *lcd = init_lcd("/dev/fb0");
// 打开字体
font *f = fontLoad("/usr/share/fonts/DroidSansFallback.ttf");
// 字体大小的设置
fontSetSize(f, 30); // 字体大小20,可以人为修改
// 创建一个画板(点阵图)宽是200个像素,高是50个像素
bitmap *bm = createBitmapWithInit(80, 30, 4, getColor(0, 255, 255, 255)); // 也可使用createBitmapWithInit函数,改变画板颜色
// bitmap *bm = createBitmap(288, 100, 4);
char buf[]="power";
// 将字体写到点阵图上 0,0表示汉字在画板的左上角显示
fontPrint(f, bm, 0, 0, buf, getColor(0, 0, 0, 0), 0);
// A B G R
// 把字体框输出到LCD屏幕上 参数0,0表示画板显示的坐标位置
show_font_to_lcd(lcd->mp, 360, 190, bm);
// 关闭字体,关闭画板
fontUnload(f);
destroyBitmap(bm);
}
int uesrpassword_show()//密码显示
{
// 初始化Lcd
struct LcdDevice *lcd = init_lcd("/dev/fb0");
// 打开字体
font *f = fontLoad("/usr/share/fonts/DroidSansFallback.ttf");
// 字体大小的设置
fontSetSize(f, 30); // 字体大小20,可以人为修改
// 创建一个画板(点阵图)宽是200个像素,高是50个像素
bitmap *bm = createBitmapWithInit(80, 30, 4, getColor(0, 255, 255, 255)); // 也可使用createBitmapWithInit函数,改变画板颜色
// bitmap *bm = createBitmap(288, 100, 4);
char buf[]="******";
// 将字体写到点阵图上 0,0表示汉字在画板的左上角显示
fontPrint(f, bm, 0, 0, buf, getColor(0, 0, 0, 0), 0);
// A B G R
// 把字体框输出到LCD屏幕上 参数0,0表示画板显示的坐标位置
show_font_to_lcd(lcd->mp, 360, 290, bm);
// 关闭字体,关闭画板
fontUnload(f);
destroyBitmap(bm);
}
二、显示屏
bmp.h文件
这里主要是一些函数头文件和声明
#ifndef _BMP_H
#define _BMP_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <strings.h>
#include <sys/mman.h>
#include <stdint.h>
// 函数声明
extern int show_anybmp(int w, int h, int x, int y, char *bmpname);
extern int show_white();
extern int show_left_right();
extern int begin_show();
extern int word_show();
extern int list_file();
extern int storenum_show(int number);
extern int show_ts();
extern int read_lcd_put();
extern int show_buysuccess();
extern int read_lcd_putsuccess();
extern int read_lcd_putdog();
extern int read_lcd_nothing();
extern int read_lcd_errorname();
extern int read_lcd_errorpassword();
extern int read_lcd_user();
extern int user_show();
extern int userword_show();
extern int updata_show();
#endif
bmp.c
由于小编是第一次写这么多代码,还不熟练,代码重复性很高,读者大大见谅
#include "bmp.h"
#define WIDTH 800
#define HEIGHT 480
int show_white()//白色背景
{
int lcdfd;
//定义指针来存放液晶屏的首地址
int *lcdmem;
int i;
//定义变量表示红颜色
// A R G B
int white=0x00ffffff;
//第一步;打开液晶屏的驱动
lcdfd=open("/dev/fb0",O_RDWR);
if(lcdfd==-1)
{
printf("打开液晶屏失败了!\n");
return -1;
}
//映射得到液晶屏在内存中的起始位置--》通过指针操作直接访问地址--》效率高
lcdmem=mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcdfd,0);
if(lcdmem==NULL)
{
perror("映射液晶屏失败了!\n");
return -1;
}
//直接把红颜色填充到地址
for(i=0; i<800*480; i++)
{
//C语言指针加法
*(lcdmem+i)=white;
}
//关闭
close(lcdfd);
//解除内存映射
munmap(lcdmem,800*480*4);
return 0;
}
int read_lcd_put()
{
int lcdfd;
// 定义指针来存放液晶屏的首地址
uint32_t *lcdmem;
int i;
uint32_t buf[WIDTH * HEIGHT];
// 第一步;打开液晶屏的驱动
lcdfd = open("/dev/fb0", O_RDWR);
if (lcdfd == -1) {
printf("打开液晶屏失败了!\n");
return -1;
}
// 映射得到液晶屏在内存中的起始位置
lcdmem = (uint32_t *)mmap(NULL, WIDTH * HEIGHT * sizeof(uint32_t), PROT_READ | PROT_WRITE, MAP_SHARED, lcdfd, 0);
if (lcdmem == MAP_FAILED) {
perror("映射液晶屏失败了!\n");
close(lcdfd);
return -1;
}
// 将当前图像数据读取到 buf 中
memcpy(buf, lcdmem, WIDTH * HEIGHT * sizeof(uint32_t));
// 弹框提示
show_ts(); // 这里会弹框一个提示
sleep(1); // 等待一秒
// 显示原来的图片
memcpy(lcdmem, buf, WIDTH * HEIGHT * sizeof(uint32_t));
// 关闭
munmap(lcdmem, WIDTH * HEIGHT * sizeof(uint32_t));
close(lcdfd);
return 0;
}
//封装在任意位置显示任意大小的bmp图片
int show_anybmp(int w,int h,int x,int y,char *bmpname)
{
int bmpfd;
int lcdfd;
int i,j;
//定义int类型的指针指向lcd在显存中的首地址
int *lcdmem;
//定义数组存放像素点的RGB
char bmpbuf[w*h*3];
//定义数组存放转换得到的ARGB
int lcdbuf[w*h]; // int占4字节
int tempbuf[w*h];
//打开你要显示的bmp图片 w*h
bmpfd=open(bmpname,O_RDWR);
if(bmpfd==-1)
{
perror("打开图片");
return -1;
}
//打开lcd的驱动
lcdfd=open("/dev/fb0",O_RDWR);
if(lcdfd==-1)
{
perror("打开lcd");
return -1;
}
//映射得到lcd在显存中对应的首地址
lcdmem=mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcdfd,0);
if(lcdmem==NULL)
{
perror("映射lcd");
return -1;
}
//跳过前面没有用的54字节
lseek(bmpfd,54,SEEK_SET);
//判断bmp图片的宽所占的字节数能否被4整除
if((w*3)%4!=0)
{
for(i=0; i<h; i++)
{
read(bmpfd,&bmpbuf[i*w*3],w*3);
lseek(bmpfd,4-(w*3)%4,SEEK_CUR); //跳过填充的垃圾数据
}
}
else
//从55字节读取bmp的像素点颜色值
read(bmpfd,bmpbuf,w*h*3); //bmpbuf[0] B bmpbuf[1] G bmpbuf[2] R 一个像素点的RGB
//bmpbuf[3] bmpbuf[4] bmpbuf[5]
//3字节的RGB-->4字节的ARGB 位运算+左移操作
for(i=0; i<w*h; i++)
lcdbuf[i]=bmpbuf[3*i]|bmpbuf[3*i+1]<<8|bmpbuf[3*i+2]<<16|0x00<<24;
//00[2][1][0]
for(i=0; i<w; i++)
for(j=0; j<h; j++)
//*(lcdmem+(y+j)*800+x+i)=lcdbuf[j*w+i]; 图片颠倒
*(lcdmem+(y+j)*800+x+i)=lcdbuf[(h-1-j)*w+i];
//关闭
close(bmpfd);
close(lcdfd);
//解除映射
munmap(lcdmem,800*480*4);
return 0;
}
int show_left_right()//页面箭头,设置,购买图标
{
show_anybmp(180,120,80,360,"123/left.bmp");
show_anybmp(180,120,320,360,"123/right.bmp");
show_anybmp(200,120,600,360,"123/buy.bmp");
show_anybmp(60,60,740,0,"123/set.bmp");
}
int begin_show()//初始屏幕显示
{
show_white(); // 白色背景
word_show(); // 智能售货机显示
show_left_right(); // 左右购买管理员标志
list_file(); // 链表文件信息;
storenum_show(1); // 商品数量
}
int show_ts()//提示
{
show_anybmp(400,140,200,150,"123/ts.bmp");
}
int show_dog()//进货表情包
{
show_anybmp(400,140,200,150,"123/dog.bmp");
}
int show_buysuccess()//购买成功提示
{
show_anybmp(400,140,200,150,"123/buysuccess.bmp");
}
int show_nothing()//没了
{
show_anybmp(300,200,250,150,"123/nothing.bmp");
}
int read_lcd_putsuccess()
{
int lcdfd;
// 定义指针来存放液晶屏的首地址
uint32_t *lcdmem;
int i;
uint32_t buf[WIDTH * HEIGHT];
// 第一步;打开液晶屏的驱动
lcdfd = open("/dev/fb0", O_RDWR);
if (lcdfd == -1) {
printf("打开液晶屏失败了!\n");
return -1;
}
// 映射得到液晶屏在内存中的起始位置
lcdmem = (uint32_t *)mmap(NULL, WIDTH * HEIGHT * sizeof(uint32_t), PROT_READ | PROT_WRITE, MAP_SHARED, lcdfd, 0);
if (lcdmem == MAP_FAILED) {
perror("映射液晶屏失败了!\n");
close(lcdfd);
return -1;
}
// 将当前图像数据读取到 buf 中
memcpy(buf, lcdmem, WIDTH * HEIGHT * sizeof(uint32_t));
// 弹框提示
show_buysuccess();
sleep(1); // 等待一秒
// 显示原来的图片
memcpy(lcdmem, buf, WIDTH * HEIGHT * sizeof(uint32_t));
// 关闭
munmap(lcdmem, WIDTH * HEIGHT * sizeof(uint32_t));
close(lcdfd);
return 0;
}
int read_lcd_putdog()
{
int lcdfd;
// 定义指针来存放液晶屏的首地址
uint32_t *lcdmem;
int i;
uint32_t buf[WIDTH * HEIGHT];
// 第一步;打开液晶屏的驱动
lcdfd = open("/dev/fb0", O_RDWR);
if (lcdfd == -1) {
printf("打开液晶屏失败了!\n");
return -1;
}
// 映射得到液晶屏在内存中的起始位置
lcdmem = (uint32_t *)mmap(NULL, WIDTH * HEIGHT * sizeof(uint32_t), PROT_READ | PROT_WRITE, MAP_SHARED, lcdfd, 0);
if (lcdmem == MAP_FAILED) {
perror("映射液晶屏失败了!\n");
close(lcdfd);
return -1;
}
// 将当前图像数据读取到 buf 中
memcpy(buf, lcdmem, WIDTH * HEIGHT * sizeof(uint32_t));
// 弹框提示
show_dog();
sleep(1); // 等待一秒
// 显示原来的图片
memcpy(lcdmem, buf, WIDTH * HEIGHT * sizeof(uint32_t));
// 关闭
munmap(lcdmem, WIDTH * HEIGHT * sizeof(uint32_t));
close(lcdfd);
return 0;
}
int read_lcd_nothing()
{
int lcdfd;
// 定义指针来存放液晶屏的首地址
uint32_t *lcdmem;
int i;
uint32_t buf[WIDTH * HEIGHT];
// 第一步;打开液晶屏的驱动
lcdfd = open("/dev/fb0", O_RDWR);
if (lcdfd == -1) {
printf("打开液晶屏失败了!\n");
return -1;
}
// 映射得到液晶屏在内存中的起始位置
lcdmem = (uint32_t *)mmap(NULL, WIDTH * HEIGHT * sizeof(uint32_t), PROT_READ | PROT_WRITE, MAP_SHARED, lcdfd, 0);
if (lcdmem == MAP_FAILED) {
perror("映射液晶屏失败了!\n");
close(lcdfd);
return -1;
}
// 将当前图像数据读取到 buf 中
memcpy(buf, lcdmem, WIDTH * HEIGHT * sizeof(uint32_t));
// 弹框提示
show_nothing();
sleep(1);
// 显示原来的图片
memcpy(lcdmem, buf, WIDTH * HEIGHT * sizeof(uint32_t));
// 关闭
munmap(lcdmem, WIDTH * HEIGHT * sizeof(uint32_t));
close(lcdfd);
return 0;
}
int read_lcd_errorname() //提示用户名错误
{
int lcdfd;
uint32_t *lcdmem;
int i;
uint32_t buf[WIDTH * HEIGHT];
lcdfd = open("/dev/fb0", O_RDWR);
if (lcdfd == -1) {
printf("打开液晶屏失败了!\n");
return -1;
}
lcdmem = (uint32_t *)mmap(NULL, WIDTH * HEIGHT * sizeof(uint32_t), PROT_READ | PROT_WRITE, MAP_SHARED, lcdfd, 0);
if (lcdmem == MAP_FAILED) {
perror("映射液晶屏失败了!\n");
close(lcdfd);
return -1;
}
memcpy(buf, lcdmem, WIDTH * HEIGHT * sizeof(uint32_t));
show_anybmp(400,140,200,150,"123/errorname.bmp");
sleep(1);
memcpy(lcdmem, buf, WIDTH * HEIGHT * sizeof(uint32_t));
munmap(lcdmem, WIDTH * HEIGHT * sizeof(uint32_t));
close(lcdfd);
return 0;
}
int read_lcd_errorpassword() //提示密码错误
{
int lcdfd;
uint32_t *lcdmem;
int i;
uint32_t buf[WIDTH * HEIGHT];
lcdfd = open("/dev/fb0", O_RDWR);
if (lcdfd == -1) {
printf("打开液晶屏失败了!\n");
return -1;
}
lcdmem = (uint32_t *)mmap(NULL, WIDTH * HEIGHT * sizeof(uint32_t), PROT_READ | PROT_WRITE, MAP_SHARED, lcdfd, 0);
if (lcdmem == MAP_FAILED) {
perror("映射液晶屏失败了!\n");
close(lcdfd);
return -1;
}
memcpy(buf, lcdmem, WIDTH * HEIGHT * sizeof(uint32_t));
show_anybmp(400,140,200,150,"123/errorpassword.bmp");
sleep(1);
memcpy(lcdmem, buf, WIDTH * HEIGHT * sizeof(uint32_t));
munmap(lcdmem, WIDTH * HEIGHT * sizeof(uint32_t));
close(lcdfd);
return 0;
}
int read_lcd_user() //提示用户名或密码没输入
{
int lcdfd;
uint32_t *lcdmem;
int i;
uint32_t buf[WIDTH * HEIGHT];
lcdfd = open("/dev/fb0", O_RDWR);
if (lcdfd == -1) {
printf("打开液晶屏失败了!\n");
return -1;
}
lcdmem = (uint32_t *)mmap(NULL, WIDTH * HEIGHT * sizeof(uint32_t), PROT_READ | PROT_WRITE, MAP_SHARED, lcdfd, 0);
if (lcdmem == MAP_FAILED) {
perror("映射液晶屏失败了!\n");
close(lcdfd);
return -1;
}
memcpy(buf, lcdmem, WIDTH * HEIGHT * sizeof(uint32_t));
show_anybmp(400,140,200,150,"123/user.bmp");
sleep(1);
memcpy(lcdmem, buf, WIDTH * HEIGHT * sizeof(uint32_t));
munmap(lcdmem, WIDTH * HEIGHT * sizeof(uint32_t));
close(lcdfd);
return 0;
}
int user_show()//管理员页面
{
show_white(); // 白色背景
userword_show(); // 管理员模式显示
show_anybmp(180,120,80,360,"123/left.bmp");
show_anybmp(180,120,320,360,"123/right.bmp");
show_anybmp(60,60,740,0,"123/ret.bmp");
show_anybmp(120,120,520,360,"123/up.bmp");
show_anybmp(120,120,660,360,"123/down.bmp");
}
int updata_show()//上架商品页面
{
show_white(); // 白色背景
show_anybmp(800,480,0,0,"123/updata.bmp");
show_anybmp(100,180,600,60,"123/0.bmp");
}
三、触摸屏
ts.h
差不多每个.h文件都是这样的格式
#ifndef _TS_H
#define _TS_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <strings.h>
#include <sys/mman.h>
#include <linux/input.h>
//函数声明
extern int ts_open();
extern int ts_read_pos(int *x,int *y);
extern int ts_close();
#endif
ts.c
#include "ts.h"
int tsfd;
// 触摸屏的打开
int ts_open()
{
// 第一步;打开触摸屏的驱动
tsfd = open("/dev/input/event0", O_RDWR);//触摸屏在开发板的位置,大家的可能不一样
if (tsfd == -1)
{
printf("打开触摸屏失败了!\n");
return -1;
}
return 0;
}
// 触摸屏读取坐标保存
int ts_read_pos(int *x, int *y)
{
// 定义一个结构体存放触摸屏的坐标
struct input_event myevent;
// 标志位(经典的编程套路),让下面的死循环退出
int count = 0;
while (1)
{
// 读取触摸屏点击的坐标位置
read(tsfd, &myevent, sizeof(myevent));
// 判断事件类型
if (myevent.type == EV_ABS) // 说明是触摸屏的驱动
{
// 判断是x坐标,还是y坐标
if (myevent.code == ABS_X) // 说明读取的x坐标
{
printf("x坐标是: %d\n", (myevent.value));
*x = (myevent.value);
count++;
}
if (myevent.code == ABS_Y) // 说明读取的y坐标
{
printf("y坐标是: %d\n", (myevent.value));
*y = (myevent.value);
count++;
}
if (count == 2)
break; // 退出死循环
}
}
return 0;
}
// 触摸屏关闭
int ts_close()
{
// 关闭
close(tsfd);
return 0;
}
四、双向循环链表
doulelist.h
#ifndef _DOUBLELIST_H
#define _DOUBLELIST_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <strings.h>
#include <sys/mman.h>
#include <dirent.h>
//定义结构体来表示双向链表
struct doublelist
{
char bmppath[50]; //数据域,存放图片的路径名
char productname[50];//产品名字
char num[50];//产品数量
char price[50];//产品价格
struct doublelist *next; //指针域
struct doublelist *prev;
};
//函数声明
extern struct doublelist *list_init();
extern int list_insert_tail(struct doublelist *head, char *newproname, char *newnum, char *newprice, char *newbmppath);
extern int list_file();
extern int read_lcd_put();
extern int read_lcd_putsuccess();
extern int show_buysuccess();
extern int buy_thing(struct doublelist *p,struct doublelist *q,int num,int place);
extern int word_stock_show(char *name,int x,int y);
extern int show_anybmp(int w,int h,int x,int y,char *bmpname);
extern int show_dog();
extern int read_lcd_nothing() ;
extern int list_txt(struct doublelist *mylist);
extern int add_thing(struct doublelist *p, struct doublelist *q, int storenum, int place);
extern int sub_thing(struct doublelist *p, struct doublelist *q, int storenum, int place);
extern int list_del(struct doublelist *head,struct doublelist *d);
#endif
doublelist.c
#include "doublelist.h"
/*
这个.c文件专门存放跟双向链表有关的函数
*/
// 双向链表初始化
struct doublelist *list_init()
{
struct doublelist *head = malloc(sizeof(struct doublelist));
head->next = head;
head->prev = head;
return head;
}
// 双向链表尾插
int list_insert_tail(struct doublelist *head, char *newproname, char *newnum, char *newprice, char *newbmppath)
{
// 准备新节点
struct doublelist *newnode = malloc(sizeof(struct doublelist));
strcpy(newnode->productname, newproname);
strcpy(newnode->num, newnum);
strcpy(newnode->price, newprice);
strcpy(newnode->bmppath, newbmppath);
newnode->next = NULL;
newnode->prev = NULL;
// 找到尾部
struct doublelist *p = head;
while (p->next != head)
p = p->next;
p->next = newnode;
newnode->next = head;
newnode->prev = p;
head->prev = newnode;
return 0;
}
// 双向循环链表删除节点
int list_del(struct doublelist *head, struct doublelist *d) // d表示要删除的
{
if (d->next == head)
{
d->prev->next = head;
head->prev = d->prev;
d->next = NULL;
d->prev = NULL;
free(d);
return 0;
}
// 删除d这个节点
d->prev->next = d->next;
d->next->prev = d->prev;
d->next=NULL;
d->prev=NULL;
free(d);
return 0;
}
struct doublelist *mylist;
int list_file() // 按行读取文件存在链表里面
{
mylist = list_init();
char bmppath[50]; // 数据域,存放图片的路径名
char productname[50]; // 产品名字
char num[50]; // 产品数量
char price[50]; // 产品价格
FILE *file = fopen("1.txt", "r"); // 打开文件
if (file == NULL)
{
perror("无法打开文件");
return 1;
}
char buffer[100]; // 用于存储读取的每一行
while (fgets(buffer, sizeof(buffer), file) != NULL)
{
sscanf(buffer, "%[^@]@%[^@]@%[^@]@%s", productname, num, price, bmppath);
list_insert_tail(mylist, productname, num, price, bmppath);
}
fclose(file); // 关闭文件
return 0;
}
int buy_thing(struct doublelist *p, struct doublelist *q, int storenum, int place)
{
int data;
char buf[100] = {0};
sscanf(q->num, "剩余:%d", &data);
if (p == q)
{
read_lcd_put(); // 提示先选择商品
}
else if (p != q && data == 0)
{
read_lcd_nothing();
}
else
{
read_lcd_putsuccess();
sprintf(q->num, "剩余:%d", data - storenum);
sprintf(buf, "%s\n%s\n%s", q->productname, q->num, q->price);
word_stock_show(buf, 60 + place * 150, 280);
}
}
int sub_thing(struct doublelist *p, struct doublelist *q, int storenum, int place)
{
int data;
char buf[100] = {0};
sscanf(q->num, "剩余:%d", &data);
if (p == q)
{
read_lcd_put(); // 提示先选择商品
}
else if (p != q && data == 0)
{
read_lcd_nothing();
}
else
{
sprintf(q->num, "剩余:%d", data - storenum);
sprintf(buf, "%s\n%s\n%s", q->productname, q->num, q->price);
word_stock_show(buf, 60 + place * 150, 280);
}
}
int add_thing(struct doublelist *p, struct doublelist *q, int storenum, int place)
{
int data;
char buf[100] = {0};
sscanf(q->num, "剩余:%d", &data);
if (p == q)
{
read_lcd_put(); // 提示先选择商品
}
else
{
sprintf(q->num, "剩余:%d", data + storenum);
sprintf(buf, "%s\n%s\n%s", q->productname, q->num, q->price);
word_stock_show(buf, 60 + place * 150, 280);
}
}
int list_txt(struct doublelist *mylist) // 链表信息写入记事本
{
struct doublelist *p1 = mylist;
char result[100] = {0};
int fd;
fd = open("1.txt", O_RDWR | O_TRUNC | O_CREAT);
if (fd == -1)
{
perror("打开文件失败!\n");
return -1;
}
while (p1->next != mylist)
{
p1 = p1->next;
sprintf(result, "%s@%s@%s@%s\n", p1->productname, p1->num, p1->price, p1->bmppath);
write(fd, result, strlen(result));
}
close(fd);
return 0;
}
五、主函数
main.c
#include "word_stock.h"
#include "bmp.h"
#include "doublelist.h"
#include "ts.h"
#define MAINBUY 1
#define MANAGE 2
#define LOGIN 3
#define upstore 4
int state = MAINBUY; // 用来记录当前系统所处的状态(目前定义三种状态)
extern struct doublelist *mylist;
int main()
{
ts_open(); // 打开触摸屏
int ts_x, ts_y; // 定义变量存放坐标
char manage[20] = {0}; // 用户名
char password[20]; // 密码
int flag1 = 0, flag2 = 0; // 标记用户名密码是否已填写
while (1)
{
switch (state)
{
case MAINBUY:
begin_show();
char buf[200] = {0};
int n = 0, i = 0, place; // place用于定位商品位置
int storenum = 1; // 用于storenum_show(1)
struct doublelist *p = mylist->next;
while (p != mylist && n < 5) // 首先刷5个出来
{
sprintf(buf, "%s\n%s\n%s", p->productname, p->num, p->price);
word_stock_show(buf, 60 + i * 150, 280); // 字的坐标
show_anybmp(100, 180, 50 + i * 150, 60, p->bmppath); // 图片的坐标
i++;
n++;
p = p->next;
}
struct doublelist *q = p;
while (1) // 读取触摸屏的坐标,判断你点击是哪个按钮
{
ts_read_pos(&ts_x, &ts_y); // 判断坐标
if (ts_x > 740 && ts_x < 800 && ts_y > 0 && ts_y < 60) // 管理员界面
{
show_anybmp(800, 480, 0, 0, "123/adm.bmp");
state = MANAGE;
break;
}
else if (ts_x > 200 && ts_x < 260 && ts_y > 360 && ts_y < 480) // 点击减号
{
if (q == p)
{ // 放图片提示没选商品,1S后恢复原界面
read_lcd_put();
}
if (storenum > 1)
{
storenum--;
storenum_show(storenum);
}
}
else if (ts_x > 320 && ts_x < 380 && ts_y > 360 && ts_y < 480) // 点击加号
{
if (q != p)
{
int numdata;
sscanf(q->num, "剩余:%d", &numdata);
if (numdata == 0)
read_lcd_nothing();
else if (numdata > storenum) // 商品数量大于购买量
{
storenum++;
storenum_show(storenum);
}
else
read_lcd_putdog();
}
else
{
// 放图片提示没选商品,1S后恢复原界面
read_lcd_put();
}
}
else if (ts_x > 380 && ts_x < 500 && ts_y > 360 && ts_y < 480) // 右键
{
whiteboard_show(); // 刷掉商品选择指示
// 没有商品选择
storenum_show(storenum = 1);
i = 0;
n = 0;
while (n < 5)
{
if (p == mylist)
p = p->next;
sprintf(buf, "%s\n%s\n%s", p->productname, p->num, p->price);
word_stock_show(buf, 60 + i * 150, 280); // 字的坐标
show_anybmp(100, 180, 50 + i * 150, 60, p->bmppath); // 图片的坐标
i++;
n++;
p = p->next;
}
q = p;
}
else if (ts_x > 80 && ts_x < 200 && ts_y > 360 && ts_y < 480) // 左键
{
whiteboard_show(); // 刷掉商品选择指示
storenum_show(storenum = 1);
for (i = 0; i < 10; i++)
{
if (p == mylist)
p = p->prev;
p = p->prev;
}
i = 0;
n = 0;
while (n < 5)
{
if (p == mylist)
p = p->next;
sprintf(buf, "%s\n%s\n%s", p->productname, p->num, p->price);
word_stock_show(buf, 60 + i * 150, 280); // 字的坐标
show_anybmp(100, 180, 50 + i * 150, 60, p->bmppath); // 图片的坐标
i++;
n++;
p = p->next;
}
q = p;
}
else if (ts_x > 600 && ts_x < 800 && ts_y > 360 && ts_y < 480) // 购买
{
buy_thing(p, q, storenum, place);
list_txt(mylist);
}
else if (ts_x > 50 && ts_x < 150 && ts_y > 80 && ts_y < 220) // 商品1位置
{
place = 0;
whiteboard_show(); // 刷掉商品选择指示
q = p;
buyloggo_show(100); // 指示商品
for (i = 0; i < 5; i++)
{
q = q->prev;
if (q == mylist)
q = q->prev;
}
storenum_show(storenum = 1); // 购买商品数量置一
}
else if (ts_x > 200 && ts_x < 300 && ts_y > 80 && ts_y < 220) // 商品2位置
{
place = 1;
whiteboard_show(); // 刷掉商品选择指示
q = p;
buyloggo_show(250); // 指示商品
for (i = 0; i < 4; i++)
{
q = q->prev;
if (q == mylist)
q = q->prev;
}
storenum_show(storenum = 1); // 购买商品数量置一
}
else if (ts_x > 350 && ts_x < 450 && ts_y > 80 && ts_y < 220) // 商品3位置
{
place = 2;
whiteboard_show(); // 刷掉商品选择指示
q = p;
buyloggo_show(400); // 指示商品
for (i = 0; i < 3; i++)
{
q = q->prev;
if (q == mylist)
q = q->prev;
}
storenum_show(storenum = 1); // 购买商品数量置一
}
else if (ts_x > 500 && ts_x < 600 && ts_y > 80 && ts_y < 220) // 商品4位置
{
place = 3;
whiteboard_show(); // 刷掉商品选择指示
q = p;
buyloggo_show(550); // 指示商品
for (i = 0; i < 2; i++)
{
q = q->prev;
if (q == mylist)
q = q->prev;
}
storenum_show(storenum = 1); // 购买商品数量置一
}
else if (ts_x > 650 && ts_x < 750 && ts_y > 80 && ts_y < 220) // 商品5位置
{
place = 4;
whiteboard_show(); // 刷掉商品选择指示
q = p;
buyloggo_show(700); // 指示商品
for (i = 0; i < 1; i++)
{
q = q->prev;
if (q == mylist)
q = q->prev;
}
storenum_show(storenum = 1); // 购买商品数量置一
}
}
break;
case MANAGE:
flag1 = 0;
flag2 = 0;
while (1)
{
ts_read_pos(&ts_x, &ts_y);
if (ts_x > 700 && ts_x < 770 && ts_y > 20 && ts_y < 90) // 关闭管理员界面
{
state = MAINBUY;
break;
}
else if (ts_x > 350 && ts_x < 580 && ts_y > 180 && ts_y < 225) // 用户名输入框
{
if (flag1 == 0)
{
printf("请输入用户名:\n");
scanf("%s", manage);
if (strcmp("power", manage) != 0) // 用户名不对
read_lcd_errorname();
else
{
uesrname_show();
flag1 = 1;
}
}
}
else if (ts_x > 350 && ts_x < 580 && ts_y > 280 && ts_y < 325) // 密码输入框
{
if (flag2 == 0)
{
printf("请输入密码:\n");
scanf("%s", password);
if (strcmp("666666", password) != 0) // 密码不对
read_lcd_errorpassword();
else
{
uesrpassword_show();
flag2 = 1;
}
}
}
else if (ts_x > 300 && ts_x < 500 && ts_y > 370 && ts_y < 420) // 点击登陆
{
if (strcmp("power", manage) != 0 || strcmp("666666", password) != 0)
read_lcd_user();
else if (strcmp("power", manage) == 0 && strcmp("666666", password) == 0)
state = LOGIN;
break;
}
}
break;
case LOGIN:
user_show();
buf[200];
n = 0;
i = 0;
place; // place用于定位商品位置
list_file();
p = mylist->next;
while (p != mylist && n < 5) // 先5个出来
{
sprintf(buf, "%s\n%s\n%s", p->productname, p->num, p->price);
word_stock_show(buf, 60 + i * 150, 280); // 字的坐标
show_anybmp(100, 180, 50 + i * 150, 60, p->bmppath); // 图片的坐标
i++;
n++;
p = p->next;
}
q = p;
while (1)
{
ts_read_pos(&ts_x, &ts_y);
if (ts_x > 740 && ts_x < 800 && ts_y > 0 && ts_y < 60) // 返回
{
state = MAINBUY;
break;
}
else if (ts_x > 380 && ts_x < 500 && ts_y > 360 && ts_y < 480) // 右键
{
whiteboard_show(); // 刷掉商品选择指示
i = 0;
n = 0;
while (n < 5)
{
if (p == mylist)
p = p->next;
sprintf(buf, "%s\n%s\n%s", p->productname, p->num, p->price);
word_stock_show(buf, 60 + i * 150, 280); // 字的坐标
show_anybmp(100, 180, 50 + i * 150, 60, p->bmppath); // 图片的坐标
i++;
n++;
p = p->next;
}
q = p;
}
else if (ts_x > 80 && ts_x < 200 && ts_y > 360 && ts_y < 480) // 左键
{
whiteboard_show(); // 刷掉商品选择指示
for (i = 0; i < 10; i++)
{
if (p == mylist)
p = p->prev;
p = p->prev;
}
i = 0;
n = 0;
while (n < 5)
{
if (p == mylist)
p = p->next;
sprintf(buf, "%s\n%s\n%s", p->productname, p->num, p->price);
word_stock_show(buf, 60 + i * 150, 280); // 字的坐标
show_anybmp(100, 180, 50 + i * 150, 60, p->bmppath); // 图片的坐标
i++;
n++;
p = p->next;
}
q = p;
}
else if (ts_x > 200 && ts_x < 260 && ts_y > 360 && ts_y < 480) // 点击减号
{
sub_thing(p, q, 1, place);
list_txt(mylist);
}
else if (ts_x > 320 && ts_x < 380 && ts_y > 360 && ts_y < 480) // 点击加号
{
add_thing(p, q, 1, place);
list_txt(mylist);
}
else if (ts_x > 660 && ts_x < 780 && ts_y > 380 && ts_y < 480) // 下架商品
{
if (q == p)
{
read_lcd_put();
}
else
{
list_del(mylist, q);
list_txt(mylist);
for (i = 0; i < 4; i++)
{
if (p == mylist)
p = p->prev;
p = p->prev;
}
i = 0;
n = 0;
while (n < 5)
{
if (p == mylist)
p = p->next;
sprintf(buf, "%s\n%s\n%s", p->productname, p->num, p->price);
word_stock_show(buf, 60 + i * 150, 280); // 字的坐标
show_anybmp(100, 180, 50 + i * 150, 60, p->bmppath); // 图片的坐标
i++;
n++;
p = p->next;
}
whiteboard_show(); // 刷掉商品选择指示
q = p;
}
}
else if (ts_x > 520 && ts_x < 640 && ts_y > 380 && ts_y < 480) // 上架商品
{
state = upstore;
break;
}
else if (ts_x > 50 && ts_x < 150 && ts_y > 80 && ts_y < 220) // 商品1位置
{
place = 0;
whiteboard_show(); // 刷掉商品选择指示
q = p;
buyloggo_show(100); // 指示商品
for (i = 0; i < 5; i++)
{
q = q->prev;
if (q == mylist)
q = q->prev;
}
}
else if (ts_x > 200 && ts_x < 300 && ts_y > 80 && ts_y < 220) // 商品2位置
{
place = 1;
whiteboard_show(); // 刷掉商品选择指示
q = p;
buyloggo_show(250); // 指示商品
for (i = 0; i < 4; i++)
{
q = q->prev;
if (q == mylist)
q = q->prev;
}
}
else if (ts_x > 350 && ts_x < 450 && ts_y > 80 && ts_y < 220) // 商品3位置
{
place = 2;
whiteboard_show(); // 刷掉商品选择指示
q = p;
buyloggo_show(400); // 指示商品
for (i = 0; i < 3; i++)
{
q = q->prev;
if (q == mylist)
q = q->prev;
}
}
else if (ts_x > 500 && ts_x < 600 && ts_y > 80 && ts_y < 220) // 商品4位置
{
place = 3;
whiteboard_show(); // 刷掉商品选择指示
q = p;
buyloggo_show(550); // 指示商品
for (i = 0; i < 2; i++)
{
q = q->prev;
if (q == mylist)
q = q->prev;
}
}
else if (ts_x > 650 && ts_x < 750 && ts_y > 80 && ts_y < 220) // 商品5位置
{
place = 4;
whiteboard_show(); // 刷掉商品选择指示
q = p;
buyloggo_show(700); // 指示商品
for (i = 0; i < 1; i++)
{
q = q->prev;
if (q == mylist)
q = q->prev;
}
}
}
break;
case upstore:
updata_show();
char array[6][20] = {0};
char arr[100] = {0};
while (1)
{
ts_read_pos(&ts_x, &ts_y);
if (ts_x > 220 && ts_x < 530 && ts_y > 30 && ts_y < 90) // 输入商品名称
{
printf("输入商品名称\n");
scanf("%s", array[0]);
word_store_show(array[0], 600, 270);
word_store_show(array[0], 230, 50);
}
if (ts_x > 220 && ts_x < 530 && ts_y > 125 && ts_y < 185) // 上架数量
{
printf("输入上架数量\n");
scanf("%s", array[1]);
sprintf(array[2], "剩余:%s", array[1]);
word_store_show(array[2], 600, 290);
word_store_show(array[1], 230, 140);
}
if (ts_x > 220 && ts_x < 530 && ts_y > 215 && ts_y < 275) // 商品价格
{
printf("输入商品价格\n");
scanf("%s", array[3]);
sprintf(array[4], "价格:¥%s", array[3]);
word_store_show(array[4], 600, 310);
word_store_show(array[4], 230, 230);
}
if (ts_x > 220 && ts_x < 530 && ts_y > 310 && ts_y < 370) // 图片位置
{
printf("输入图片位置\n");
scanf("%s", array[5]);
show_anybmp(100, 180, 600, 60, array[5]);
word_store_show(array[5], 230, 320);
}
if (ts_x > 285 && ts_x < 435 && ts_y > 390 && ts_y < 450) // 确认
{
sprintf(arr, "%s@%s@%s@%s\n", array[0], array[2], array[4], array[5]);
int fd1 = open("1.txt", O_RDWR | O_APPEND);
if (strlen(arr) > 5)
write(fd1, arr, strlen(arr));
close(fd1);
bzero(array, 120);
bzero(arr, 100);
state = LOGIN;
break;
}
//else if(ts_x > 630 && ts_x < 750 && ts_y > 370 && ts_y < 450)//返回
// {
// state = LOGIN;
// break;
// }
}
break;
}
}
ts_close();
return 0;
}
六、记事本内容格式
这关系到链表从记事本读取信息到节点,一定要按这个格式来
名称@数量@价格@图片路径名
@是必不可少的,其他可略作修改
波浪薯片@剩余:3@价格:¥5@123/1.bmp
可比克@剩余:8@价格:¥2.5@123/3.bmp
花椒锅巴@剩余:7@价格:¥2.8@123/6.bmp
香辣鸭脖@剩余:15@价格:¥3.5@123/7.bmp
芒果干@剩余:12@价格:¥4@123/8.bmp