搜索
bottom↓
回复: 0

《I.MX6U嵌入式Linux C应用编程指南》第二十三章 LCD横屏切竖屏

[复制链接]

出0入234汤圆

发表于 2021-8-26 15:32:29 | 显示全部楼层 |阅读模式
1)实验平台:正点原子i.MX6ULL Linux阿尔法开发板
2)  章节摘自【正点原子】I.MX6U嵌入式Linux C应用编程指南
3)购买链接:https://item.taobao.com/item.htm?&id=603672744434
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/arm-linux/zdyz-i.mx6ull.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子Linux技术交流群:1027879335
1.png

2.jpg


3.png



第二十三章 LCD横屏切换为竖屏

       之前在技术交流群里边有人提到了LCD横屏切换为竖屏的问题,笔者觉得还是很有必要给大家讲一下,所以这里单独做一章内容来讲一讲怎么样实现LCD横屏切换为竖屏,其实个人觉得还是非常简单地。首先给大家普及一个基本的知识点,这种横屏、竖屏的切换与驱动程序无关,是应用层需要去解决的一个问题!
本章将会讨论如下主题。
横屏显示如何切换为竖屏显示;
编写代码验证;

1.1横屏显示如何切换为竖屏显示
       开发板配套使用的这些LCD屏都是横屏显示的,包括正点原子4.3寸480*272、4.3寸800*480、7寸800*480、7寸1024*600以及10.1寸1280*800等这些RGB LCD屏;LCD屏正向放置情况下(以800*480分辨率为例),它的左上角就是坐标(0, 0)、左下角坐标是(0, 480-1)、右上角坐标是(800-1, 0)、右下角坐标是(800-1, 480-1),如下所示:
第二十三章 LCD横屏切换为竖屏411.png

图 23.1.1 LCD屏正向放置

       这是硬件上固定的,它是一种不可修改的硬件属性,譬如你不能对LCD硬件进行配置,将屏幕左下角设置为起点(0, 0),这是不可以的;像素点的排列顺序是从左到右、从上到下,我们对LCD上不同像素点进行操作时,需要找到该像素点对应的显存地址,同样也是基于这种标准来的;假设显存基地址为(unsigned char *)base,那么定位一个(x, y)坐标像素点对应的地址的公式为base + (y * width + x) * pix_bytes,其中pix_bytes表示一个像素点使用pix_bytes个字节来描述。
       示意图如下所示:
第二十三章 LCD横屏切换为竖屏741.png

图 23.1.2 LCD像素点与显存对应关系示意图

       上图已经很直观、明了的说明了LCD屏上各个像素点与显存空间的对应关系。
       但是在很多的应用场合中,往往需要以竖屏的方式来显示画面,譬如手机就是一个很好的例子,相信大家的手机都是竖屏方式显示的;甚至还有一些电子产品既能支持横屏也能支持竖屏显示,当然这是针对应用程序而言。
       那我们的应用程序中如何将LCD屏修改为竖屏显示呢?其实原理上非常简单,我相信大家都可以想到,图 23.1.1所示屏幕,如果我们要将其作为竖屏显示,譬如在应用程序中将左下角作为起点(0, 0),那么左上角对应就是(480-1, 0)、右下角对应就是(0, 800-1)、右上角对应就是(480-1, 800-1),如下图所示:
第二十三章 LCD横屏切换为竖屏1133.png

图 23.1.3 竖屏方式坐标分布

       以上便是竖屏显示情况下,其中的一种坐标分布情况,当然这是应用程序认为的一种坐标分布,对于LCD硬件来说,实际物理上的起点坐标依然是图 23.1.1中左上角的位置。
       那么在上图中竖屏这种情况下,应用程序的坐标对应的像素点,它的显存地址就不能使用base + (y * width + x) * pix_bytes公式进行计算了;譬如上图竖屏方式下,起点坐标(0, 0)对应的实际物理坐标是(0, 480-1),同理它的显存地址也是通过实际物理坐标(0, 480-1)这个坐标计算而来、而不是通过(0, 0)计算。
       在上图中竖屏方式下,应用程序的(x, y)坐标点对应的显存地址可通过如下公式进行计算:
base + ((height - 1- x) * width + y)) * pix_bytes;
       公式中的x和y分别表示竖屏方式下的(x, y)坐标,当然这个公式仅适用于上图这种竖屏方式;你也可以把图 23.1.3旋转180度倒过来,同样也是竖屏,这种情况就不能用上面这条公式了。公式推导非常简单,没什么可解释的。
1.2编写示例代码
       通过上小节的介绍,我们已经知道了如何将横屏切换为竖屏显示,本小节我们将对示例代码 20.4.1进行修改,将其修改为竖屏显示,示例代码笔者已经给出,如下所示。
