LED Blink (ATMega328)
This example illustrates the classic “ATMega328 running a firmware to blink a LED” example. It is composed of two files:
mega328_blink_fw.c : source code for the firmware to run inside the model
mega328_blink.py : simulation script
The Firmware uses the GPIO pin PB0 as output to drive a (virtual) LED.
It uses the Timer1 configured in CTC mode to trigger overflow interrupts at a 0.5Hz frequency. On each interrupt, the pin PB0 output state is toggled, thus giving it a 1 Hz period, 50% duty cycle.
The simulation script reads the firmware binary file and loads it into a ATMega328 model. It connects a SignalHook that simply prints out the state of the LED whenever it changes. Finally, it runs the simulation for 10 secs of simulated time.
Firmware source
mega328_blink.fw.c:
#define F_CPU 1000000
#include <avr/io.h>
#include <avr/interrupt.h>
/****************************************/
ISR(TIMER1_COMPA_vect)
{
//Toggle pin PB0
PORTB ^= 0x01;
}
void main() {
//Port config, PB0 as output, initially low
DDRB = 0x01;
PORTB = 0x00;
//Timer1 configuration
//The counter period threshold is set to 0.5s
OCR1A = F_CPU / 1024 / 2;
//Timer1 enabled, configured in CTC mode with 1024 prescaling factor
TCCR1B = _BV(WGM12) | _BV(CS12) | _BV(CS10);
//Enable the interrupt vector for Timer1 output compare A
TIMSK1 = _BV(OCIE1A);
//Infinite loop
sei();
while(1);
}
Simulation script
mega328_blink.py:
import os
from yasimavr.device_library import load_device
from yasimavr.lib import core as corelib
#This function implements a hook that will be called by the pin signal.
#It captures digital state change notifications and prints the new LED state.
def pin_hook(sigdata, hooktag):
#Filter the notifications, we're only interested in digital state changes
if sigdata.sigid == corelib.Wire.SignalId.DigitalChange:
#Obtain the new pin state
pin_state = sigdata.data.as_uint()
led_state = 'ON' if pin_state else 'OFF'
#Print the state
print('LED state:', led_state)
def main():
#Uncomment this to have the global trace and logging
#corelib.global_logger().set_level(corelib.Logger.Level.Trace)
#Create a new MCU model ATMega328
device = load_device('atmega328')
#Uncomment this to have the device trace and logging
#device.logger().set_level(corelib.Logger.Level.Trace)
#device.find_peripheral('TC_1').logger().set_level(corelib.Logger.Level.Trace)
#Create a simulation loop for this device
simloop = corelib.SimLoop(device)
#Load the firmware with a MCU clock of 1MHz
fw_path = os.path.join(os.path.dirname(__file__), 'mega328_blink_fw.elf')
firmware = corelib.Firmware.read_elf(fw_path)
firmware.frequency = 1000000
device.load_firmware(firmware)
#Find the pin driving the LED and connect a hook to its signal
pin = device.find_pin('PB0')
hook = corelib.CallableSignalHook(pin_hook)
pin.signal().connect(hook)
#Run the simulation for 10 million cycles (representing 10 secs of simulated time).
#Because the fast simulation mode is disabled, this should be roughly 10 secs of
#real world time as well.
simloop.run(10000000)
if __name__ == '__main__':
main()
Note
The script assumes that the user has compiled the firmware code into a ELF file named mega328_blink_fw.elf in the same directory.