@gaoda
看这个:好多人喜欢开源的LVGL,那就随手加个演示吧,代码在gitee上。
文档还是在线的好。官方可以直接修改和增加内容,变更直达用户,不会碎片化。
像乐鑫这样的在线文档系统可以借鉴以下:
https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/index.html
好多人喜欢开源的LVGL,那就随手加个演示吧,代码在gitee上。
https://gitee.com/xuyao2020/F1C100s_with_Keil_RTX4_emWin5.git
(打开FELinside-LVGL.uvproj项目文件编译得到如下视频演示的bin)
gcc编译环境对于习惯了STM32的老人真是太难了,抽空移植到了keil,有需要的可以联系,目前因boot跟公司其他产品通用,暂时没有开源
https://whycan.com/files/members/8049/微信截图_20240329133209.jpg
大佬,请给我一份你截图中的工程,谢谢!我的邮箱是:26750452@qq.com
楼主,这个emWin5.32版的ARM9版库是从哪里抠出来的呢?NXP?
lvgl\examples\porting\lv_port_disp_template.c
void lv_port_disp_init(void)
{
/*-------------------------
* Initialize your display
* -----------------------*/
disp_init();
/*-----------------------------
* Create a buffer for drawing
*----------------------------*/
/**
* LVGL requires a buffer where it internally draws the widgets.
* Later this buffer will passed to your display driver's `flush_cb` to copy its content to your display.
* The buffer has to be greater than 1 display row
*
* There are 3 buffering configurations:
* 1. Create ONE buffer:
* LVGL will draw the display's content here and writes it to your display
*
* 2. Create TWO buffer:
* LVGL will draw the display's content to a buffer and writes it your display.
* You should use DMA to write the buffer's content to the display.
* It will enable LVGL to draw the next part of the screen to the other buffer while
* the data is being sent form the first buffer. It makes rendering and flushing parallel.
*
* 3. Double buffering
* Set 2 screens sized buffers and set disp_drv.full_refresh = 1.
* This way LVGL will always provide the whole rendered screen in `flush_cb`
* and you only need to change the frame buffer's address.
*/
/* Example for 1) */
static lv_disp_draw_buf_t draw_buf_dsc_1;
static lv_color_t buf_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/
lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/
/* Example for 2) */
static lv_disp_draw_buf_t draw_buf_dsc_2;
static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/
static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10]; /*An other buffer for 10 rows*/
lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_1, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/
/* Example for 3) also set disp_drv.full_refresh = 1 below*/
static lv_disp_draw_buf_t draw_buf_dsc_3;
static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*A screen sized buffer*/
static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*An other screen sized buffer*/
lv_disp_draw_buf_init(&draw_buf_dsc_3, buf_3_1, buf_3_2, MY_DISP_VER_RES * LV_VER_RES_MAX); /*Initialize the display buffer*/
/*-----------------------------------
* Register the display in LVGL
*----------------------------------*/
static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
/*Set up the functions to access to your display*/
/*Set the resolution of the display*/
disp_drv.hor_res = 480;
disp_drv.ver_res = 320;
/*Used to copy the buffer's content to the display*/
disp_drv.flush_cb = disp_flush;
/*Set a display buffer*/
disp_drv.draw_buf = &draw_buf_dsc_1;
/*Required for Example 3)*/
//disp_drv.full_refresh = 1
/* Fill a memory array with a color if you have GPU.
* Note that, in lv_conf.h you can enable GPUs that has built-in support in LVGL.
* But if you have a different GPU you can use with this callback.*/
//disp_drv.gpu_fill_cb = gpu_fill;
/*Finally register the driver*/
lv_disp_drv_register(&disp_drv);
}
uc/gui-3.98在15年前就网上轻易能下载到了,而且没有内存设备和抗锯齿字体支持的。对目前来说已经太古老了。
还是用这个吧,emWin5.06源代码,组件更多代码更新:
emWin506-Src.7z
搬出我多年前的收藏给楼主助威吧,NEHE OpenGL 教学例程,例程个个精美:
nehegl.zip
https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain
以上A配置和RM配置的都支持armv5te,内置none-eabi的newlib库用于生成裸机程序。
能编译linux内核和uboot,但不能编译linux应用程序和rootfs工具集。
深表赞同!
不久的新闻有人微信被封,付出了生命的代价,还不放弃微信。不要过渡依赖微信,要是经营多年的公众号哪天被封搞个鸡儿。
什么平台都不要过渡依赖。那年渣浪被整改,清空了所有用户网盘存的东西,我多年在渣浪积累的无形资产文档都没了。
网友存档
https://info.williamlong.info/2016/04/blog-post_25.html
不要徒方便把自己的无形资产交给平台,搞公众号不如学哇酷站长搞个网站,牢牢拽在自己手里的无形资产。
https://whycan.cn/files/members/4270/22_20200617-0127.png
为什么明明在里面也不能。颜色有什么含义吗
是不是应该这样“./iwlist”
执行mksunxi.exe 时候 报出找不到MSVCR120D.dll,找了个msvcr120d.dll和mksunxi.exe放到一块,弄好了。。。。。
换个版本的mksunxi.exe吧,你这个不是mingw编译的;mingw编译的不会依赖这个dll。
用这个:
mksunxi.7z
可以用类似我开头那种做法,完全可以一次性读完,不需要64字节分组收发嘛,就是不知道为何不对:(
试试看这样写行不?
void sys_spi_flash_read(int addr, void* buf, int count)
{
uint8_t *p = (uint8_t *)buf;
sys_spi_select();
SPI0->MBC = 4;
SPI0->MTC = 4;
SPI0->BCC = 4;
SPI0->TXD = 0x03 | util_rev(addr);
SPI0->TCR |= SPI_TCR_XCH;
while (SPI0->FSR & 0xff) < 4);
read8((uint32_t)&SPI0->RXD);
read8((uint32_t)&SPI0->RXD);
read8((uint32_t)&SPI0->RXD);
read8((uint32_t)&SPI0->RXD);
SPI0->MBC = count;
SPI0->MTC = 0;
SPI0->BCC = 0;
SPI0->TCR |= SPI_TCR_XCH;
while (count > 0) {
if((SPI0->FSR & 0xFF) > 0)
{
*p++ = read8((uint32_t)&SPI0->RXD);
count -= 1;
}
}
sys_spi_deselect();
}
读FIFO时,SPI是停止的;这里的确浪费了传输机会。
不过,count循环中并不会重复发送4字节命令和地址,因为在读取数据时,sys_spi_transfer函数的txbuf参数是NULL,所以循环内的sys_spi_write_txbuf函数会立即返回,没有实际数据发送的。
https://whycan.cn/files/members/1592/2020-05-29_100040.png
我还没用逻辑分析仪测量过。要把这些时间省下来,就只能靠DMA了。
感谢Armstrong的亲临指导,我今天再去测试一下原来代码spi读写的情况,spi频率太高了示波器太烂丢脉冲,不过应该可以发现问题的
原来的代码相当于将总的数据收发切割成64字节成组收发,并且读取rxfifo时候还是spi停止的
64+4字节收发->读rxfiofo,然后再重复这个过程
读FIFO时,SPI是停止的;这里的确浪费了传输机会。
不过,count循环中并不会重复发送4字节命令和地址,因为在读取数据时,sys_spi_transfer函数的txbuf参数是NULL,所以循环内的sys_spi_write_txbuf函数会立即返回,没有实际数据发送的。
上层的flash读函数实际上这样解释:
static int sys_spi_write_then_read(void* txbuf, int txlen, void* rxbuf, int rxlen)
{
// 发送命令+地址
if (sys_spi_transfer(txbuf, NULL, txlen) != txlen)
return -1;
// 连续接收纯数据
if (sys_spi_transfer(NULL, rxbuf, rxlen) != rxlen)
return -1;
return 0;
}
用链接器的redirect功能,把库里的某几个函数旁路掉,自己写函数代替。
或者索性自己写个GUI_DEVICE_API,别用内置<GUIDRV_Lin.h>文件里的驱动就行。
自定义驱动形式如下,有模板的,不难实现,针对内存操作的更简单:
const GUI_DEVICE_API GUIDRV_Template_API = {
//
// Data
//
DEVICE_CLASS_DRIVER,
//
// Drawing functions
//
_DrawBitmap,
_DrawHLine,
_DrawVLine,
_FillRect,
_GetPixelIndex,
_SetPixelIndex,
_XorPixel,
//
// Set origin
//
_SetOrg,
//
// Request information
//
_GetDevFunc,
_GetDevProp,
_GetDevData,
_GetRect,
};
针对tiny200v2的忘了去除lcd屏的pd0+pd12管脚,已修正:
【lichee-nano + tiny200v1】
Tiny200v1_LCD480x272_NS2009A.zip
【tiny200v2】
Tiny200v2_LCD480x272_NS2009A.zip
比如我想从SD NAND(贴片SD卡)里启动,要么在SD NAND 焊到板子上之前将程序烧进去,要么焊到板子上再烧程序上去,这时如果能将DRAM模拟成U盘,直接在电脑上将spl和app拖进去,然后将spl和app烧录进SD NAND,实现程序烧录
嗯,懂了。理论上你可以基于FELinside-f1c100s或者f1c100s-uboot制作出这么个东西来,尚且称它未fel-boot吧。
fel-boot由sunxi-fel送入ddr运行,负责把dram模拟成u盘,等着用户把uboot和linux内核放进“u盘”。但我没搞定usb呢,做不了啊。
我看这东西做起来即使不开源,对别人也是很有用的。
完成同样功能,还有个思路就是自己添加sunxi-fel功能,因为sunxi-fel可以通过usb接口传输一段ARM指令到设备,然后执行它;目前的任何sunxi-fel功能都是这么实现的。可以通过这种方式增加写tf卡和sd-nand的功能;不用模拟成u盘。扩展后的命令形如:
sunxi-fel -p sdnand-write 0 xxx.bin
感谢,刚才对比看了V1 V2原理图触摸驱动触摸的IO完全不一样。
谢谢,对比了一下pdf,还真是改过了!所以又适配了一下:
tiny200v1
Tiny200v1_LCD480x272_NS2009.zip
tiny200v2
Tiny200v2_LCD480x272_NS2009.zip
感谢,刚才对比看了V1 V2原理图触摸驱动触摸的IO完全不一样。
那我这个固件测试看看,会不会跳点:
F1C100s_RTX_emWin_LCD480x272_NS2009.zip
用F1C100s硬件IIC的,充分利用中断的方式开发,不用浪费高速CPU死循环检测INT_FLAG标志!也不是用os_delay插在循环中。
好多人都说NS2009有跳点,其实不是芯片本身的问题。我做好了这个大家拿起测试看看,稳得很啊。
F1C100s_RTX_emWin_LCD480x272_NS2009.zip
不仅没有跳点,还每秒钟向GUI送100个触摸点,这个频率响应够快了吧!可以连接UART0看输出,点击屏幕就有输出。
电阻触摸屏测试程序:
RTX-emWin-480x272-ns2009.zip
还有一个指定index的操作,不过这个操作无法针对虚拟地址,几乎无用。除此之外应该是没办法了。
Cortex-M7也有cache需要管理,它的库里也是只能遍历来操作指定内存区域:void MMU_InvalidateDCacheArray(unsigned long mva, unsigned long num) { signed long size = num; while (size > 0) { MMU_InvalidateDCacheMVA(mva); mva += CACHE_ALIGN; size -= CACHE_ALIGN; } }
另外一种惯用的方法就是,自己定义一个heap,这个heap用于管理non-cacheable的内存块;
就学学linux,用kmalloc申请的内存可以指定GFP_DMA类型,它就是从关闭cache的内存页分配。
看了这几个cache相关函数的实现,有个疑问。函数里需要遍历整块内存,以32字节为单位去调用 MCR p15……。这样岂不是很低效?
如果我有几百K的数据从外设读回来,实际上CPU cache只有一点点,并不需要遍历几百K去invalidate cache。
那怎样能够仅仅invalidate需要的cache呢?当然可以invalidate整个cache,还有没有更精简的方法呢?void MMU_InvalidateDCacheArray(unsigned long mva, unsigned long num) { signed long size = num; while (size > 0) { MMU_InvalidateDCacheMVA(mva); mva += CACHE_ALIGN; size -= CACHE_ALIGN; } }
还有一个指定index的操作,不过这个操作无法针对虚拟地址,几乎无用。除此之外应该是没办法了。
Cortex-M7也有cache需要管理,它的库里也是只能遍历来操作指定内存区域:
__STATIC_FORCEINLINE void SCB_CleanDCache_by_Addr (uint32_t *addr, int32_t dsize)
{
#if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
if ( dsize > 0 ) {
int32_t op_size = dsize + (((uint32_t)addr) & (__SCB_DCACHE_LINE_SIZE - 1U));
uint32_t op_addr = (uint32_t)addr /* & ~(__SCB_DCACHE_LINE_SIZE - 1U) */;
__DSB();
do {
SCB->DCCMVAC = op_addr; /* register accepts only 32byte aligned values, only bits 31..5 are valid */
op_addr += __SCB_DCACHE_LINE_SIZE;
op_size -= __SCB_DCACHE_LINE_SIZE;
} while ( op_size > 0 );
__DSB();
__ISB();
}
#endif
}