页次: 1
armstrong 说:各种mapper是有标准的吗?还是全靠逆向分析出来的?
好全的nes资料啊,多谢分享
DOUT+DMA模式,太快了!若SPI时钟设为100MHZ,能达到25MB/S;保险起见设置SPI为50MHZ,也有12.5MB/S。
启动个RTOS应用简直了。
#include <stdint.h>
#include <string.h>
#include "f1c100s/reg-ccu.h"
#include "io.h"
#define SPI_USE_DMA (1)
enum {
SPI_GCR = 0x04,
SPI_TCR = 0x08,
SPI_IER = 0x10,
SPI_ISR = 0x14,
SPI_FCR = 0x18,
SPI_FSR = 0x1c,
SPI_WCR = 0x20,
SPI_CCR = 0x24,
SPI_MBC = 0x30,
SPI_MTC = 0x34,
SPI_BCC = 0x38,
SPI_TXD = 0x200,
SPI_RXD = 0x300,
};
#if SPI_USE_DMA > 0
enum {
DMA0 = 0,
DMA1,
DMA2,
DMA3,
};
enum {
NDMA = 0,
DDMA,
};
#define NDMA_TRANS_LEN (128u*1024)
#define DDMA_TRANS_LEN (16u*1024*1024)
/********** DMA info *************/
#define DMA_NO (DMA0)
#define DMA_MODE (NDMA) /* SPI only support NDMA */
#define DMA_TRANS_LEN ((DMA_MODE == NDMA) ? (NDMA_TRANS_LEN) : (DDMA_TRANS_LEN))
#define DMA_BASE (0x01C02000)
#define DMA_ICR (DMA_BASE + 0x00)
#define DMA_ISR (DMA_BASE + 0x04)
#define DMA_PCR (DMA_BASE + 0x08)
#define NDMA_CR(dma_n) (DMA_BASE + 0x100 + 0x20*dma_n + 0x0)
#define NDMA_SRC_ADDR(dma_n) (DMA_BASE + 0x100 + 0x20*dma_n + 0x4)
#define NDMA_DES_ADDR(dma_n) (DMA_BASE + 0x100 + 0x20*dma_n + 0x8)
#define NDMA_BCR(dma_n) (DMA_BASE + 0x100 + 0x20*dma_n + 0xC)
#define DDMA_CR(dma_n) (DMA_BASE + 0x300 + 0x20*dma_n + 0x0)
#define DDMA_SRC_ADDR(dma_n) (DMA_BASE + 0x300 + 0x20*dma_n + 0x4)
#define DDMA_DES_ADDR(dma_n) (DMA_BASE + 0x300 + 0x20*dma_n + 0x8)
#define DDMA_BCR(dma_n) (DMA_BASE + 0x300 + 0x20*dma_n + 0xC)
#define DDMA_PR(dma_n) (DMA_BASE + 0x300 + 0x20*dma_n + 0x18)
#define DDMA_GD(dma_n) (DMA_BASE + 0x300 + 0x20*dma_n + 0x1C)
static void sdelay(int loops)
{
loop_again:
__asm volatile {
SUBS loops, loops, #1
BNE loop_again
}
}
static void sys_dma_init(void)
{
/* Enable gate for DMA clock, and perform softreset */
write32(F1C100S_CCU_BASE + CCU_BUS_CLK_GATE0, read32(F1C100S_CCU_BASE + CCU_BUS_CLK_GATE0) | (0x1 << 6));
write32(F1C100S_CCU_BASE + CCU_BUS_SOFT_RST0, read32(F1C100S_CCU_BASE + CCU_BUS_SOFT_RST0) & (~(0x1 << 6)));
sdelay(20);
write32(F1C100S_CCU_BASE + CCU_BUS_SOFT_RST0, read32(F1C100S_CCU_BASE + CCU_BUS_SOFT_RST0) | (0x1 << 6));
}
static void sys_dma_deinit(void)
{
write32(F1C100S_CCU_BASE + CCU_BUS_CLK_GATE0, read32(F1C100S_CCU_BASE + CCU_BUS_CLK_GATE0) & (~(0x1 << 6)));
}
static uint32_t sys_dma_transfer_len_get(void)
{
return DMA_TRANS_LEN;
}
static void sys_spi_dma_set(void* dst, void* src, uint32_t len)
{
uint32_t val;
write32(NDMA_SRC_ADDR(DMA_NO), (uint32_t)src);
write32(NDMA_DES_ADDR(DMA_NO), (uint32_t)dst);
write32(NDMA_BCR(DMA_NO), len);
val = (1u << 31) | (0x11 << 16) | (0x1 << 5) | (0x4 << 0);
write32(NDMA_CR(DMA_NO), val);
}
static void sys_spi_dma_start(uint32_t len)
{
uint32_t reg_base = 0x01c05000;
uint32_t val;
write32(reg_base + SPI_MBC, len);
write32(reg_base + SPI_MTC, 0);
write32(reg_base + SPI_BCC, (1 << 28)); // dual-mode
val = read32(reg_base + SPI_FCR);
val |= (1 << 8) | (1 << 0);
write32(reg_base + SPI_FCR, val);
write32(reg_base + SPI_TCR, read32(reg_base + SPI_TCR) | (1u << 31));
}
static void sys_dma_wait_end(void)
{
/* when the dma end, it clear this bit automatically */
while (read32(NDMA_CR(DMA_NO)) & (1u << 31));
}
#endif
void sys_spi_flash_init(void)
{
uint32_t addr;
uint32_t val;
/* Config GPIOC0, GPIOC1, GPIOC2 and GPIOC3 */
addr = 0x01c20848 + 0x00;
val = read32(addr);
val &= ~(0xf << ((0 & 0x7) << 2));
val |= ((0x2 & 0x7) << ((0 & 0x7) << 2));
write32(addr, val);
val = read32(addr);
val &= ~(0xf << ((1 & 0x7) << 2));
val |= ((0x2 & 0x7) << ((1 & 0x7) << 2));
write32(addr, val);
val = read32(addr);
val &= ~(0xf << ((2 & 0x7) << 2));
val |= ((0x2 & 0x7) << ((2 & 0x7) << 2));
write32(addr, val);
val = read32(addr);
val &= ~(0xf << ((3 & 0x7) << 2));
val |= ((0x2 & 0x7) << ((3 & 0x7) << 2));
write32(addr, val);
/* Deassert spi0 reset */
addr = 0x01c202c0;
val = read32(addr);
val |= (1 << 20);
write32(addr, val);
/* Open the spi0 bus gate */
addr = 0x01c20000 + 0x60;
val = read32(addr);
val |= (1 << 20);
write32(addr, val);
/* Set spi clock rate control register, divided by 4 */
addr = 0x01c05000;
write32(addr + SPI_CCR, 0x00001001);
/* Enable spi0 and do a soft reset */
addr = 0x01c05000;
val = read32(addr + SPI_GCR);
val |= (1UL << 31) | (1 << 7) | (1 << 1) | (1 << 0); // Transmit Pause Enable (TP_EN)
write32(addr + SPI_GCR, val);
while (read32(addr + SPI_GCR) & (1UL << 31));
val = read32(addr + SPI_TCR);
val &= ~(0x3 << 0);
val |= (1 << 6) | (1 << 2);
write32(addr + SPI_TCR, val);
val = read32(addr + SPI_FCR);
val |= (1UL << 31) | (1 << 15);
write32(addr + SPI_FCR, val);
#if SPI_USE_DMA > 0
sys_dma_init();
#endif
}
void sys_spi_flash_exit(void)
{
uint32_t addr = 0x01c05000;
uint32_t val;
/* Disable the spi0 controller */
val = read32(addr + SPI_GCR);
val &= ~((1 << 1) | (1 << 0));
write32(addr + SPI_GCR, val);
#if SPI_USE_DMA > 0
sys_dma_deinit();
#endif
}
static void sys_spi_select(void)
{
uint32_t addr = 0x01c05000;
uint32_t val;
val = read32(addr + SPI_TCR);
val &= ~((0x3 << 4) | (0x1 << 7));
val |= ((0 & 0x3) << 4) | (0x0 << 7);
write32(addr + SPI_TCR, val);
}
static void sys_spi_deselect(void)
{
uint32_t addr = 0x01c05000;
uint32_t val;
val = read32(addr + SPI_TCR);
val &= ~((0x3 << 4) | (0x1 << 7));
val |= ((0 & 0x3) << 4) | (0x1 << 7);
write32(addr + SPI_TCR, val);
}
static void sys_spi_write_txbuf(uint8_t* buf, int len)
{
uint32_t addr = 0x01c05000;
int i;
if (!buf)
len = 0;
write32(addr + SPI_MTC, len & 0xffffff);
write32(addr + SPI_BCC, len & 0xffffff);
for (i = 0; i < len; ++i)
write8(addr + SPI_TXD, *buf++);
}
static int sys_spi_transfer(void* txbuf, void* rxbuf, int len)
{
uint32_t addr = 0x01c05000;
int count = len;
uint8_t* tx = txbuf;
uint8_t* rx = rxbuf;
uint8_t val;
int n, i;
while (count > 0) {
n = (count <= 64) ? count : 64;
write32(addr + SPI_MBC, n);
sys_spi_write_txbuf(tx, n);
write32(addr + SPI_TCR, read32(addr + SPI_TCR) | (1UL << 31));
while ((read32(addr + SPI_FSR) & 0xff) < n);
for (i = 0; i < n; i++) {
val = read8(addr + SPI_RXD);
if (rx)
*rx++ = val;
}
if (tx)
tx += n;
count -= n;
}
return len;
}
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;
}
void sys_spi_flash_read(int addr, void* buf, int count)
{
uint8_t tx[4];
tx[0] = 0x03;
tx[1] = (uint8_t)(addr >> 16);
tx[2] = (uint8_t)(addr >> 8);
tx[3] = (uint8_t)(addr >> 0);
sys_spi_select();
sys_spi_write_then_read(tx, 4, buf, count);
sys_spi_deselect();
}
void sys_spi_flash_read_dualout(int addr, void* buf, int count)
{
uint32_t reg_base = 0x01c05000;
uint8_t* rxbuf = buf;
uint8_t tx[5];
int n, i, c;
n = 0;
tx[n++] = 0x3b; // fast read dual-output
tx[n++] = (uint8_t)(addr >> 16);
tx[n++] = (uint8_t)(addr >> 8);
tx[n++] = (uint8_t)(addr >> 0);
tx[n++] = 0;
sys_spi_select();
write32(reg_base + SPI_MBC, n);
write32(reg_base + SPI_MTC, n);
write32(reg_base + SPI_BCC, n);
for (i = 0; i < n; i++) {
write8(reg_base + SPI_TXD, tx[i]);
}
write32(reg_base + SPI_TCR, read32(reg_base + SPI_TCR) | (1u << 31));
while (read32(reg_base + SPI_TCR) & (1u << 31));
write32(reg_base + SPI_FCR, read32(reg_base + SPI_FCR) | 0x80008000u);
while (count > 0) {
n = ((count <= 4096) ? count : 4096);
write32(reg_base + SPI_MBC, n);
write32(reg_base + SPI_MTC, 0);
write32(reg_base + SPI_BCC, (1 << 28)); // dual-mode
write32(reg_base + SPI_TCR, read32(reg_base + SPI_TCR) | (1u << 31));
for (i = n; i > 0;) {
if ((c = (read32(reg_base + SPI_FSR) & 0xff)) > 0) {
i -= c;
while (c-- > 0) {
*rxbuf++ = read8(reg_base + SPI_RXD);
}
}
}
count -= n;
}
sys_spi_deselect();
}
#if SPI_USE_DMA > 0
void sys_spi_flash_read_dma(int addr, void* buf, uint32_t count)
{
uint32_t reg_base = 0x01c05000;
uint32_t dma_max_len;
uint8_t* rxbuf = buf;
uint8_t tx[5];
uint32_t n, i;
n = 0;
tx[n++] = 0x3b; // fast read dual-output
tx[n++] = (uint8_t)(addr >> 16);
tx[n++] = (uint8_t)(addr >> 8);
tx[n++] = (uint8_t)(addr >> 0);
tx[n++] = 0;
sys_spi_select();
write32(reg_base + SPI_MBC, n);
write32(reg_base + SPI_MTC, n);
write32(reg_base + SPI_BCC, n);
for (i = 0; i < n; i++) {
write8(reg_base + SPI_TXD, tx[i]);
}
write32(reg_base + SPI_TCR, read32(reg_base + SPI_TCR) | (1u << 31));
while (read32(reg_base + SPI_TCR) & (1u << 31));
write32(reg_base + SPI_FCR, read32(reg_base + SPI_FCR) | 0x80008000u);
dma_max_len = sys_dma_transfer_len_get();
while (count > 0) {
n = ((count <= dma_max_len) ? count : dma_max_len);
sys_spi_dma_set(rxbuf, (void*)(reg_base + SPI_RXD), n);
sys_spi_dma_start(n);
sys_dma_wait_end();
rxbuf += n;
count -= n;
}
sys_spi_deselect();
}
#endif
在spl代码中,用下面的代码替代sys_spi_flash_read函数,就可以最大速度加载app到ram执行。
我的AHB频率是200MHZ,SPI配置为50Mhz(100Mhz不太稳定),下面的代码启用DOUT模式传输,所以理论读取速度是12.5MB/秒。
BROM加载SPL大约150ms,而这段代码加载3MB的app代码仅需250ms,总共400毫秒就能启动3MB的应用程序。
void sys_spi_flash_read_dualout(int addr, void* buf, int count)
{
uint32_t reg_base = 0x01c05000;
uint8_t* rxbuf = buf;
uint8_t tx[5];
int n, i, c;
n = 0;
tx[n++] = 0x3b; // fast read dual-output
tx[n++] = (uint8_t)(addr >> 16);
tx[n++] = (uint8_t)(addr >> 8);
tx[n++] = (uint8_t)(addr >> 0);
tx[n++] = 0;
sys_spi_select();
write32(reg_base + SPI_MBC, n);
write32(reg_base + SPI_MTC, n);
write32(reg_base + SPI_BCC, n);
for (i = 0; i < n; i++) {
write8(reg_base + SPI_TXD, tx[i]);
}
write32(reg_base + SPI_TCR, read32(reg_base + SPI_TCR) | (1u << 31));
while (read32(reg_base + SPI_TCR) & (1u << 31));
write32(reg_base + SPI_FCR, read32(reg_base + SPI_FCR) | 0x80008000u);
while (count > 0) {
n = ((count <= 4096) ? count : 4096);
write32(reg_base + SPI_MBC, n);
write32(reg_base + SPI_MTC, 0);
write32(reg_base + SPI_BCC, (1 << 28)); // dual-mode
write32(reg_base + SPI_TCR, read32(reg_base + SPI_TCR) | (1u << 31));
for (i = n; i > 0;) {
// 这里代码看起来很累赘,但是这样却能快速读空RXFIFO!
if ((c = (read32(reg_base + SPI_FSR) & 0xff)) > 0) {
i -= c;
while (c-- > 0) {
*rxbuf++ = read8(reg_base + SPI_RXD);
}
}
}
count -= n;
}
sys_spi_deselect();
}
@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
}
在哇酷网在线时间并不算长,但却很快就能找到合适的学习资料。这是因为有大量热爱分享的网友给哇酷添砖加瓦,也是由于哇酷提供了交流的平台,节省大家的时间。
对于全志F1C100s/F1C200s芯片,官方主导的玩法是linux,社区也是顺着这种玩法,不断扩充。但是广大单片机开发者通常缺乏Linux开发经验,而且对具备MMU+Cache的ARM9也不够熟悉,因而无法快速上手这几款性价比无敌的SoC,只能望洋兴叹。况且,在与好多网友交谈后得知,好多项目并不适合使用linux——linux不是万金油,RTOS才是真爱。于是,我结合自己一直以来对ARM9的熟悉,制作了这个移植,基于单片机开发者熟悉的keil-mdk环境,为广大单片机开发者降低门槛,让你打开熟悉的Keil就有可用的RTOS【RTX4】和GUI【emWin5】!其它组件在keil下也并非难事。(我的环境是MDK5.29,尽量使用相同版本,如果比我低可能会丢失项目某些选项!)
项目的好多驱动是基于xboot和网友的精简代码,引用的文件首部保留原作者信息文本。我做的工作是整合代码,以及RTX4+emWin5的移植部分,RTOS的基础是中断控制和Timer,这部分重新实现了。具体的内容你拿到源码查看就能发现,不再多说。
因为我对RTX4比较了解,移植的质量可以保证稳定【除非不是我的代码部分】;emWin很好用,我觉得很多人都用过。大家尽情使用吧,把F1Cx00s玩溜起来!启动1.7MB程序时间大约500毫秒,视程序大小而定。
项目直接支持的开发板有:
widora的tiny200 v1
widora的tiny200 v2
sipeed的lichee-pi nano
推荐使用widora的tiny200开发板,板子为单片机开发者设计了boot和reset按钮,因而进入FEL模式非常方便!板子还自带USB转串口芯片,可作为终端串口,方便调试信息输出。据说V2板还加了音频功放电路,直驱小喇叭。
优酷演示视频:https://v.youku.com/v_show/id_XNDYxNjc2MTk4MA==.html
国内码云地址【推荐,只是因为快】:https://gitee.com/xuyao2020/F1C100s_with_Keil_RTX4_emWin5
国外github【好慢,图片几乎全挂,因为gfw的存在,国内开发者总在夹缝中求生存】:
https://github.com/hongxuyao/F1C100s_with_Keil_RTX4_emWin5
源码打包下载:
AllwinnerSoC_part1.rar
AllwinnerSoC_part2.rar
AllwinnerSoC_part3.rar
直接写入开发板的bin文件:
RTX4_emWin5_F1C100s_tiny200v1.zip
RTX4_emWin5_F1C100s_tiny200v2.zip
图赏
如何适配tiny200v2【因为终端串口有变】
我只是搬运了一下代码。。我参考的是这篇帖子,基于Frambuffer后端实现
https://blog.kaaass.net/archives/1340
你这个演示bin,cpu主频和ddr频率是多少啊?写入flash运行,经常性宕机。
@armstrong
大神我在f1c100上面试了: 提示打不开/dev/ge2d,没有硬件加速。。。另外我编译的时候用的是arm-none-linux-gnueabi-gcc不知道影响不?
# ./NuvotonGUIDemo
uVideoSize = 0x177000
var.xres = 0x320
var.yres = 0x1e0
pVideoBuffer = 0xb6d8f000
Main Task thread
WM_SetCreateFlags(WM_CF_MEMDEV)
GUI_Init()
GUI_X_Config()
GUI_ALLOC_GetNumUsedBlocks()=0x0
LCD_X_Config()
### g_xres=800 g_yres=480 g_bits_per_pixel=32
LCD_GetSwapXY()
LCD_SetVRAMAddrEx(0, (void *)pVideoBuffer)
No LCD calibration
Cannot open ge2d!
^C
#
#
sorry,你不能使用nuvoton里的LCDConf.c,要自己适配一个。
用这个模板即可,稍微改一下就行的。
比如你制作了自己的GUIDRV_Template16_API或者GUIDRV_Template32_API,然后把这个替换LCDConf.c里的这里
if (g_bits_per_pixel == 32)
GUI_DEVICE_CreateAndLink(&GUIDRV_Template32_API, GUICC_M888, 0, 0);
else
GUI_DEVICE_CreateAndLink(&GUIDRV_Template16_API, GUICC_M565, 0, 0);
模板:
https://whycan.cn/files/members/1592/GUIDRV_Template_c.txt
为什么要自己适配呢?因为nuvoton改写了emWin源码,把库里的GUIDRV_LIN_32/GUIDRV_LIN_16改成依赖ge2d的了。
自己适配一个就不用它们,自己来实现一个在内存块里绘制点、线、面的驱动程序。当然这并不复杂,因为emWin有模板。
还记得某位大神分享的emWin演示吗?它用的就是nuvoton发布的emWin库,运行几天几夜不死机。
https://whycan.cn/t_2689.html
上面的大神不是说没有限制么,另外好像新塘芯片没有id,
现在只待大神解惑,
或者晚上闲了回去再试试大神说的这个版本,
你用这个:
https://github.com/OpenNuvoton/NUC970_Linux_Applications/tree/master/emWin
注意自己适配一下LCDConf.c文件,里面的代码有点烂。
山无棱 说:包邮不是随便说说的,哈哈
好了,明确下:大陆内的挖坑网网友包邮
冲你这句话,我决定注册个账号加入挖坑网!就等歪朵拉出S3/S3L的板子了。
页次: 1