CircuitPython on Teensy 4!

tannewt

Well-known member
Thanks to @arturo182's work on the iMX RT series in Python, we can now easily support Teensy 4!

There are many issues still to work out but a bunch of stuff does work. Check the GitHub label for relevant issues here: https://github.com/adafruit/circuitpython/issues?q=is:issue+is:open+label:mimxrt10xx The main one currently is that after loading CircuitPython with the Teensy loader you have to unplug and plug the Teensy back in. That process is only needed to update CircuitPython though. (Expect to see lots of bug fixes in the coming weeks and months.) Changing the Python code is as easy as changing the files on the drive.

For an intro to CircuitPython check out this guide: https://learn.adafruit.com/welcome-to-circuitpython
It also has a troubleshooting section that can be helpful: https://learn.adafruit.com/welcome-to-circuitpython/troubleshooting
The essentials guide covers all of the core APIs: https://learn.adafruit.com/circuitpython-essentials
The API reference is here: https://circuitpython.readthedocs.io/en/latest/

Most other guides also have CircuitPython examples as well.
For real-time help we have a Discord chat (see #help-with-circuitpython and #circuitpython): https://adafru.it/discord
Adafruit's CircuitPython tech support forum is here: https://forums.adafruit.com/viewforum.php?f=60
We'll try to follow up here as well.

As I said, this is all very new code. My working branch is here: https://github.com/tannewt/circuitpython/tree/teensy4
Once it is merged into mainline (this week most likely) per-commit releases are available on S3: https://adafruit-circuit-python.s3.amazonaws.com/index.html?prefix=bin/
Proper releases are available here: https://circuitpython.org/downloads
All of our development is done in the open. Mainline is here: https://github.com/adafruit/circuitpython
We have a weekly meeting about CircuitPython on our Discord. Past meetings are available here: https://www.youtube.com/watch?v=F7YWP_iHWHk&list=PLjF7R1fz_OOUvw7tMv45xjWp0ht8yNgg0
We also have a call for thoughts on CircuitPython in 2020 here: https://blog.adafruit.com/2020/01/0...thon-in-2020-circuitpython2020-circuitpython/ See what other's posted here: https://blog.adafruit.com/tag/circuitpython2020/

Anyway, welcome to the CircuitPython community! We're excited to support Teensy 4 and will answer questions and give help below.


Moderator Edit: newer code is available on msg #42.
 

Attachments

  • circuitpython-teensy40-early.hex.zip
    265.1 KB · Views: 246
Thanks! I was wondering how it worked. I didn't think drag and drop would be possible on a Teensy 3.x/4 so I thought the only choice might have been microphyton.
 
Thanks. I posted as a comment on the video how it was done so this answers that question. Did you see Paul's comment on the last 4K of memory: https://forum.pjrc.com/threads/59028-Teensy-4-Circuit-Phython?p=226156&viewfull=1#post226156. Does the current version take this into account?

Changing the Python code is as easy as changing the files on the drive.
Ok changing files on the drive? I am assuming that after CircuitPython is installed it appears as a drive in windows. Forgive my ignorance - never used circuit python before.
 
Thanks. I posted as a comment on the video how it was done so this answers that question. Did you see Paul's comment on the last 4K of memory: https://forum.pjrc.com/threads/59028-Teensy-4-Circuit-Phython?p=226156&viewfull=1#post226156. Does the current version take this into account?

Ok changing files on the drive? I am assuming that after CircuitPython is installed it appears as a drive in windows. Forgive my ignorance - never used circuit python before.

Yep, when you power up an MCU running circuitpython, a disk drive pops up. you can drop/edit .py files on the disk and then execute the .py files.

you should get an adafruit circuit playground express and/or the M4 feather (SAMD51). You can either run cirucitpython, or use the Arudino IDE to upload your usual sketches. ;) The adafruit STM32F405 can also run micropython and has microSD.

another link for the NXP1062 feather (WIP)
https://circuitpython.org/board/feather_mimxrt1062/
It has LCD connector, sort of like on NXP EVK boards, and a microSD slot on the bottom side. But the feather footprint doesn't bring out very many pins (ADC, digital, SPI, I2C,UART). it's a proof-of-concept, not an Adafruit product (yet?)
 
Last edited:
@manitou
Thanks for the explanation. Will probably have to pick one of the up at some point - too many things on the to do list right now to start playing with that one. Still not sure which way to go - circuitpython or micropython? Not sure if you saw what I posted on another thread about someone ported mircopython at nxp to their 1062 sdk:

Interesting thing is that they already have a port of micropython for Teensy 3.x series: https://github.com/micropython/micro...sy-3.1-3.5-3.6. Haven't tried it.

As for the Teensy 4: https://forum.micropython.org/viewtopic.php?t=6783 and this: https://github.com/RockySong/micropython-rocky. RockySong is from NXP and ported it to the 1050 and 1060. So if anyone is interested
 
Thanks. I posted as a comment on the video how it was done so this answers that question. Did you see Paul's comment on the last 4K of memory: https://forum.pjrc.com/threads/59028-Teensy-4-Circuit-Phython?p=226156&viewfull=1#post226156. Does the current version take this into account?

Ok changing files on the drive? I am assuming that after CircuitPython is installed it appears as a drive in windows. Forgive my ignorance - never used circuit python before.

No, this version doesn't account for the 4k block. It's probably not an issue though because FAT likely won't use it until it's filled up. I'll keep working on the linker scripts for it tomorrow and account for it then.

@manitou
Thanks for the explanation. Will probably have to pick one of the up at some point - too many things on the to do list right now to start playing with that one. Still not sure which way to go - circuitpython or micropython? Not sure if you saw what I posted on another thread about someone ported mircopython at nxp to their 1062 sdk:

I'd recommend trying both MicroPython and CircuitPython. They both have their strengths. :) You don't have to pick just one.
 
OK, I fetched the zip file and loaded the .hex on to one of my T4's. CIRCUITPY disk popped up as expected. terminal/REPL on /dev/ttyACM0. copied my blink.py on to CIRCUITPY disk and import blink.py from REPL ... and sure enough, i have a blinking LED on my T4. here's blink.py
Code:
import board
import digitalio
import time

led = digitalio.DigitalInOut(board.D13)
led.direction = digitalio.Direction.OUTPUT

while True:
    led.value = True
    time.sleep(0.5)
    led.value = False
    time.sleep(0.5)
and thankfully i was still able to run IDE and load up a regular sketch on the T4:D

nice work
 
Thanks for this 1st image of CircuitPython for Teensy 4.0 !

First tests :
Code:
$ screen /dev/ttyACM0
Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.

Press any key to enter the REPL. Use CTRL-D to reload.
Adafruit CircuitPython 5.0.0-beta.3-6-g926375d99-dirty on 2020-01-10; Teensy 4.0 with IMXRT1062DVJ6A
>>> import gc
>>> gc.collect()
>>> gc.mem_free()
746976
>>> import os
>>> os.uname()
(sysname='mimxrt10xx', nodename='mimxrt10xx', release='5.0.0', version='5.0.0-beta.3-6-g926375d99-dirty on 2020-01-10', machine='Teensy 4.0 with IMXRT1062DVJ6A')
>>> help('modules')
__main__          busio             microcontroller   struct
_os               collections       micropython       supervisor
_pixelbuf         digitalio         neopixel_write    sys
_time             errno             os                time
analogio          gamepad           pulseio           touchio
array             gc                random            usb_hid
bitbangio         io                re                usb_midi
board             json              rtc
builtins          math              storage
Plus any modules on the filesystem

A USB meter shows 88 mA (at 5.06V) when using REPL on Teensy 4.0 connected to microUSB cable, without any running program.
 
I'm playing with python a bit. Looks like the time.sleep() function may be off by 1 millisecond.

When I run this I'm expecting to see ~100 Hz on pin 13.

Code:
import board
import digitalio
import time

led = digitalio.DigitalInOut(board.D13)
led.switch_to_output()

while True:
        led.value = True
        time.sleep(0.005)
        led.value = False
        time.sleep(0.005)

But the actual frequency is 125 Hz.

file.png

I tried several other tests. Looks like the actual delay is 1 millisecond less than it should be.


I also tried to recreate the USB serial lines/sec benchmark in python code. Not sure if this is really the best way?

Code:
import board
import time

count = 0;
prior_count = 0;
count_per_second = 0
prior_msec = time.monotonic_ns() / 1000000

while True:
        print("count=", count, "lines/sec=", count_per_second)
        count = count + 1
        msec = time.monotonic_ns() / 1000000
        if (msec - prior_msec > 1000):
                prior_msec = prior_msec + 1000
                count_per_second = count - prior_count
                prior_count = count
 
I tried several other tests. Looks like the actual delay is 1 millisecond less than it should be.

that might be a "feature" -- i saw the same thing on other circuitpython boards, and using .00501 gets you closer to 5 ms
Code:
>>> import time
>>> t1 = time.monotonic(); time.sleep(0.001); t2 = time.monotonic(); t2-t1
0.0
>>> t1 = time.monotonic(); time.sleep(0.00101); t2 = time.monotonic(); t2-t1
0.000976563
>>> t1 = time.monotonic(); time.sleep(0.005); t2 = time.monotonic(); t2-t1
0.00390625
>>> t1 = time.monotonic(); time.sleep(0.00501); t2 = time.monotonic(); t2-t1
0.00488281
>>> t1 = time.monotonic_ns(); time.sleep(0.00501); t2 = time.monotonic_ns(); t2-t1
5000000
? round up?
 
