Topics

► Games

► Sound & Music

► Watches & Clocks

► GPS

► Power Supplies

► Computers

► Graphics

► Thermometers

► Wearables

► Test Equipment

► Tutorials

► Libraries

► PCB-Based Projects

By processor

AVR ATtiny

► ATtiny10

► ATtiny2313

► ATtiny84

► ATtiny841

► ATtiny85

► ATtiny861

► ATtiny88

AVR ATmega

► ATmega328

► ATmega1284

AVR 0-series and 1-series

► ATmega4809

► ATtiny1604

► ATtiny1614

► ATtiny3216

► ATtiny3227

► ATtiny402

► ATtiny404

► ATtiny414

► ATtiny814

AVR DA/DB-series

► AVR128DA28

► AVR128DA32

► AVR128DA48

► AVR128DB28

ARM

► ATSAMD21

► RP2040

► RA4M1

About me

  • About me
  • Twitter
  • Mastodon

Feeds

RSS feed

Building Gates with the AVR Event System

12th February 2024

This article shows how to use the AVR Event System to make buffers or inverters in the DA, DB, DD, EA, or EB series microcontrollers, and gives some practical examples, such as a simple Blink application:

EvsysBlinkDD.jpg

Using the Event System on an AVR128DA28 to blink an LED without running a program.

Introduction

If you're designing a project based on an AVR processor and you find you need a few buffers or inverters, one option would be to add a CMOS gate package to your circuit. However, as I found in a recent project, you can often solve the application without any extra components by using the AVR Event System.

The Event System is an under-appreciated aspect of the AVR microcontrollers, probably because it's a bit unintuitive to use. Although this article only covers simple applications, using buffers or inverters, it should be helpful in understanding how to use the Event System to interconnect other peripherals in the processor.

History

Microchip introduced the Event System as a feature of their new ATtiny 1-series processors, and it has been included in all AVR processors released since then. There have been three iterations of the Event System:

  • The original version, introduced with the ATtiny 0-series and 1-series, distinguishes between asynchronous channels and synchronous channels.
  • A second version, introduced with the AVR DA, DB, and DB series processors, simplified the use of channels in events by having just one type of channel; events are synchronous or asynchronous depending on which peripheral is being used. This version of the Event System is also used in the ATtiny 2-series.
  • A third version, introduced with the AVR EA and EB series processors, extends the second version with more flexibility about which channels you can use with each peripheral.

This tutorial only deals with the second and third versions of the Event System.

How it works

The Event System effectively allows you to configure patch leads inside the microcontroller, going between specified peripheral outputs and peripheral inputs. This article is going to look at the simplest application of this, connecting an I/O input pin to an I/O output pin to make a buffer or inverter.

Terminology

The Event System avoids using the terms "input" or "output" because this could get confusing. For example, the input to the Event System could be the output from a Timer/Counter. It therefore uses the terms "event generator" for the signal initiating the event, and "event user" for the signal that will be changed by the event.

Event generator

In our application the signal coming from the I/O port input is the event generator. You can usually use any I/O port input as an event generator.

There can only be one event generator; it wouldn't make sense to have more than one input, because there would be a conflict if two inputs had different logic levels.

Event user

In our application the I/O port output that will be changed by the event is the event user. You can have multiple event users for an event.

There is one event user available per port, and the signal is called EVOUTA to EVOUTG. The following table shows which pins the Event System allows you to use as the event user for different members of the Dx and Ex series microcontrollers, and how many gates you could therefore create:

