感谢楼主踩坑。我在这个基础上移植了,有两个问题不知道楼主有没有遇到过?
1. 背景音乐速度比正常的快、音调高,但画面速度是正确的(和电脑上的模拟器比较过)。APU_SAMPLE_RATE改过的,和驱动里的采样率一样。
2. 屏幕上每16列有一条竖线。
(05.25 update)问题2解决了,竖线是一直没有写入的缓存,即ppu代码里每过16个像素有一个跳过了没写。
找到如下这段代码:
for(i=sx;i<ex;i++)
{
plcd[i]=NES_Palette[ppu->dummy_buffer[i++]];
plcd[i]=NES_Palette[ppu->dummy_buffer[i++]];
plcd[i]=NES_Palette[ppu->dummy_buffer[i++]];
plcd[i]=NES_Palette[ppu->dummy_buffer[i++]];
plcd[i]=NES_Palette[ppu->dummy_buffer[i++]];
plcd[i]=NES_Palette[ppu->dummy_buffer[i++]];
plcd[i]=NES_Palette[ppu->dummy_buffer[i++]];
plcd[i]=NES_Palette[ppu->dummy_buffer[i++]];
plcd[i]=NES_Palette[ppu->dummy_buffer[i++]];
plcd[i]=NES_Palette[ppu->dummy_buffer[i++]];
plcd[i]=NES_Palette[ppu->dummy_buffer[i++]];
plcd[i]=NES_Palette[ppu->dummy_buffer[i++]];
plcd[i]=NES_Palette[ppu->dummy_buffer[i++]];
plcd[i]=NES_Palette[ppu->dummy_buffer[i++]];
plcd[i]=NES_Palette[ppu->dummy_buffer[i++]];
plcd[i]=NES_Palette[ppu->dummy_buffer[i]];
}
改成这样就好了(可能是编译器优化-O3的原因)
for(i=sx;i<ex;i++)
{
plcd[i]=NES_Palette[ppu->dummy_buffer[i]]; i++;
plcd[i]=NES_Palette[ppu->dummy_buffer[i]]; i++;
plcd[i]=NES_Palette[ppu->dummy_buffer[i]]; i++;
plcd[i]=NES_Palette[ppu->dummy_buffer[i]]; i++;
plcd[i]=NES_Palette[ppu->dummy_buffer[i]]; i++;
plcd[i]=NES_Palette[ppu->dummy_buffer[i]]; i++;
plcd[i]=NES_Palette[ppu->dummy_buffer[i]]; i++;
plcd[i]=NES_Palette[ppu->dummy_buffer[i]]; i++;
plcd[i]=NES_Palette[ppu->dummy_buffer[i]]; i++;
plcd[i]=NES_Palette[ppu->dummy_buffer[i]]; i++;
plcd[i]=NES_Palette[ppu->dummy_buffer[i]]; i++;
plcd[i]=NES_Palette[ppu->dummy_buffer[i]]; i++;
plcd[i]=NES_Palette[ppu->dummy_buffer[i]]; i++;
plcd[i]=NES_Palette[ppu->dummy_buffer[i]]; i++;
plcd[i]=NES_Palette[ppu->dummy_buffer[i]]; i++;
plcd[i]=NES_Palette[ppu->dummy_buffer[i]];
}
}
F1C100s上写数据到Nand Flash,LittleFS文件系统。保存的数据有很多bit错误。
调试发现和LittleFS层无关,不经过文件系统直接写Flash也会出错。
花了一天时间换不同的数据测试写Flash,情况非常奇怪,写入比较“规律”的数据则很少出错,写入比较“乱”的数据则很多错误。
是坏块的问题吗?不像,因为出错的数目远超过正常的坏块数。
而且写入不同的数据,出错的地方也不一样。每次都是一个bit错误,应当是1的bit变成了0。
…………
…………
在快要下班的时候,换块Flash试试。咦?Flash ID怎么不一样了?
换了Flash竟然完全正常了!!
原来是从代理商那拿了两种样品,两种的型号只差一点点,代理商说用起来是一样的。我照着一个规格书实现了驱动。
但其实两种型号,一个是内置4-bit ECC,一个是需要host自己做8-bit ECC。
我一开始焊的是不带ECC的那种,所以发生好多bit错误。换成内置ECC的就全好了!
…………
愚蠢的一天啊[哭][哭][哭]
以前没做过ECC相关的,不知道Nand Flash内部其实会有这么多错误,非常依赖ECC。
GNU Arm Embedded Toolchain 官网 https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads
搜到了ST的Smart Reset IC系列专门干这个事情 https://www.st.com/en/reset-and-supervisor-ics/smart-reset-ics.html,型号为STM65xx/SR1/SR2。有长按1个键的,有同时长按2个键的。长按延时几秒到十几秒后触发reset。
本来以为用几个分立器件能搭起来,但似乎没那么简单。又要长按开机,又要能长按更长时间reset,不太好做。
4楼说的watchdog电路也是要软件里喂狗的,软件如果有bug,比如在死循环里喂狗,那外部watchdog也没办法。
5楼的cat809是电压检测,供电电压低于多少一小段时间再恢复,则触发reset。
另外还想请教各位大佬一个问题:在sys-dram.c中声明了一个结构体变量,在声明的同时赋值,但是这样cpu就不知道跑哪去了,改成下面这样就好了
https://whycan.cn/files/members/1924/03.png
但page_size放外面赋值总觉得有点刺挠:),想问问各位大佬有啥解决办法吗。
把几个变量赋值顺序调成和结构体声明里的一样试试?
找找官方文档,STM8不知道,STM32的ADC上电后要等待校准
//Enable ADC reset calibration register
ADC_ResetCalibration(adc);
//Check the end of ADC reset calibration register
while (ADC_GetResetCalibrationStatus(adc)) {}
//Start ADC calibration
ADC_StartCalibration(adc);
//Check the end of ADC calibration
while (ADC_GetCalibrationStatus(adc)) {}
这两天临时出去有事了,所以没能按时试。
经测试RTT用GCC编译,没有这样的问题。WDT正确的工作了。rt_kprintf("start wdg test----------------------------------\n");
struct A* a = (struct A*)malloc(sizeof(struct A));
// ...
free(a);
// a被释放,指向的地址重新分配后写入了其他内容
// ...
a->func(); // 野指针使用,危险!
rt_kprintf("end wdg test----------------------------------\n");
我那个只是示意代码。是要向那块被释放的地址里写入合适的数据,使a->func的值为0xdba00000 ~ 0xffff4040。
继续研究。C++对象内存布局虽然没有固定标准,是编译器实现的,但基本是按照《深度探索C++对象模型》的样子。
一个带有虚函数的类的指针,指向的内存区域最开头应该是类的虚函数表指针。该指针指向一个数组,数组成员是函数指针。
在上面的例子中,当调用p->g();时,由于p指向的内存区域全是0,所以被当做是地址0x0处开始为虚函数表。
g()是类的第二个虚函数,所以把地址0x4当做函数指针。
打印出地址0x0附近的内存看:
0: 0xea00000d
1: 0xe59ff014
2: 0xe59ff014
3: 0xe59ff014
4: 0xe59ff014
5: 0xe59ff014
6: 0xe59ff014
7: 0xe59ff014
8: 0x80000260
看到0x4地址处的内容是0xe59ff014,接下来会把0xe59ff014当做函数地址去调用。就是经过这个非法函数调用后,看门狗失效并且死机!
复现此bug的代码可以再精简为:
typedef void (*FuncPtr)();
FuncPtr f = (FuncPtr)0xe59ff014;
f(); // 触发看门狗bug
继续做实验发现,并不是只有这一个特殊的地址调用会触发bug,实际上有很大范围的地址都能引起看门狗无效。
从 0xdba00000 ~ 0xffff4040,这个区域内的地址,如果赋值给函数指针来调用,都会引发看门狗失效bug!!小于0xdba00000或大于0xffff4040的地址调用,则能正常死机并触发看门狗。(具体地址会变化,还跟CPU频率有关,不是完全精确的范围。)
这个范围是很大的,也就是说如果程序写错了,还是会有挺大的可能性遇到此bug。
比如考虑下面这个常见的野指针访问代码,触发看门狗bug的概率并不小。
struct A
{
int data;
void (*func)();
};
A* a = (A*)malloc(sizeof(A));
// ...
free(a);
// a被释放,指向的地址重新分配后写入了其他内容
// ...
a->func(); // 野指针使用,危险!
刚调了好几个小时的灵异bug,现象是启动后有时候会死机。这时候已经启动了看门狗,但狗没有起作用,系统一直卡在那不reset。
最后调出来是个空指针错误。空指针毕竟是编程比较常见的错误,空指针能导致看门狗无效,F1C100s的这个bug也是有点严重。
一番精简之后,找到了最小复现代码:
只要用一个基类指针,指向一块内容为0的区域,调用基类的第二个虚函数,就很容易使看门狗失效并死机。
这个bug可能跟编译器的具体实现(C++类对象内存布局)有关,我用的GCC,其他编译器不一定是这样。
class Base
{
public:
virtual void f() = 0;
virtual void g() = 0;
};
// main():
// 设置看门狗,5s的时长
uint8_t empty[1024];
memset(empty, 0, sizeof(empty));
Base* p = (Base*)∅ // 野指针,指向了一块内容全为0的内存
feedWatchdog(); // 喂狗
mdelay(10); // 等待超过1500ms则不出bug,等待0ms也不出bug,这之间都会遇到bug
p->g(); // 调用基类的第二个虚函数(第一个没问题),从这里就死机了。看门狗无效。
求赐教,这两个寄存器是什么功能?
https://whycan.cn/files/members/1964/test.png
Wait State意思不知道。
Burst Length大约是每次传输数据,在总线上连续传几个“Data Width”那么大的数据。
对于从内存到内存,Burst Length设为4直接就可以用。
对于从内存到外设或外设到内存的DMA,如果Burst Length设为4,外设的FIFO一般有trigger level也要对应改。比如trigger level默认为1,表示FIFO里只要有1个数据就发送DRQ,让DMA来拷走。当Burst Length为4,trigger level也要>=4。
反过来,如果是从内存到外设拷数据的,FIFO的trigger level需要 <= (FIFO总长度 - 4)。
cpu把数据写到内存,然后交给外设或dma,要用clean;
cpu读取由外设或dma放在ram的数据之前,要invalid;
dcacheinvalid和dcacheclean指令,要么是针对mva的,要么是针对cache-line的,要么就整个dcache
所以MMU_InvalidateDCacheArray函数封装了mva的遍历。
对ram变量也有cache-line对齐的要求,即变量或数组的大小和地址必须32字节对齐,否则会破坏变量。
看了这几个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;
}
}
1. 初步调通SPI DMA,成果喜人。
SPI Tx和Rx都用上DMA,接示波器看CLK信号。当通过寄存器设置SPI速率设为100MHz的时候,CLK信号实际能跑到96MHz。SPI速率设为50MHz的时候,实际CLK为49.xMHz。
(用XBoot不管怎么设速率,实际的只能到25MHz。一方面是没用DMA,一方面是XBoot的代码不够优化。)
2. 实验用DMA加持的SPI驱动ST7789串口屏。240x240分辨率、RGB565,SPI跑满50MHz,屏幕帧率可以到54。非常满意了。
实验时是用杜邦线接的屏,也许走线好一点的话还能支持更高速率。我现在是只能跑到50MHz了,多1M显示就开始乱了。
3. 实验DMA做内存拷贝,结果如下。DMA拷贝内存,速度并没比memcpy快多少。
有几个参数能影响DMA拷贝速度:1) DMA的种类,用NDMA还是DDMA;2) data width 3) burst length。
而memcpy速度受CPU主频影响(一点点)。
拷贝128KB数据比较:
memcpy 2.4ms(640MHz主频)
DDMA 1.9ms(data width = 32-bit,burst length = 4)
NDMA 2.6ms(data width = 32-bit,burst length = 4)
DDMA拷贝内存最快能到memcpy的1.25倍。
另外,发现DDMA拷贝较大的数据会出错,比如拷贝1MB数据,第一次成功,第二次就从1020KB开始数据不正确了。可能是缓存的原因,待研究。
https://s.taobao.com/search?q=HY951180A
https://item.szlcsc.com/35646.html
都是 HY951180A, 淘宝卖4块, LC卖13.5块?
其实LC上要看批量价格8.58多含税,淘宝是仿品+不含税,差的就没那么大了。
我也对这点感到好奇,毕竟打开某宝也找不到这个价格的F1C200s。
顺带一提,我翻了一下博客,发现博主就是之前搞信用卡上跑Linux的老哥:My Business Card Runs Linux。在这个帖子里面写了F1C100s是$1.42。
原来是这位。帖子里说,是全志主动联系他,白给了些F1C200s样品。
root@yu-virtual-machine:/home/yu/lichee/lvgl_f1c100s# make
arm-eabi-objcopy -v -O binary build/firmware.elf build/firmware.bin
copy from `build/firmware.elf' [elf32-littlearm] to `build/firmware.bin' [binary]
Make header information for brom booting
tools/mksunxi/mksunxi: 1: tools/mksunxi/mksunxi: Syntax error: ")" unexpected
Makefile:140: recipe for target 'build/firmware.bin' failed
make: *** [build/firmware.bin] Error 2各位大佬, 我用这个编译gcc-linaro-5.3.1-2016.05-i686_arm-eabi.tar
出现了以上的错误,请教下是什么问题?
makefile贴一下,多写了个括号吧?
这个可以,理论上用libusb写的功能都可以实现,所以完全可以把sunxi-fel用网页重写一遍。
WebUSB是Google推的,还没成为正式标准,所以只有Chrome支持。标准和API见 https://github.com/WICG/webusb
TF卡隔壁圆的是喇叭?一直想找这种喇叭, 请问喇叭型号是什么?
五向开关。
所以你是想找贴片的喇叭?这种贴片蜂鸣器可以当音质很差的喇叭用 https://item.szlcsc.com/96500.html
CS引脚加个上拉试试?把CS引脚用10K电阻接到3.3V。
看了几种开发板,CS都是有上拉的。
root@hj-virtual-machine:/home/hj# sunxi-fel ver
AWUSBFEX soc=00001663(F1C100s) 00000001 ver=0001 44 08 scratchpad=00007e00 00000000 00000000
root@hj-virtual-machine:/home/hj# sudo sunxi-fel -p read 0x20000000 10240 read.dat
root@hj-virtual-machine:/home/hj# sunxi-fel ver
AWUSBFEX soc=00001663(F1C100s) 00000001 ver=0001 44 08 scratchpad=00007e00 00000000 00000000
root@hj-virtual-machine:/home/hj#
如果read内存10kB可以,spiflash-read不行,那可能还不是USB的问题。
sunxi-fel spiflash-info 试试。如果涉及flash的都出错,那把flash部分的原理图贴一下看看?
可以参考这个外国人的作品,用NFC存储个人信息、点亮LED的名片 https://www.instructables.com/id/PCB-Business-Card-With-NFC/
读写都不行
root@hj-virtual-machine:/home/hj# sunxi-fel ver
AWUSBFEX soc=00001663(F1C100s) 00000001 ver=0001 44 08 scratchpad=00007e00 00000000 00000000
root@hj-virtual-machine:/home/hj# sudo sunxi-fel -p spiflash-read 0x0 1024 read.dat
usb_bulk_send() ERROR -1: Input/Output Error
root@hj-virtual-machine:/home/hj# sunxi-fel ver
usb_bulk_send() ERROR -1: Input/Output Error
不是spiflash-read,就是read/readl读内存。看不涉及flash的操作有没有问题;读小块内存、读大块内存有没有区别。
自己画了个板子,usb能识别,但是烧写报错是怎么回事
root@hj-virtual-machine:/home/hj# sunxi-fel ver
AWUSBFEX soc=00001663(F1C100s) 00000001 ver=0001 44 08 scratchpad=00007e00 00000000 00000000
root@hj-virtual-machine:/home/hj# sudo sunxi-fel -p spiflash-write 0 u-boot/u-boot-sunxi-with-spl.bin
usb_bulk_send() ERROR -1: Input/Output Error
用sunxi-fel的read和write读写大块内存试试,看是不是USB传输数据量比较大时就出错。
有没有极薄的电子纸?
以前看到好几个国外玩家做PCB名片的,都是插USB供电。比较有意思的有这个 StyloCard https://mitxela.com/projects/stylocard,在名片上做了个MIDI键盘。
https://whycan.cn/files/members/1228/agrgdd.png
还有1个SOC的输出,可不可以即接到耳机孔,也接到功放呢?
如果是想要同时接功放和耳机,一般会让插耳机时喇叭静音,需要用到图中耳机插座的1和5脚。
未插耳机时1和5脚是内部有个铜片是闭合的,当耳机插入时把铜片顶开,1和5脚断开。利用这个信号改变功放芯片的使能,使喇叭静音。同时,主控端也可以利用这个信号检测耳机插入,做到耳机和外放有不同的音量控制。
我看到这样一个原理图,将SOC的HPR 和 HPL,通过串联电容和电阻,汇聚到了一点,然后再通过电容电阻接到了功放上面。
我画个图示意一下:
https://whycan.cn/files/members/1228/%20(2).png
我想,我如果有2个SOC,我是不是可以将两个SOC的HPR HPL都接到一个3.5MM耳机座上。像这样。
https://whycan.cn/files/members/1228/asvbsSdfdd.png
不知道这样可不可行,对音频这部分不太懂...
图1电路的作用是双声道转单声道,即把两个声道的信号取平均值。
按图2,两个Soc输出的左声道用电阻接在一起,当只有一个Soc输出声音时,另一边不一定处于什么状态。如果是HPR1是高阻态,则最后输出的信号应该约等于HPR2;如果HPR1是静音但仍在输出,则最后的信号约为HPR2的一半。所以可能会有音量不确定的情况。
没有休眠模式
你可以试试用这个汇编进入类似于休眠状态,但功耗也不会非常低。
__asm__ __volatile__("mcr p15, 0, %0, c7,c0,4" :: "r"(0));
这是ARM926EJ-S手册上提到的 Dynamic power management (wait for interrupt mode) 。我只简单试了一下,在我的板子上低了10mA。没试过唤醒:D。
请问,f1c100s用电池供电的话
1. 电池电量怎么检测,用LRADC吗?大概是个什么用法?
2. 像电池供电设备长按开关机是怎么实现的?
3. USB给锂电池充电求便宜好用的芯片
这几个我正都有用到,说说做法:
先说长按开关机
S1是电源键,按下后Q1导通,系统上电。系统启动后把PWD_EN引脚置高,就能保持开机。
S1的按下状态可以通过BUT_POWER检测到,长按关机就是把PWD_EN置低。
我这里是需要较大电流放电,所以Q1用了功率MOS管。也可以直接接DC-DC的使能。
电量检测是把VIN接两个电阻分压,分到1.xV以下,接LRADC。LRADC有寄存器可以读到电压值,精度比较粗,勉强够用。
便宜的单节锂电池充电用4056。
谢谢指点。 1mv档位, 8格算8mv的话, 噪声有1/8。
实际上我用到20mv档位, 在这个档位,也有10mv的噪声,勉强能用。默认垂直位置不居中,如第一个图,向上偏移了一格,是故障吗 ?
不知道你是用来测什么。在大多数应用里,根本不需要测到这个精度。不管是1mV还是10mV,都是很小的。
实际上我平时用的示波器最小一档精度也就20mV,20mV档时噪声很多,只能看个大概。很少用的着100mV以下档位。
你可以先把右上角“耦合-交流”切换成“直流”,调到1V档位,直接量电池的正负极。波形如果很直,没有噪声,那就基本没问题。再慢慢调到更高精度档位,看什么时候出现明显的噪声。
至于在1mV档位下,向上偏了0.5mV……实在太小了,就忽略吧。
qiefei 说:the brom codes are on the github!
在哪里,有没有地址?
uboot: go 0xffff0020
这样进入FEL模式,可以用sunxi-fel操作SPI Flash吗?我在自己的代码中跳转到0xffff0020,能进入FEL模式,但操作Flash失败。
之前研究了一下下最后也没成功 https://whycan.cn/t_2072.html
手册可以分享一下吗?一直在用C600的手册做C100s。
@hoel I develop for F1C100s on OSX, but the toolchain configuration may depend on what you are building. I only build barebone code and XBoot, no Linux or U-Boot.
You can first try the barebone code minimal_f1c100s_framebuffer.zip from this post: https://whycan.cn/t_1457.html.
1. Download the official gcc-arm-none-eabi from https://launchpad.net/gcc-arm-embedded/+download. I'm using an older version gcc-arm-none-eabi-5_2-2015q4. Extract and add `gcc-arm-none-eabi/bin` to PATH.
2. Extract minimal_f1c100s_framebuffer.zip. Edit `Makefile`, change `CROSS_COMPILE` to `arm-none-eabi-`.
3. `cd tools/mksunxi`, delete the Linux version `mksunxi` and make the OSX version.
3. Run `make` in `minimal_f1c100s`. The build succeeds if you see output "The bootloader head has been fixed".
4. `git clone https://github.com/Icenowy/sunxi-tools.git -b spi-rebase`, then build the OSX version of sunxi-tools.
5. Run `sunxi-fel -p spiflash-write 0 build/firmware.bin` to download the firmware to F1C100s.