Search code examples
c++arduinobit-manipulationbitwise-operatorssamd21

Low-level bit shift register class using port manipulation for Arduino Zero


I'm currently trying to code a lower-lever bit shift register (74HC595) for Arduino Zero (that is based on SAMD21 Cortex M0).

I've already done a higher-level class with looks like that :

BitRegister.h

#ifndef BitRegister_h
#define BitRegister_h

#include "Arduino.h"

class BitRegister
{
  public:
    BitRegister(byte dataPin, byte clockPin, byte latchPin, uint8_t registerSize = 1);
    
    void sendData(uint8_t led);
    void shiftOut2(uint8_t bitOrder, uint8_t val);

  private:
    byte m_dataPin;
    byte m_clockPin;
    byte m_latchPin;
    uint8_t m_registerSize; //allows register's cascade
};

#endif

BitRegister.cpp

#include "BitRegister.h"

//**************************************************************
  
BitRegister::BitRegister(byte dataPin, 
                        byte clockPin, 
                        byte latchPin,
                        uint8_t registerSize)
:   m_dataPin(dataPin),
    m_clockPin(clockPin),
    m_latchPin(latchPin),
    m_registerSize(registerSize) //Number of register (if cascade)
{
  pinMode(m_dataPin, OUTPUT);
  pinMode(m_clockPin, OUTPUT);
  pinMode(m_latchPin, OUTPUT);
}

//************************************************************** 

void BitRegister::sendData(uint8_t led)
{
  led -= 1; //first LED is number 1

  digitalWrite(m_latchPin, LOW);
  for(int i = 1; i < m_registerSize + 1; i++){
    int registerNumber = m_registerSize - i; //The number of the register we're working on in the loop
    shiftOut(m_dataPin, m_clockPin, MSBFIRST, ~( 1 << (led - registerNumber*8) ) & 0xFF);
  }

  digitalWrite(m_latchPin, HIGH);
}

void BitRegister::shiftOut2(uint8_t bitOrder, uint8_t val)
{
    uint8_t i;

    for (i = 0; i < 8; i++)  {
        if (bitOrder == LSBFIRST)
            digitalWrite(m_dataPin, !!(val & (1 << i)));
        else    
            digitalWrite(m_dataPin, !!(val & (1 << (7 - i))));

        digitalWrite(m_clockPin, HIGH);
        digitalWrite(m_clockPin, LOW);        
    }
} 

