computers

Reading I2C Registers from Circuitpython

I had some code that used the Python SMBus library on a Raspberry Pi to drive an I2C comms board, and I wanted to use it from an RP2040 (as used on the Raspberry Pi Pico) board running Circuitpython.  It turns out it isn’t a straight forward swap of one set of I2C support for another!

If I was using Micropython, then I found a “translation layer” to provide the same API on top of the Micropython machine.I2C code here: https://github.com/gkluoe/micropython-smbus.

But Circuitpython appears to have a different approach again to I2C as far as I can see.  There are (as you might expect) some great tutorials on getting up and running with I2C on Circuitpython, but I’ve not been able to personally translate that over into what to do if you want to access specific registers on a device.  As far as I can see, the examples are for relatively simple “register-less” devices. I think the assumption is that if you want to talk to anything more complicated then you’d go off and dig out an appropriate library (which is a fair assumption in most cases!).

As far as I can make out there are two levels at which you could be accessing I2C from Circuitpython:

The second is meant to be a helper class to take away some of the complexities of managing the I2C bus yourself.  In fact the documentation for busio.I2C states:

“Using this class directly requires careful lock management. Instead, use I2CDevice to manage locks.”

But I was trying to replace code using write_i2c_block_data and read_i2c_block_data to access a specific register of a device, and there doesn’t seem to be an equivalent in either of these that I could find.

In the end I used the following code to read and write to I2C registers. I don’t know if this is the right way, but it works for me at present. I think the key thing I was missing is that to read a register, you first need to write the required register out to the device so it can respond (this is standard I2C stuff, but only once you know it!).  Hence, I’m using the “writeto_then_readfrom” function for reading.  But you only need the “writeto” when writing, but again you need to ensure the first value in your data being written is the register to write to.

i2cbus = board.I2C()

def i2c_read_reg(i2cbus, addr, reg, result):
  while not i2cbus.try_lock():
    pass
  try:
    i2cbus.writeto_then_readfrom(addr, bytes([reg]), result)
    return result
  finally:
    i2cbus.unlock()
    return None

def i2c_write_reg(i2cbus, addr, reg, data):
  while not i2cbus.try_lock():
    pass
  try:
    buf = bytearray(1)
    buf[0] = reg
    buf.extend(data)
    i2cbus.writeto(addr, buf)
  finally:
    i2cbus.unlock()

I believe it should be possible to use the adafruit_bus_device class to handle the bus locking and so on but I couldn’t get that working, so I used busio.i2c directly.

So this all seems to work for my situation, but do let me know if you know better or if you can tell me what I’m missing with the existing tutorials and libraries!  I still don’t know a lot about Circuitpython and I’m still working out I2C if I’m honest too!

Here are some tutorials and guides that talk a bit more about the I2C hardware and software protocols, including accessing registers on devices.

Kevin

computers · maker

Arduino Nano Every I2C and SPI Displays

In this part of my series on the Arduino Nano Every, I take a look at a couple of common displays to see how they work with the Nano Every.  In short – pretty well.  Read on for how I hooked up I2C OLED and an SPI TFT displays.

I’m using the standard Arduino environment as described in my introduction to the Nano Every.

I2C OLED Displays

I thought I’d try an OLED display first.  There are some very common ones using the SSD1306 as the interface chip that hook up to the I2C bus.  The Nano Every maintains the original Nano’s I2C pins:

  • A4 = SDA
  • A5 = SCL

You have to watch out when wiring up your OLED display as there are a number of different combinations of pinout!  In my own collection I have displays with different arrangements for VCC and GND for example.

Nano Every OLED_bb

The basic circuit is as shown on the left.  It needs just four connections: VCC (5V), GND, SCL (A5), SDA (A4).  Also above you can see how you could wire it up for two displays, but one of them will need to have the I2C address changed, before you can do this, but more on that in a moment.

I’m using the Adafruit graphics library for the SSD1306 range of displays.  The default is to use hardware I2C so all that is required is to set the I2C address to match the display you have.  My displays come with a default address of 0x3C (although it is marked as 0x78 on the actual display if it is marked at all – the this is due to the least significant bit not be part of the address itself, but it is often included – so 0x3C is the address, but “on the wire” it has to be shifted one bit to the left, which then “looks” like 0x78… but anyway…).

I’ve grabbed the example from: Examples -> SSD1306 -> ssd1306128x64_i2c.  The line that sets the address is the following one:

#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32

The datasheet referred to is for Adafruit’s own displays where the 128×64 is on 0x3D and the 128×32 is on 0x3C.  For most other displays the default is probably like mine – 0x3C.

I’ve attempted to run two displays before, but an Arduino Uno or Nano just doesn’t have enough memory to support two complete frame buffers for two 128×64 displays.  I’ve managed to configure them as 128×32 displays and got two of them running, but that is all.  So I was keen to try with the Nano Every as it has more memory!

Some displays have a jumper on the back to let you change the address, but on mine they are a fiddly surface mount resistor which needs desoldering from one link and moving to another.  Some displays aren’t configurable at all. I have some of both, as shown below.

IMG_5624

You can perhaps spot how my dodgy soldering has just about managed to move the jumper from the 0x78 position to the 0x7A position! Mind you, it took a couple of goes!

In terms of showing it working, I replicated a couple of lines of the Adafruit demo code to get as far as showing the logo on the second screen.  I didn’t change anything else.  At the top of the file, the following lines need to be added.

#define SCREEN2_ADDRESS 0x3D ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display2(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

Then in the setup() code, I added the following.

if(!display2.begin(SSD1306_SWITCHCAPVCC, SCREEN2_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
} else {
display2.display();
}

This means that if a second display (on address 0x3D) is found then it will show the initial logo and then nothing else for that display, whilst the code goes on the run through the full demo on the other display (on address 0x3C).

IMG_5623

ST7735 SPI TFT Display

Next I turned to the cheap TFT displays I’ve looked at before.

Note the display in the Fritzing diagram below is just for illustration as I don’t have an exact match (the observant among you might notice I have one extra pin on mine!). Be sure to check and double check the required pins and expected voltage level for your display module.

Nano Every TFT_bb

One of the differences between the Nano Every and the original Nano is that the SPI SS pin is on pin 8, not pin 10. This means the required pin connections for my display are as follows:

 1-VCC - 5V
2-GND - GND
3-N/C
4-N/C
5-N/C
6-N/C
7-CLK - 13 (SCLK)
8-SDA - 11 (MOSI)
9-RS - 10 (D10)
10-RST - 9 (D9)
11-CS - 8 (D8)

I’m using the Adafruit ST7735 library this time.  Taking the code from:

Examples -> Adafruit ST7735 and ST7739 Library -> graphicstest

the pins must be configured at the top as follows:

#define TFT_CS   8
#define TFT_RST 9
#define TFT_DC 10

Then all the graphics primitives from the Adafruit GFX Library should be available.

IMG_5627

Conclusion

I was anticipating some issues with the displays, but actually they worked pretty well.  Most of this is down to the fact that the Wire (I2C) and SPI Libraries are part of the “built-in” library support provided by the Arduino environment and the low-level drivers for the displays provided by Adafruit are written well with minimal reliance on hardware specific features of any single microcontroller.

Kevin

 

computers · maker

Arduino Nano Every Analog Inputs

The next part in my series on the Arduino Nano Every, looks at how to access the additional 6 analog inputs that can be found scattered around the digital IO pins.

As I mentioned in my introductory article, the Arduino Nano Every works hard to be “pin compatible” with the original Arduino Nano.  This means that it supports 8 analog inputs, labelled A0 to A7, all along one side of the board.  But the ATmega4809 actually has 18 analog inputs, and the additional ones can be found as extra functions on some of the other pins.  Reading the data sheet, we can see that the 16 analog inputs are mapped as follows (I’ve included the Arduino pin numbers for reference):

  • AIN0 -> PD0 – A3
  • AIN1 -> PD1 – A2
  • AIN2 -> PD2 – A1
  • AIN3 -> PD3 – A0
  • AIN4 -> PD4 – A6
  • AIN5 -> PD5 – A7
  • AIN6 -> PD6
  • AIN7 -> PD7 – AREF
  • AIN8 -> PE0 – D11
  • AIN9 -> PE1 – D12
  • AIN10 -> PE2 – D13
  • AIN11 -> PE3 – D8
  • AIN12 -> PF2 – A4
  • AIN13 -> PF3 – A5
  • AIN14 -> PF4 – D6
  • AIN15 -> PF5 – D3

Note that AIN6 (PD6) is not connected and that AIN7 (PD7) is used for AREF.

To allow the Arduino environment to support the extra analog inputs, there are a few lists and structures in the Arduino “pins” definition file for the Nano Every (the same place that we tweaked to add serial ports last time):

...../arduino15/packages/arduino/hardware/megaavr/1.8.5/variants/nona4809/pins_arduino.h

There are several entries in this file that determine the mapping of Arduino pin labels to MCU ports and pins.  However there is a slight issue.  The pins for A0 to A7 are contiguous in the Arduino’s pin mappings – they map onto (internal) pins 14-21.  This means that you can do things like the following quite happily and it will all work:

for (int i=0; i<8; i++) {
aval[i] = analogRead(A0+i);
}

So to extend the range of analog inputs to include A8 to A13, ideally we’d extend the original range so that “A0+8 = A8” but these (internal) pin numbers are already taken for other functions (in this case the I2C and USB serial connections).  One option is to “shuffle” these other functions down to make room.  The other is to accept that “A7+1 != A8” and leave a gap. In what follows I’ve opted to leave a gap…

So here are the changes we need to make to pins_arduino.h.

There is a definition of NUM_ANALOG_INPUTS that isn’t really the number of analog inputs, but is used to determined the “highest numbered analog channel” used.  This is currently set to 14, but needs updating to 16.

#define NUM_ANALOG_INPUTS 16

We now need to set up some new pin definitions (e.g. PIN_A8 and A8 onwards) as follows. This is where the “gap” first becomes apparent as “internal” pin numbers 22 to 25 are already in use.

#define PIN_A0 (14) // AIN3
#define PIN_A1 (15) // AIN2
#define PIN_A2 (16) // AIN1
#define PIN_A3 (17) // AIN0
#define PIN_A4 (18) // PF2 / AIN12
#define PIN_A5 (19) // PF3 / AIN13
#define PIN_A6 (20) // AIN5
#define PIN_A7 (21) // AIN4

// Additional analog inputs
// Arduino pin numbering starts after OFFICIAL pin
// numbers end...
// Note: AIN6/AIN7 don't make it to Arduino pins
#define ANALOG_INPUT_EXTRA 26
#define PIN_A8 (ANALOG_INPUT_EXTRA) // AIN8
#define PIN_A9 (ANALOG_INPUT_EXTRA+1) // AIN9
#define PIN_A10 (ANALOG_INPUT_EXTRA+2) // AIN10
#define PIN_A11 (ANALOG_INPUT_EXTRA+3) // AIN11
#define PIN_A12 (ANALOG_INPUT_EXTRA+4) // AIN14
#define PIN_A13 (ANALOG_INPUT_EXTRA+5) // AIN15

static const uint8_t A0 = PIN_A0;
static const uint8_t A1 = PIN_A1;
static const uint8_t A2 = PIN_A2;
static const uint8_t A3 = PIN_A3;
static const uint8_t A4 = PIN_A4;
static const uint8_t A5 = PIN_A5;
static const uint8_t A6 = PIN_A6;
static const uint8_t A7 = PIN_A7;
static const uint8_t A8 = PIN_A8;
static const uint8_t A9 = PIN_A9;
static const uint8_t A10 = PIN_A10;
static const uint8_t A11 = PIN_A11;
static const uint8_t A12 = PIN_A12;
static const uint8_t A13 = PIN_A13;

Then there are four structures that map these internal pin numbers onto various properties of the hardware.  Each of these needs our additional six new entries adding in at the end.

const uint8_t digital_pin_to_port[] = {
... skip "internal" pins 0 through to 25 ...
PE, // 26 PE0/AI8
PE, // 27 PE1/AI9
PE, // 28 PE2/AI10
PE, // 29 PE3/AI11
PF, // 30 PF4/AI14
PF, // 31 PF5/AI15
};

const uint8_t digital_pin_to_bit_position[] = {
... skip "internal" pins 0 through to 25 ...
PIN0_bp, // 26 PE0/AI8
PIN1_bp, // 27 PE1/AI9
PIN2_bp, // 28 PE2/AI10
PIN3_bp, // 29 PE3/AI11
PIN4_bp, // 30 PF4/AI14
PIN5_bp, // 31 PF5/AI15
};

const uint8_t digital_pin_to_bit_mask[] = {
... skip "internal" pins 0 through to 25 ...
PIN0_bm, // 26 PE0/AI8
PIN1_bm, // 27 PE1/AI9
PIN2_bm, // 28 PE2/AI10
PIN3_bm, // 29 PE3/AI11
PIN4_bm, // 30 PF4/AI14
PIN5_bm, // 31 PF5/AI15
};

const uint8_t digital_pin_to_timer[] = {
... skip "internal" pins 0 through to 25 ...
NOT_ON_TIMER, // 26 PE0/AI8
NOT_ON_TIMER, // 27 PE1/AI9
NOT_ON_TIMER, // 28 PE2/AI10
NOT_ON_TIMER, // 29 PE3/AI11
NOT_ON_TIMER, // 30 PF4/AI14
NOT_ON_TIMER, // 31 PF5/AI15
};

Finally there is a macro that is used to spot if the analogRead function has been given a “An” label or a “Dn” number and maps that onto the appropriate analog input channel on the chip.  That needs to be updated in two ways:

  • The extra six analog inputs need the appropriate analog channels (AINxx) defining and adding.
  • The “translation” macro, digitalPinToAnalogInput(p), needs to take into account the gap in the analog input range.
const uint8_t analog_pin_to_channel[] = {
3,
2,
1,
0,
12,
13,
4,
5,
// Start of extra analog inputs
8,
9,
10,
11,
14,
15
};

#endif

#define digitalPinToAnalogInput(p) ((p < ANALOG_INPUT_OFFSET) ? analog_pin_to_channel[p] : \
(p < ANALOG_INPUT_EXTRA) ? (analog_pin_to_channel[p - ANALOG_INPUT_OFFSET] ) : (analog_pin_to_channel[p - ANALOG_INPUT_EXTRA + 8] ) )

