show_bitmap frames per second?
isparks opened this issue · 7 comments
Great library, thank you.
I'm experimenting with using show_bitmap to flash between images to see how many frames/second you can expect with show_bitmap. I think I can get about 8 or 9 FPS on a micro-bit v1 before I start to see images drop out.
from ssd1306 import initialize
from ssd1306_bitmap import show_bitmap
import time
initialize()
while 1:
show_bitmap("new_logo")
time.sleep(1.0/8)
show_bitmap("microbit_logo")
time.sleep(1.0/8)
I didn't see a way to change the I2C frequency (changing it with i2c.initi(frequency=....) didn't seem to do anything.
9 FPS isn't terrible but am I missing any obvious way to do better with this method?
I guess the maximum frequency of 400KHz for I2C limits the real refresh rate to that amount more or less. With an SPI OLED device you might get better refresh rates...
Ok. I did a bit more experimentation. With these hacks to the show_bitmap I can display my 18 frames in various times based on setting the i2c frequency (now 18 frames in under 1 second)
from microbit import i2c
from ssd1306 import initialize, clear_oled, command, set_zoom, set_pos, ADDR
from ssd1306_text import add_text
import utime
def show_bitmap(filename):
#set_pos()
#command([0xae]) <-- don't clear screen, we're going to overwrite all pixels anyway!
with open(filename, 'rb') as my_file:
for i in range(0, 16):
i2c.write(ADDR, b'\x40' + my_file.read(64))
set_zoom(0) # required to invert image but if your image is not inverted you won't need this
# command([0xaf])
i2c.init(freq=400000) # <-- set clock to max rate
initialize()
now = utime.ticks_ms()
for i in range(18):
frame_num = str(i)
while len(frame_num) < 2:
frame_num = "0" + frame_num
fname = "frame_" + frame_num + ".bin"
show_bitmap(fname)
clear_oled()
end = utime.ticks_ms()
add_text(0, 2, str(utime.ticks_diff(end, now)))
With this I can get:
Frequency Time (ms) for 18 frames written
100000 2400
200000 2400 (odd, does not change, tested over and over)
300000 1200
400000 990
Omitting some of the calls in show_bitmap might leave things in a bad state for following show_bitmap calls after writing text using add_text() I find a call to set_pos() clears this problem for me.
Also, I might be imagining it but the old python speed-up trick to remove object lookups seems to make it 5-10ms faster overall:
w = i2c.write
....
for i in range(0, 16):
w(ADDR, b'\x40' + my_file.read(64))
One thing I don't understand (and this is my limited microcontroller knowledge) why this does not work:
for i in range(0, 16):
i2c.write(ADDR, b'\x40')
i2c.write(ADDR, my_file.read(64))
In normal python I believe appending the my_file.read() operation to a byte would end up doing a new allocation. I thought I'd try with an array so we read into the array over and over rather than allocate new memory for 64 bytes on every loop:
w = i2c.write
def show_bitmap(filename):
b = bytearray(64)
with open(filename, 'rb') as my_file:
ri = my_file.readinto
for i in range(0, 16):
ri(b, 64)
w(ADDR, b'\x40' + b)
set_zoom(0) # required to invert image but if your image is not inverted you won't need this
And indeed, this combination seems to save 80-90ms across all 18 frames (4-5ms/show_bitmap). Shame we can't re-use the screen buffer already declared by updating bytes 1-65 of the screen buffer (0 already holds 0x40) and then writing screen[0:65] to the I2C. Wasn't obvious to me how to do it..
Nice! Yes I remember trying the two consecutive writes and not working for me either. This code was done quite a while ago and I am no longer working with microbits, but I am glad it inspired you to create something better!
Regarding reusing position 0, you might be able to accomplish it using memoryview. And maybe making the buffer global in order not to allocate it in every iteration. Finally, use "%02d" for the 0 padding of the frame number.
Thanks @fizban99 - I was looking for memoryview also but could not find it in the micro:bit version of micropython and putting buffer outside the function I considered but worried about "stealing" 64 bytes permanently from the user of the library.
Are you taking PR's or am I better to fork? I don't know enough about microcontrollers to actively maintain.
Very much appreciate your efforts to make it. Very excited the first time I wrote a pixel to the screen!
I think it is better to fork. As I said, I am no longer working with microbits, This was an old project I published in github for anyone to use, experiment and play with. This was also used in a school workshop I gave.
I found that micropython on the microbit was quite limited, mainly because of the reduced amount memory available for the actual code. I remember finding myself having to remove all the comments, use variables with short names and such in order not to get out of memory errors. So, although having generic libraries might seem like a good idea, in practice and because of the memory limitations of the microbit people might have to actually copy and paste only those pieces of code that they are interested in, in order to make their program work and even modify the copied code to fit their specific purpose...
I believe other microcontrollers with more memory benefit more from these type of libraries. The circuit playground seemed a good alternative, but I never got a chance to play with it.
Yes, totally agree. It reminds me a lot of the original BBC Micro - I remember having to shorten variable names to make my BBC BASIC programs fit the 32K. Thanks again.