- A+
产品设计出来之后啊,大家使用的时候觉得反过来使用更加便捷。但是屏幕显示是反的。那怎么办那?????
修改硬件费时费工,那能否软件实现那?????
如果纯软件使用那就太费系统资源了。于是就想到了使用全志R528 自带的G2D功能(硬件加速功能)。
使用它进行旋转,后又发现uboot阶段系统没有G2D导致开机logo不能自动旋转,内核启动后G2D 启动logo 又旋转了。
(好烦啊!!!!!!!!!!!!)
于是就需要把uboot 阶段手动把图片数据旋转过来。在G2D启动前把uboot 传递给内核的logo 图片数据也旋转过来。
下面具体步骤:
一、开启G2D功能。
1、由于此前公版默认在modules.mk屏蔽了屏旋转相关配置, 如果你的版本是禁用旋转的, 需要把相关配置去掉.
device/config/chips/r528/configs/evb1/linux-5.4/config-5.4
2759:# CONFIG_SUNXI_DISP2_FB_DISABLE_ROTATE is not set
以下3个相关选项
CONFIG_DISP2_SUNXI=m #CONFIG_SUNXI_DISP2_FB_DISABLE_ROTATE=y #CONFIG_SUNXI_DISP2_FB_ROTATION_SUPPORT=n #CONFIG_SUNXI_DISP2_FB_HW_ROTATION_SUPPORT=n
修改为:
CONFIG_DISP2_SUNXI=m #CONFIG_SUNXI_DISP2_FB_DISABLE_ROTATE=n #CONFIG_SUNXI_DISP2_FB_ROTATION_SUPPORT=n #CONFIG_SUNXI_DISP2_FB_HW_ROTATION_SUPPORT=y
2、硬件旋转需要确保G2D驱动已经使能
make kernel_menuconfig Device Drivers ---> <*> SUNXI G2D Driver [*] sunxi g2d mixer module [*] sunxi g2d rotate module [] sunxi sync fence implement for rotate jobs synchronous
3.打开显示驱动旋转支持
make kernel_menuconfig Device Drivers ---> Graphics support ---> Frame buffer Devices ---> Video support for sunxi ---> DISP2 Framebuffer rotation support (Disable rotation) ---> ( ) Disable rotation ( ) Software rotation support (不要选这个,方案未支持) (X) Hardware(G2D) rotation support (选择G2D旋转)
4.dts配置
board.dts 和 uboot-board.dts同步修改.
&disp{ ..... disp_rotation_used = <1>;/* 使能旋转功能 */ degree0 = <2>; /* X:screen index; 0:0 degree; 1:90 degree; 3:270 degree */ fb0_width = <800>;/*fb 的长宽交换*/ fb0_height = <480>; ..... };
5.旋转后framebuffer编程是需要注意,旋转后的buffer不会直接显示到屏幕上, 需要在应用刷屏的地方调用FBIOPAN_DISPLAY接口.同步旋转后的buffer到LCD上.
以修改公版旋转的GUI刷屏示例:
路径:package/gui/littlevgl-6/lv_drivers/display/fbdev.c void fbdev_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p){ .... lv_disp_flush_ready(drv); ioctl(fbfd, FBIOPAN_DISPLAY, &vinfo); /*函数最后,在刷屏函数后面,调用 FBIOPAN_DISPLAY 接口*/ }
我们系统时自己的UI系统。是在重绘回调函数中,增加FBIOPAN_DISPLAY
main()
{ int fp=0; struct fb_var_screeninfo vinfo; struct fb_fix_screeninfo finfo; fp = open("/dev/fb0", O_RDWR); if(fp < 0) { printf("Error : Can not open framebuffer device/n"); exit(1); } if(ioctl(fp, FBIOGET_VSCREENINFO, &vinfo)){ printf("Error reading variable information/n"); exit(3); } vinfo.xoffset = 0; vinfo.yoffset = 0;
} void sys_paint(void) // 每次重绘调用一次 { ioctl(fbfd, FBIOPAN_DISPLAY, &vinfo); /*函数最后,在刷屏函数后面,调用 FBIOPAN_DISPLAY 接口*/ }
二、uboot阶段手动修改旋转图片数据
追踪uboot logo 执行过程
static int run_main_loop(void) { .... #ifdef CONFIG_ARCH_SUNXI initr_sunxi_plat, #endif .... } initr_sunxi_plat { .... #ifdef CONFIG_BOOT_GUI void board_bootlogo_display(void); board_bootlogo_display(); #else .... } void board_bootlogo_display(void) { .... #if defined(CONFIG_CMD_SUNXI_BMP) sunxi_bmp_display("bootlogo.bmp"); //指定bootlogo的名字。通过修改这个地方可以修改指定logo的名称 #elif defined(CONFIG_CMD_SUNXI_JPEG) sunxi_jpeg_display("bootlogo.jpg"); #endif .... } int sunxi_bmp_display(char *name) { int ret = -1; char *argv[6]; char bmp_head[32];273 char bmp_name[32]; char part_info[16] = {0}; char size[32] = {0}; int partno = -1; unsigned long file_size = 0; char *bmp_head_addr; struct bmp_image *bmp; bmp = memalign(CONFIG_SYS_CACHELINE_SIZE, ALIGN(sizeof(struct bmp_header), CONFIG_SYS_CACHELINE_SIZE)); if (bmp) { sprintf(bmp_head, "%lx", (ulong)bmp); } else { pr_error("sunxi bmp: alloc buffer for %s failn", name); goto out; } partno = sunxi_partition_get_partno_byname("bootloader"); /*android*/ if (partno < 0) { partno = sunxi_partition_get_partno_byname( "boot-resource"); /*linux*/ if (partno < 0) { pr_error("Get bootloader and boot-resource partition number fail!n"); goto free1; } } snprintf(part_info, 16, "0:%x", partno); strncpy(bmp_name, name, sizeof(bmp_name)); snprintf(size, 16, "%lx", (ulong)sizeof(struct bmp_header)); argv[0] = "fatload"; argv[1] = "sunxi_flash"; argv[2] = part_info; argv[3] = bmp_head; argv[4] = bmp_name; argv[5] = size; if (do_fat_fsload(0, 0, 6, argv)) { pr_error("sunxi bmp info error : unable to open logo file %sn", argv[4]); goto free1; } if ((bmp->header.signature[0] != 'B') || (bmp->header.signature[1] != 'M')) { pr_error("this is not a bmp picturen"); goto free1; } file_size = bmp->header.file_size; bmp_head_addr = memalign(CONFIG_SYS_CACHELINE_SIZE, ALIGN(file_size, CONFIG_SYS_CACHELINE_SIZE)); if (bmp_head_addr) { sprintf(bmp_head, "%lx", (ulong)bmp_head_addr); } else { pr_error("sunxi bmp: alloc buffer for %s failn", name); goto free1; } snprintf(size, 16, "%lx", (ulong)file_size); tick_printf("bmp_name=%s size %ldn", bmp_name, file_size); if (do_fat_fsload(0, 0, 6, argv)) { pr_error("sunxi bmp info error : unable to open logo file %sn", argv[4]); goto free2; } //在调用show_bmp_on_fb 之前将bmp_head_addr中的图片数据进行旋转
// 调用旋转函数
rotateBMP180(bmp_head_addr);
ret = show_bmp_on_fb(bmp_head_addr, FB_ID_0); if (ret != 0) pr_error("show bmp on fb failed !%dn", ret); free2: free(bmp_head_addr); free1: free(bmp); out: return ret; }
旋转函数:
主要发现uboot 阶段malloc不能申请太大的内存空间,所以代码中申请的行的大小。
#pragma pack(push, 1) typedef struct { uint16_t type; uint32_t size; uint16_t reserved1; uint16_t reserved2; uint32_t offset; } BMPFileHeader; #pragma pack(pop) // 定义BMP图像信息头结构体 #pragma pack(push, 1) typedef struct { uint32_t header_size; int32_t width; int32_t height; uint16_t planes; uint16_t bit_count; uint32_t compression; uint32_t image_size; int32_t x_pixels_per_meter; int32_t y_pixels_per_meter; uint32_t colors_used; uint32_t colors_important; } BMPInfoHeader; #pragma pack(pop) // 旋转BMP图像180度 void rotateBMP180(char * bmp_head_add) { // 读取文件头 BMPFileHeader* file_header = (BMPFileHeader*)bmp_head_add; // 读取图像信息头 BMPInfoHeader* info_header = (BMPInfoHeader*)(bmp_head_add + sizeof(BMPFileHeader)); // 获取图像宽度、高度和每行像素所占字节数 int32_t width = info_header->width; int32_t height = info_header->height; uint32_t row_size = (info_header->bit_count * width + 31) / 32 * 4; // 创建临时缓冲区存储旋转后的图像数据 uint8_t* temp_data = (uint8_t*)malloc(row_size); if (temp_data == NULL) { printf("Failed to allocate memory for temporary data.n"); return; } // 旋转图像 for (int32_t row = 0; row < height / 2; ++row) { for (int32_t col = 0; col < width; ++col) { // 计算当前像素位置和对应的对称像素位置 int32_t original_index = row * row_size + col * 3; int32_t symmetric_index = (height - 1 - row) * row_size + (width - 1 - col) * 3; // 交换像素颜色值 uint8_t temp_red = bmp_head_add[file_header->offset + original_index]; uint8_t temp_green = bmp_head_add[file_header->offset + original_index + 1]; uint8_t temp_blue = bmp_head_add[file_header->offset + original_index + 2]; bmp_head_add[file_header->offset + original_index] = bmp_head_add[file_header->offset + symmetric_index]; bmp_head_add[file_header->offset + original_index + 1] = bmp_head_add[file_header->offset + symmetric_index + 1]; bmp_head_add[file_header->offset + original_index + 2] = bmp_head_add[file_header->offset + symmetric_index + 2]; bmp_head_add[file_header->offset + symmetric_index] = temp_red; bmp_head_add[file_header->offset + symmetric_index + 1] = temp_green; bmp_head_add[file_header->offset + symmetric_index + 2] = temp_blue; } } // 释放临时缓冲区内存 free(temp_data); }
三、kernel阶段手动修改旋转logo图片数据
追踪内核执行过程
disp_module_init{ ........ platform_driver_unregister(&disp_driver); #ifndef CONFIG_OF platform_device_unregister(&disp_device); #endif ........ } static int disp_probe(struct platform_device *pdev){ ........ bsp_disp_init(para); ........ } static s32 disp_init(struct platform_device *pdev) { ........ lcd_init(); bsp_disp_open(); fb_init(pdev); ........ } s32 fb_init(struct platform_device *pdev) { ........ ret = display_fb_request(i, &fb_para); ........ } static s32 display_fb_request(u32 fb_id, struct disp_fb_create_info *fb_para) { ........ Fb_map_kernel_logo(sel, info); ........ } static int Fb_map_kernel_logo(u32 sel, struct fb_info *info) { ........ paddr = bootlogo_addr; if (paddr == 0) { __inf("Fb_map_kernel_logo failed!"); return Fb_copy_boot_fb(sel, info); } ........ } static int Fb_copy_boot_fb(u32 sel, struct fb_info *info) { enum { BOOT_FB_ADDR = 0, BOOT_FB_WIDTH, BOOT_FB_HEIGHT, BOOT_FB_BPP, BOOT_FB_STRIDE, BOOT_FB_CROP_L, BOOT_FB_CROP_T, BOOT_FB_CROP_R, BOOT_FB_CROP_B, }; char *boot_fb_str = NULL; char *src_phy_addr = NULL; char *src_addr = NULL; char *src_addr_b = NULL; char *src_addr_e = NULL; int src_width = 0; int src_height = 0; int fb_height = 0; int src_bpp = 0; int src_stride = 0; int src_cp_btyes = 0; int src_crop_l = 0; int src_crop_t = 0; int src_crop_r = 0; int src_crop_b = 0; char *dst_addr = NULL; int dst_width = 0; int dst_height = 0; int dst_bpp = 0; int dst_stride = 0; int ret; unsigned long map_offset; if (info == NULL) { __wrn("%s,%d: null pointern", __func__, __LINE__); return -1; } boot_fb_str = (char *)disp_boot_para_parse_str("boot_fb0"); if (boot_fb_str != NULL) { int i = 0; char boot_fb[128] = { 0 }; int len = strlen(boot_fb_str); if (sizeof(boot_fb) - 1 < len) { __wrn("need bigger array size[%d] for boot_fbn", len); return -1; } memcpy((void *)boot_fb, (void *)boot_fb_str, len); boot_fb[len] = '