The digitalPinToAnalogInput macro basically says:

  • IF provided pin is in digital range, just use the pin number directly as the index into the analog_pin_to_channel array.
  • ELSE IF provided pin is in the original A0 to A7 range, then subtract the number for A0, then use as an index into the analog_pin_to_channel array.
  • ELSE provided pin is in the new extended range, so subtract the number for A8 and add 8, and use that as the index into the analog_pin_to_channel array.

In all cases we should end up reading out which AINxx channel to use from the analog_pin_to_channel array and it will work for all the following input ranges:

  • 0 to 13 – analog inputs A0 to A13.
  • A0 (14) to A7 (21) – analog inputs A0 to A7.
  • A8 (26) to A13 (31) – analog inputs A8 to A13.

With those changes, A0 through to A13 should now all be usable, although remember that A8 does not directly follow (numerically) A7.

Here is some test code to read all 14 analog inputs.

void setup() {
Serial.begin(9600);
}

void loop() {
for (int i=0; i<8; i++) {
int aval = analogRead(A0+i);
Serial.print(aval);
Serial.print("\t");
}
for (int i=0; i<6; i++) {
int aval = analogRead(A8+i);
Serial.print(aval);
Serial.print("\t");
}
Serial.print("\n");
delay(100);
}

Conclusion

I’d really prefer to have made the range of “internal” pin values contiguous but I don’t know what else would break.  To be perfectly honest, the above may break other functions of the Arduino in quite spectacular ways – I just don’t know yet!  But it does seem to give access to all 14 analog inputs.

Once again, MegaCoreX seems to already include all this and a lot more beside.  In fact it appears to support all 16 analog inputs, even though two of them don’t actually appear as pins on the Nano Every.

Kevin

 

computers · maker

Arduino Nano Every Serial Ports

Having now introduced the Arduino Nano Every in my previous post, this time I thought I’d turn to the additional serial ports.

IMG_5615

As I mentioned last time, the “out of the box” Arduino experience for the Nano Every has Serial attached to the USB serial line and Serial 1 representing the hardware UART on D0/D1.  These are mapped onto the ATmega4809 MCU as follows:

  • Serial -> UART 3
  • Serial 1 -> UART 1 -> D0/D1

The ATmega4809 UARTs

As you might now be thinking, what happened to UART 2?  Well there are indeed four UARTs on the ATmega4809, so UART 0 and 2 pop out on the following IO pins:

  • UART 2 -> D3 (RX) / D6 (TX)
  • UART 0 -> D7 (RX) / D2 (TX)

In the case of UART2 this is via the first “alternative function” mapping of the IO pins.  Many of the IO pins carry alternative functions which are selected using the built-in multiplexer configuration which is all detailed in section 4 of the ATmega4809 datasheet: “4. I/O Multiplexing and Considerations”.  The full list of functions for each pin is detailed in the table in section 4.1.

The PORTMUX register itself is detailed in the table in section 15.2: “Register Summary – PORTMUX”.  There are two bits in the USARTROUTEA register for each of the serial ports to select between “Default” (00), “ALT 1” (01), and “None” (11).  (10) is reserved for future use in each case.

The general pattern is that four of the ATmega4809 ports support a UART with TX/RX on Px0/Px1 respectively, with the “ALT 1” alternate pin mapping on Px4/Px5.  UART 0 uses PORTA; UART 1 uses PORTC; UART 2 uses PORTF; and UART 3 uses PORTB.

The full listing of Arduino pins to serial port function to ATmega4809 pins is given below.

Arduino Pin Function ATmega4809 pin
D2 0TXD   PA0
D7 0RXD   PA1
  0TXD ALT1 PA4
  0RXD ALT1 PA5
(D9) 3TXD   PB0
(D10) 3RXD   PB1
USB 3TXD ALT1 PB4
USB 3RXD ALT1 PB5
  1TXD   PC0
  1RXD   PC1
D1 1TXD ALT1 PC4
D0 1RXD ALT1 PC5
  2TXD   PF0
  2RXD   PF1
D6 2TXD ALT1 PF4
D3 2RXD ALT1 PF5

 

The upshot of all this is that all the serial ports are available on pins that are broken out on the Nano Every, although UART3 is configured for its “alternative function” configuration which is connected to the USB interface, not to D9/D10.

But how is this usable from code?

It turns out that the official Arduino megaAVR core that supports the Arduino Nano Every has all the code required to support four serial ports present, it just isn’t enabled.  There is a discussion about what is required in this thread on the official Arduino forums: “Arduino Nano EVERY access to 4 Serial”.  To enable access to the additional two serial ports requires the following change to the following file:

...../arduino15/packages/arduino/hardware/megaavr/1.8.5/variants/nona4809/pins_arduino.h

If you are on Windows then this directory can be found in your “C:/Users/<username>/AppData/Local” profile directory.

Find the two lines that say the following:

#define HWSERIAL2_MUX (PORTMUX_USART0_NONE_gc)
#define HWSERIAL3_MUX (PORTMUX_USART2_NONE_gc)

And change them to the following:

#define HWSERIAL2 (&USART0)
#define HWSERIAL2_DRE_VECTOR (USART0_DRE_vect)
#define HWSERIAL2_DRE_VECTOR_NUM (USART0_DRE_vect_num)
#define HWSERIAL2_RXC_VECTOR (USART0_RXC_vect)
#define HWSERIAL2_MUX (PORTMUX_USART0_DEFAULT_gc)
#define PIN_WIRE_HWSERIAL2_RX (7)
#define PIN_WIRE_HWSERIAL2_TX (2)

#define HWSERIAL3 (&USART2)
#define HWSERIAL3_DRE_VECTOR (USART2_DRE_vect)
#define HWSERIAL3_DRE_VECTOR_NUM (USART2_DRE_vect_num)
#define HWSERIAL3_RXC_VECTOR (USART2_RXC_vect)
#define HWSERIAL3_MUX (PORTMUX_USART2_ALT1_gc)
#define PIN_WIRE_HWSERIAL3_RX (3)
#define PIN_WIRE_HWSERIAL3_TX (6)

The forum post also talks about some additional definitions required to provide additional definitions to use when using serial ports from your own code, but I’ve not bothered with those.  The above enables application code to use the following:

  • Serial -> USB interface
  • Serial1 -> D0 (RX) / D1 (TX)
  • Serial2 -> D7 (RX) / D2 (TX)
  • Serial3 -> D3 (RX) / D6 (TX)

Here is a test application that links all four serial ports together so that something typed on the serial console (connected via USB) is sent out through each port in turn and then finally echoed back to the console.  This requires that each of the three hardware serial ports are connected in a “loopback” fashion – i.e. UART 0 TX is connected to UART 0 RX and so on.  This means the following Arduino Nano Every pins need connecting via jumper wires:

  • D0 to D1
  • D7 to D2
  • D3 to D6

Here is the code.  If you type in a character into the serial monitor, when you hit send, you will see it appear on all four ports.  If you want to check it is working, remove one of the jumper wires and see where it stops!

void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
Serial1.begin(9600);
Serial2.begin(9600);
Serial3.begin(9600);
}

void loop() {
// Pass on data between the serial ports
if (Serial.available() > 0) {
int data = Serial.read();
Serial.print("\nRecv 0: 0x");
Serial.print(data, HEX);
Serial1.write(data);
}
if (Serial1.available() > 0) {
int data = Serial1.read();
Serial.print("\tRecv 1: 0x");
Serial.print(data, HEX);
Serial2.write(data);
}
if (Serial2.available() > 0) {
int data = Serial2.read();
Serial.print("\tRecv 2: 0x");
Serial.print(data, HEX);
Serial3.write(data);
}
if (Serial3.available() > 0) {
int data = Serial3.read();
Serial.print("\tRecv 3: 0x");
Serial.print(data, HEX);
}
}

Conclusion

It really wasn’t too hard to add back in support for the additional two serial ports (thanks to that forum post!).  The downside of doing this though is that if the official megaAVR core gets an update, then the changes will almost certainly get overwritten and will have to be applied again. It really isn’t a good idea to go hacking about in the official core files!

There are two alternative options:

  1. Find out how to drive the serial ports yourself and add that either as an Arduino library or your own application code.
  2. Take a look at the unofficial MegaCoreX that supports the Nano Every. I believe this already has the changes built-in to support all four serial ports.

Kevin

 

computers · maker

Getting to know the Arduino Nano Every

The Arduino Nano Every board is a strange beast and it certainly seems to have divided opinions about it.  It is sold as a pin-compatible Arduino Nano replacement with a more powerful processor.

This is the start of a series of posts taking a more detailed look at it.  Here are other posts in the series:

So this is what it looks like (the image below is taken from the official Arduino product page).

In my opinion, the Arduino Nano Every is very definitely being somewhat oversold when described as being “Nano compatible”.  The use of a completely different processor (ATmega4809) in the AVR family means that pretty much none of the hardware registers are compatible with any code using direct hardware manipulation (i.e. almost any library driving hardware) of the original Nano’s ATmega328 peripherals.

For a really honest review, I can recommend the following article on electromaker.io: Maker Board Monday: Arduino Nano Every

We’ll be looking at the Nano Every in a little more detail later in this article, but first, let’s answer the important question. Should you buy an Arduino Nano Every?

In a word: No. Despite the low price and updated architecture, there are a few small but important issues with the Nano Every, making it not as beginner-friendly as it’s predecessor and, in turn, somewhat limiting for advanced users wanting to get started with the ATmega4809 and the megaAVR-0 family of chips.

You can see what Arduino was trying to do with the Every – giving the familiar Nano form factor an upgrade and preserving backward compatibility. In practice, it’s hard to see who it is for. Beginners would be better off buying an original Nano or clone. Advanced users can get much more out of the ATmega4809 using an evaluation board designed for the chip itself rather than Arduino backward compatibility.

I agree that a beginner will almost certainly only find disappointment using this board.  But I have to say that these boards are rapidly growing on me as I’m getting to know them and finding ways around the throttling of the 4809’s functions for backwards compatibility.

So I think they are well worth persevering with and I’ll explain why in a moment.

But they really can’t be considered a drop-in replacement for the original Nano.

So the bad news first

The following are, in my opinion, at best misleading, and at worst, verging on miss-selling:

From the Arduino product page:

If you used Arduino Nano in your projects in the past, the Nano Every is a pin-equivalent substitute. Your code will still work, and you will NOT need to re-wire those motors you planned in your original design. The main differences are: a better processor, and a micro-USB connector.

Well the re-wiring bit is correct, but the chances of your code still working if you are using any libraries that utilise direct IO access to the Arduino’s PORTs, timers, or other peripherals, are basically nil.

But wait, in the introductory “deep dive” post about the Nano Every, there are a few hopeful hints:

The Nano is back! The new entry-level Arduino Nano Every manages to pack in even more features at an even lower price — just $9.90 / €8.00 without headers — and is backwards compatible with the original.

There is that claim again…. reading on…

So the processor is the same as the Uno WiFi R2 and it has more Flash and more RAM. The sketches made for the Nano are going to run on the Every as they are? Is it truly a replacement with zero modification in any Nano based project? Please elaborate.

Actually the ATmega4809 we use on the Uno WiFi R2 and Nano Every is not directly compatible with ATmega328P; however, we’ve implemented a compatibility layer that translates low level register writes without any overhead so the result is that most libraries and sketches, even those accessing directly GPIO registers, will work out of the box.

Ah – saved!  There is a compatibility layer that has no overhead!  Fantastic.  Except, it really isn’t – it is wrong on both accounts.

First of all, it is a software layer that defines the following ATmega328 registers: PORTB, PORTC, PORTD, DDRB, DDRC, DDRD.  And that is it.  No PIN. No timers, ADC, interrupt registers, PWM control… non of the other hardware registers for the ATmega328 are in this compatibility layer at all.

I guess the idea is that any code using direct PORT IO could be made to work by turning on this compatibility layer and the appropriate PORT/DDR commands would magically map over onto the correct ATmega4809 pins.  And they sort of do.  However…

When someone is using direct PORT IO it is usually because they are after top performance with very minimal overhead.  One common example is to using a single “PORTD = value;” command rather than calling digitalWrite() eight times for D0 to D7.  It can be dangerous, as you might get clashes or overwriting values if you don’t know what you are doing… but it is fast.

So what does the compatibility layer look like for this then?  You can find it in the files: NANO_compat.h and NANO_compat.cpp.  Here is the definition of PORTD:

class PORTDClass {
public:
PORTDClass(PORT_t * porta, PORT_t * portb, PORT_t * portc, PORT_t * portf);

PORTDClass & operator = (uint8_t const value);
PORTDClass & operator &= (uint8_t const value);
PORTDClass & operator |= (uint8_t const value);

private:
PORT_t * _porta, * _portb , * _portc, * _portf;
};

extern
PORTDClass PORTD;

There are similar definitions for PORTB, PORTC, DDRB, DDRC, and DDRD.  What was a single register access is now a complete software C++ class, complete with overloaded bitwise operators.

I won’t copy out out the implementation of the classes from the cpp file, but each is pretty big, being a sequence of individual bit operations on the actual ports used on the ATmega4809 to provide the Arduino IO pin functionality.  There is a lot of code in there!

A lot of this complexity comes from the final port mapping used on the Nano Every.  For example, on the Nano, D0 to D7 map nicely onto PD0-PD7.  So how does that compare with the Every?  From the pinout, we can see the following:

  • D0 – PC5
  • D1 – PC4
  • D2 – PA0
  • D3 – PF5
  • D4 – PC6
  • D5 – PB2
  • D6 – PF4
  • D7 – PA1

Yes, what was a single IO PORT with 8 bits is now spread across ports A, B, C and F on the 4809, and in no particularly useful (for optimisation purposes) order.  This is all about maintaining that “pin-equivalent substitute” pinout with the original Nano – ensuring things like PWM are all on the same pins and so on.  It gets more complicated when the maps for SPI, I2C and the ADC are taken into account.  It is actually a very complex mapping required.

So the compatibility layer does not translate “low level register writes”, except for just 6 of the ATmega328’s 200+ IO control registers.  And replacing a single direct register access with a whole class of individual bit operations is most definitely NOT “without any overhead”.

So is this “dead in the water” then?  Well I’d argue that the “Registers emulation: ATMEGA328” function is.  If you care enough about performance to be using direct PORT IO then you really won’t want all the extra code that comes with that compatibility layer.

On the other hand, if you switch to using the new PORT assignments for PORT IO yourself, then much of the original simplicity of IO pins to PORTS is now lost in an effort to keep the pinout the same as the original Nano.  So it won’t be easy.  So why bother?

The Hopeful Bit

What does the ATmega4809 give us then that makes it worth looking at and how much of that can be used with the Nano Every?  Well it gives us quite a bit actually:

  • More memory.
  • 20 MHz operation.
  • Four hardware serial ports (one of which maps onto the USB link).
  • Five hardware timers.
  • 16 channels for the ADC.
  • 10 external interrupts.

The problem is that within the default Arduino environment, this is knocked back to 16MHz, 8 ADC and just 2 serial ports (“Serial” for USB serial; “Serial1” for D0/D1 RX/TX – still am improvement over the original Nano, but only half what is possible).

For a detailed comparison, and discussions of the enforced limitations, see Tom Almy’s post: “First Look at Arduino Nano Every”.

The Good Bit

Much of the additional functionality is still available on the IO pins brought out on the Nano Every though if you know where to look.  So there are ways to get around the imposed limitations of the Arduino configuration.  By way of an example, here is my annotated version of the pinout diagram with some of the extra functions added in.

It doesn’t have everything, but you can already see two additional serial ports and six additional analog inputs.  I’ll talk about how to use them, and maybe even get into looking at the six timers, in some follow-up posts.

But for now, here are some resources for further reading:.

And everything you need to get to the point of running the basic “blink” sketch should be explained here:

In summary – as I say, this board is growing on me and I’m determined to see what it can do!

Kevin

computers · maker

Getting CTC Mode on Timer 4 on an ATmega32U4

I seem to be spending more and more time these days messing around with timers on microcontrollers!  The latest effort was looking at utilising all four timers on the ATmega32U4, in my case wrapped up in a Sparkfun Pro Micro clone board, but this is also the MCU used for the Arduino Leonardo.

In an ideal world this would be usable as an “Arduino Uno with built-in USB support” and at one level this is true.  However once you get into the details of programming the hardware directly there are a few differences.

The datasheet of course has all the details, and instantly there are a couple of things to note:

  • It has four timers: one 8-bit, two 16-bit, and one 10-bit timer.
  • Timer 0 (8-bit) appears fully compatible with the 8-bit timer 0 on the ATmega328.
  • Similarly Timers 1 and 3 (16-bit) also appear fully compatible with 16-bit timer 1 (328) and 3 (2560, etc).
  • It has no timer 2.
  • Timer 4 definitely isn’t compatible with timer 4 in the 328 datasheet or elsewhere.  It is a 10-bit timer.

There are a number of complexities with using Timer 4.  It has five control registers for a start and some sophisticated PWM modes designed for accurate control.  But in my case, I just needed to replicate the simple “CTC” mode found on the other timers.  But it doesn’t seem to exist!

CTC stands for “Clear Timer on Compare match” and is essentially a free-running counter that resets on a match with one of the compare registers.  You’ll often see it used with the TIMERn_COMPA_vect interrupt with the comparison (and resetting) trigged when the counter reaches the value stored in OCRnA.  It is often one of the easier modes to think about if all you want is a periodic “tick” interrupt at a specific frequency.

But Timer 4 doesn’t have a CTC mode.

What it appears to have instead is a more complex “normal” mode.  For the timers with a CTC mode, “normal” mode typically just counts from BOTTOM to TOP (using the terminology from the datasheets) which for an 8-bit timer would be 0 to 255, and for a 16-bit timer would be 0 to 65535.  You can still set and trigger interrupts on compare matches, but the counter keeps counting after the match unless you manually reset it from your interrupt routine.

Timer 4 on the other hand has four compare registers, described as follows in the datasheet:

“The Timer/Counter (TCNT4) and Output Compare Registers (OCR4A, OCR4B, OCR4C and OCR4D) are 8-bit registers that are used as a data source to be compared with the TCNT4 contents. The OCR4A, OCR4B and OCR4D registers determine the action on the OC4A, OC4B and OC4D pins and they can also generate the compare match interrupts. The OCR4C holds the Timer/Counter TOP value, i.e. the clear on compare match value. “

So there are four pins associated with compare registers A, B and D, which is similar to those used on other timers and key for having PWM outputs.  But note the comment about OCR4C – “holds the Timer/Counter TOP value”.  This allows the “normal” mode of Timer 4 to act like a CTC mode of other timers.  When the counter reaches the value in OCR4C, in normal mode it will automatically reset.

So to get our periodic interrupt it is just a case of using the TIMER4_COMPC_vect interrupt then?  Well not quite – there isn’t one!  For some reason the ATmega32U4 has TIMER4_COMPA, B and D, but no C.  To generate an interrupt from the “almost CTC mode” you have to trigger on overflow, not compare match.  So you have to use TIMER4_OVF_vect and enable it using the TOIE4 bit in TIMSK4 not any OCIE4x bit.

