I have been developing my own software SPI library but testing it I am having problems with receiving MISO data.
The .transfer function is sending the byte data okay and the SRAM chip I'm testing it on is returning the expected result when looked at using a logic analyser but I am not getting what I expected when reading the MISO port & pin.
Could some have a look and point out where I'm going wrong please.
The library is using direct port manipulation to try and speed things up and when initialized it grabs and stores the port & mask for the supplied arduino pin numbers.
I have attached a logic screendump to show the SRAM is responding to a read memory request with the correct data.
The test sketch
#include "SPIsoft.h"
#define cs 13
#define miso 6
#define mosi 5
#define clk 7
#define WRITE 2
#define READ 3
SPIsoft sSPI(miso, mosi, clk);
const uint32_t ramSize = 0x1FFFF; // 128K x 8 bit
char buffer1[] = {"Hello"};
char buffer2[sizeof(buffer1)];
void setup(){
Serial.begin(115200);
sSPI.begin();
delay(2000);
Serial.println("Setup Stage 1");
digitalWrite(cs,HIGH);
pinMode(cs,OUTPUT);
//sSPI.setBitOrder(LSBFIRST);
Serial.println("\r\nFill Memory with Buffer.");
digitalWrite(cs,LOW);
sRam_writeBuffer(0, buffer1, sizeof(buffer1) - 1);
digitalWrite(cs,HIGH);
}
void loop(){
delay(2000);
Serial.println("\r\nRead Buffer.");
digitalWrite(cs,LOW);
sRam_readBuffer(0,buffer2,sizeof(buffer2) - 1);
digitalWrite(cs,HIGH);
for(byte i = 0; i < sizeof(buffer2) - 1; i++){
Serial.print((byte)buffer1[i],BIN);
Serial.print("\t");
Serial.println((byte)buffer2[i],BIN);
}
Serial.println(buffer1);
Serial.println(buffer2);
}
void sRam_readBuffer(uint32_t address, char *buffer, uint32_t length) {
setAddressMode(address, READ);
byte x;
for (uint32_t i = 0; i < length; i++) buffer[i] = sSPI.transfer(0xFF);
}
void sRam_writeBuffer(uint32_t address, char *buffer, uint32_t length) {
setAddressMode(address, WRITE);
for (uint32_t i = 0; i < length; i++) sSPI.transfer(buffer[i]);
}
void setAddressMode(uint32_t address, byte mode) {
sSPI.transfer(mode);
sSPI.transfer((byte)(address >> 16));
sSPI.transfer((byte)(address >> 8));
sSPI.transfer((byte)address);
}
Library header
#ifndef _SPIsoft_H
#define _SPIsoft_H
#if ARDUINO < 100
#error Not supported on IDE's < v1.0
#else
#include <Arduino.h>
#endif
// Copied from SPI.h (Dangerous)
#define SPI_CLOCK_DIV4 0x00
#define SPI_CLOCK_DIV16 0x01
#define SPI_CLOCK_DIV64 0x02
#define SPI_CLOCK_DIV128 0x03
#define SPI_CLOCK_DIV2 0x04
#define SPI_CLOCK_DIV8 0x05
#define SPI_CLOCK_DIV32 0x06
#define SPI_MODE0 0x00
#define SPI_MODE1 0x04
#define SPI_MODE2 0x08
#define SPI_MODE3 0x0C
class SPIsoft {
public:
SPIsoft(uint8_t, uint8_t, uint8_t);
void begin();
void end();
byte transfer(byte _data);
void setBitOrder(uint8_t);
void setDataMode(uint8_t);
void setClockDivider(uint8_t);
private:
volatile uint8_t *_misoPort;
uint8_t _misoMask;
volatile uint8_t *_mosiPort;
uint8_t _mosiMask;
volatile uint8_t *_clkPort;
uint8_t _clkMask;
uint8_t _bitOrder;
uint8_t _clkPin;
uint8_t _mosiPin;
uint8_t _misoPin;
};
#endif
Library core
#include "pins_arduino.h"
#include <SPIsoft.h>
SPIsoft::SPIsoft(uint8_t misoPin, uint8_t mosiPin, uint8_t clkPin){
uint8_t port;
// Store the pins
_misoPin = misoPin;
_mosiPin = mosiPin;
_clkPin = clkPin;
// Break down the pin numbers into port & mask values for direct port manipulation
port = digitalPinToPort(misoPin);
_misoPort = portInputRegister(port);
_misoMask = digitalPinToBitMask(misoPin);
port = digitalPinToPort(mosiPin);
_mosiPort = portOutputRegister(port);
_mosiMask = digitalPinToBitMask(mosiPin);
port = digitalPinToPort(clkPin);
_clkPort = portOutputRegister(port);
_clkMask = digitalPinToBitMask(clkPin);
// Set default bit order, mode & speed
setBitOrder(MSBFIRST);
//setClockDivider(SPI_CLOCK_DIV64);
//setDataMode(SPI_MODE0);
}
void SPIsoft::begin(){
digitalWrite(_misoPin,LOW); // *** Change if datamode implemented
pinMode(_misoPin,INPUT); // Really needed?
pinMode(_mosiPin,OUTPUT);
digitalWrite(_clkPin,LOW); // *** Change if datamode implemented
pinMode(_clkPin,OUTPUT);
}
void SPIsoft::end(){
// Do nothing
}
byte SPIsoft::transfer(byte _data){
uint8_t i, tmpByte;
if(_bitOrder == MSBFIRST) { // Need to swap bit order?
for(i = 0;i < 8;i++){
tmpByte |= ((_data >> i) & 1) << (7 - i);
}
_data = tmpByte;
}
uint8_t oldSREG = SREG; // Backup register
tmpByte = 0;
cli(); // turn off interrupts to transfer
for (i = 0; i <8; i++){ // 8 bits to transfer
if (_data & (1 << i)){
*_mosiPort |= _mosiMask; // Set mosi pin
}
else{
*_mosiPort &= ~_mosiMask; // Clear mosi pin
}
*_clkPort |= _clkMask; // Set clock pin
delayMicroseconds(1); // Delay (* needs to be a better way)
*_clkPort &= ~_clkMask; // Clear clock pin
delayMicroseconds(3); // Delay to allow slave time to load bit
// *********** Suspect problem is here
if (*_misoPort & _misoMask){ // Read the miso port bit
tmpByte &= ~(1 << i);
}
else{
tmpByte |= (1 << i);
}
}
SREG = oldSREG; // Restore register (turn interrupts back on)
*_mosiPort &= ~_mosiMask; // Ensure MOSI is cleared *** Change if mode implemented
_data = tmpByte;
// Reconstruct the return value bit order if needed
if(_bitOrder == MSBFIRST) { // Need to swap bit order?
for(i = 0;i < 8;i++){
tmpByte |= ((_data >> i) & 1) << (7 - i);
}
}
return tmpByte; // Return with read data
}
void SPIsoft::setBitOrder(uint8_t bitOrder){
_bitOrder = bitOrder;
}
void SPIsoft::setDataMode(uint8_t dataMode){
// Not implemented yet
//#error Not_Yet_Implemented
}
void SPIsoft::setClockDivider(uint8_t clockDivider){
// Not implemented yet
//#error Not_Yet_Implemented
}