Series Pincount EVOUTA EVOUTB EVOUTC EVOUTD EVOUTE EVOUTF EVOUTG Gates
DA 28 PA2, PA7   PC2 PD2, PD7       3
32 PA2, PA7   PC2 PD2, PD7   PF2   4
48 PA2, PA7 PB2 PC2, PC7 PD2, PD7 PE2 PF2   6
64 PA2, PA7 PB2, PB7† PC2, PC7 PD2, PD7 PE2, PE7† PF2 PG2, PG7 7
DB 28 PA2, PA7   PC2* PD2, PD7       3
32 PA2, PA7   PC2* PD2, PD7   PF2   4
48 PA2, PA7 PB2 PC2*, PC7* PD2, PD7 PE2 PF2   6
64 PA2, PA7 PB2, PB7 PC2*, PC7* PD2, PD7 PE2, PE7 PF2 PG2, PG7 7
DD 14     PC2* PD7   PF7   3
20 PA2, PA7   PC2* PD7   PF7   4
28 PA2, PA7   PC2* PD2, PD7   PF7   4
32 PA2, PA7   PC2* PD2, PD7   PF2, PF7   4
EA 28 PA2, PA7   PC2 PD2, PD7   PF7   4
32 PA2, PA7   PC2 PD2, PD7   PF2, PF7   4
48 PA2, PA7 PB2 PC2, PC7 PD2, PD7 PE2 PF2, PF7   6
EB 14 PA2, PA7   PC2 PD7   PF7   4
20 PA2, PA7   PC2 PD7   PF7   4
28 PA2, PA7   PC2 PD2, PD7   PF7   4
32 PA2, PA7   PC2 PD2, PD7   PF2, PF7   4

Pincount: The size of the package.
EVOUTAEVOUTG: The pins available for the event user EVOUT signals.
Gates: The maximum number of buffers or inverters you can make.
* These pins support Multi-Voltage I/O (MVIO) (see Multi-Voltage Input/Output).
† According to the AVR128DA errata: port pins PB[7:6] and PE[7:4] are not connected to the event system [1]

Where two pins are shown for a port, such as PA2 and PA7, these are alternatives and you can't use both. Where available, you select the alternative output on bit 7 using the PORTMUX EVSYSROUTEA register.

Channels

Each patch lead, linking an event generator with one or more event users, is called a "channel". Depending on the processor there can be between 6 and 10 channels, numbered from CHANNEL1 to CHANNEL10.

The way in which channels and event generators interact is different between the DX series and EX series processors, as described in the following sections.

Channels and event generators in the Dx series

In the DA, DB, and DD series there are constraints on which channels you can use with event generators in each port, as shown in the following table:

Ports Channels
PORTA and PORTB CHANNEL0 and CHANNEL1
PORTC and PORTD CHANNEL2 and CHANNEL3
PORTE and PORTF CHANNEL4 and CHANNEL5
PORTG CHANNEL6 and CHANNEL7

So you can use at most two I/O pins from PORTA and PORTB, two from PORTC and PORTD, and so on.

If you try to use an I/O pin with a channel that doesn't support it you'll get a compile error, such as:

'EVSYS_CHANNEL2_PORTA_PIN0_gc' was not declared in this scope

Channels and event generators in the Ex series

The EA and EB series are slightly more flexible: you can use any two I/O pins from each port, and you can associate them with any of the available channels. This means that the commands to configure them are slightly different.

The two event generator pins in each port are called EVGEN0 (or EV0) and EVGEN1 (or EV1), and a new EVGENCTRLA register for each port lets you specify which two pins correspond to EVGEN0 and EVGEN1; see the example Blink on EA series below.

It's asynchronous

Channels between I/O pins are asynchronous; in other words, their switching is independent of the processor's clock rate and could run at a higher frequency than the clock rate.

Multi-Voltage Input/Output

The DB and DD series support Multi-Voltage Input/Output (MVIO), which allows you to power the I/O pins on PORTC from a different voltage rail, VDDIO2. This lets you make a buffer or inverter that performs logic-level translation: for example, it would let you make a buffer that translates the logic level of another pin on the processor that is not available on PORTC.

Tutorial

To create a gate using the Event System on the Dx series microcontrollers you need to go through the following steps:

  • Choose the I/O pin you want to use as the event generator; this will be the input of the gate.
  • Choose the channel you want to use to link the event generator to the event user; depending on the port you used in the previous step there may be two options; see Channels and event generators above.
  • Link the event generator to the channel.

For example, if you chose PA1 as the event generator and CHANNEL0 as the channel, give the command:

EVSYS.CHANNEL0 = EVSYS_CHANNEL0_PORTA_PIN1_gc;
  • Choose the I/O pin you want to use as the event user, from the output pins table above. This will be the output of the gate.
  • Link the same channel to the event user.

For example, if you chose PA2 as the event user, this is the default option for EVOUTA and you would give the command:

EVSYS.USEREVSYSEVOUTA = EVSYS_USER_CHANNEL0_gc;

Alternatively, if you chose PA7 as the event user, this is the alternate option for EVOUTA and you would need to give the commands:

PORTMUX.EVSYSROUTEA = PORTMUX_EVOUTA_ALT1_gc;
EVSYS.USEREVSYSEVOUTA = EVSYS_USER_CHANNEL0_gc;

Note that you don't need to configure the EVOUT pin as an output; this is done automatically.

  • Finally, if you want to make an inverter rather than a buffer, invert the input or the output.

For example, with the above options you could invert PA1 with:

PORTA.PIN1CTRL = PORT_INVEN_bm;

Applications

For the following applications I've used an AVR128DA28 in a PDPIP package because it is convenient to use on a breadboard, but you could use almost any DA, DB, or DD series processor; the memory requirements are minimal. For the EA example I've used a Curiosity Nano board.

Blink

The first application is a simple RC oscillator, using two inverters and a buffer, to flash an LED at about 1Hz like the classic Arduino Blink program. The difference is that this blinking LED works without running a program.

Here's the equivalent circuit:

EvsysBlink.gif

The third gate, which can be a buffer or an inverter, drives the LED to isolate it from affecting the timing of the RC oscillator. The frequency is given by:

f = 
11.4 R C
 = 0.7 Hz

Here's the full circuit showing the wiring of the AVR128DA28:

EvsysBlinkDA.gif

And here are the statements for the AVR128DA28 to configure the three gates:

void setup() {
  // Inverter: Channel 2, PD5 input, PD2 output
  EVSYS.CHANNEL2 = EVSYS_CHANNEL2_PORTD_PIN5_gc;      // PD5 generator on CHANNEL2
  EVSYS.USEREVSYSEVOUTD = EVSYS_USER_CHANNEL2_gc;     // EVOUTD (PD2) CHANNEL2 user 
  PORTD.PIN2CTRL = PORT_INVEN_bm;                     // Invert output PD2

  // Inverter: Channel 3, PD1 input, PC2 output
  EVSYS.CHANNEL3 = EVSYS_CHANNEL3_PORTD_PIN1_gc;      // PD1 generator on CHANNEL3
  EVSYS.USEREVSYSEVOUTC = EVSYS_USER_CHANNEL3_gc;     // EVOUTC (PC2) CHANNEL3 user 
  PORTC.PIN2CTRL = PORT_INVEN_bm;                     // Invert output PC2

  // Buffer: Channel 0, PA0 input, PA2 output
  EVSYS.CHANNEL0 = EVSYS_CHANNEL0_PORTA_PIN0_gc;      // PA0 generator on CHANNEL0
  EVSYS.USEREVSYSEVOUTA = EVSYS_USER_CHANNEL0_gc;     // EVOUTA (PA2) CHANNEL0 user 
}

void loop() { }

I tested it on a mini breadboard:

EvsysBlinkDA.jpg

The Blink application using the Event System on an AVR128DA28.

It should work with any member of the AVR DA, DB, or DD family.

Blink using fewer pins

The previous version of Blink uses six external I/O pins, two for each gate. However, we can save two I/O pins by using the event system to make the connections between PD2 and PD1, and between PC2 and PA0. We just need to specify the same output pin from each gate as the input pin for the next gate in the cascade.

EvsysBlinkSimpler.gif

To make this work I've had to use PA7 instead of PD5 as the event generator for the first inverter, because we can't have three generators from PORTC and PORTD; see Channels and Event Generators above.

Here's the modified circuit:

EvsysBlinkDD.gif 

Here are the statements to configure this:

void setup() {
  // Inverter: Channel 0, PA7 input, PD2 output
  EVSYS.CHANNEL0 = EVSYS_CHANNEL0_PORTA_PIN7_gc;      // PA7 generator on CHANNEL0
  EVSYS.USEREVSYSEVOUTD = EVSYS_USER_CHANNEL0_gc;     // EVOUTD (PD2) CHANNEL0 user 
  PORTD.PIN2CTRL = PORT_INVEN_bm;                     // Invert input and output PD2

  // Inverter: Channel 3, PD2 input, PC2 output
  EVSYS.CHANNEL3 = EVSYS_CHANNEL3_PORTD_PIN2_gc;      // PD2 generator on CHANNEL3
  EVSYS.USEREVSYSEVOUTC = EVSYS_USER_CHANNEL3_gc;     // EVOUTC (PC2) CHANNEL3 user

  // Buffer: Channel 2, PC2 input, PA2 output
  EVSYS.CHANNEL2 = EVSYS_CHANNEL2_PORTC_PIN2_gc;      // PC2 generator on CHANNEL2
  EVSYS.USEREVSYSEVOUTA = EVSYS_USER_CHANNEL2_gc;     // EVOUTA (PA2) CHANNEL2 user 
}

void loop() { }

There's one other subtlety: the PORT_INVEN configuration inverts both the input and output of an I/O pin. Therefore the statement:

  PORTD.PIN2CTRL = PORT_INVEN_bm;

inverts both the output of the first gate and the input of the second gate. Thus, we no longer need the statement:

  PORTC.PIN2CTRL = PORT_INVEN_bm;

otherwise the middle gate will be inverted twice.

Here's the circuit on a breadboard:

EvsysBlinkDD.jpg

A simpler version of the Blink application, using the Event System on an AVR128DA28.

I also tested the same Blink application on an AVR64EA48, using the Microchip Curiosity Nano board. To make it easier to lay the components out on the breadboard I changed the I/O pins used for the gates, but it's essentially the same circuit as the earlier examples:

EvsysBlinkGatesEA.gif

Here are the statements to implement this circuit:

void setup() {
  // Inverter: Channel 0, PF4 input, PF2 output
  PORTF.EVGENCTRL = PORT_EVGEN0SEL_PIN4_gc;           // PF4 generator for PORTF EV0
  EVSYS.CHANNEL0 = EVSYS_CHANNEL_PORTF_EV0_gc;        // PORTF EV0 goes to CHANNEL0
  EVSYS.USEREVSYSEVOUTF = EVSYS_USER_CHANNEL0_gc;     // EVOUTF (PF2) CHANNEL0 user
  PORTF.PIN2CTRL = PORT_INVEN_bm;                     // Invert output PF2 

  // Inverter: Channel 1, PC1 input, PA2 output
  PORTC.EVGENCTRL = PORT_EVGEN0SEL_PIN1_gc;           // PC1 generator for PORTC EV0
  EVSYS.CHANNEL1 = EVSYS_CHANNEL_PORTC_EV0_gc;        // PORTC EV0 goes to CHANNEL1
  EVSYS.USEREVSYSEVOUTA = EVSYS_USER_CHANNEL1_gc;     // EVOUTA (PA2) CHANNEL1 user 
  PORTA.PIN2CTRL = PORT_INVEN_bm;                     // Invert output PA2

  // Buffer: Channel 2, PF5 input, PB2 output
  PORTF.EVGENCTRL |= PORT_EVGEN1SEL_PIN5_gc;          // PF5 generator for PORTF EV1
  EVSYS.CHANNEL2 = EVSYS_CHANNEL_PORTF_EV1_gc;        // PORTF EV1 goes to CHANNEL2
  EVSYS.USEREVSYSEVOUTB = EVSYS_USER_CHANNEL2_gc;     // EVOUTB (PB2) CHANNEL2 user
}

void loop() { }

On the EA series there's an additional step in configuring each gate: you have to choose whether to send the event generator on each port to EV0 or EV1, and configure the EVENCTRL register for that port accordingly. Note that we have two event generators on PORT F; hence the "|=" on the line:

  PORTF.EVGENCTRL |= PORT_EVGEN1SEL_PIN5_gc;          // PF5 generator for PORTF EV1

Here's the circuit connected up on a breadboard:

EACuriosityNano.jpg