So the upshot of all this is that to configure Timer 4 as a counter to trigger periodic interrupt, you have to do something like the following.

  • As the timer can count using 10-bits the largest counter compare value will be 1023.
  • The timer will “tick” at a frequency given by (from the datasheet):

Freq = FreqSys / (2 . N . (1 + OCR4C))

  • So it follows that we can calculate the OCR4C value required from the following:

OCR4C = FreqSys / (2 . N . Freq)   – 1

We can thus find the prescalar value (N) that gives us an OCR4C  in the required counter range (either 10 or 8 bits as required).

Here is an example – to make an output pin toggle at a rate of 2 Hz (so we can see it) we have two choices.

If we are using it as a 10-bit timer, then we can use a pre-scalar of 4096, meaning the calculation is as follows:

OCR4C = 16MHz / (2 . 4096 . 2)  – 1 = 975

If we want to stick to using 8-bits, we can still do it, but for a frequency this low we have to use the largest prescalar value of 16384 as follows:

OCR4C = 16MHz / (2 . 16384 . 2) – 1 = 243

In order to use 10-bit values, even though the compar registers are described as “8-bit registers” in the datasheet, there is an addition “cheat” register that is used to hold the higher bits for the other registers to make them “kind of 10-bit”.  That is TC4H and it has to be set first before setting the register you want to update.  Think of it as a transitory holding pen for the extra two bits you need to use.

The resulting code is as follows

// Uses Timer 4 interrupt to toggle an LED every second
#define OUTPUT_PIN 2

// Interrupt routine just toggles an output pin
int togval;
ISR(TIMER4_OVF_vect)
{
togval = !togval;
digitalWrite(OUTPUT_PIN, togval);
}

void setup() {
pinMode (OUTPUT_PIN, OUTPUT);

// COM4A/B and PWM4A/B are all unset: "normal mode" and no compare output pins in use.
TCCR4A = 0;
// Prescalar set to clk/16384
TCCR4B = (1<<CS43) | (1<<CS42) | (1<<CS41) | (1<<CS40);
// COM4D unset
TCCR4C = 0;
// WGM mode 00 - i.e. "normal" mode
TCCR4D = 0;
// No enhanced features
TCCR4E = 0;
// Initialise the counter value using 10-bit mode
// for 2 Hz tick.
// OCR4C = 16MHz / (2 . 16384 . 2Hz) - 1 = 975
TC4H = 975 >> 8; // top 2 bits
OCR4C = 975 & 0xFF; // bottom 8 bits
// Now enable the interrupt to start the clock
TIMSK4 = (1<<TOIE4);
}

void loop() {
// Nothing to do
}

There is a lot more to Timer 4 than this, but my main goal was to find the “CTC” equivalent “simple” mode for Timer 4.  And I believe this is it.

Kevin

computers

Cheap ILI9488 TFT LCD Displays

I’ve been continuing my experiments with cheap TFT displays but my latest experiments have proved a little more tricky than I anticipated.

So I’ll start off by saying, if you like to tinker and work out how things work (like I do), read on, but if you want a display to plug-in and “just work” then it is really well-worth paying a bit more and pick up a well supported screen from the likes of Adafruit or Raspberry Pi or Pimoroni or similar.

If you’re still planning on “going it alone” then I suggest having a proper thorough look through the “lcdwiki” to see if the board you’re looking at is there – http://www.lcdwiki.com/Main_Page.  This might make things a lot easier!

This post is all about my display that looks like the following and supports a resistive touchscreen:

There are a number of variants of this display, but mine has the following properties:

  • Screen has an SPI interface using MISO, CLK, MOSI, with additional pins for CS, DC, RESET.
  • Touchscreen has an SPI interface too.
  • Requires 3.3V logic levels.
  • Will (apparently) accept 3.3V to 5V power.
  • Supports an SD card SPI interface (that I’ve not used).
  • 320×480 pixel resolution in full 24-bit colour.
  • Uses the LCD driver chip: ILI9488.
  • Uses the Touchscreen driver chip: XPT2046.

Now the usual way to short-cut any of the hard work of working out how to use something like this display is to look for some existing libraries where someone has already worked their way through the driver chip datasheet, worked out all the configuration parameters for the specific display being driven, and wrapped it all up in something you can just “install and go”.  Well that is the benefit of purchasing one of the above mentioned, very well supported modules – that is what you get and you are supporting all the time and effort that has gone into working out the technical details to make things easy for you.

The next level is to see if your cheap module shares any driver code with any of the above mentioned modules.  And in many cases you’ll find that the open source Adafruit libraries cover a huge range of devices.  However, they don’t have a product themselves based on the ILI9488, it is isn’t supported directly in their libraries. They have one using the ILI9341 which is close, but driving these displays is complicated, so “close” won’t do it!

So what are your options if you’ve got this far and haven’t turned up an obvious “just install and go” library?  Here is the list of all things I’ve looked into so far with varying degrees of success.

For Microcontrollers:

For Linux/Raspberry Pi:

The short summary being that these displays take a bit of work whatever you are trying to do with them!  And some of the links above are now out of date, so even when they were a “recipe” for getting them going, chances are you will need to do some figuring out yourself still.

The “TL;DR” version is that to use the display with the widest range of microcontrollers, I’d recommend taking a look at the Arduino_GFX library by “Moon on our Nation” and read the LCD Wiki for more specific details.

This post details my experiments getting the display and touchscreen to work with the Arduino Uno.  Further posts might look at other options in the future.

Arduino Uno and ILI9488

So, as my starting point was “can I get this running on my Arduino Uno”, there are several issues right off:

  • The board is 3.3V logic (even though it says it will accept 3.5-5V power), so we are going to need some kind of 5V to 3.3V logic shifter that can work at SPI speeds.
  • The most complete library support from the list above do not support 8-bit microcontrollers.  And for good reason – driving a 320×480 full colour display will be slow!
    • Update: After my initial experiments, I discovered the Arduino_GFX library by “Moon on our Nation”, which appears to do the job better than any of the rest, so that is what I ended up using.

But I thought I’d have a go anyway.

There is a jumper on the back of my display (marked J1) that the LCD wiki seems to think would allow 5V operation.

3-5 TFT SPI 480x320 jumper

But it also cautionsThe short-circuit method has the advantages of simple operation, short wiring, and no need for external devices.  The disadvantage is that the module generates a large amount of heat during operation.  Will affect the life of the module.”

I was tempted to give it a try, but as I’m also thinking of using this display in the future with a 3.3V device, I decided not to go this route.  Instead I’m using two 74HC125 buffers as the level shifters as shown below.

Arduino ILI9844 SPI Display_bb

The 74HC125 is a “Quadruple Buffers with 3-State Outputs” and includes four buffers that take an input and simply echo its state onto an output.  It can work with anything from 2 to 6V (check your specific datasheet for details of course), so powering it from 3.3V means that a 5V logic level in becomes a 3.3V logic level out.  Most importantly, unlike many of the simple “level shifter boards” you might find cheaply online, this approach will work at SPI speeds!  Those cheap level-shifters are simply not quick enough.  They work ok for I2C (and I’ve used them myself like this), but they just seem to completely mangle the higher speed SPI signals.

There is also an (active low) “enable” pin too, so each buffer can be individually enabled or disabled as required.

74HC125 - pinout74HC125 - table

This is my (electronics amateur person) simplified understanding of the device anyway, so I suggest you do your own research or find a tutorial by someone who really knows what they are talking about.

I’ve used two devices as there are five signals that need level shifting: MOSI, CLK, CS, DC, RESET.  MISO does not need to be level shifted as this is the “LCD to microcontroller” line, so that is being driven at 3.3V but going into a 5V microcontroller line which is fine.

Note that I’ve not bothered connecting the LED pin for the backlight to the microcontroller, instead just connecting it straight to 3.3V.  This mean that the backlight is “always on”. If you want to be able to control that from the Arduino too, then that will require a 6th level shift.

Hardware wise this does seem to largely do the job for me!  However the display is a little large for leaving balanced on a solderless breadboard so it is vulnerable to dodgy connections.  If you are going this route, and having issues, it is worth taking things slowly and checking things like:

  • Can you see a 5V signal on the SPI lines?
  • Can you see these coming out as 3.3V signals on the other sides of the buffers?
  • Have connected 3.3V and GND to both 74HC125s?
  • Have you connected the OE pins for all used buffers to GND to “enable” them?
  • Is the display a bit wobbly?  It is worth finding it some support to ensure good connections.
  • Double check (preferably before switching it all on) that you are connected to the Arduino’s 3V3 output not the 5V.

Software wise it was a little more tricky.  Initially, my best result was from Jaret Burkett, who wasn’t one to give up with this and created a library for the Uno that supports the ILI9488 based on the Adafruit ILI9341 library.  It supports the Adafruit GFX graphics primitives that are familiar to many other devices using the Adafruit libraries.

Unfortunately it is now four years out of date, so whilst it works “as is”, it doesn’t seem to be maintained anymore and I don’t know if it will continue to work with the rest of the Adafruit GFX libraries or with any future release of the library.

I did take a quick look myself at the latest version of the ILI9341 library to see if I could port it across myself.  Whilst I managed to get something on the display, the scaling and rotations were all a bit weird. I think the main issue is probably, as Jaret describes in his readme for his own library:

“the typical Adafruit TFT libraries are designed to work with 16bit color (RGB565), and the ILI9488 can only do 24bit (RGB888) color in 4 wire SPI mode.”

“What this means is, things will be slower than normal. Not only do you have to write twice as many pixels as a normal 240×320 display, 153,600px (320×480) vs 76,800px (240×320), but you also have to do a lightweight conversion on each color, and write 3 bytes vs 2bytes per pixel.”

But I did get something going with the “graphicstest” example configured as follows to match the Fritzing diagram above.

#define TFT_CS 8
#define TFT_DC 9
#define TFT_RST 10

ILI9488 tft = ILI9488(TFT_CS, TFT_DC, TFT_RST);

Some further experimentation implies we can probably get away without needing a RESET signal.  The official Adafruit libraries include the option to disable RESET and will go on to use a “software RESET” command over SPI.  The library I’m using can be told to ignore RESET but doesn’t replace the hardware RESET with a software RESET.  But it still seemed to work ok.

As I was experimenting I read about another library – Arduino_GFX by “Moon on our Nation”.  This appears to take its inspiration from a whole range of other attempts, including many of those I list above and supports both the ATmega328 based Arduino boards and the ILI9488.  The level of device support in this library is staggering.  I don’t know what the developer is planning for the future, but for the time being, this definitely appears to be your best bet for getting this display working with an 8-bit based Arduino.

Arduino Uno ILI9488 Display Shield

I want to try to get the touchscreen working, so my initial attempt was to add wiring into the touchscreen interface via the level shifters on a breadboard.  But it was proving so unreliable in terms of connections, that eventually I gave up and decided to make a DIY “shield” to support the module.

Here is an approximation for what I’m doing, focusing first on the display itself.

Note: This needed updating to support the touchscreen and display at the same time, for reasons I’ll go into later, but for now, just note that the circuit below isn’t the final version if you want touchscreen support, although it is fine for just the display.