Last edited:
circuitpython and micropython performance:
Code:
            sinperf   100!   llutm  sieve  toggle   raytrace(32x32)
pyboard     14112    1722    2229  44774   9  us    4.581 s   micropython v1.17   10/9/21
STM32F405   10529    1683    1970  45437   8.8      4.33  s   micropython v1.17
STM32F405   11993    1099    1007  63049  14.5      4.887 s   circuitpython@168mhz v7 10/8/21
M4          13916    2075     977  65918  10.8      5.708 s   circuitpython v7 10/8/21
T4          21972    2963    5127         32.4      8.176     4/29/20 teensy4 v5.2.0
T4dev-cache 20752     665     977          3.1      1.884 s   1/29/20 teensy4-dev dcache v5
T4v6 dcache  4028     977     977  28992   3.28     1.33  s   10/19/20  v6  dcache
T4 v7        3052    1953     977  37994   3.2      1.6   s   10/6/21 v7
T4           1564     336    2523   9711   1.4      0.9   s   micropython v1.17-134  11/4/21
T4 C          599     117       4     58            0.726 s   -O2
rpi pico     2075     977    1953  27954  14        1.68  s   circuitpython v7 10/7/21
rpi pico    17033    3127    4847  84970  13        9.2   s   micropython v1.17  10/9/17
esp32       28397    1965    8289  40794   8       11.4   s   micropython v1.17  @240mhz 10/31/21
F767ZI      15137    4395     976 146484  19        7.5   s   circuitpyton 7.3.3 @216mhz  12/2/22
F767ZI       4590     841     905  24523   4.1      2.3   s   micropython  v1.19.1 @216mhz 12/2/22
1010         2930    1221     977          1.6      1.3   s   circuitpython 8.1.0 @500mhz  5/28/23

            hsquare   wator32x32   fibo   pystone    counter
pyboard         90 us   1.0 s     3.546 s   2230      1313250   micropython v1.17
STM32F405       88      0.5       3.539     2289      1325203   micropython v1.17
STM32F405       46      0.4       4.132     1727       541006   circuitpython v7
M4              54      0.5       4.448     1407       580218   v7
T4              90      0.5       3.9       1039       314907   4/29/20
T4dev dcache    10      1.0       0.87      6248      3301024
T4 v6 dcache    13      0.1       1.27      4995      2122479   v6  10/19/20
T4 v7           20      0.1       1.09      4739      1649476   v7  10/6/21
T4              17      0.19      0.61     12027      5836267   micropython v1.17
T4 C             0.1    0.0002    0.005            2999699985  -O2 wator approx
rpi pico        21      0.14      1.09      5055      1465394   circuitpython v7
rpi pico       156      1.8      10.1        877       546981   micropython v1.17
esp32           85      1.5       3.7        948       766144   micropython v1.17
F767ZI          79      0.57      6.5       1133       434038   circuitpython 7.3.3
F767ZI          43      0.38      1.5       4241      3036393   micropython v1.19.1
1010            14                0.097     5152      2808355   circuitpython 8.1.0

toggle circuit playground express  64.8 us   SAMD21@48MHz


   gc.mem_free() circuitpython vs micropython
    MCU        MHz   circuit   micro
    Teensy 4   600   937520   773984
    1010       500    40912
    F767ZI     216   306480   275584
    SMT32F405  168    84240   101872
    esp32      240            108928
    M4         120   154624
    pico       125   209520   187536
    cpx         48    17984
For pystone and counter bigger is better, others are elapsed times (s or us)

revised 4/29/20 T4.0 and v5.2.0
UPDATE 10/19/20: v6 T4 DCache support
update 3/11/21: rpi pico @125MHz no FPU
update 10/6/21 circuitpython v7, micropython v1.17 11/4/21
update 12/3/22 F767ZI
update 5/28/23 RT1010

circuitpython uses single precision float
scope used to measure toggle period

? more memory/cache optimizations needed ?

ref: v5 DCache hack
Teensy 4 micropython
 
Last edited:
@tannewt

One of the things I like about the T4 is the ability to overclock if I want directly from the sketch.

Poking around your repository, there's a lot there so probably missed it, it there a way to change clock speeds for the T4. Also didn't see where you were setting the clock so begs me to ask what's your default clock speed for the T4 right now.

You did a great job on getting this ported over the T4 so please hope you don't take anything as criticism especially since I haven't even tried it yet.
 
