Ever wanted to be “logged in” to your AVR microcontroller? Ever thought it would be cool to “cat” a register to see its contents? Have you always wanted a way to power up and power down individual peripheral sub-systems of your AVR or Arduino in *real time* ? Me, too, so I wrote the AVR Shell, a UNIX-like shell.
It’s UNIX-like because it’s reminiscent of the shell account you went out and bought to run your irc nick collision bots on, as well as having a command or two in common. It also has a filesystem that resembles UNIX extfs, using an external EEPROM, but that’s become a project unto itself so I’ll be releasing that module separately under a different instructable when it’s production-ready.
Here’s a list of the things you can currently do with the AVR Shell:
- Read all your Data Direction Registers (DDRn), ports, and pins in real-time
- Write to all your DDRn’s, ports, and pins to turn on motors, LED’s, or read sensors in real-time
- List all known registers on the system
- Create and store values in user-defined variables backed up by EEPROM.
- Create a root password and authenticate against it (used for telnet access)
- Read the configured CPU clock speed
- Change your CPU clock speed by setting a prescaler
- Start and stop 16-bit timers for timing of various things
- Power up and/or power down peripheral sub-systems: Analog to Digital Converters (ADC), Serial Peripheral Interface (SPI), Two-wire Interface (TWI/I2C), UART/USART. Useful for when you want to reduce power consumption of the microcontroller or to enable certain functions.
- Written in C++ with reusable objects.
This instructable will walk through the installation, use, and customization of avrsh.
Step 1: What You’ll Need
This instructable doesn’t require much except that you:
- Have an Arduino or ATmega328P. Other AVR’s could work, but you may need to modify the code to list any registers that are unique to your MCU. The names only need to match what is listed in the <avr/io*.h> header file unique to your MCU. Many of the register names are the same between AVRs, so your mileage may vary when porting.
- Have a way to connect to the serial USART of your Arduino/AVR. The system has been tested most extensively with the AVR Terminal, a Windows app that makes a serial connection via your USB or COM port. Works with Arduinos using the USB connection and any AVR using the USB-BUB from Moderndevice.com. Other terminal options include: Putty, minicom (Linux and FreeBSD), screen (Linux/FreeBSD), Hyperterminal, Teraterm. I’ve found putty and teraterm send some garbage when connecting so your first command may be garbled.
- Have the AVR Shell firmware installed and running, which you can download from these pages, or always get the latest version at BattleDroids.net.
To install the AVR Terminal, just unpack it and run it. To install the AVR Shell firmware, download it and either directly upload the hex file and connect your serial terminal at 9600 baud, or compile it yourself with “make” and then “make program” to upload the hex. Note, you may need to change the AVRDUDE settings to reflect your COM port.
Note: The PROGMEM attribute is broken in the current AVR GCC implementation for C++ and this is a known bug. If you compile it, expect to get many warning messages saying “warning: only initialized variables can be placed into program memory area.” Besides being annoying to see, this warning is harmless. As C++ on the embedded platform isn’t high on the AVR GCC priorities list, it is unknown when this will be fixed. If you check out the code, you will see where I have made work arounds to reduce this warning by implementing my own attribute statements.
Pretty simple. Download and install anything that you might need to then flip the page and let’s get crackin’.
Step 2: Reading and Writing Registers
The AVR Shell was written primarily to access some sensors that I had connected to my AVR. It started with a simple LED then moved to light sensors, temperature sensors, and finally to two ultrasonic transducers. avrsh can set the digital components of these sensors by writing to the registers that control them.
Manipulating AVR registers while running
To get a list of all known registers on your Arduino, type:
and you’ll get a printout looking like this…
I know about the following registers: TIFR0 PORTC TIFR1 PORTD TIFR2 DDRD PCIFR DDRB EIFR DDRC EIMSK PINB EECR PINC EEDR PIND SREG EEARL GPIOR0 EEARH GPIOR1 GTCCR GPIOR2 TCCR0A TCCR0B TCNT0 OCR0A OCR0B SPCR SPDR ACSR SMCR MCUSR MCUCR SPMCSR WDTCSR CLKPR PRR OSCCAL PCICR EICRA PCMSK0 PCMSK1 TIMSK0 TIMSK1 TIMSK2 ADCL ADCH ADCSRA ADCSRB ADMUX DIDR0 DIDR1 TCCR1A TCCR1B TCCR1C TCNT1L TCNT1H ICR1L ICR1H OCR1AL OCR1AH OCR1BL OCR1BH TCCR2A TCCR2B TCNT2 OCR2A OCR2B ASSR TWBR TWSR TWAR TWDR TWCR TWAMR UCSR0A UCSR0B UCSR0C UBRR0L UBRR0H UDR0 PORTB root@ATmega328p>
Step 3: Reading and Writing Fuses
Fuses are special types of registers. They control everything from the clock speed of your microcontroller to what programming methods are available to write-protecting EEPROM. Sometimes you will need to change these settings, especially if you’re creating a stand-alone AVR system. I’m not sure you should change your fuse settings on Arduino. Be careful with your fuses; you can lock yourself out if you set them incorrectly.
In a previous instructable, I demonstrated how you can read and set your fuses using your programmer and avrdude. Here, I’ll show you how to read back your fuses at run time to see how your MCU has actually set them. Note, that this isn’t the compile-time setting that you get from the definitions in <avr/io*.h> but the actual fuses as the MCU reads them at run time.
From Table 27-9 in the ATmega328P datasheet (databook, more like it) the bits of the Fuse Low Byte are as follows:
CKDIV8 CKOUT SUT1 SUT0 CKSEL3 CKSEL2 CKSEL1 CKSEL0
An interesting thing to note is that with fuses, 0 means programmed and a 1 means that that particular bit is unprogrammed. Somewhat counter-intuitive, but once you know it you know it.
- CKDIV8 sets your CPU clock to be divided by 8. The ATmega328P comes from the factory programmed to use its internal oscillator at 8MHz with CKDIV8 programmed (ie set to 0) giving you a final F_CPU or CPU frequency of 1MHz. On Arduino’s, this is changed since they are configured to use an external oscillator at 16MHz.
- CKOUT when programmed will output your CPU clock on PB0, which is digital pin 8 on Arduinos.
- SUT[1..0] specifies the startup time for your AVR.
- CKSEL[3..0] sets the clock source, such as the internal RC oscillator, external oscillator, etc.
When you read your fuses, it will be returned to you in hexadecimal. This is the format that you need if you want to write the fuses via avrdude. On my arduino, here’s what I get when I read the lower fuse byte:
root@ATmega328p> read lfuseLower Fuse: 0xff
So, all bits are set to 1. I did the same procedure on an Arduino clone and got the same value. Checking one of my stand-alone AVR systems, I got 0xDA which is the value I had set some time back when configuring the chip.
The same procedure is used for checking the High Fuse Byte, Extended Fuse Byte, and Lock fuses. The calibration and signature fuse bytes have been disabled in the code with an #if 0 preprocessor directive, which you can change if you feel scrappy.
Step 4: Other Commands
CPU Clock Frequency Settings
You can find out what your firmware has been configured to use as the CPU clock settings with the fcpu command:
root@ATmega328p> fcpuCPU Freq: 16000000
That’s 16 million, or 16 million herz, more commonly known as 16 MHz. You can change this on the fly, for whatever reason, with the clock command. This command takes one argument: the prescaler to use when dividing your clock speed. The clock command understands these prescaler values: