Skip to content

9. Embedded programming

This week I programmed the board which was designed during week 7

ATTiny 44

I skimmed through the Datasheet for the ATTiny44 to get an overall idea of the architecture and the key features. Below are some main pictures from the datasheet.

Pinout.
Register file.
Stack pointer.
Memory map.
Interrupt Vectors.

Programming

I modified Niel’s code to blink a LED. In my hardware board, I had connected a white LED on the PB2 pin of the AVR, where AVR is sinking the current, which means that the LED will glow when 0 is written on the PB2. The code changes required were quite simple, all I had to do was set the PB2 pin as output and then write an appropriate word on the data register of the port.

DDRB |= (1<<PB2); % this is how you set the direction of PB2 to output

PORTB |= (1<<PB2); % this is how you write 1 on PB2 without modifying any other bits. This will turn off the LED

PORTB &= ~(1<<PB2); %this is how you write 0 on PB2 without modifying any other bits. This will turn ON the LED

An extremely short C tutorial

Let’s deconstruct each of the lines shown above, word(token) by word(token).

DDRB |= (1<<PB2);

DDRB –> this is a macro (written by keyword #define) specifying the data direction register. it is defined in iotnx4.h it expands to _SFR_IO8(0x17). _SFR_IO8 is another macro defined in sfr_defs.h and it expands to

#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)
#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))

which ultimately comes to a following statement (*(volatile uint8_t *)0Xxx) --> here 0Xxx is the address in hexadecimal. it is a cast to pointer to a volatile byte. This means that the compiler will assume that it is possible for the variable that the memory location being pointed to have changed even if there is nothing in the source code to suggest that this might occur

|= is a bitwise OR operator where the token on left side (Lval) is one input to the bitwise OR operation. the second input for the OR operation is the value on the right (rval). Furthermore, the result is stored back in the token on the left.

PB2 is another macro which decomposes to a number, say 2.

<< is a left shift operation

(1<<PB2) is shifting binary 1 to left to make a new number, for exacple if PB2 is 2 then (1<<2) = '00000100' or 0x04 or 4 in decimal

PORTB |= (1<<PB2);

This is almost similar to the above line with the only difference being in the memory address to which a value is being written to.

PORTB &= ~(1<<PB2);

Here, we are again writing something to a memory location but instead of using btwise OR operation we are using bitwise AND operation to build the value. The ~ is a bitwise NOT operator which flips the bits. for example as shown above, (1<<2) will give us 0x04. Now doing a bitwise NOT on it will give us 0xFB or in binary 11111011.

Complete Code for blinking the LED

/*
 * GccApplication3.c
 *
 * Created: 20.3.2019 0.43.21
 * Author : asethi
 */ 

#include <avr/io.h>
#include <util/delay.h>


int main(void)
{
    unsigned int delay = 100;
    DDRB |= (1<<PB2);
    /* Replace with your application code */
    while (1) 
    {
           _delay_ms(delay);

           PORTB &= ~(1<<PB2);

           _delay_ms(delay);


           PORTB |= (1<<PB2);

    }
}

WSL

Since earlier I had installed the windows subsystem for linux, it was fairly easy to download the tools required. I used apt command to install the compiler, C library for the AVR and other tools.

sudo apt install avrdude gcc-avr avr-libc make

Even though I had installed avrdude, I soon found out that USB device tree is not supported by the WSL which means I cannot use the avrdude in WSL to program the board. I had to use avrdude under windows environment only. Given the fact that there is no IDE available for the WSL and the makefiles involved are so simple that I can just call gcc directly, I will be anyway going to use the Atmel Studio.

Atmel Studio

It is always nice to use an IDE if you are planning to write anything larger than couple of lines of code and as I am planning to use the Atmels Advanced Software Framework in the future, I will be using Atmel Studio for further development.

Start with a new project

Making new project.
Device selection.

To build the Niel’s code a pre-processor macro named F_CPU has to be defined. It can be defined in the code or can be defined in the tool-chain setting. I defined it in the tool-chain settings. The screenshot below shows the location.

Go to the properties of the solution.
Add the symbol.
All defined pre-processor macro.

Build the solution

Build the solution.

Add the programmer. Keep in mind that I am using USBasp programmer instead of the the FabISP. Change the avrdude command accordingly.

Defining custom programming tool in the avr studio.

Add the avrdude to the path of the user, if not done already. Should be done for the windows user who is using the Atmel Studio, not for the administrator.

Adding location of avrdude to the local user PATH variable.

LED up.

STM32

We used a Nucleo-F429ZI board. The board support package came from STM itself and we used FreeRTOS as a scheduler to blink a couple of LEDs. Eclipse was used as an IDE and openocd was used for debugging.

ToolChain and IDE

Follow this page for help on getting the toolchain and eclipse helper plugin. Furthermore, install STM32CubeMx application for generating the board support package, middleware and correct startup configuration.

Firmware

Start with the STM32CubeMx application

Starting with a new project.
Searching for the specific board under board selector and then starting the project.
Defining the configuration and using Timer1 as a source for time tick instead of Sysclock..
Clicking generate code to generate the startup code.
Select the tool-chain as Makefile.
Do click “copy all used libraries into the project folder”.

Compiling and Running

In the previous step all the required boilerplate plate was generated. Now it will be compiled, edited and then will be run on the board.

Start the eclipse and make a new project.
Select a makefile based project with an existing code.
Point to the location where the code was generated initially using the STM32CubeMX.
In the project setting, make sure that correct tool-chain in selected.
Now the code should be compilable. Time to define the debug configuration
Make a new openOCD configuration.
Finally, can start a debugging session.
Traces from openOCD showing the progress of the debug session.
Eclipse automatically inserts a breakpoint on the main function.

After successfully building and running just the boilerplate code, we can modify the code and add some user specific code to make two leds toggle. Add the following code in main.c, compile and run.

void StartDefaultTask(void const * argument)
{

  /* USER CODE BEGIN 5 */
  /* Infinite loop */
  for(;;)
  {
    HAL_GPIO_WritePin(GPIOB, LD3_Pin|LD2_Pin, GPIO_PIN_RESET);
    osDelay(300);
    HAL_GPIO_WritePin(GPIOB, LD3_Pin|LD2_Pin, GPIO_PIN_SET);
    osDelay(300);
  }
  /* USER CODE END 5 */ 
}

Make sure to plug an Ethernet cable in an active port otherwise the board will get stuck in Ethernet initialization. To temporarily get around it, you can remove the infinite loop in the following error handler. In an actual application, this error should be handled properly instead of an infinite loop.

void _Error_Handler(char *file, int line)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  while(0)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

Comparison

The STM32 family of 32-bit microcontrollers are based on the Arm® Cortex®-M processor. The STM32 F4-series, compared to earlier versions, has higher clock speed (upto 180 MHz), 384 KB of static RAM, upto 2 MB of flash memory, and faster ADCs. All devices offer one 12-bit ADC, a low-power RTC, six general-purpose 16-bit timers including one PWM timer for motor control, two general-purpose 32-bit timers. They also feature standard and advanced communication interfaces.

AVR microcontroller was developed in the year of 1996 by Atmel Corporation. These are modified Harvard architecture 8-bit RISC single-chip microcontrollers. AVR Microcontrollers are available in three Categories

  • TinyAVR:- The have small size with 0.5–32 KB program memory. They are appropriate for simpler applications.
  • MegaAVR:- These are the mainly popular ones having a good quantity of memory (up to 256 KB), higher number of inbuilt peripherals and appropriate for modest to complex applications.
  • XmegaAVR:- They have big ICs with upto 100 pins and upto 384 KB program memory. They also support advanced features like cryptography and event management.

An ARM processor is one of a family of CPUs based on the RISC (reduced instruction set computer) architecture developed by Advanced RISC Machines (ARM). They are available in both 32-bit and 64-bit RISC multi-core processors. RISC processors are designed to perform a smaller number of types of computer instructions so that they can operate at a higher speed, performing extra millions of instructions per second (MIPS). By stripping out unnecessary instructions and optimizing pathways, RISC processors give outstanding performance at a part of the power demand of CISC (complex instruction set computing) procedure. They are widely used in customer electronic devices such as smart phones, tablets, multimedia players and other mobile devices, such as wearables.

Reflection

What questions do you have? Regarding embedded programming, none.

What would you like to learn more about? JTAG and other debugging tools. How to debug AVR microcontrollers.

Problems

  • FTDI cable is not working correctly on my laptop. All I receive is raw 0x00 irrespective of whatever the board is sending. I have checked via oscilloscope that what board is sending is correct and other PCs do show correct result with the same FTDI cable.

Files