don't take my word for it but i found
Code:
ports/mimxrt10xx/peripherals/mimxrt10xx/MIMXRT1062/clocks.c
            #define BOARD_BOOTCLOCKRUN_CORE_CLOCK             600000000
so i think circuitpython is trying to run at 600mhz. micropython actually has pyb.freq() to change clock from python, though i don't know if T4 implements that?

I guess we're drifting away from Teensy topics ...
 
Thanks for pointing me to it - still on first cup of coffee and eyes have closed. Will take a look. Maybe a improvement that can be made to circuit python in the future.
 
Bugs so far :
- CircuitPython halts after :
Code:
import microcontroller
microcontroller.cpu.temperature
- REPL disconnects after pasting more than approx. 4 lines in paste mode (CTRL+E). I have to disconnect from the USB port and connect to REPL work again.
 
I'm playing with python a bit. Looks like the time.sleep() function may be off by 1 millisecond.

When I run this I'm expecting to see ~100 Hz on pin 13.

Code:
import board
import digitalio
import time

led = digitalio.DigitalInOut(board.D13)
led.switch_to_output()

while True:
        led.value = True
        time.sleep(0.005)
        led.value = False
        time.sleep(0.005)

But the actual frequency is 125 Hz.

View attachment 18734

I tried several other tests. Looks like the actual delay is 1 millisecond less than it should be.


I also tried to recreate the USB serial lines/sec benchmark in python code. Not sure if this is really the best way?

Code:
import board
import time

count = 0;
prior_count = 0;
count_per_second = 0
prior_msec = time.monotonic_ns() / 1000000

while True:
        print("count=", count, "lines/sec=", count_per_second)
        count = count + 1
        msec = time.monotonic_ns() / 1000000
        if (msec - prior_msec > 1000):
                prior_msec = prior_msec + 1000
                count_per_second = count - prior_count
                prior_count = count

Weird! I would expect it to be slower than 100Hz because time.sleep() should be pessimistic. For timing sensitive functions we usually use the peripherals via C code instead. For example, PWM will be more accurate through `pulseio.PWMOut`.

I'm actually impressed that these are the issues you have found so far. It's all very early still. :)

@tannewt

One of the things I like about the T4 is the ability to overclock if I want directly from the sketch.

Poking around your repository, there's a lot there so probably missed it, it there a way to change clock speeds for the T4. Also didn't see where you were setting the clock so begs me to ask what's your default clock speed for the T4 right now.

You did a great job on getting this ported over the T4 so please hope you don't take anything as criticism especially since I haven't even tried it yet.

Adding the ability to overclock will come at some point. We'd like it for the SAMD51 as well. (Clocked at 120MHz now but can probably do 180-200MHz.) It's not a high priority though because I want to verify we're being smart about using CPU cache and TCM first. Supporting most of our hardware APIs is a higher priority too because it will enable more of the existing examples.

If you'd like to take a crack at adding it, let me know. :) I'm always happy to help folks dig into the code.

Bugs so far :
- CircuitPython halts after :
Code:
import microcontroller
microcontroller.cpu.temperature
- REPL disconnects after pasting more than approx. 4 lines in paste mode (CTRL+E). I have to disconnect from the USB port and connect to REPL work again.

Thanks for finding those! Would you mind creating GitHub issues for them? That is how we keep track of things. Here is the url to do it: https://github.com/adafruit/circuitpython/issues/new
 
Weird! I would expect it to be slower than 100Hz because time.sleep() should be pessimistic.

I ran the same code on a Grand Central M4, same 125 Hz waveform.

Looks like a bug in Circuit Python. Seems the delay seem to be limited to an integer number of milliseconds, but the conversion rounds down to the next lower integer.

FWIW, Arduino had this exact same bug in delay() about 12 years ago (long before they used Google Code to track bugs, later migrating to github).

DSC_0684_web.jpg

file.png
 
Though why Python can't give microsecond precision delay is also a good question. Arduino supports delayMicroseconds() on all boards. The slower boards usually have a minimum delay of several microseconds, so it's far from perfect, but certainly a lot better than 1 millisecond resolution.
 
Though why Python can't give microsecond precision delay is also a good question. Arduino supports delayMicroseconds() on all boards. The slower boards usually have a minimum delay of several microseconds, so it's far from perfect, but certainly a lot better than 1 millisecond resolution.
there is a microcontroller.delay_us() in micropython
Code:
>>> import time
>>> import microcontroller
>>> t1 = time.monotonic_ns(); microcontroller.delay_us(3000); t2 = time.monotonic_ns(); t2-t1
3000000
I don't know how robust or accurate delay_us() is.
 
Back
Top