香橙派5 驱动1.47寸lcd播放视频

一、目的

使用香橙派5驱动1.47寸oled并播放视频,
效果见:https://www.bilibili.com/video/BV1vQ4y1w7CS
代码见仓库的opencv-local分支,链接: https://github.com/jibi17/lcd-opencv.git

二、物料

香橙派5 、中景园1.47寸lcd屏幕(型号:ZJY147S0800TG01),杜邦线若干

三、过程

1.在香橙派上编译安装opencv c++
2.安装wiringop
3.写代码驱动屏幕(这里我用的开发环境是在windows上使用vscode的remote插件,直接在香橙派上本地编译,没有使用交叉编译的方法。
具体remote怎么安装建议百度,然后最好把密钥配好,不然每次打开工程都输密码很麻烦。)

1.安装opencv

链接: https://opencv.org/releases/去官网下。
我试了几个版本,只有4.5.5编译不报错,其他的都编译报错。
在这里插入图片描述
下载源码。

参考这位大佬的博客安装就行,这里就不重复写了:
链接: https://blog.csdn.net/qq_16792139/article/details/124079537

2.安装wiringop

默认情况下应该是已经安装了wiringop
输入:

gpio readall

如果能看到下面的输出,说明wiringOP 已经预装并且能正常使用。
在这里插入图片描述
否则需要自己手动安装。
下面是安装过程,按照官方的使用手册安装:

1)下载wiringop的代码

sudo apt update
sudo apt install -y git
git clone https://github.com/orangepi-xunlong/wiringOP.git -b next

建议看着手册安装!!!不同的机器安装过程不一样,这里是香橙派5的

2)安装wiringop

cd wiringOP
sudo ./build clean
sudo ./build

输入:

gpio readall

如果能看到下面的输出,说明wiringOP 已经安装并且能正常使用。
和上面一样的。

3.屏幕驱动

1)首先要打开香橙派5的spi,这里按照手册打开就行了,具体的自己看手册。
打开后注意我们能使用的是/dev/spidev4.1,不是/dev/spidev4.0!!!
2)然后是lcd的驱动,我这里是在商家给的资料上改的。先来看lcd_init.h 和lcd_init.cpp。首先lcd_init.h 中定义了RESET_PIN,CS_PIN,DC_PIN,BLK_PIN,SPI_CHAN的针脚,可以根据自己的喜好来改,我这里定义如下

#define RESET_PIN 13
#define CS_PIN 15
#define DC_PIN 16
#define BLK_PIN 10
#define	SPI_CHAN 4

然后是定义了如下函数

void lcd_init(void);//LCD初始化
void bsp_init(void);//初始化GPIO
void LCD_Writ_Bus(u8 dat);//模拟SPI时序
void LCD_WR_DATA8(u8 dat);//写入一个字节
void LCD_WR_DATA(u16 dat);//写入两个字节
void LCD_WR_REG(u8 dat);//写入一个指令
void LCD_Address_Set(u16 x1,u16 y1,u16 x2,u16 y2);//设置坐标函数
void spi_send_frame(u8 *data,int sizeOfData);

这么多函数本质上就是需要通过spi向屏幕发送数据,所以我们只需要把最底层的发送数据的代码改成wiringop的发送函数就好。这里面就两个要改,一个是LCD_Writ_Bus(u8 dat)。

void LCD_Writ_Bus(u8 dat) 
{	
	LCD_CS_Clr();
	wiringPiSPIDataRW(SPI_CHAN,&dat,sizeof(dat));//wiringopi的发送函数
	LCD_CS_Set();
}

还有一是个spi_send_frame,这个好像是我自己写的。代码如下

void spi_send_frame(u8 *data,int sizeOfData)
{
	LCD_CS_Clr();
	uint8_t temp[4096];	
	for(int i=0;i<sizeOfData;i=i+sizeof(temp))
	{
		if(i<=sizeOfData && (i+sizeof(temp))>=sizeOfData)
		{
			int size_least=sizeOfData-i;
			memmove(temp,data+i,(uint32_t)size_least);
			if(wiringPiSPIDataRW(SPI_CHAN,temp,size_least)==-1)
			{
				printf ("SPI failure:2 \n") ;
			}
#if DEBUG			
			std::cout << "send last buf:" << size_least<<std::endl;
#endif			
			break;
		}
		memmove(temp,data+i,sizeof(temp));
		if(wiringPiSPIDataRW(SPI_CHAN,temp,sizeof(temp))==-1)
		{
		 	printf ("SPI failure: %s\n", strerror (errno)) ;
		}
	}
	LCD_CS_Set();
}