The Blink application using the Event System on an AVR64EA48 Curiosity Nano board.

Push button debouncer

A frequent problem when using push buttons with a microcontroller is that they have contact bounce, which can cause one press on the push button to generate multiple input signals. One approach is to solve the problem in software, by leaving a delay for the contacts to settle before sampling the push button again.

An alternative solution is to add a hardware monostable, to extend the pulse from the push button until after the contacts have settled. Here's a typical circuit:

EvsysMonostable.gif 

The diode prevents the output PD2 from discharging the capacitor when the push button is released, and it can be any small-signal diode.

The period of this monostable is given by:

T = 0.8 R C = 0.8sec

I chose the 1µF capacitor to give a visible flash so you can see the operation of the circuit, but to debounce a push button a more appropriate value would be about 10 to 15nF, which would give a debounce delay of about 8 to 12msec.

Here's the complete circuit:

EvsysDebounceDA.gif

The Event System allows you to implement this without any ICs, and in some cases this will be more convenient than solving it in software.

Here are the settings for the AVR128DA28, or any other Dx series processor:

void setup() {
  // Inverter: Channel 2, PC0 input with pullup, PD2 output
  PORTC.PIN0CTRL = PORT_PULLUPEN_bm;                  // Enable pullup on PC0
  EVSYS.CHANNEL2 = EVSYS_CHANNEL2_PORTC_PIN0_gc;      // PC0 generator on CHANNEL2
  EVSYS.USEREVSYSEVOUTD = EVSYS_USER_CHANNEL2_gc;     // EVOUTD (PD2) CHANNEL2 user 
  PORTD.PIN2CTRL = PORT_INVEN_bm;                     // Invert output PD2

  // Buffer: Channel 3, PD6 input, PA2 output
  EVSYS.CHANNEL3 = EVSYS_CHANNEL3_PORTD_PIN6_gc;      // PD6 generator on CHANNEL3
  EVSYS.USEREVSYSEVOUTA = EVSYS_USER_CHANNEL3_gc;     // EVOUTA (PA2) CHANNEL3 user
}

void loop() { }

We avoid the need for the 20kΩ pullup resistor on PC0 by turning on its input pullup.

Here's the circuit connected up on a breadboard:

EvsysDebounce.jpg

A push button debouncer using the Event System on an AVR128DA28.

Compiling the programs

Compile the programs for the DA, DB, or DD series processors using Spence Konde's Dx Core on GitHub. I tested it with DxCore 1.5.11.

Choose the appropriate option under the DxCore heading on the Board menu; for example AVR DA-series (no bootloader). Check that the Chip option is set as appropriate; for example:

Chip: "AVR128DA28"

You can leave the other options at their defaults.

Then upload each program using a UPDI programmer. The recommended option is to use a USB to Serial board, such as the SparkFun FTDI Basic board [2], connected with a 4.7kΩ resistor as follows:

SerialUPDIProgrammer.gif

Set the Programmer option to Serial UPDI - 230400 baud. Then choose Upload.

EA and EB series devices

As of DxCore 1.5.11 Serial UPDI doesn't work with the EA or EB series devices [3]. Hopefully this will be fixed in a future release.

To test the Blink example on the AVE64EA48 I therefore used a Curiosity Nano, which allows you to program it by dragging and dropping a .hex file onto a mass storage device. Here's the procedure:

  • Compile the program in DxCore by using Export compiled Binary on the Arduino IDE Sketch menu.
  • Connect the Curiosity Nano board to your computer's USB port.

A CURIOSITY drive should appear on your desktop.

  • Drag the compiled binary .hex file from your program's project folder in the Arduino folder, and drop it onto the CURIOSITY drive.

The PS Power/Status light on the board should flash, and the program will be uploaded to the Curiosity Nano.


  1. ^ AVR128DA28/32/48/64 Silicon Errata on Microchip.
  2. ^ SparkFun FTDI Basic Breakout - 5V on SparkFun.
  3. ^ SerialUPDI does not work with EA-series on GitHub.

Previous: RA4M1 Nano Board


blog comments powered by Disqus