Embedded Programming

By Dan Chen, September 12, 2014

Programing with .c and ,make

To program my board, I use the hello.ftdi.44.echo.c file (first block) and hello.ftdi.44.echo.c.make (2nd block) as template.

// hello.ftdi.44.echo.c
//
// 115200 baud FTDI character echo, with flash string
//
// set lfuse to 0x7E for 20 MHz xtal
//
// Neil Gershenfeld
// 12/8/10
//
// (c) Massachusetts Institute of Technology 2010
// Permission granted for experimental and personal use;
// license for commercial sale available from MIT.
//

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

#define output(directions,pin) (directions |= pin) // set port direction for output
#define set(port,pin) (port |= pin) // set port pin
#define clear(port,pin) (port &= (~pin)) // clear port pin
#define pin_test(pins,pin) (pins & pin) // test for port pin
#define bit_test(byte,bit) (byte & (1 << bit)) // test for bit set
#define bit_delay_time 8.5 // bit delay for 115200 with overhead
#define bit_delay() _delay_us(bit_delay_time) // RS232 bit delay
#define half_bit_delay() _delay_us(bit_delay_time/2) // RS232 half bit delay
#define char_delay() _delay_ms(10) // char delay

#define serial_port PORTA
#define serial_direction DDRA
#define serial_pins PINA
#define serial_pin_in (1 << PA0)
#define serial_pin_out (1 << PA1)

#define max_buffer 25

void get_char(volatile unsigned char *pins, unsigned char pin, char *rxbyte) {
   //
   // read character into rxbyte on pins pin
   //    assumes line driver (inverts bits)
   //
   *rxbyte = 0;
   while (pin_test(*pins,pin))
      //
      // wait for start bit
      //
      ;
   //
   // delay to middle of first data bit
   //
   half_bit_delay();
   bit_delay();
   //
   // unrolled loop to read data bits
   //
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 0);
   else
      *rxbyte |= (0 << 0);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 1);
   else
      *rxbyte |= (0 << 1);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 2);
   else
      *rxbyte |= (0 << 2);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 3);
   else
      *rxbyte |= (0 << 3);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 4);
   else
      *rxbyte |= (0 << 4);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 5);
   else
      *rxbyte |= (0 << 5);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 6);
   else
      *rxbyte |= (0 << 6);
   bit_delay();
   if pin_test(*pins,pin)
      *rxbyte |= (1 << 7);
   else
      *rxbyte |= (0 << 7);
   //
   // wait for stop bit
   //
   bit_delay();
   half_bit_delay();
   }

void put_char(volatile unsigned char *port, unsigned char pin, char txchar) {
   //
   // send character in txchar on port pin
   //    assumes line driver (inverts bits)
   //
   // start bit
   //
   clear(*port,pin);
   bit_delay();
   //
   // unrolled loop to write data bits
   //
   if bit_test(txchar,0)
      set(*port,pin);
   else
      clear(*port,pin);
   bit_delay();
   if bit_test(txchar,1)
      set(*port,pin);
   else
      clear(*port,pin);
   bit_delay();
   if bit_test(txchar,2)
      set(*port,pin);
   else
      clear(*port,pin);
   bit_delay();
   if bit_test(txchar,3)
      set(*port,pin);
   else
      clear(*port,pin);
   bit_delay();
   if bit_test(txchar,4)
      set(*port,pin);
   else
      clear(*port,pin);
   bit_delay();
   if bit_test(txchar,5)
      set(*port,pin);
   else
      clear(*port,pin);
   bit_delay();
   if bit_test(txchar,6)
      set(*port,pin);
   else
      clear(*port,pin);
   bit_delay();
   if bit_test(txchar,7)
      set(*port,pin);
   else
      clear(*port,pin);
   bit_delay();
   //
   // stop bit
   //
   set(*port,pin);
   bit_delay();
   //
   // char delay
   //
   bit_delay();
   }

void put_string(volatile unsigned char *port, unsigned char pin, char *str) {
   //
   // print a null-terminated string
   //
   static int index;
   index = 0;
   do {
      put_char(port, pin, str[index]);
      ++index;
      } while (str[index] != 0);
   }

int main(void) {
   //
   // main
   //
   static char chr;
   static char buffer[max_buffer] = {0};
   static int index;
   //
   // set clock divider to /1
   //
   CLKPR = (1 << CLKPCE);
   CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);
   //
   // initialize output pins
   //
   set(serial_port, serial_pin_out);
   output(serial_direction, serial_pin_out);
   //
   // main loop
   //
   index = 0;
   while (1) {
      get_char(&serial_pins, serial_pin_in, &chr);
      put_string(&serial_port, serial_pin_out, "hello.ftdi.44.echo.c: you typed \"");
      buffer[index++] = chr;
      if (index == (max_buffer-1))
         index = 0;
      put_string(&serial_port, serial_pin_out, buffer);
      put_char(&serial_port, serial_pin_out, '\"');
      put_char(&serial_port, serial_pin_out, 10); // new line
      }
   }
PROJECT=hello.ftdi.44.echo
SOURCES=$(PROJECT).c
MMCU=attiny44
F_CPU = 20000000

CFLAGS=-mmcu=$(MMCU) -Wall -Os -DF_CPU=$(F_CPU)

$(PROJECT).hex: $(PROJECT).out
	avr-objcopy -O ihex $(PROJECT).out $(PROJECT).c.hex;\
	avr-size --mcu=$(MMCU) --format=avr $(PROJECT).out
 
$(PROJECT).out: $(SOURCES)
	avr-gcc $(CFLAGS) -I./ -o $(PROJECT).out $(SOURCES)
 
program-bsd: $(PROJECT).hex
	avrdude -p t44 -c bsd -U flash:w:$(PROJECT).c.hex

program-dasa: $(PROJECT).hex
	avrdude -p t44 -P /dev/ttyUSB0 -c dasa -U flash:w:$(PROJECT).c.hex

program-avrisp2: $(PROJECT).hex
	avrdude -p t44 -P usb -c avrisp2 -U flash:w:$(PROJECT).c.hex

program-avrisp2-fuses: $(PROJECT).hex
	avrdude -p t44 -P usb -c avrisp2 -U lfuse:w:0x5E:m

program-usbtiny: $(PROJECT).hex
	avrdude -p t44 -P usb -c usbtiny -U flash:w:$(PROJECT).c.hex

program-usbtiny-fuses: $(PROJECT).hex
	avrdude -p t44 -P usb -c usbtiny -U lfuse:w:0x5E:m

program-dragon: $(PROJECT).hex
	avrdude -p t44 -P usb -c dragon_isp -U flash:w:$(PROJECT).c.hex

Looking at the end of hello.ftdi.44.echo.c code, the “main” section is the setup that only runes once, and the while loop repeats forever.
I start from something simple by blinking an LED, then go one to blink 2nd one and the third one.

To blink the LED, we need to first understand which LED goes to which pin. Here is the pin map.

ATtiny44-841 ATTINY44-20

I looked up my board and here is what I have, yours might be different. It’s handy to put this in your code when coding and debuting your hardware, so you don’t need to look it up over and over again.

LED 1 = PA2 = Pin2
LED2 = PA3 = Pin3
LED 3= PA7 = Pin 7
Button = PB2 = Pin 8 Screen Shot 2014-10-14 at 5.29.40 PM

fin_stuff_board2

Below is what I use to replace the hello.ftdi.44.echo.c file. To avoid confusion, I rename the hello.ftdi.44.echo.c files. to blink_and_button.c. I then open the hello.ftdi.44.echo.c.make file and edit the first line from hello.ftdi.44.echo to blink_and_button.c. This will make the make file reference the blink_and_button.c when compiling the program. The .make file contains instructions on how to compiling the .c file.

There seemed to be a lot of questions about what PINA and DDRA and PORTB and such actually did.

Here’s an excellent write up from Greg Borenstein over in Playful Systems that starts with the basic Arduino blink code and breaks it down into what is ACTUALLY going on in the AVR and in its registers.
http://urbanhonking.com/ideasfordozens/2009/05/18/an_tour_of_the_arduino_interna/
Although he’s talking about an AVR with A,B,C,and D pins, just pretend he’s talking about your tiny44 with only A and B. (Dan Novy for this link)
#include <avr/io.h>
#include <avr/delay.h>

#define blink_delay 10

/*
Pin Mapping
LED 1 = PA2 = Pin 2
LED2 = PA3 = Pin 3
LED 3= PA7 = Pin 7
Button = PB2 = Pin 8 
*/

int main(void)
{
	DDRA |= _BV(PA2);	// Enable output on LED pin 2
	DDRA |= _BV(PA3);	// Enable output on LED pin 3
	DDRA |= _BV(PA7);	// Enable output on LED pin 7

	// Loop
	while (1)
	{
		                // Upward direction
				PORTA |= _BV(PA2);	// turn LED on
				_delay_ms(blink_delay);
				PORTA = 0;			// turn LED off
				_delay_ms(blink_delay);

				PORTA |= _BV(PA3);	// turn LED on
				_delay_ms(blink_delay);
				PORTA = 0;			// turn LED off
				_delay_ms(blink_delay);

				PORTA |= _BV(PA7);	// turn LED on
				_delay_ms(blink_delay);
				PORTA = 0;			// turn LED off
				_delay_ms(blink_delay);



		                // Downward direction
				PORTA |= _BV(PA7);	// turn LED on
				_delay_ms(blink_delay);
				PORTA = 0;			// turn LED off
				_delay_ms(blink_delay);

		
				PORTA |= _BV(PA3);	// turn LED on
				_delay_ms(blink_delay);
				PORTA = 0;			// turn LED off
				_delay_ms(blink_delay);


				PORTA |= _BV(PA2);	// turn LED on
				_delay_ms(blink_delay);
				PORTA = 0;			// turn LED off
				_delay_ms(blink_delay);
	}
}

Now with the button

 

 

 

After I have all LED setup and blinking, I then added the button to trigger the if or else statement.

I created the bi-directional blink LED pattern, where the LED blink from left to right without button press, then the other direction when the button is pressed.

Here is my c code.
Here is my .make file which references the .c file at the beginning of the code.

To compile the program use this in your terminal (make sure you cd the right directory )
$ sudo make -f hello.ftdi.44.echo.c.make

To make the the chip using the external clock so the programmer can talk to it use.
$ sudo make -f hello.ftdi.44.echo.c.make program-usbtiny-fuses

To upload the program use this in your terminal
$ sudo make -f hello.ftdi.44.echo.c.make program-usbtiny

Now with the button

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

#define blink_delay 10

/*
Pin Mapping
LED 1 = PA2 = Pin 2
LED2 = PA3 = Pin 3
LED 3= PA7 = Pin 7
Button = PB2 = Pin 8 
*/

int main(void)
{
	DDRA |= _BV(PA2);	// Enable output on LED pin 2
	DDRA |= _BV(PA3);	// Enable output on LED pin 3
	DDRA |= _BV(PA7);	// Enable output on LED pin 7

	PORTB = _BV(PB2);	// Activate button


	// Loop
	while (1)
	{
		if (PINB & _BV(PB2)) {

		                // Upward direction
				PORTA |= _BV(PA2);	// turn LED on
				_delay_ms(blink_delay);
				PORTA = 0;			// turn LED off
				_delay_ms(blink_delay);

				PORTA |= _BV(PA3);	// turn LED on
				_delay_ms(blink_delay);
				PORTA = 0;			// turn LED off
				_delay_ms(blink_delay);

				PORTA |= _BV(PA7);	// turn LED on
				_delay_ms(blink_delay);
				PORTA = 0;			// turn LED off
				_delay_ms(blink_delay);

		}
		else
		{
		                // Downward direction
				PORTA |= _BV(PA7);	// turn LED on
				_delay_ms(blink_delay);
				PORTA = 0;			// turn LED off
				_delay_ms(blink_delay);

		
				PORTA |= _BV(PA3);	// turn LED on
				_delay_ms(blink_delay);
				PORTA = 0;			// turn LED off
				_delay_ms(blink_delay);


				PORTA |= _BV(PA2);	// turn LED on
				_delay_ms(blink_delay);
				PORTA = 0;			// turn LED off
				_delay_ms(blink_delay);

		}

			
		

	}
}

Download the Files

Programing with Arduino

Use it like Arduino, here is the pin map.

ATtiny44-84

Here is my blinking code & video

Using internal pullup to sense the push of the button since I don’t have a pull up resistor on the board.
pinMode(buttonPin, INPUT_PULLUP);

const int buttonPin = 8;     // the number of the pushbutton pin
int buttonState = 0;         // variable for reading the pushbutton status
int switcher=0;
int delaytime =80;
void setup() {   
  
  pinMode(buttonPin, INPUT_PULLUP); // using pullup to sense the push of the button
  pinMode(7, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(2, OUTPUT);
}

void loop() {

  buttonState = digitalRead(buttonPin);
  
    if (buttonState == HIGH) { switcher = 1; }
    else { switcher = 0; }    
  
  if (switcher == 1 ) {     
  
        digitalWrite(7, HIGH);   
        delay (delaytime);
        digitalWrite(7, LOW);           
       digitalWrite(3, HIGH);   
        delay (delaytime);
        digitalWrite(3, LOW);   
        digitalWrite(2, HIGH);   
        delay (delaytime);
        digitalWrite(2, LOW);   
  } 
    else if (switcher == 0 ) {     
        digitalWrite(2, HIGH);   
        delay (delaytime);
        digitalWrite(2, LOW);   
        digitalWrite(3, HIGH);   
        delay (delaytime);
        digitalWrite(3, LOW);   
        digitalWrite(7, HIGH);   
        delay (delaytime);
        digitalWrite(7, LOW);                           
  }
}

Arduino Servo Library will not work with Attiny 44

ATtiny44 has 8-bit Timer/Counter, but the Servo-library relies on having a 16-bit timer available.

You can download Servo8Bit Servo library for the ATtiny45/85.

 

Adding a light sensor to light up the LED

int sensorPin = 7;    // select the input pin for the potentiometer
int led = 6;    // select the input pin for the potentiometer
int sensorValue;
int brightness;
void setup() {
  // declare the ledPin as an OUTPUT:

  pinMode(led, OUTPUT);

}

void loop() {
  
  sensorValue = analogRead(sensorPin);
  brightness=map(sensorValue, 0, 20, 0, 255);
  analogWrite(led, brightness);
}

Serial Print … Fail

Serial.begin(57600);  // does not work
Attiny 44 does not have serial communication.