Semaphore

Hi,

is it possible to implement semaphores on a Arduino? Thanks in advance!

Best regards, Michael

Yes. You can take a look at how FreeRTOS handles this subject, or if you're just worried about interrupts you can take various steps to protect your data so that it's interrupt-friendly.

  • Ben

Yes. You can take a look at how FreeRTOS handles this subject, or if you’re just worried about interrupts you can take various steps to protect your data so that it’s interrupt-friendly.

Thanks for the quick reply. At present my problem is that I send data over SPI to a another MCU. I have code in an ISR (timer overflow) which sends data to the MCU and code which sends data in the main loop over SPI to the MCU. At some point it looks like that one SPI write call overwrites the SPDR of the other SPI write call. Maybe you have an idea to solve that without semaphores.

Thanks!

#include <avr/interrupt.h>

// Set the timer frequency to 250 Khz 
#define TIMER_CLOCK_FREQ 250000.0
// Master Output Slave Input
#define MOSI 11
// Master Input Slave Output
#define MISO 12
// Serial Clock
#define SCK  13
// Slave Select 1
#define SS1   2
// Slave Select 2
#define SS2   3
// Slave Select 3
#define SS3   8
// Slave Select 4
#define SS4   9
// Row 0
#define R0    4
// Row 1
#define R1    5
// Row 2
#define R2    6
// Row 3
#define R3    7
// Define the baudrate
#define BAUDRATE 9600
// The led register
byte leds[250];
//
byte clear;
//
int data = 0;
//
int size = 0;
//
#define INIT_TIMER_COUNT 6
//
#define RESET_TIMER TCNT2 = INIT_TIMER_COUNT  

// ISR for Timer/Counter Overflow
ISR(TIMER2_OVF_vect){  
   //RESET_TIMER;  
   static int rowNumber = 1;
   digitalWrite(SS1, LOW);
   // Signalize through SPI that the next packet will be the row number
   writeData(0xFF);
   // Send the row number
   writeData(rowNumber);
   digitalWrite(SS1, HIGH);
   digitalWrite(SS2, LOW);
   // Signalize through SPI that the next packet will be the row number
   writeData(0xFF);
   // Send the row number
   writeData(rowNumber);
   digitalWrite(SS2, HIGH);
   digitalWrite(R0, ((rowNumber & 0x01) ? LOW : HIGH));
   digitalWrite(R1, ((rowNumber & 0x02) ? LOW : HIGH));
   digitalWrite(R2, ((rowNumber & 0x04) ? LOW : HIGH));
   digitalWrite(R3, ((rowNumber & 0x08) ? LOW : HIGH));
   rowNumber = (rowNumber == 15) ? 1 : rowNumber + 1;
   
}

// Write bytewise data over SPI
char writeData(volatile unsigned char data){
  // Start the transmission
  SPDR = data;
  // Wait till the end of the transmission
  while(!(SPSR & (1 << SPIF))){};
  // Return the received byte
  return SPDR;
}

// Initialize serial connection and set pin modes
void setup(){
  // Set the baudrate
  Serial.begin(BAUDRATE);
  // Set the pin modes
  pinMode(MOSI, OUTPUT);
  pinMode(MISO, INPUT);
  pinMode(SCK, OUTPUT);
  pinMode(SS1, OUTPUT);
  pinMode(SS2, OUTPUT);
  pinMode(SS3, OUTPUT);
  pinMode(SS4, OUTPUT);
  pinMode(R0, OUTPUT);
  pinMode(R1, OUTPUT); 
  pinMode(R2, OUTPUT);
  pinMode(R3, OUTPUT);
  // Disable the slave devices
  digitalWrite(SS1, HIGH);
  digitalWrite(SS2, HIGH);
  digitalWrite(SS3, HIGH);
  digitalWrite(SS4, HIGH);
  // Disable the rows
  digitalWrite(R0, LOW);
  digitalWrite(R1, LOW);
  digitalWrite(R2, LOW);
  digitalWrite(R3, LOW);
  // Set the SPI Control Register SPCR to the value 01010000
  SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0);
  // Overwrite previous junk with the SPI status register
  clear = SPSR;
  // Overwrite previous junk with the SPI data register      
  clear = SPDR;
  // Timer 2 Settings: Timer Prescaler /64 , mode 0
  // Timer clock = 16MHz/64  = 250 Khz
  TCCR2A = 0;
  //TCCR2B = (1 << CS22 | 0 << CS21 | 0 << CS20);
  TCCR2B = (1 << CS22 | 1 << CS21 | 1 << CS20);
  // Timer 2 Overflow Interrupt Enable
  TIMSK2 = 1 << TOIE2;
}

void loop(){
  static int i = 0;

  digitalWrite(SS1, LOW);
  writeData(0xEE);
  writeData(i);
  digitalWrite(SS1, HIGH);
  digitalWrite(SS1, LOW);
  writeData(0xFF);
  writeData(0x00);
  digitalWrite(SS1, HIGH);

  Serial.println("ACK");
  delay(1500);
  i++;
  if(i == 15){
    i = 0;
  }
}

There are several ways to deal with this.

A) Don't initiate an SPI transmission while one is in progress. When a transmission is in progress, the SPIF bit of the SPSR register is cleared. This bit will also be cleared before your program makes its first transmission, so you might need an extra variable to help you figure out when you're transmitting. This means that your writeData() method will need to start with a routine that waits for any existing transmission to finish (this is important since one could be in progress when your ISR calls writeData()).

B) Disable interrupts in your main loop while the SPI transmission is taking place, and re-enable them when it is finished. You can only use this option if your timer overflow period is longer than the time it will take for you to carry out your main loop transmission, otherwise you will miss overflow interrupts by doing this.

C) Don't perform an SPI transmission from your ISR. Instead, have it use global variables to queue a transmission that will then be carried out by the main loop. For example:

volatile unsigned char transmit = 0; ISR(timer overflow) { transmit = 1; }

void loop() { if (transmit) { transmit = 0; initiate SPI transmission } ... }

  • Ben

Hi, another way is to have an small RTOS. We have ported FreeRTOS past year (2009), and it has Semaphores and MUTEX, you can look at these links:

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1256745982/0

The versión 0.1 Alpha (for the 0017 IDE) is available here:

http://www.multiplo.org/duinos/wiki/index.php?title=Main_Page

And there is a v0.2 Alpha which runs in 0018 at:

http://novell.chel.ru/get.php?file=DuinOS_v0.2_Alpha

We did not make the v0.2, but are working in the v0.3.

Regards, Julián http://robotgroup.com.ar Back to top