这个uint8_t temp[4096]是spi一次发送的数据量,好像驱动里面的spi发送的buffer就这么大,再大发送就要报错了。这个函数的作用就是连续发送一个大的数据包。
3)下面来看lcd.h和lcd.cpp
首先定义了一帧的大小

uint8_t show_frame[LCD_W*LCD_H*2];

这个屏幕是320x172 每个像素占用2byte,用的是rgb565格式。
下面是lcd.h实现了哪些函数

void LCD_Fill2(u16 xsta,u16 ysta,u16 xend,u16 yend,u16 color);
void LCD_Fill(u16 xsta,u16 ysta,u16 xend,u16 yend,u16 color);//指定区域填充颜色
void LCD_DrawPoint(u16 x,u16 y,u16 color);//在指定位置画一个点
void LCD_DrawLine(u16 x1,u16 y1,u16 x2,u16 y2,u16 color);//在指定位置画一条线
void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2,u16 color);//在指定位置画一个矩形
void Draw_Circle(u16 x0,u16 y0,u8 r,u16 color);//在指定位置画一个圆
void LCD_ShowChinese(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode);//显示汉字串
void LCD_ShowChinese12x12(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode);//显示单个12x12汉字
void LCD_ShowChinese16x16(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode);//显示单个16x16汉字
void LCD_ShowChinese24x24(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode);//显示单个24x24汉字
void LCD_ShowChinese32x32(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode);//显示单个32x32汉字
void LCD_ShowChar(u16 x,u16 y,u8 num,u16 fc,u16 bc,u8 sizey,u8 mode);//显示一个字符
void LCD_ShowString(u16 x,u16 y,const u8 *p,u16 fc,u16 bc,u8 sizey,u8 mode);//显示字符串
u32 mypow(u8 m,u8 n);//求幂
void LCD_ShowIntNum(u16 x,u16 y,u16 num,u8 len,u16 fc,u16 bc,u8 sizey);//显示整数变量
void LCD_ShowFloatNum1(u16 x,u16 y,float num,u8 len,u16 fc,u16 bc,u8 sizey);//显示两位小数变量
void LCD_ShowPicture(u16 x,u16 y,u16 length,u16 width,const u8 pic[]);//显示图片
void LCD_ShowPicture2(u16 x,u16 y,u16 length,u16 width,const u8 pic[]);
void clear_frame(void);

这里主要看LCD_ShowPicture2(),这个函数是我在原来的基础上改的,用来实现将一帧数据给显示到屏幕上,

void LCD_ShowPicture2(u16 x,u16 y,u16 length,u16 width,const u8 pic[])
{
	u16 i,j;
	u32 k=0;
	LCD_Address_Set(0,0,LCD_W,LCD_H);//设置显示范围
	//there is a postion offset
	//this is the start postion
	uint32_t offset=y*LCD_W*2+x*2;
	for(int i=0;i<width;i++)
	{
		//std::cout << "i:" <<i<< std::endl;
		for(int j=0;j<length;j++)
		{
			//std::cout << "j" <<j<< std::endl;
			show_frame[offset+j*2]=pic[k];
			show_frame[offset+j*2+1]=pic[k+1];
			k=k+2;
		}
		offset=offset+LCD_W*2;//every time just plause one line
	}
	spi_send_frame(show_frame,LCD_W*LCD_H*2);				
}
void clear_frame()
{
	memset(show_frame,0xff,num_of_pixs*2);
}

就是把一帧的数据放到show_frame,然后把show_frame发给屏幕。

4.opencv处理图像

1)在main函数里面是建了两个线程。一个生产者线程用于不断的解码每一帧并放到缓冲buffer。一个消费者线程把缓冲buffer发送给屏幕。
建两个线程的原因:当把数据发送给屏幕的时候会大量的使用io会阻塞线程,效率就变低了。
main.cpp如下:

    // 创建生产者和消费者线程
    std::thread producerThread(create);
    std::thread consumerThread(display);

    // 等待线程完成
    producerThread.join();
    consumerThread.join();
    

下面先看生产者线程:

const int QUEUE_SIZE = 10;
std::queue<uint8_t*> imageQueue;
std::mutex mtx;
std::condition_variable var;
void create()
{
    //打开视频
    cv::VideoCapture cap("./test.mp4");
    // 检查视频是否成功打开
    if (!cap.isOpened()) {
        std::cerr << "Error opening video file." << std::endl;
        return ;
    }
    cv::Mat frame;
    while(1){
        //先生成以一个image
        cap >> frame;  // 从视频捕获设备读取一帧
        // 检查是否成功读取一帧
        if (frame.empty()) {
            std::cerr << "End of video" << std::endl;
            break;
        }
        // 缩放图像为320x172
        cv::Mat resizedImage;
        cv::resize(frame, resizedImage, cv::Size(320, 172),cv::INTER_CUBIC);
        //cv::imshow("Resized RGB565 Image", resizedImage);
        //转换为RGB565格式
        cv::Mat rgb565Image;
        cv::cvtColor(resizedImage, rgb565Image, cv::COLOR_BGR2BGR565);
        // 获取图像的宽度和高度
        int width = rgb565Image.cols;
        int height = rgb565Image.rows;
        uint8_t* imageData = new uint8_t[110080];
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                // 获取RGB565值
                uint16_t rgb565Value = rgb565Image.at<uint16_t>(y, x);
                // 分开高位和低位,并写入数组
                uint8_t highByte = (rgb565Value >> 8) & 0xFF;
                uint8_t lowByte = rgb565Value & 0xFF;
                imageData[(y * width + x) * 2]=highByte;
                imageData[(y * width + x) * 2 + 1]=lowByte;
        //现在获得到了image——temp要把他放到队列里面去
        // 生产者获取锁
        std::unique_lock<std::mutex> lock(mtx);
        // 检查队列是否满
        var.wait(lock, [] { return imageQueue.size() < QUEUE_SIZE; });
         // 将图片放入队列
        imageQueue.push(imageData);
        var.notify_one();
       // std::cout<<"进入一次生产者"<<std::endl;
    }
    return;

因为两个线程要访问共享的资源所以在开始加了互斥锁。
下面是消费者的部分:

void display()
{
    while(1){
        {std::unique_lock<std::mutex> lock(mtx);
        // 使用条件变量等待,直到队列非空或达到一定大小
        var.wait(lock, [] { return !imageQueue.empty() || imageQueue.size() >= QUEUE_SIZE; });
        // 取出队列中的数据进行处理
         uint8_t* imageData = nullptr;
        imageData = imageQueue.front();
        imageQueue.pop();
        LCD_ShowPicture2(0,0,320,172,imageData);
        delete[] imageData;
        }
        var.notify_one();
        //std::cout<<"进入一次消费者"<<std::endl;
    }
    return;
}

就是不停的取数据出来发送到屏幕。
最后cmake的部分就不多赘述了。
我自己都感觉写的有点粗制滥造,而且很多地方我自己也是糊里糊涂的,请各位批评指正

  • 21
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于鱼鹰光电® 1.47TFT触摸屏,一般需要使用相应的驱动程序才能在单片机或开发板上正常运行。以下是一般的驱动程序设置步骤: 1. 确认触摸屏型号:首先要确认所使用的触摸屏的具体型号和规格,以便获取正确的驱动程序和相关文档。 2. 下载触摸屏驱动程序:访问鱼鹰光电®官方网站或者其他可靠的渠道,下载适用于该型号触摸屏的驱动程序。通常会提供驱动程序和相应的示例代码。 3. 配置硬件连接:根据触摸屏驱动程序的要求,配置单片机或开发板与触摸屏之间的硬件连接。这可能涉及到引脚配置、电源和地线连接等。 4. 导入驱动程序:将下载好的触摸屏驱动程序导入到单片机或开发板的开发环境中。具体导入方法会根据使用的开发环境而有所不同。 5. 配置驱动程序:根据驱动程序提供的文档或示例代码,配置触摸屏的分辨率、通信方式和其他参数。这些参数可以根据实际需求进行调整。 6. 编译和烧录:根据开发环境的要求,对驱动程序进行编译,并将生成的可执行文件烧录到单片机或开发板中。 7. 测试和调试:将单片机或开发板与触摸屏连接好,进行测试和调试。可以使用示例代码中提供的测试程序或者自己编写简单的代码,验证触摸屏的功能是否正常。 需要注意的是,具体的驱动程序设置步骤会根据触摸屏的型号和使用的单片机或开发板而有所不同。因此,在进行驱动程序设置之前,建议参考触摸屏和单片机/开发板的官方文档和示例代码,以获取准确和详细的设置指导。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值