Arduino ILI9844 SPI Display Shield V2_bb

I’ve included the connections for the touchscreen, which largely consist of adding it to the SPI bus and adding two IO pins for the Select and IRQ lines.  I’ve used slightly different IO pins for some of the TFT connections too to make the wiring slightly simpler. The updated wiring for the display is as follows:

#define TFT_CS  10
#define TFT_DC  7
#define TFT_RST 9
#define T_IRQ   2
#define T_CS    8

My first iteration had both of the new touchscreen pins going through the level shifter, then I realised (doh) that the IRQ will be an INPUT pin, so actually can be connected straight to the Arduino… Also, that the Arduino only has two external interrupt pins on D2 and D3, so T_IRQ has to be one of those (I’d originally wired it to D6)!

The diagram above shows the updated circuit, but that is why there is an unused level shifter output close to the pins.  With that in mind, you might get a more simplified design if you route MISO via that spare shifter, but that’s up to you.  Like all these things, this is a first go and I’m sure there is a more optimal way to wire it up if someone worked at it.

When building, I decided some of those wires would work better underneath too, so that is what I did.  The following all went on the underside of the board:

  • All GND links.
  • Level shifter to level shifter 3.3V power.
  • CLK to level shifter output.
  • D10, D9 and D7 to the level shifter input.

But again, do what seems to work best for you!

Component list:

  • blank “PCB shield”
  • Arduino male header pins
  • 1×14 way female header
  • 2x 14 way (2×7) DIP sockets
  • 2x HC74125 level shifters
  • Connecting wires

Here are some photos of the build process for reference.  I’ve tried to stick to the same colours as the Fritzing diagram (although my “ochre” looks very like my “orange” in places!).

I’ve started with the two sockets, then the header socket and then hooked up the GND connections on the underside.  Note that one of the GND connections to the buffer gets removed again later!

Next up are the power lines.  This includes hard-wiring the LED signal on the header connector to 3.3V. Note that I opted to tie the two VCC lines on the chips together on the underside.

The MISO link does not need to go via the level shifters in the same way.  The initial build of the shield has them linked directly as shown below.  This was the cyan link in the diagram, but I’ve used pink wire here. It turns out that the display and touchscreen don’t play nicely together on the SPI bus, so I ended up having to do something different with MISO for the display, but more on that later.

Next up are all the “ochre” wires which are the input side of the level shifters. As you can see below, I wired the three that need to cross the chips to the underside of the board.  Also this is where the mistake with T_IRQ creeps in.  That ochre wire to D6 is actually not needed (you’ll see it in a few photos before I’ve fixed it).

The blue wires complete the links between the level shifters and the TFT header.  Once again, you can see the extra wire to T_IRQ which I remove later! I opted to take one of these underneath, the CLK signal seemed to make more sense that what as shown below.

So about that mistake – here you can see the before (erroneous) and after (fixed) versions.  Basically T_IRQ should go direct into D2.  The observant among you might notice something odd going on with MISO too in that second photo, but more about that later!

Finally a set of male Arduino header pins are added as shown below.

At this point is is a really good idea to visually inspect with a magnifying glass, visually follow through the connections from Arduino via the shifter to the header, and finally check for shorts and continuity with a multimeter.

Once I was confident everything seemed ok, it was a case of plugging in the two HC74125 chips (observing the correct orientation of course) and trying it with an Arduino and the screen.  One thing to consider – that screen is quite large to be supported just by a single set of headers on one side, so I used mounting holes in the shield PCB to add some 10mm M3 stand-offs.  There were two minor issues with this:

  • The nut for one wouldn’t fit alongside the additional SCL/SDA header pins – so I had to trim it down.  If I’d have noticed this first, I would probably have just used a 6-way set of pins rather than the full 8.
  • One of the spacers part clashes with the SD socket on the back of the display – so again, I cut part of it out.  There isn’t anything I could do about this unless I wanted to move the header (I don’t, and if I redesigned it, not sure I’d want to either) or create a new mounting hole in the shield a little further in.

IMG_5247

Here it is with the chips in and all mounted and ready to go.

IMG_5243

It is certainly a lot more reliable than what came before:

IMG_5249

Touchscreen Support

Now I have a reliable way to actually talk to the display module, I can get on with trying to drive the touchscreen.  If you want to follow along with a solderless breadboard (not recommended – it caused me no end of grief as there are just so many connections) here is an updated Fritzing diagram.

Arduino ILI9844 SPI Display Touch_bb

There are a number of points to note:

  • First of all, I personally gave up trying to use solderless breadboard, so I believe the above is correct, but haven’t verified it as seen myself – I just went straight to my “proto shield” version.
  • You can hopefully see that I’ve wired up T_CS (cyan) via the level shifter and connected T_IRQ (purple) directly to the Arduino D2.
  • I’ve also added the loops to connect MOSI and CLK together.
  • Initially I also hooked up MISO directly to the Arduino (and the display’s MISO link), but that caused me problems, so read on below for the update and how it was resolved.

After enabling the touchscreen, I was finding that no-matter what I did I couldn’t get the touchscreen controller to give me a reading.  The touchscreen controller is a XPT2046 and there is a library from Paul Stoffregen (designer and developer of the Teensy Microcontroller), so that is what I was using.  It uses hardware SPI and just needs to be told the IRQ pin (optional) and “chip select” (CS) pin to be used for the touch controller.  You can find it here: https://github.com/PaulStoffregen/XPT2046_Touchscreen

But I couldn’t get anything to work.  Eventually I jumpered out the display from my shield and disconnected the SPI links to the display leaving just the touchscreen.  It turns out that the problems I was seeing are quite common with cheap, poorly implemented devices.  Paul Stoffregen once again provides the complete details of the issue in this article.  There are two common problems, which both manifest themselves if you attempt to use more than one SPI device on the SPI bus:

  • The chip select pins often require an external “pull up” resistor to ensure they have a good “inactive” state when the devices are not in use. This is particularly critical on start-up before the IO pins are set up properly.
  • The MISO line, which is the “device to microcontroller” link, sometimes isn’t properly disabled for the device when it isn’t selected.  The technical details are in the above article, but essentially you get one device interfering with the sending of information from another.

With MISO from the display unconnected, the touch screen worked. With both display and touch screen on the SPI bus, it doesn’t, although the display keeps working fine.  The solution is to build in a “tri-state buffer” to the MISO line for the problematic device and only have it enabled when that device’s “chip select” is active.

As it happens, I have a spare buffer on my shield!  Now I’ve been using the buffer to go from 5V to 3.3V with each line of the buffer permanently enabled by tying the enable pin to GND.

But there is no reason why the “input” signal can’t from from the 3.3V side and the “output” signal fed directly into a 5V logic pin even though that “output” signal will be at 3.3V too. And as the CS pin is already active low, it can be used directly to drive the “enable” pin for the buffer too.  Either the 5V or 3.3V CS pin can be used.

This means the SPI interface to the display is now something like this.

IMG_5368

I’ve reflected all this in the Fritzing diagram above, but it is untested as I went back to my proto shield and updated it as shown below.

Arduino ILI9844 SPI Display Shield V3_bb

I’ve had to remove the GND enable link to the bottom left buffer, hook it’s enable pin to the CS pin on the buffer right next to it (purple), and then tie in MISO from the display header as the input (cyan), and link the output to the microcontrollers MISO pin (also cyan).

I’ve updated this based on the fact I’ve already built most of my shield, so I’m using the spare buffer at the bottom of the left hand 74HC125 as it is easier to get to. If you are building this fresh from the start, then it might make more sense to use the spare buffer on the 74HC125 on the right, which is close to the display’s MISO pin.  The downside is it is further away from the display’s CS pin.

Alternatively, although I didn’t need to do this, you might want to consider using the remaining buffer for the touchscreen MISO pin too, so that both MISO connections go through enabling buffers.  I didn’t seem to need this with mine, but see how you go.  Maybe the XPT2046 does SPI better than the ILI9488.

Basically I’m saying this is definitely a first, evolved iteration.  If you were building from scratch there is almost certainly a better mapping of buffers to pins possible.

I toyed with the idea of adding pull-up resistors on the two CS pins, as described in the article I mentioned above, but  in the end opted for the “software solution” to pre-initialise the two CS pins and set then HIGH “by hand” prior to initialising the SPI devices. This seems to work fine for me.

If you wanted to do it properly, you’d need to find a place to install two 10k resistors (or thereabouts) between each CS pin and VCC.  I don’t know if it matters which side of the buffer you do this, but if you do it on the 5V side you’ll need to pull-up to 5V, but if you do it on the 3.3V side, you pull-up to 3.3V. I’ve not tried this myself, so you’ll need to do some reading and be sure to do the right thing here as I’m just guessing from not very much knowledge of such things.

Here are photos of the “final” build (for now).

Here is my test code to drive the display and respond to touch, with debug information over the serial port.  It includes two functions at the end that convert touch coordinates (0 to 4095) into display coordinates to match the screen resolution.  This is all relative to the current rotation providing that w and h are set appropriately i.e. obtained from the GFX library once setRotation() has been called.

Note that the touch screen library and the GFX library have the same understanding of rotation, which is really handy, but I suspect that is because both are designed to work with or are based on the original Adafruit GFX library.

#include <XPT2046_Touchscreen.h>
#include <SPI.h>
#include <Arduino_GFX_Library.h>

// XPT2046 Touchscreen Definitions
//
#define T_CS 8
// MOSI=11, MISO=12, SCK=13
#define T_IRQ 2 // Set to 255 to disable interrupts and rely on software polling
XPT2046_Touchscreen ts(T_CS, T_IRQ);

// ILI9488 Display Definitions
//
#define ILI_CS 10
#define ILI_DC 7
#define ILI_RST 9
Arduino_DataBus *bus = new Arduino_HWSPI(ILI_DC, ILI_CS);
Arduino_GFX *gfx = new Arduino_ILI9488_18bit(bus, ILI_RST, 0, false);

unsigned w, h;
#define ROTATION 0

void setup() {
  Serial.begin(9600);
  Serial.println("Arduino_GFX library ILI9488 and XPT2046 Test");

  // Pre-initialise the "chip select" pins to "deselected" (HIGH)
  pinMode(ILI_CS, OUTPUT);
  pinMode(T_CS, OUTPUT);
  digitalWrite(ILI_CS, HIGH);
  digitalWrite(T_CS, HIGH);
  delay(10);

  ts.begin();
  ts.setRotation(ROTATION);

  gfx->begin();

  gfx->setRotation(ROTATION);
  w = gfx->width();
  h = gfx->height();

  gfx->fillScreen(WHITE);
  gfx->fillScreen(RED);
  gfx->fillScreen(GREEN);
  gfx->fillScreen(BLUE);
}

unsigned long millicnt;
void loop() {
  if (millis() > millicnt) {
    gfx->fillScreen(BLACK);
    millicnt = 10000+millis();
  }

  if (ts.touched()) {
    TS_Point p = ts.getPoint();
    Serial.print("Pressure = ");
    Serial.print(p.z);
    Serial.print(", x = ");
    Serial.print(p.x);
    Serial.print(", y = ");
    Serial.print(p.y);
    Serial.print(", (");
    Serial.print(xt2xdsp(p.x));
    Serial.print(",");
    Serial.print(yt2ydsp(p.y));
    Serial.print(")\n");

    gfx->drawFastHLine(0, yt2ydsp(p.y), w, RED);
    gfx->drawFastVLine(xt2xdsp(p.x), 0, h, RED); 
  }
}