The problem is that I have to use it with many registers in cascade ant the shiftOut2 method (based on Arduino's shiftOut method) is really slow (I think due to the multiple digitalWrite).

So based on this SAMD21 Cortex M0 tutorial and on the Arduino Zero Pinout Diagram (below), I tried to create a lower-level of my bit shift register class. Arduino Zero Pinout Diagram

The problem I'm currently facing is that I don't manage to rewrite the totality of my shiftOut2 method so for my test I have to write the ports numbers in hard directly in the method's body.

I connect my registers to Arduino's pins 10, 11 and 12 which are SAMD21 ports 18, 16 and 19.

The code of my class (which works) is the following :

LowBitRegister.h is the same as BitRegister.h (except class name and class constructor name).

LowBitRegister.cpp

#include "LowBitRegister.h"

//**************************************************************  

LowBitRegister::LowBitRegister(byte dataPin, 
                        byte clockPin, 
                        byte latchPin,
                        uint8_t registerSize)
:   m_dataPin(dataPin),
    m_clockPin(clockPin),
    m_latchPin(latchPin),
    m_registerSize(registerSize) //Number of register (if cascade)
{
  REG_PORT_DIR0 |= (1 << 18) | (1 << 19) | (1 << 16); //Set dataPin, clockPin and latchPin to OUTPUT
}

//************************************************************** 

void LowBitRegister::sendData(uint8_t led)
{
  led -= 1; //first LED is number 1
  
  REG_PORT_OUT0 &= ~(1 << 16); //Set latchPin to LOW
  
  for(int i = 1; i < m_registerSize + 1; i++){
    int registerNumber = m_registerSize - i; //The number of the register we're working on in the loop
    shiftOut2(MSBFIRST, ~( 1 << (led - registerNumber*8) ) & 0xFF);
  }
  
  REG_PORT_OUT0 |= (1 << 16); //Set latchPin to HIGH
}

void LowBitRegister::shiftOut2(uint8_t bitOrder, uint8_t val)
{
  uint8_t i;

  for (i = 0; i < 8; i++)  {
    if (bitOrder == LSBFIRST)
      digitalWrite(m_dataPin, !!(val & (1 << i)));
    else    
      digitalWrite(m_dataPin, !!(val & (1 << (7 - i))));

    REG_PORT_OUT0 |= (1 << 19); //Set clockPin to HIGH
    REG_PORT_OUT0 &= ~(1 << 19); //Set clockPin to LOW     
  }
}

As you can see, the only parts I don't manage to rewrite are :

digitalWrite(m_dataPin, !!(val & (1 << i)));

and

digitalWrite(m_dataPin, !!(val & (1 << (7 - i))));

If you have an idea to solve my problem I will be happy to read it.

Thank you!


Solution

  • I finally found a solution (but if you see any optimization I am interested) :

    LowBitRegister.h

    #ifndef LowBitRegister_h
    #define LowBitRegister_h
    
    #include "Arduino.h"
    
    
    class LowBitRegister
    {
      public:
        LowBitRegister(byte dataPin, byte clockPin, byte latchPin, uint8_t registerSize = 1); //Multiple registers cascade
        
        void sendData(uint8_t led);
        void lowShiftOut(uint8_t bitOrder, uint8_t val);
    
      private:
        byte m_dataPin;
        byte m_clockPin;
        byte m_latchPin;
        uint8_t m_registerSize;
    };
    
    #endif
    

    LowBitRegister.cpp

    #include "LowBitRegister.h"
    
    //**************************************************************  
    
    LowBitRegister::LowBitRegister(byte dataPin, 
                            byte clockPin, 
                            byte latchPin,
                            uint8_t registerSize)
    :   m_dataPin(dataPin),
        m_clockPin(clockPin),
        m_latchPin(latchPin),
        m_registerSize(registerSize) //Number of register (if cascade)
    {
      REG_PORT_DIR0 |= (1 << m_dataPin) | (1 << m_clockPin) | (1 << m_latchPin); //Set dataPin, clockPin and latchPin to OUTPUT
    }
    
    //************************************************************** 
    
    void LowBitRegister::sendData(uint8_t led)
    {
      led -= 1; //first LED is number 1
      
      REG_PORT_OUT0 &= ~(1 << m_latchPin); //Set latchPin to LOW
      
      for(int i = 1; i < m_registerSize + 1; i++){
        int registerNumber = m_registerSize - i; //The number of the register we're working on in the loop
        lowShiftOut(MSBFIRST, ~( 1 << (led - registerNumber*8) ) & 0xFF);
      }
      
      REG_PORT_OUT0 |= (1 << m_latchPin); //Set latchPin to HIGH
    }
    
    void LowBitRegister::lowShiftOut(uint8_t bitOrder, uint8_t val)
    {
      uint8_t i;
    
      for (i = 0; i < 8; i++) {
        if (bitOrder == LSBFIRST){
          if(val & (1 << i))
            REG_PORT_OUT0 |= (1 << m_dataPin);
          else
            REG_PORT_OUT0 &= ~(1 << m_dataPin);
        }
        else {
          if (val & (1 << (7 - i)))
            REG_PORT_OUT0 |= (1 << m_dataPin);
          else
            REG_PORT_OUT0 &= ~(1 << m_dataPin);
        }
        REG_PORT_OUT0 |= (1 << m_clockPin); //Set clockPin to HIGH
        REG_PORT_OUT0 &= ~(1 << m_clockPin); //Set clockPin to LOW     
      }
    }