本例程源码对应的路径为:开发板光盘->11、Linux C应用编程例程源码->23_lcd_vertical_display->lcd_vertical_display.c。
       示例代码 23.2.1 LCD横屏切换为竖屏显示
  1. /***************************************************************
  2. Copyright © ALIENTEK Co., Ltd. 1998-2021. All rights reserved.
  3. 文件名 : lcd_vertical_display.c
  4. 作者 : 邓涛
  5. 版本 : V1.0
  6. 描述 : FrameBuffer应用编程之横屏切换为竖屏显示
  7. 其他 : 无
  8. 论坛 : <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  9. 日志 : 初版 V1.0 2021/6/15 邓涛创建
  10. ***************************************************************/

  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <sys/types.h>
  14. #include <sys/stat.h>
  15. #include <fcntl.h>
  16. #include <unistd.h>
  17. #include <sys/ioctl.h>
  18. #include <sys/mman.h>
  19. #include <linux/fb.h>

  20. #define argb8888_to_rgb565(color)   ({ \
  21.             unsigned int temp = (color); \
  22.             ((temp & 0xF80000UL) >> 8) | \
  23.             ((temp & 0xFC00UL) >> 5) | \
  24.             ((temp & 0xF8UL) >> 3); \
  25.             })

  26. static int lcd_width;                   //LCD X分辨率
  27. static int lcd_height;                  //LCD Y分辨率
  28. static int lcd_max_y;                   //LCD Y坐标最大值
  29. static int user_width;                  //竖屏模式下X分辨率
  30. static int user_height;                 //竖屏模式下Y分辨率
  31. static unsigned short *screen_base = NULL;      //映射后的显存基地址

  32. /********************************************************************
  33. * 函数名称: lcd_draw_point
  34. * 功能描述: 打点
  35. * 输入参数: x, y, color
  36. * 返 回 值: 无
  37. ********************************************************************/
  38. static void lcd_draw_point(unsigned int x, unsigned int y, unsigned int color)
  39. {
  40.     unsigned short rgb565_color = argb8888_to_rgb565(color);//得到RGB565颜色值

  41.     /* 对传入参数的校验 */
  42.     if (x >= user_width)
  43.         x = user_width - 1;
  44.     if (y >= user_height)
  45.         y = user_height - 1;

  46.     /* 填充颜色 */
  47.     screen_base[(lcd_max_y-x) * lcd_width + y] = rgb565_color;
  48. }

  49. /********************************************************************
  50. * 函数名称: lcd_draw_line
  51. * 功能描述: 画线(水平或垂直线)
  52. * 输入参数: x, y, dir, length, color
  53. * 返 回 值: 无
  54. ********************************************************************/
  55. static void lcd_draw_line(unsigned int x, unsigned int y, int dir,
  56.             unsigned int length, unsigned int color)
  57. {
  58.     unsigned short rgb565_color = argb8888_to_rgb565(color);//得到RGB565颜色值
  59.     unsigned int end;
  60.     unsigned long temp;

  61.     /* 对传入参数的校验 */
  62.     if (x >= user_width)
  63.         x = user_width - 1;
  64.     if (y >= user_height)
  65.         y = user_height - 1;

  66.     /* 填充颜色 */
  67.     temp = (lcd_max_y-x) * lcd_width + y;
  68.     if (dir) {  //水平线
  69.         end = x + length - 1;
  70.         if (end >= user_width)
  71.             end = user_width - 1;

  72.         for ( ; x <= end; x++, temp -= lcd_width)
  73.             screen_base[temp] = rgb565_color;
  74.     }
  75.     else {  //垂直线
  76.         end = y + length - 1;
  77.         if (end >= user_height)
  78.             end = user_height - 1;

  79.         for ( ; y <= end; y++, temp++)
  80.             screen_base[temp] = rgb565_color;
  81.     }
  82. }

  83. /********************************************************************
  84. * 函数名称: lcd_draw_rectangle
  85. * 功能描述: 画矩形
  86. * 输入参数: start_x, end_x, start_y, end_y, color
  87. * 返 回 值: 无
  88. ********************************************************************/
  89. static void lcd_draw_rectangle(unsigned int start_x, unsigned int end_x,
  90.             unsigned int start_y, unsigned int end_y,
  91.             unsigned int color)
  92. {
  93.     int x_len = end_x - start_x + 1;
  94.     int y_len = end_y - start_y - 1;

  95.     lcd_draw_line(start_x, start_y, 1, x_len, color);//上边
  96.     lcd_draw_line(start_x, end_y, 1, x_len, color); //下边
  97.     lcd_draw_line(start_x, start_y + 1, 0, y_len, color);//左边
  98.     lcd_draw_line(end_x, start_y + 1, 0, y_len, color);//右边
  99. }

  100. /********************************************************************
  101. * 函数名称: lcd_fill
  102. * 功能描述: 将一个矩形区域填充为参数color所指定的颜色
  103. * 输入参数: start_x, end_x, start_y, end_y, color
  104. * 返 回 值: 无
  105. ********************************************************************/
  106. static void lcd_fill(unsigned int start_x, unsigned int end_x,
  107.             unsigned int start_y, unsigned int end_y,
  108.             unsigned int color)
  109. {
  110.     unsigned short rgb565_color = argb8888_to_rgb565(color);//得到RGB565颜色值
  111.     unsigned long temp;
  112.     unsigned long step_size_count;
  113.     int x;

  114.     /* 对传入参数的校验 */
  115.     if (end_x >= user_width)
  116.         end_x = user_width - 1;
  117.     if (end_y >= user_height)
  118.         end_y = user_height - 1;

  119.     /* 填充颜色 */
  120.     temp = (lcd_max_y-start_x) * lcd_width + start_y;
  121.     for ( ; start_y <= end_y; start_y++, temp++) {

  122.         step_size_count = 0;
  123.         for (x = start_x; x <= end_x; x++, step_size_count += lcd_width)
  124.             screen_base[temp - step_size_count] = rgb565_color;
  125.     }
  126. }

  127. int main(int argc, char *argv[])
  128. {
  129.     struct fb_fix_screeninfo fb_fix;
  130.     struct fb_var_screeninfo fb_var;
  131.     unsigned int screen_size;
  132.     int fd;

  133.     /* 打开framebuffer设备 */
  134.     if (0 > (fd = open("/dev/fb0", O_RDWR))) {
  135.         perror("open error");
  136.         exit(EXIT_FAILURE);
  137.     }

  138.     /* 获取参数信息 */
  139.     ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);
  140.     ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);

  141.     screen_size = fb_fix.line_length * fb_var.yres;
  142.     lcd_width = fb_var.xres;
  143.     lcd_height = fb_var.yres;
  144.     lcd_max_y = lcd_height - 1;
  145.     user_width = fb_var.yres;
  146.     user_height = fb_var.xres;

  147.     /* 将显示缓冲区映射到进程地址空间 */
  148.     screen_base = mmap(NULL, screen_size, PROT_WRITE, MAP_SHARED, fd, 0);
  149.     if (MAP_FAILED == (void *)screen_base) {
  150.         perror("mmap error");
  151.         close(fd);
  152.         exit(EXIT_FAILURE);
  153.     }

  154.     /* 画正方形方块 */
  155.     int w = user_height * 0.25;//方块的宽度为1/4屏幕高度
  156.     lcd_fill(0, user_width-1, 0, user_height-1, 0x0); //清屏(屏幕显示黑色)
  157.     lcd_fill(0, w, 0, w, 0xFF0000); //红色方块
  158.     lcd_fill(user_width-w, user_width-1, 0, w, 0xFF00);   //绿色方块
  159.     lcd_fill(0, w, user_height-w, user_height-1, 0xFF);   //蓝色方块
  160.     lcd_fill(user_width-w, user_width-1, user_height-w, user_height-1, 0xFFFF00);//黄色方块

  161.     /* 画线: 十字交叉线 */
  162.     lcd_draw_line(0, user_height * 0.5, 1, user_width, 0xFFFFFF);//白色水平线
  163.     lcd_draw_line(user_width * 0.5, 0, 0, user_height, 0xFFFFFF);//白色垂直线

  164.     /* 画矩形 */
  165.     unsigned int s_x, s_y, e_x, e_y;
  166.     s_x = 0.25 * user_width;
  167.     s_y = w;
  168.     e_x = user_width - s_x;
  169.     e_y = user_height - s_y;

  170.     for ( ; (s_x <= e_x) && (s_y <= e_y);
  171.             s_x+=5, s_y+=5, e_x-=5, e_y-=5)
  172.         lcd_draw_rectangle(s_x, e_x, s_y, e_y, 0xFFFFFF);

  173.     /* 退出 */
  174.     munmap(screen_base, screen_size);  //取消映射
  175.     close(fd);  //关闭文件
  176.     exit(EXIT_SUCCESS);    //退出进程
  177. }
复制代码

       示例代码中自定义的4个函数:lcd_draw_point()、lcd_draw_line()、lcd_draw_rectangle()、lcd_fill()都是基于竖屏显示方式进行定义的,我们传入的坐标都是竖屏坐标,函数内部会将其转为实际的物理坐标,然后操作写对应的显存地址。
       代码就不在讲解了,没什么好说,主要是一些转换上的逻辑代码,理解了上小节所介绍的内容,这个代码是不难理解的。
       接着我们编译示例代码,将编译得到的可执行文件拷贝到开发板Linux系统家目录下,执行测试程序,此时LCD显示效果如下所示:
第二十三章 LCD横屏切换为竖屏8487.png

图 23.2.1 LCD显示效果

大家可以与图 20.4.3进行对比,画面的显示的效果,一个是横向显示、另一个是竖向显示。

回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-4 07:46

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表