unsigned xt2xdsp(unsigned touch_x) {
  return (unsigned)((((unsigned long)w)*touch_x)/4096UL);
}

unsigned yt2ydsp(unsigned touch_y) {
  return (unsigned)((((unsigned long)h)*touch_y)/4096UL);
}

This is probably as far as I’ll go with this display on an Arduino Uno now.  I’ve shown it works, but I suspect anyone wanting to really use one of these displays should consider using a more powerful microcontroller.

Kevin

IMG_5377

computers · maker

Raspberry Pi Pico, Arduino Core and Timers

Up to now I’ve only really played with MicroPython and CircuitPython on my Raspberry Pi Pico and I’ve been putting off taking a look at the C/C++ sdk, largely because I don’t want to install yet another development environment on my PC.  But with the recent release of the official Arduino core for the Pico I thought I’d take a look.

Many of my projects end up needing a high performance (microsecond) timer triggering some kind of periodic code, partly because many of my projects involve some kind of digital audio output.  So following my recent success with the SAMD51 timers I thought I’d take a look at the art of the possible with my Raspberry Pi Pico too.

The official Arduino support for the Raspberry Pi Pico is easy to install. Just open the board manager as you would for any other board an search for “pico”.  Then you’ll see “Arduino Mbed OS RP2040 Boards” as an option and just click “install” and you’re away!  After plugging in my Pico and powering it up with BOOTSEL held, I was able to download and run “blink” in no time at all!  So far so good.

But I want to set up a sub 100 microsecond periodic routine running.  On an Atmel328 or ATtiny Arduino system this usually involves some low-level configuration of the timers and attaching an interrupt routine.  And on the SAMD boards, this involved attaching peripheral clocks to one of the existing (usually the main) generic clocks.  The RP2040 chip at the heart of the Pico is a dual-core Cortex M0+ chip, so in theory you’d think it was pretty similar to the SAMD21, which is a single core Cortex M0+ device.  But it isn’t that straight forward.

To start with, the Arduino support for the RP2040 is based on mbed.  You can read the original reasoning for using the mbed environment for new microcontrollers here, so I’m guessing this applied for the RP2040 too.  This might make supporting devices easier, but it also means that there are quite a few software layers involved in getting an Arduino-like interface to your Pico:

  • The Arduino environment is based on…
  • Wiring/processing which runs on…
  • Mbed which is built on top of the…
  • Raspberry Pi Pico C/C++ SK which runs on the…
  • RP2040 chip which may or may not (I haven’t quite worked it out yet) implement the…
  • Cortex Microcontroller Software Interface Standard (CMSIS).

On the plus side (I think its a plus) you also get access to a whole range of different APIs and software frameworks from within the Arduino IDE:

  • The Arduino API itself that is familiar to anyone who’s used any of the Arduino boards in the past.
  • Via the mbed:: namespace, a whole range of mbed APIs too.
  • The Raspberry Pi Pico C/C++ SDK.

And of course, eventually you also have the option of poking the RP2040 directly without any of the API frameworks in place too.

In terms of setting up a microsecond, periodic callback function, I appear to have several options available to me, as I explore below.  If you know of a better way to achieve this please do let me know!

Note that each of the following examples will toggle IO pin GP7, which gives me something to measure to see how its working.

Mbed Tickers

Mbed supports the concept of Tickers and Timeouts and this looked like the most sensible way to get a microsecond-resolution, periodic function called for any mbed based setup.

Here is the Arduino code that should do it.

#include <mbed.h>

mbed::Ticker audioTicker;

int ledval;
void rp2040PeriodicFunction() {
ledval = !ledval;
digitalWrite(7, ledval);
}

void setup() {
pinMode (7, OUTPUT);
audioTicker.attach_us(&rp2040PeriodicFunction, 500);
}

void loop() {
// put your main code here, to run repeatedly:
}

And this does indeed work.  Until you want a function running at 50uS or less.  Now I’m working up to doing work with audio, so something like a 32kHz sample rate is not untypical.  That requires samples being output at 1/32000 = 31.25uS.  For some reason I just couldn’t get anything reliably working below around 60uS.

So this looks fine for anything in the tenths of milliseconds range, but below that I was struggling on the Pico.

Raspberry Pi Pico Repeating Timer

This looks ideal.  It is a Pico SDK function for setting up a repeating timer to trigger a function each time (see chapter 4.2.12 in the Pico C/C++ SDK datasheet).  Here is the code.

#include <pico/time.h>

int ledval;
bool rp2040PeriodicFunction (struct repeating_timer *t) {
ledval = !ledval;
digitalWrite(7,ledval);
return true;
}

void setup() {
struct repeating_timer timer;

// NB: A negative period means "start to start" - i.e. regardless of how long the callback takes to run
long usPeriod = -1000000/32000;
add_repeating_timer_us(usPeriod, rp2040PeriodicFunction, NULL, &timer);
}

void loop () {
}

Unfortunately this does not build on the official Arduino RP2040 core.   It turns out that much of the APIs for the micro-second alarm and timer functions are conditional on PICO_TIME_DEFAULT_ALARM_POOL_DISABLED not being set.  But for the current build this is set (I don’t know why – I haven’t looked into it further) – so not only is there no “default alarm pool” to which you can attach repeating timers and alarms, but even the functions to handle such things are “compiled out”.

At some point in the future, when the Arduino for RP2040 has a default alarm pool set up, this might work, but for now it isn’t an option for me.

Raspberry Pi Pico Alarm System

Just to be sure, I also looked into the Pico SDK’s alarm system (see chapter 4.2.11 in the Pico C/C++ SDK datasheet). This provides a number of APIs for setting up alarms based on the system clock and includes such APIs as “add_alarm_in_us”.  But as with the repeated timer system this appears not to be available (at present) in the official Arduino mbed based RP2040 core.

Pico Low-level Timer Hardware API

The last option I looked at (spoilers, it worked, so I stopped looking at other options after this one) was to use the Pico SDK’s “low level hardware API” directly (see chapter 4.1.22 in the Pico C/C++ SDK datasheet, and chapter 4.6 in the RP2040 datasheet).

The RP2040 has the concept of alarms which can be triggered on the main system timer, and that can be configured as shown in the “timer_lowlevel example” in the Pico GitHub area.

The code I used is as follows.

#include <hardware/timer.h>
#include <hardware/irq.h>

#define ALARM_NUM 1
#define ALARM_IRQ TIMER_IRQ_1

#define ALARM_FREQ 32000
uint32_t alarmPeriod;

int ledval;
static void alarm_irq(void) {
hw_clear_bits(&timer_hw->intr, 1u << ALARM_NUM);
alarm_in_us_arm(alarmPeriod);

ledval = !ledval;
digitalWrite(7, ledval);
}

static void alarm_in_us_arm(uint32_t delay_us) {
uint64_t target = timer_hw->timerawl + delay_us;
timer_hw->alarm[ALARM_NUM] = (uint32_t) target;
}

static void alarm_in_us(uint32_t delay_us) {
hw_set_bits(&timer_hw->inte, 1u << ALARM_NUM);
irq_set_exclusive_handler(ALARM_IRQ, alarm_irq);
irq_set_enabled(ALARM_IRQ, true);
alarm_in_us_arm(delay_us);
}

void dumpTimerHwReg (char *str, unsigned long reg) {
SerialUSB.print(str);
SerialUSB.print("\t");
SerialUSB.print(reg);
SerialUSB.print("\t");
SerialUSB.print(reg, HEX);
SerialUSB.print("\n");
}

void dumpTimerHwRegs() {
SerialUSB.println("-------------------------------");
dumpTimerHwReg("timehr", timer_hw->timehr);
dumpTimerHwReg("timelr", timer_hw->timelr);
dumpTimerHwReg("alarm0", timer_hw->alarm[0]);
dumpTimerHwReg("alarm1", timer_hw->alarm[1]);
dumpTimerHwReg("alarm2", timer_hw->alarm[2]);
dumpTimerHwReg("alarm3", timer_hw->alarm[3]);
dumpTimerHwReg("armed", timer_hw->armed);
dumpTimerHwReg("timerawh", timer_hw->timerawh);
dumpTimerHwReg("timerahl", timer_hw->timerawl);
dumpTimerHwReg("dbgpause", timer_hw->dbgpause);
dumpTimerHwReg("pause", timer_hw->pause);
dumpTimerHwReg("intr", timer_hw->intr);
dumpTimerHwReg("inte", timer_hw->inte);
dumpTimerHwReg("intf", timer_hw->intf);
dumpTimerHwReg("ints", timer_hw->ints);
SerialUSB.println("");
}

void setup () {
pinMode (7, OUTPUT);
pinMode (LED_BUILTIN, OUTPUT);
alarmPeriod = 1000000/ALARM_FREQ;
alarm_in_us(alarmPeriod);
SerialUSB.begin(9600);
SerialUSB.println("Hello Start");
}

int ledval2;
void loop () {
ledval2 = !ledval2;
digitalWrite(LED_BUILTIN, ledval2);
delay(1000);
dumpTimerHwRegs();
SerialUSB.print("Alarm Frequency: ");
SerialUSB.print(ALARM_FREQ);
SerialUSB.print("Period: ");
SerialUSB.println(alarmPeriod);
SerialUSB.println("");
}

You can see this has a lot of debug information in it.  For starters, as well as the interrupt routing timing IO access on GP7, it also flashes the build-in LED every second in the main loop, so I know something is going on. It also dumps the entire register set of the hardware timer every second so I can work out a) what is already configured from elsewhere in the code; and b) if my settings are having an effect.

From the debug code it would appear that alarm[0] is already used somewhere, I haven’t investigated where, so I set everything up to use alarm[1].  Note that the alarm interrupt routine (alarm_irq) must do two things in addition to what ever you need it to do:

  • Clear the interrupt by writing to the appropriate bit in INTR.
  • Re-arm the alarm to trigger for its next run.

This manual “re-arming” process means that if you need a perfectly accurate “tick” you won’t get it.  The next triggering of the alarm will be equal to the requested period plus whatever time it has taken to run the code in the interrupt service routine up to the point of you re-arming the alarm.  But that is probably close enough for me.

The key things to note in the debug output are:

  • inte shows which of the alarms are enabled.
  • alarm[n] shows the time (in microseconds) when the next alarm will trigger.
  • timelr and timerawl will update each time as the number of microseconds is constantly updating.

Here you can see the output on GP7 of the above code.

2021-04-18 12.33.22

It is reading a frequency of 16.1kHz as the ISR is toggling the IO pin – so half the time the ISR runs it is setting it high and the other half it sets it low, so a 16kHz square wave corresponds to a 32kHz ISR.  It is reading 16.1 rather than a pure 16 largely due to the error tolerances of my cheap digital oscilloscope, but you get the idea.

Other Options to Try

