Skip to content
Snippets Groups Projects

Programming in C

Introduction

Programming in C was not that hard for me, I have learned C++ and I have a previous experience with Java. I know the loops like for and while, conditional statement like if, variables like int, classes and libraries. I have also programmed an Arduino before, but I have never programmed in C or an micro controller on a lower level.

The problem was not the syntax of C, the problem was to understand the registers and how to use them for a specific function.

The micro controller used is ATTiny44.

Input and Output

ATTiny44 has 14 pins, 12 of them are for reading inputs and applying outputs. An input or an output can be either digital or analog. Any operation of a computer or a micro controller is simply a 0 or 1 which is 0v or 5v, digital is represented by 0 and 1 whereas the analog is represented as voltage range.

The micro controller should know if a specific pin will be used as an input or an output. If it is an input, the DDR register has to be 0 then it should know if it will write or read a specific pin. If the pin is an input and the operation is to write then the 0 is to deactivate pull up resistor and 1 is to activate pull up resistor, if the operation is to read then 0 is means 0v and 1 means 5v. If the pin is output, DDR registers has to be 1 then if it writes 0 means 0v and 1 means write 5v.

The process programmatically can be seen in the following code which I have used as the starting point in C programming.

Hardware is explained in Electronics documentation

/*
This code was created by Ahmad Fares on 4th-Dec-2017.
The code is used to switch on an LED when a Button is pressed.

MIT License
*/
#define F_CPU 1000000UL //Define the speed 1Mhz
#include <avr/io.h> //Include io library
#include <util/delay.h> //include delay library

int main (void)
{

 DDRA = 0b00001000; // set PA3 as output in DDRA.
 DDRB = 0b00000000; // set PB2 as input in DDRB.
 PORTB = 0b00000100; // activate pull up resistor.

 while(1) {

   if(PINB == 0b00000000){ //Read PB2

     PORTA = 0b00000000; //Switch on the LED
   }else{
     PORTA = 0b00001000; //Switch off the LED
   }

}

}

Another way to define inputs and outputs

It seems hard to define inputs and outputs at the way explained above, sometimes I forget a bit, sometimes I need to define only one pin not all of them so another way is to use bitwise method.

(register) |= (1 << (bit)) means to write 1 (register) &= ~(1 << (bit)) means to write 0 (register) & (1 << (bit)) to read statues.

The following code explains this method:

/*
This code was created by Ahmad Fares on 4th-Dec-2017.
The code is used to switch on an LED when a Button is pressed using bitewise method

MIT License
*/
#define F_CPU 1000000UL //Define the speed 1Mhz
#include <avr/io.h> //Include io library
#include <util/delay.h> //include delay library

int main (void)
{


 DDRA |= (1 << PA3); // set PA3 as output in DDRA.
 DDRB &= ~(1 << PB2); // set PB2 as input in DDRB.

 PORTB |= (1 << PB2); // activate pull up resistor.

 while(1) {

   if(PINB & (1 << PB2)){ //Read PB2

      PORTA &= ~(1 << PA3); //Switch on the LED
   }else{
     PORTA |= (1 << PA3);; //Switch off the LED
   }

}

}

Macron

A much simpler way of defining inputs and outputs and writing zeros and ones is macron.

Using macron, I have been able to write a code in human understandable language rather than registers.

The following code explains the macron method

/*
This code was created by Ahmad Fares on 4th-Dec-2017.
The code is used to switch on an LED when a Button is pressed using Macron

MIT License
*/
#define F_CPU 1000000UL //Define speed

//Define Macron
#define setbit(register, bit)   (register) |= (1 << (bit))
#define clearbit(register, bit)   (register) &= ~(1 << (bit))
#define testbit(register, bit)   (register) & (1 << (bit))

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

int main (void)
{

 setbit(DDRA, PA3); // set PA3 as output in DDRA.
 clearbit(DDRB, PB2); // set PB2 as input in DDRB.
 setbit(PORTB, PA3); // activate pull up resistor.

 while(1) {

   if(testbit(PINB, PB2)){ //Read button statues

     setbit(PORTA, PA3); //Switch off LED
   }else{
     clearbit(PORTA, PA3); // Switch on LED
   }

}

}

Interrupts

Interrupts are used to force the microcontroller to pause the code and execute a specific block of code inside ISR function.

I have used interrupts to switch on an LED when a button is pressed instead of asking the microcontroller "Is button pressed?" by continuously reading the PB2 statues.

/*
This program was created by Ahmad Fares on 7th-Dec-2017
The purpose of this program is to use interrupt to detect a button statues
MIT License
*/

#define F_CPU 1000000UL //Define speed

//Define Macron
#define setbit(register, bit)   (register) |= (1 << (bit))
#define clearbit(register, bit)   (register) &= ~(1 << (bit))
#define testbit(register, bit)   (register) & (1 << (bit))

#include <avr/interrupt.h> //include interrupt library
#include <avr/io.h> //include io library


// interrupt function, PCINT1_vect is the vector for interrupt request
 ISR(PCINT1_vect)
{
 if ( PINB & (1<<PB2) ) // test PINA7 if true then
  setbit(PORTA,PA3); //turn off LED
 else
  clearbit(PORTA,PA3); //Turn on LED
}

int main (void){
  sei(); // activate interrupts globally
  setbit(DDRA, PA3); // set PA3 as output in DDRA.
  clearbit(DDRB, PB2); // set PB2 as input in DDRB.
  setbit(PORTB, PB2); // activate pull up resistor.
  setbit(GIMSK,PCIE1); //Enable pin change interrupt PORTB
  setbit(PCMSK1, PCINT10); //Enabl pin change interrupt in pin PB2
  setbit(PINA,PA3);

while(1){
//Doing nothing until the interrupt occurs
}
}

Timers

Timers are registers inside the microcontroller, but run independently and can be used to run a specific code after a certain time. In my case, the ATTiny44 has three 8-bit counter registers: TCNT0, TCNT1L and TCNT1H.

Those registers don't actually count time, they count cycles and from these cycles we can count time. There are two operation modes: normal and CTC (clear time on compare match). In the normal mode a specific block of code is executed after the register overflows whereas in the CTC mode which is the most commonly used, a block of code is executed after a certain number of cycles or seconds.

How to count 1 second

ATTiny44 runs at 1Mhz, which means 1 cycle every 1 micro seconds, if a normal mode is chosen so the specific task will be executed every 0.065536 seconds which is very fast, In my case I wanted to toggle the LED state every second so I have chose CTC mode and counted the 1 second as follows:

When the register TCNT overflows at normal mode, the number is 65536 which is 2^16 (16 bit since TCNT is two 8-bit register) so a prescalar is used to slow it down. The prescalars available are 1, 8, 64, 256, 1024.

First is choose the first prescalar which gives a time higher than the required 1 second.

1 0.065536
8 0.524288
64 4.194304

64 prescalar gives 4.194304 seconds, using the rule of third, I have calculated the number corresponds to 1 second as follows

4.194304 --> 65536

1 ---------> X

X = 65536/4.194304 = 15625 exact.

Example

/*
This code was created by Ahmad Fares on 10-Dec-2017
Timer with timer/counter 1 hardware to toggle LED state every 1 seconds
MIT License
*/
#define setbit(register, bit)   (register) |= (1 << (bit))
#define clearbit(register, bit)   (register) &= ~(1 << (bit))
#define testbit(register, bit)   (register) & (1 << (bit))
#define togglebit(register,bit)  (register) ^= (1<<bit)
#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

ISR(TIM1_COMPA_vect){
  togglebit(PORTA,PA3);

}

int main (void) {

  DDRA |= (1 << PA3); // set LED pin as output
  OCR1A = 15625; // at 1/64 prescalar this is exactly 1 second.
  setbit(TCCR1B, WGM12);
  sei();
  setbit(TIMSK1,OCIE1A); // enable CTC interrupt when TCNT1 = OCR1A
  setbit(TCCR1B,CS11);
  setbit(TCCR1B,CS10); //These 2 registers set prescalar to 1/64 and start the counter.
//  setbit(PORTA,PA3);

while(1) {

}
  }

ADC

Until now, I have been dealing with digital inputs and outputs, it was easy and direct since the microcontroller is a digital device which understands only 0 and 1, but when analog sensors and signals are needed then the analog to digital converter should be used.

ADC is an integrated hardware which converts the analog signals to discrete digital values. In my case, the attiny44 has a 10 bit ADC which converts the voltage range read by the input pin to a corresponding value ranging from 0 to 1023.

There two operating modes for the ADC, single conversion and free running. The single conversion mode requires the user to initiate each conversion process whereas free running mode keeps doing the conversion process automatically. I have used free running mode as it makes ADC easier.

Sample Code.

In sample code put also problems see notebook

Write more about registers in interrupt and other

Push LED, Interrupt, delay, withoutPullUp

Add video of each code