Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
RT-Thread一般讨论
你们认为这个驱动是错的请帮我分析不要 争论!
发布于 2013-03-15 00:47:25 浏览:3946
订阅该版
``` #include "lcd.h" #include "ssd1289.h" #include "rtthread.h" #include
#include
#include
#include
//rtgui驱动接口 struct rtgui_graphic_driver_ops lcd_ili_ops = { ssd1289_lcd_set_pixel, ssd1289_lcd_get_pixel, ssd1289_lcd_draw_hline, ssd1289_lcd_draw_vline, ssd1289_lcd_draw_raw_hline }; //定义系统驱动 lcd驱动 struct rt_device _lcd_device; static rt_err_t lcd_init(rt_device_t dev) { return RT_EOK; } static rt_err_t lcd_open(rt_device_t dev, rt_uint16_t oflag) { return RT_EOK; } static rt_err_t lcd_close(rt_device_t dev) { return RT_EOK; } static rt_err_t lcd_control(rt_device_t dev, rt_uint8_t cmd, void *args) { switch (cmd) { case RTGRAPHIC_CTRL_GET_INFO: { struct rt_device_graphic_info *info; info = (struct rt_device_graphic_info*) args; RT_ASSERT(info != RT_NULL); info->bits_per_pixel = 16; info->pixel_format = RTGRAPHIC_PIXEL_FORMAT_RGB565P; info->framebuffer = RT_NULL; info->width = 320; info->height = 240; } break; case RTGRAPHIC_CTRL_RECT_UPDATE: /* nothong to be done */ break; default: break; } return RT_EOK; } void rt_hw_lcd_init(void) { _lcd_device.type = RT_Device_Class_Graphic; _lcd_device.init = lcd_init; _lcd_device.open = lcd_open; _lcd_device.close = lcd_close; _lcd_device.control = lcd_control; _lcd_device.read = RT_NULL; _lcd_device.write = RT_NULL; _lcd_device.user_data = &lcd_ili_ops; LCD_Initializtion(); LCD_Clear(0x0); rt_device_register(&_lcd_device, "lcd", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE); } ``` lcd.c ``` void ssd1289_lcd_update(rtgui_rect_t *rect) { /* nothing for none-DMA mode driver */ } rt_uint8_t * ssd1289_lcd_get_framebuffer(void) { return RT_NULL; /* no framebuffer driver */ } void ssd1289_lcd_set_pixel(rtgui_color_t *c, int x, int y) { LCD_WriteReg(0x004e,y); //行 LCD_WriteReg(0x004f,319-x); //列 LCD_WriteRAM_Prepare(); //写准备 LCD_WriteRAM(*c); //写数据 } void ssd1289_lcd_get_pixel(rtgui_color_t *c, int x, int y) { //设置坐标 LCD_WriteReg(0x004e,y); //行 LCD_WriteReg(0x004f,319-x); //列 *c = rtgui_color_from_565p( LCD_ReadRAM()); } void ssd1289_lcd_draw_hline(rtgui_color_t *c, int x1, int x2, int y) { //设置坐标 LCD_WriteReg(0x004e,y); //行 LCD_WriteReg(0x004f,319-x1); //列 LCD_WriteRAM_Prepare(); //写准备 while(x1 < x2) { LCD_WriteRAM(*c); //写数据 x1++; } } void ssd1289_lcd_draw_vline(rtgui_color_t *c, int x, int y1, int y2) { unsigned short p; p = 319-x; LCD_WriteReg(0x0011,0x6050); //y方向自增 // ili9320_SetCursor(x,y1++); //设置坐标 LCD_WriteReg(0x004e,y1++); //行 LCD_WriteReg(0x004f,p); //列 LCD_WriteRAM_Prepare(); //写准备 while (y1 < y2) { LCD_WriteRAM(*c); y1++; } LCD_WriteReg(0x0011,0x6058); //x较蜃栽? } void ssd1289_lcd_draw_raw_hline(rt_uint8_t *pixels, int x1, int x2, int y) { rt_uint16_t *ptr; /* get pixel */ ptr = (rt_uint16_t*) pixels; // ili9320_SetCursor(x1++,y); //设置坐标 LCD_WriteReg(0x004e,y); //行 LCD_WriteReg(0x004f,319-x1); //列 LCD_WriteRAM_Prepare(); //写准备 while (x1 < x2) { LCD_WriteRAM(*ptr++); //填充数据 x1++; } } ``` ssd1289.h ```/* graphic driver operations */ struct rtgui_graphic_driver_ops { /* set and get pixel in (x, y) */ void (*set_pixel)(rtgui_color_t *c, int x, int y); void (*get_pixel)(rtgui_color_t *c, int x, int y); void (*draw_hline)(rtgui_color_t *c, int x1, int x2, int y); void (*draw_vline)(rtgui_color_t *c, int x , int y1, int y2); /* draw raw hline */ void (*draw_raw_hline)(rt_uint8_t *pixels, int x1, int x2, int y); };```
查看更多
10
个回答
默认排序
按发布时间排序
prife
2013-03-15
这家伙很懒,什么也没写!
那我们真是闲的蛋疼了。就算证明你是错的又能怎么样?
aozima
2013-03-15
调网络不抓包,调I2C等时序不上逻辑分析仪,就像电工不用万用表!多用整理的好的文字,比截图更省流量,还能在整理过程中思考。
先分析一下代码: rtgui_graphic_set_device中分判断驱动是否有framebuffer ``` if (info.framebuffer != RT_NULL) { /* is a frame buffer device */ _driver.ops = rtgui_framebuffer_get_ops(_driver.pixel_format); } else { /* is a pixel device */ _driver.ops = rtgui_pixel_device_get_ops(_driver.pixel_format); } ``` 从上面的代码大概可以得知,LCD是否有framebuffer,会使用不同的操作方法,但接口是一样的。 跟综得到_driver.ops的类型定义是 ``` // file: components/rtgui/server/driver.c struct rtgui_graphic_driver _driver; // file: components/rtgui/include/rtgui/driver.h struct rtgui_graphic_driver { /* pixel format and byte per pixel */ rt_uint8_t pixel_format; rt_uint8_t bits_per_pixel; rt_uint16_t pitch; /* screen width and height */ rt_uint16_t width; rt_uint16_t height; /* framebuffer address and ops */ volatile rt_uint8_t *framebuffer; struct rt_device* device; const struct rtgui_graphic_driver_ops *ops; const struct rtgui_graphic_ext_ops *ext_ops; }; /* graphic driver operations */ struct rtgui_graphic_driver_ops { /* set and get pixel in (x, y) */ void (*set_pixel)(rtgui_color_t *c, int x, int y); void (*get_pixel)(rtgui_color_t *c, int x, int y); void (*draw_hline)(rtgui_color_t *c, int x1, int x2, int y); void (*draw_vline)(rtgui_color_t *c, int x , int y1, int y2); /* draw raw hline */ void (*draw_raw_hline)(rt_uint8_t *pixels, int x1, int x2, int y); }; ``` 由上面代码可见,RTGUI中,像素的单位是 rtgui_color_t 其定义如下: ``` #define RTGUI_RGB_R(c) ((c) & 0xff) #define RTGUI_RGB_G(c) (((c) >> 8) & 0xff) #define RTGUI_RGB_B(c) (((c) >> 16) & 0xff) #define RTGUI_RGB_A(c) (((c) >> 24) & 0xff) typedef unsigned long rtgui_color_t; ``` 字段解析应该RGBA8888,4个字节。 接下来我们所需要了解的是像素rtgui_color_t是如何输出到LCD上面的。 假设我们的LCD是16位的RGB565,我们先跟综一下 framebuffer 的LCD设备。 因为framebuffer就是LCD内部的GRAM,比较好理解。 ``` const struct rtgui_graphic_driver_ops *rtgui_framebuffer_get_ops(int pixel_format) { switch (pixel_format) { case RTGRAPHIC_PIXEL_FORMAT_RGB565: return &_framebuffer_rgb565_ops; } } ``` _framebuffer_rgb565_ops中的_rgb565_set_pixel实现如下: ``` /* convert rtgui color to BBBBBGGGGGGRRRRR */ rt_inline rt_uint16_t rtgui_color_to_565(rtgui_color_t c) { rt_uint16_t pixel; pixel = (rt_uint16_t)(((RTGUI_RGB_B(c) >> 3) << 11) | ((RTGUI_RGB_G(c) >> 2) << 5) | (RTGUI_RGB_R(c) >> 3)); return pixel; } static void _rgb565_set_pixel(rtgui_color_t *c, int x, int y) { *GET_PIXEL(rtgui_graphic_get_device(), x, y, rt_uint16_t) = rtgui_color_to_565(*c); } ``` 可见4字节的rtgui_color_t按LCD的硬件定义转换成了两个字节,并存储到framebuffer中,待update时更新到LCD硬件上面去。 再来跟综一下非framebuffer的LCD。 ``` const struct rtgui_graphic_driver_ops *rtgui_pixel_device_get_ops(int pixel_format) { switch (pixel_format) { case RTGRAPHIC_PIXEL_FORMAT_RGB565: return &_pixel_rgb565_ops; } } ``` _pixel_rgb565_ops中的_pixel_rgb565_set_pixel实现如下: ``` #define rt_graphix_ops(device) ((struct rt_device_graphic_ops *)(device->user_data)) #define gfx_device (rtgui_graphic_get_device()->device) #define gfx_device_ops rt_graphix_ops(gfx_device) static void _pixel_rgb565_set_pixel(rtgui_color_t *c, int x, int y) { rt_uint16_t pixel; pixel = rtgui_color_to_565(*c); gfx_device_ops->set_pixel((char *)&pixel, x, y); } ``` 由上面代码分析得出:GUI输出像素给_pixel_rgb565_set_pixel,按LCD的物理特性转换成2字节的RGB565,然后调用驱动程序的set_pixel接口输出到LCD设备上面去。 也就是说驱动收到的是16位RGB565的地址,而非rtgui_color_t的地址。 LCD驱动的实现: ``` // stm32radio/ssd1289.c /* 设置像素点 颜色,X,Y */ void ssd1289_lcd_set_pixel(const char* pixel, int x, int y) { rt_uint16_t * p = (rt_uint16_t*)pixel; rt_uint16_t data = *p; lcd_SetCursor(x,y); rw_data_prepare(); write_data(data); } struct rt_device_graphic_ops ssd1289_ops = { ssd1289_lcd_set_pixel, ssd1289_lcd_get_pixel, ssd1289_lcd_draw_hline, ssd1289_lcd_draw_vline, ssd1289_lcd_blit_line }; ``` 因为RTGUI传过来的已经是转换之后的数据(前提是LCD的属性要正确), 所以可以直接输出到LCD上面。 再来分析对比一下楼主的驱动, ``` void ssd1289_lcd_set_pixel(rtgui_color_t *c, int x, int y) { LCD_WriteReg(0x004e,y); //行 LCD_WriteReg(0x004f,319-x); //列 LCD_WriteRAM_Prepare(); //写准备 LCD_WriteRAM(*c); //写数据 } ``` 因为rtgui_color_t是4字节的,我们使用 uint32_t 代替,上面的代码再细分一下。 ``` void ssd1289_lcd_set_pixel(uint32_t *c, int x, int y) { uint32_t data = *c; uint16_t data_lcd = (uint16_t)data; LCD_WriteReg(0x004e,y); //行 LCD_WriteReg(0x004f,319-x); //列 LCD_WriteRAM_Prepare(); //写准备 LCD_WriteRAM(data_lcd); //写数据 } ``` 因为驱动的set_pixel实际获得的参数是一个16位数据的指针,也就是_pixel_rgb565_set_pixel中pixel的地址, (NOTE:因为可能有多种LCD,所以类型为const char *,由驱动自己强制转换成所需要的类型,指针的类型转换是不会改变指针的值的,但这里是否改用 const void * 会更好?) 又因为系统是小端存储,所以整个过程中,不管楼主的驱动是使用 rtgui_color_t *c,还是按rtdef.h中规定的那样使用const char *pixel, 都不会造成数据本身的改变(这里只是凑巧)。 所以楼主所说的颜色出错与这个参数无关的,但据楼主所说确实有不对。 那么可以按如下调试: ``` static void lcd_debug_write(rt_uint16_t pixel_data) { rt_kprintf("lcd_debug_write :%02X ", pixel_data); LCD_WriteRAM(pixel_data); } void ssd1289_lcd_set_pixel(rtgui_color_t *c, int x, int y) { LCD_WriteReg(0x004e,y); //行 LCD_WriteReg(0x004f,319-x); //列 LCD_WriteRAM_Prepare(); //写准备 // LCD_WriteRAM(*c); //写数据 lcd_debug_write(*c); } void ssd1289_lcd_set_pixel(const char* pixel, int x, int y) { LCD_WriteReg(0x004e,y); //行 LCD_WriteReg(0x004f,319-x); //列 LCD_WriteRAM_Prepare(); //写准备 // LCD_WriteRAM(*c); //写数据 // write_data(*(rt_uint16_t*)pixel); lcd_debug_write(*(rt_uint16_t*)pixel); } ``` 其实查出这个问题最简单的办法,就是在set_pixel中下一个断点,然后直接观察内存地址pixel处的数据,为什么不做呢? 反而是 那么问题出在哪呢?我们对比一下struct rt_device_graphic_ops和struct rtgui_graphic_driver_ops ``` /** * graphic operations */ struct rt_device_graphic_ops { void (*set_pixel) (const char *pixel, int x, int y); void (*get_pixel) (char *pixel, int x, int y); void (*draw_hline)(const char *pixel, int x1, int x2, int y); void (*draw_vline)(const char *pixel, int x, int y1, int y2); void (*blit_line) (const char *pixel, int x, int y, rt_size_t size); }; ``` ``` /* graphic driver operations */ struct rtgui_graphic_driver_ops { /* set and get pixel in (x, y) */ void (*set_pixel)(rtgui_color_t *c, int x, int y); void (*get_pixel)(rtgui_color_t *c, int x, int y); void (*draw_hline)(rtgui_color_t *c, int x1, int x2, int y); void (*draw_vline)(rtgui_color_t *c, int x , int y1, int y2); /* draw raw hline */ void (*draw_raw_hline)(rt_uint8_t *pixels, int x1, int x2, int y); }; ``` 因为结构体的结构类似,而且我们知道不管指针用什么类型,其地址都不会改变。 所以前面4个接口只是编译时有警告,实际使用并不会有问题(实际项目中要是有这样的代码直接枪毙)。 但第5个就有问题了 ``` void (*blit_line) (const char *pixel, int x, int y, rt_size_t size); void (*draw_raw_hline)(rt_uint8_t *pixels, int x1, int x2, int y); ``` 根据ARM的参数传递规则(APCS),管你名称对不对,反正他会按参数的序号传递。 所以楼主的参数实际是: ``` pixel = pixel (撞大运) x1 = x (继续撞大运) x2 = y (杯具鸟) y = size (杯具鸟) ``` 再仔细对比楼主的天才驱动 ``` void ssd1289_lcd_draw_raw_hline(rt_uint8_t *pixels, int x1, int x2, int y) { rt_uint16_t *ptr; /* get pixel */ ptr = (rt_uint16_t*) pixels; // ili9320_SetCursor(x1++,y); //设置坐标 LCD_WriteReg(0x004e,y); //行 LCD_WriteReg(0x004f,319-x1); //列 LCD_WriteRAM_Prepare(); //写准备 while (x1 < x2) { LCD_WriteRAM(*ptr++); //填充数据 x1++; } } ``` 和RT-Thread的stm32_radio的驱动 ``` /* blit a line */ void ssd1289_lcd_blit_line(const char* pixels, int x, int y, rt_size_t size) { rt_uint16_t *ptr; ptr = (rt_uint16_t*)pixels; /* [5:4]-ID~ID0 [3]-AM-1垂直-0水平 */ write_reg(0x0011,0x6070 | (0<<3)); // AM=0 hline lcd_SetCursor(x, y); rw_data_prepare(); /* Prepare to write GRAM */ while (size) { write_data(*ptr ++); size --; } } ``` 发现问题在哪了吗?但最神奇的是楼主说使用16位色的BMP图片可以正常显示? >wZw(167***298) 2013-03-14 23:23:25 >那怎么会出现这种问题 我把图标存为16位色就没问题了 >wZw(167***298) 2013-03-14 23:55:51 >现在图片没问题了 --- 我只能说,楼主一定得去买张彩票,中了以后分我万分之一足矣。 另外推荐楼主一定要看下这个文章:[http://coolshell.cn/articles/2058.html](各种流行的编程风格)
xiao苦
2013-03-15
这家伙很懒,什么也没写!
- -BMP的保存格式本来就是左下到右上的顺序,当然能正常显示啊。。。
bernard
2013-03-15
这家伙很懒,什么也没写!
aozima,你闲的蛋疼啊
王周旺
2013-03-15
这家伙很懒,什么也没写!
bmp 一点用24位表示是 解码后得到3个字节 液晶屏的数据时565格式 两个字节 如果解码引擎不处理 24位色转16位色显示肯定不正常
王周旺
2013-03-15
这家伙很懒,什么也没写!
多谢aozima分析,5五个驱动函数确实有问题 现在明白了 不过之前我用const char*传递颜色值确实出了颜色值不对的问题 我现在把这个驱动重新改过了再看显示图标有没有问题,
grissiom
2013-03-15
这家伙很懒,什么也没写!
>aozima,你闲的蛋疼啊 --- 应该是被气到了…… 不过这篇分析非常好的,应该可以当作教程了~
王周旺
2013-03-15
这家伙很懒,什么也没写!
经过一翻折腾着个问题彻底解决了 很多时候我们都是知其然而不知其所以然才会犯这样的错误 像grissiom 说的这篇分析可以作为RTGUI初学者的好教程
nongxiaoming
2013-04-14
rt-thread大师兄
aozima真牛,一步一步帮楼主解决问题,证明了楼主是错的~
撰写答案
登录
注册新账号
关注者
0
被浏览
3.9k
关于作者
王周旺
这家伙很懒,什么也没写!
提问
14
回答
33
被采纳
0
关注TA
发私信
相关问题
1
有关动态模块加载的一篇论文
2
最近的调程序总结
3
晕掉了,这么久都不见layer2的踪影啊
4
继续K9ii的历程
5
[GUI相关] FreeType 2
6
[GUI相关]嵌入式系统中文输入法的设计
7
20081101 RT-Thread开发者聚会总结
8
嵌入式系统基础
9
linux2.4.19在at91rm9200 上的寄存器设置
10
[转]基于嵌入式Linux的通用触摸屏校准程序
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
最新文章
1
【RT-Thread】【ci】【scons】将ci.attachconfig.yml和scons结合使用
2
Rt-thread中OTA下载后,bootloader不搬程序
3
ulog 日志 LOG_HEX 输出时间改为本地日期时间
4
在RT-Thread Studio中构建前执行python命令
5
研究一了一段时间RTT,直接标准版上手太难,想用nano,但又舍不得组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
I2C_IIC
ESP8266
UART
WIZnet_W5500
ota在线升级
cubemx
PWM
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
SFUD
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
出出啊
1518
个答案
343
次被采纳
小小李sunny
1444
个答案
290
次被采纳
张世争
813
个答案
177
次被采纳
crystal266
547
个答案
161
次被采纳
whj467467222
1222
个答案
149
次被采纳
本月文章贡献
出出啊
1
篇文章
5
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
3
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部