As I said, once I found something that worked, I pretty much stopped experimenting, but the following all like other worthy candidates to explore at some point.

  • Each Cortex M0+ core has a SysTick microsecond timer.  I’m not sure what these are used for, so might be available for application use.
  • The PIO system can count and generate interrupts, so this might be useful too.
  • PWM has counters and timers, so this is another option.v
  • If all else fails, there is a second core just sitting around doing nothing right now – it could simply sit and monitor the microsecond counter and act on things as and when…

Conclusion

I’ve found a way to do the equivalent of setting up a 328 timer/compare/interrupt system for microsecond resolutions on the Pico.  I don’t know if its the best way, or the only way, but at least I have something.

As I’m thinking about this for potential audio applications, I might still look in more detail at the PIO subsystem.  As this can run independently handling IO it may be that rather than have a regular “tick” on the main core side “pushing” updates to IO, it could come as a result of an interrupt from the PIO – so more of a “pull” of data when the PIO system wants more.

But if you only need millisecond resolution, then using the mbed Ticker API is probably the simplest- (and probably more portable, at least among mbed supported targets) way to go.  As the Arduino RP2040 core matures it maybe that using the Pico sdk’s high-level timer functions will be feasible at some point in the future.

But for now, driving the timer hardware at this level appears to be required to get sub 50uS periods. But it is still simpler than poking hardware registers directly.

I haven’t really worked out how the clock systems all fit together on the Pico yet – I need to spend some more time with chapter 2.15 in the RP2040 datasheet. It certainly seems to be very different to what I know about the SAMD21 (another Cortex M0+ microcontroller) clocks and options.

Kevin

 

 

computers

Comparing Timers on SAMD21 and SAMD51 Microcontrollers

I’ve been looking at the SAMD21 version of some code and wondering what would be required to get it running on a SAMD51 board.  The main issue I’ve faced is the configuration of timers, as most of the rest of it is largely microcontroller independent code.

The SAMD architectures support a very flexible and configurable (i.e. somewhat complex) timer setup.

Please note: Everything written here is based on my reading of the datasheets, others source code, and any available material I’ve managed to dig up, but it is a complex area and I’ve only just started looking into it.  If you spot something wrong, do let me know in the comments! But so far, this seems to have worked for me.

Also, do please let me know if you know of a good tutorial or better source for this information!

Kevin.

Timers on the SAMD21

For the SAMD21 family there are a number of resources out there to help, some of which are:

The basic idea is that you configure some generic clocks, or work out how they are already configured for your environment, then connect them to peripherals to make them “active”. Here is the main block diagram of the clocks from the official overview.

samd21-clock-system-block-diagram-detail

Despite all this information, I’ve still not found a simple summary of the clock options on the various boards available.  The closest I’ve come is deciphering the “configuration options” table in the SAMD21 family datasheet (section 2) and then the various header files in Adafruit’s CircuitPython code, the Arduino SAMD core code (and Adafruit’s own variant that includes the SAMD51 support).  Supported by various examples around the Internet, I believe that the SAMD21 family can have 3 or 5 general purpose timers (TCs) and 3 or 4 timer/counters for control applications (TCCs).

For the SAMD21 these are referenced in code terms as follows:

  • Timer Counters 1 to 3 = TC3, TC4, TC5
  • Timer Counters for Control 1 to 3 = TCC0, TCC1, TCC2
  • Timer Counters 4 to 5 (optional) = TC6 ,TC7
  • Timer Counters for Controllers 4 = TCC3

Many of the boards I’ve been using (mostly Adafruit ones) seem to favour the SAMD21E series of MCUs which support 3 of each, so the full list of timers is TCC0, TCC1, TCC2, TC3, TC4, TC5.

In the Arduino environment TC5 is used for the Tone functionality. I’ve not waded through the rest of the code to work out what else is used and so far haven’t found a simple documentation page that lists the hardware expectations of the Arduino SAMD core.

Timers on the SAMD51

I’ve not found any detailed information for the SAMD51 family clocks other than the full datasheet itself.  Here is the information I have found:

  • Microchip’s SAMD51 Family Reference page – including links to the datasheet.
  • “Clock System Configuration and Usage on SAM E5x (Cortex M4) Devices” application note.

Here is a block diagram of the SAME54 clocks from the above application note, which as far as I can see, is pretty much the clock architecture for the SAMD51 too.

SAME54 Clock System

From browsing source, reading between the lines, and once again trying to decipher the “configuration options” in the SAMD51 Family datasheet, I believe the SAMD51 MCUs can have 4, 6 or 8 timer counters (TCs) and 3 or 5 timer counters for control applications (TCCs).  These are referenced as follows:

  • Timer Counters 1 to 4 = TC0, TC1, TC2, TC3
  • Timer Counters for Control 1 to 3 = TCC0, TCC1, TCC2
  • Timer Counters 5 and 6 = TC4, TC5
  • Timer Counters for Control 4 and 5 = TCC3, TCC4
  • Timer Counters 7 and 8 = TC6, TC7

For the SAMD51G devices I have, this means they have the following: TCC0, TCC1, TCC2, TC0, TC1, TC2, TC3.

How do you find out what your board has and supports?  You have two real options:

  • Go back to deciphering the datasheet (there are almost 2000 pages).
  • Browse through the device specific includes in the Arduino “CMSIS” source code.

The Cortex Microcontroller Software Interface Standard

CMSIS is the ARM Cortex Microcontroller Software Interface Standard and is the source code that contains all the officially sanctioned MCU definitions for the SAMD architecture for use with the C language.  Here are the resources you need to go digging around here:

CMSIS is the core “API” – i.e. application programming interface – to the microcontroller’s features.  But each one is specific to the microcontroller family, and in some instances, the specific version, of the microcontroller, in use.

So putting all of this together so far, it is the CMSIS header definitions that provides the magic that allows you to write code like the following from the Arduino environment:

  GCLK->CLKCTRL.reg = (uint16_t)(GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 |
GCLK_CLKCTRL_ID(GCM_TC4_TC5));
while (GCLK->STATUS.bit.SYNCBUSY)
;

The SMSIS API can be viewed online on the above links, but can also be found locally as part of your Arduino IDE installation in a path something like the following:

%USER%\AppData\Local\Arduino15\packages\arduino\tools\CMSIS-Atmel\1.2.0\CMSIS\Device\ATMEL\samd51\include

Where there are a range of header files for each variant of MCU you might be using.  You should really make sure you’ve read the “Accessing SAM MCU Registers in C” post at this point or none of what follows will make sense.

In general there are C definitions that map the names of registers and bits from the datasheet into the C language. It tends to use C unions, so you get a choice of accessing the whole register in one go:

   GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN;

or individual bits:

   GCLK->CLKCTRL.bit.CLKEN = 1;

and there are a whole pile of “direct access” definitions that can be used if required too:

   REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN;

All three of these are essentially the same, although you have to watch out for “collateral” bit setting and clearing if accessing whole registers at a time.

Arduino (Adafruit) SAMD Core Timer Usage

The information about how a specific MCU is set up for Arduino can be found by looking at the “Arduino core” for that architecture.  In the case of the SAMD21 and SAMD51 there are two sources:

In both cases there are several places to look:

  • The boards.txt file in the root contains global compilation definitions for the different supported boards.
  • The cores/arduino area contains the main source.  Key for looking at the timers is the file startup.c.
  • The variants area contains board and microcontroller specific definitions such as IO pin numbers and so on.

I’m primarily working off the Adafruit Arduino SAMD cores as this supports both architectures (and I’m using Adafruit boards) with occasional reference to the official Arduino SAMD core if I need to check something.

In terms of the generic clock configuration from cores/arduino/startup.c it appears that the GCLK clock sources are set up as follows.

SAMD21 Clock Setup

Function Clock Source Notes
Main Clock GCLK 0 DFLL48M 48MHz CPU clock
32KHz Oscillator GCLK 1 XOSC32K Use external osc source if present
32KHz Oscillator GCLK 1 OSC23K Use internal osc source if no external osc present
Watchdog Timer GCLK 2 OSCULP32K Ultra low power internal 32KHz osc
8MHz Oscillator GCLK 3 OSC8M 8MHz internal osc
DFLL48M MUX 0 GCLK 1 48MHz Internal DFLL

SAMD51 Clock Setup

Function Clock Source Notes
Main Clock GCLK 0 DPLL0 120MHz CPU clock
32KHz Oscillator GCLK 3 XOSC32K Use external osc source if present
32KHz Oscillator GCLK 3 OSCULP32K Use internal low-power osc if no external osc present
48MHz Clock GCLK 1 DFLL “USB and stuff”
100MHz Clock GCLK 2 DPLL1 “Other Peripherals”
12MHz Clock GCLK 4 DFLL/4 “For DAC”
1MHz Clock GCLK 5 DFLL/48  

Although as part of the configuration, GCLK->GENCTRL[0] (i.e. GCLK 0) source is set to OSCULP32K only later to be overwritten to use DPLL0 using the line:

  GCLK->GENCTRL[GENERIC_CLOCK_GENERATOR_MAIN].reg = GCLK_GENCTRL_SRC(MAIN_CLOCK_SOURCE)

Where:

  #define GENERIC_CLOCK_GENERATOR_MAIN (0u)
#define MAIN_CLOCK_SOURCE GCLK_GENCTRL_SRC_DPLL0

The two boards I’m using don’t have an external crystal, so the boards.txt file includes a -DCRYSTALLESS compiler option which is used in startup.c to select the internal oscillator case for each.

I’ve not looked in detail what TC/TCC clocks are then mapped onto these generic clock sources and how they are used, other than to note that Tone uses TC5 on the SAMD21 and TC0 for the Adafruit core for the SAMD51.

Porting from SAMD21 to SAMD51

So back to the original subject of this post.  Here is the code written for the SAMD21 that I want to port over to the SAMD51.

static bool tcIsSyncing() {
return TC5->COUNT16.STATUS.reg & TC_STATUS_SYNCBUSY;
}

static void tcReset() {
TC5->COUNT16.CTRLA.reg = TC_CTRLA_SWRST;
while (tcIsSyncing())
;
while (TC5->COUNT16.CTRLA.bit.SWRST)
;
}

static void tcConfigure(uint32_t sampleRate) {
GCLK->CLKCTRL.reg = (uint16_t)(GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 |
GCLK_CLKCTRL_ID(GCM_TC4_TC5));
while (GCLK->STATUS.bit.SYNCBUSY)
;

tcReset();

TC5->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16;
TC5->COUNT16.CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ;
TC5->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV1 | TC_CTRLA_ENABLE;
TC5->COUNT16.CC[0].reg = (uint16_t)(SystemCoreClock / sampleRate - 1);
while (tcIsSyncing())
;

NVIC_DisableIRQ(TC5_IRQn);
NVIC_ClearPendingIRQ(TC5_IRQn);
NVIC_SetPriority(TC5_IRQn, 0);
NVIC_EnableIRQ(TC5_IRQn);

TC5->COUNT16.INTENSET.bit.MC0 = 1;
while (tcIsSyncing())
;
}

To port this code from the SAMD21 over to the SAMD51 you really need four things:

  • The datasheet for the SAMD21 family.
  • The datasheet for the SAMD51 family.
  • Some awareness of the CMSIS API for the SAMD21 processor originally used.
  • Some awareness of the CMSIS API for the SAMD51 processor you want to use.

Things are complicated a little further by the fact that the timers can be configured for 8-bit, 16-bit or 32-bit mode, and there are different definitions for accessing TCs depending on the mode.  However all this code is using the default 16-bit timer counter modes, so you’ll see TCx->COUNT16… a lot in both cases.  It just needs to be noted that there is a different section in the datasheets for the different modes.

To get an idea of what is broken and needs looking at, you can change your build settings to a SAMD51 architecture, hit “build” and see what breaks. I started from this point, by having my original code build for the Adafruit Trinket M0 (successfully) and then switching over to the Adafruit ItsyBitsy M4, which naturally breaks.

So what is the difference?  Here are the key ones:

  • There is no TC5 on the SAMD51 I’m using – the SAMD51G19A for the ItsyBitsy M4 – so this needs switching to another one – I’ve chosen TC0, figuring that if the code worked fine on the SAM21 TC5, which is used for Tone(), then I’ll override the timer used for the SAMD51 version of Tone – TC0.  If you’ve been following along, you might note that there are some SAMD51 family MCUs that have more timers, and so will have a TC5 so it might be ok to leave as TC5 depending on your own MCU.
  • The SAMD21 has one generic clock configuration register: GCLK->GENCTRL, but it uses an “ID” field to choose which generic clock is being configured (GCLKGEN0 to GCLKGEN8).  The SAMD51 has 12 separate registers.
  • The way to tie peripherals to generic clocks is also different.  For the D21 it configures a multiplexer by setting IDs in the CLKCTRL register.  The D51 has separate peripheral clock registers, PCHCTRLn.  In fact it has 48 of them with the index sort of corresponding to the “IDs” of the SAMD21’s CLKCTRL->ID field.  So, by way of example, GCLK_CLKCTRL_ID(GCM_TC4_TC5) is used to set the ID for TC5 in CLKCTRL for the SAMD21, but PCHCTRL[TC5_GCLK_ID] is used to configure TC5 on the SAMD51.
  • The SYNCBUSY bits are in different places.  For the SAMD51 they are their own registers: GCLK->SYNCBUSY and TCx->COUNT16.SYNCBUSY.
  • The WAVE setting is also now an independent register: TCx->COUNT16.WAVE.

For reference, here are the COUNT16 mode registers for the SAMD21 followed by the SAMD51 (take from the datasheets).

And here are the GCLK registers.

So taking all of the above in turn, we can start the process of porting it across to the SAMD51 family.

The tcIsSyncing() function becomes:

static bool tcIsSyncing() {
return (TC0->COUNT16.SYNCBUSY.reg > 0);
}

There are a range of “SYNCBUSY” bits depending on what is going on.  In this case, I’m going “all in” and waiting for any syncing to complete by making sure no SYNCBUSY bits are set at all.  There are cases later on where a more specific check is required, but I’ll deal with them independently.

The reset function needs the timer changing, but also the reading of the SWRST bit is different.  It is R/W on the D21, but W only on the D51, but there is a SWRST specific SYNCBUSY bit instead.  Also, the datasheets say you should disable the TC before resetting it, so I’ve added that in too.

static void tcReset() {
TC0->COUNT16.CTRLA.bit.ENABLE = 0;
while (TC0->COUNT16.SYNCBUSY.bit.ENABLE)
;
TC0->COUNT16.CTRLA.reg = TC_CTRLA_SWRST;
while (TC0->COUNT16.SYNCBUSY.bit.SWRST)
;

The initial configuration of the timer needs updating now too but that is a little more complicated as we need to untangle the use of CLKCTRL into the PCHCTRL registers.

The SAMD21 code tied TC5 to GCLK0, which is the CPU “main clock” running at 48MHz.  We can take a similar approach here, but after looking at how the GCLKs are used, there is a choice:

  • Do we use the CPU clock again (GCLK 0), which is 120MHz on the D51?
  • Or do we stick with 48MHz, and so go with GCLK 1 (“USB and stuff”)?

Well considering we are calculating samples for something at a set rate, then whatever we choose we’ll need to refer back to the processors clock rate at some point, so I’ve gone with the CPU main clock again (GCLK0).

Note that whilst the original had to wait on SYNCBUSY for the GCLK accesses, I’ve seen no mention of SYNCing being an issue when setting up PCHCTRL registers, so I’m not doing anything else after writing to the register here.

static void tcConfigure(uint32_t sampleRate) {
GCLK->PCHCTRL[TC0_GCLK_ID].reg = (GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN_GCLK0);

The next part of the code configures the timer.  The mode and prescaler sections are the same.  Recall that we’re tying TC0 to GCLK0 which is now 120MHz (compared to the SAMD21 48MHz), but we can keep the prescaler value the same (DIV1) so that the TC and GCLK are both running at the same speed.  This also means that the use of SystemCoreClock should still be fine to calculate the sample rate too.

What is SystemCoreClock?  This is initialised in startup.c to VARIANT_MCK which is set in the various variants/board/variant.h to F_CPU which is defined in the board.txt file as either 48000000 for the D21 or 120000000 for the D51.  So this means that for a sample rate of, say, 32kHz, the counter value will be:

48000000 / 32000 – 1 = 1499 for the SAMD21

120000000 / 32000 – 1 = 3749 for the SAMD51

So the period for each “tick” (i.e. when the counter has counted down) will be (for the D51) 3750 / 120000000 = 31.25uS, which does indeed correspond to 32kHz.

One thing that does need to change however is the WAVEGEN register, which is now its own register.

  TC0->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16;
TC0->COUNT16.WAVE.reg |= TC_WAVE_WAVEGEN_MFRQ;
TC0->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV1 | TC_CTRLA_ENABLE;
TC0->COUNT16.CC[0].reg = (uint16_t)(SystemCoreClock / sampleRate - 1);
while (tcIsSyncing())
;

The following functions (NVIC…) appear to be part of the SAM hardware API, so I believe should be the same for both.  These aren’t actually defined in the Arduino CMSIS headers listed above, but they do exist in the official ARM CMSIS definitions, which can be found here: https://github.com/ARM-software/CMSIS_5.  If I’m honest, I’m starting to lose track of what is used by what and where it is officially defined… in my  installation, in  the “%USERS%\AppData\Local\Arduino15\packages\(adafruit or arduino)\tools” area I can see a CMSIS and a CMSIS-Atmel directory, so I’m guessing the former is “ARM” and the latter is the SAMD…

The Arduino CMSIS does define TC0_IRQn though for our MCU so we just need to allow for the use of TC0 rather than TC5 and then we can define the TC0_Handler routine later on.

  NVIC_DisableIRQ(TC0_IRQn);
NVIC_ClearPendingIRQ(TC0_IRQn);
NVIC_SetPriority(TC0_IRQn, 0);
NVIC_EnableIRQ(TC0_IRQn);

Finally, the “interrupt set” register must be set.  This is the same for both MCUs, so we just swap over to TC0.

  TC0->COUNT16.INTENSET.bit.MC0 = 1;
while (tcIsSyncing())
;

Verification

This is all very well, but how do I know it is doing what I’m expecting?  Well the easiest way is to connect the timers to IO pins.  Typically you might be doing this for control reasons, for example to set up a PWM output, but it is also fairly straightforward to just toggle the state of an IO pin in the timers interrupt handler.

In this case, I have an interrupt handler as follows, toggling IO Pin 13 which also happens to be the built-in LED.

int timertestpin;
void TC0_Handler(void) {
// If this interrupt is due to the compare register matching the timer count
if (TC0->COUNT16.INTFLAG.bit.MC0 == 1) {
TC0->COUNT16.INTFLAG.bit.MC0 = 1;
timertestpin = !timertestpin;
digitalWrite(13, timertestpin);
}
}

Note that if you don’t clear the interrupt flag by writing to the MC0 bit it will remain enabled and keep triggering regardless of the counter matching status.  My initial version didn’t do this and I was seeing the pin toggling with a frequency of around 1.2MHz and this was independent of the CC counter value!  I’m guessing that this was essentially a “free running” interrupt handler consuming all processing power – as soon as one interrupt was handled it triggered immediately straight away.  When I tried a similar experiment on a 48MHz SAMD21, i.e. not clearing the interrupt, I got a similar result – seeing an equivalent frequency of around 480kHz, largely independent of the compare counter.

But with the right interrupt resetting going on, if I run this, using a sample rate of 32000 I can see a nice 16kHz square wave on pin 13.  16kHz as it toggles every two “ticks” so one period is high and one period is low.  So it all seems to work!

Kevin

computers · maker

Adafruit Trinket M0 Moving COM Ports in Arduino

I’ve been playing around with the Adafruit Trinket M0 board in an effort to a) move over to 32-bit Cortex M processors from 8-bit AVRs; and b) get to know CircuitPython a little more. I know there are more powerful boards, but I liked the small form factor of the Trinket M0 and see it as a good way into some of the other SAMD based boards supported by the Arduino IDE in the future too.

But the thing I want to do right now isn’t supported in CircuitPython, so I’m using the Arduino IDE.

It’s great how the board can support either, and it all started out fine, but somehow I’ve managed to get into the position where the board keeps coming up as two different serial ports under Windows.  It is COM35 when in bootloader mode (when you can see the TRINKETBOOT drive) but once I load in a sketch from the Arduino IDE and it starts running it it switches over to COM36. It was starting to get really irritating, especially when I wanted to use the serial monitor, as I was constantly telling the IDE which port to use!

There does seem to be a fix though.  I don’t know if this is a universal fix and it might cause problems further down the line, but it worked for me for now.  I also don’t know if this is a common thing for SAMD M0 boards – I can imagine it being a common issue for any board that re-uses the USB port as a serial port but under a different environment (like the Trinket does in the two modes).

In Device Manager you should be able to see the Trinket under “Ports” with the two COM port assignments as it changes.

Device-Manager-Ports

But you can tell Windows what port assignment to use, so to make them the same do the following:

  1. Check you see which COM port is in use in bootloader mode and then load a sketch and check the COM port number when running a sketch.  For me these were 35 and 36 respectively, but it will depend what other devices you’ve used (I used a lot of different microcontrollers!)
  2. In Device Manager right click on the sketch COM port under “ports” and select Properties.
  3. In Properties, if you aren’t already admin on your machine, you need to select “Change Settings” which has the admin icon.
  4. Under Port Settings, select Advanced.
  5. At the bottom you will see COM Port Number – you can select this and swap it from the sketch COM port to the same as the bootloader COM port.  Windows will show it as “in use” and check that you are sure you know what you are doing. I just pretended I did and hit “ok”.

Windows-COM-Port-Settings

You must be sure to get the right COM port here, I don’t know what happens if you choose the wrong one!  Presumably you’ll get weird things going on if you ever plug in both devices Windows things are that COM port at the same time.

Assuming you chose wisely, at this point whichever mode the Trinket is in – bootloader or running sketches, it should be recognised by Windows as the same COM port and you no longer need to change anything in the Arduino IDE, although I still have to “double click” reset to enter bootloader mode to upload sketches.  I don’t know if this is typical – I guess so, but I can live with that.

Note that dropping the CircuitPython bootloader back onto the Trinket sets it back to a different COM port again, but that just seems to be what it does… I’ll update this post as I discover more and if I uncover and weird side effects…

Kevin