Hi,
I am working on switching a series of solenoid valves via an NXP/Freescale MCZ33996 controlled over SPI from an Arduino (Pro Mini, eventually, but I also have a Uno & Nanos for testing). I have worked on some electronics before, but not on anything of this scale and not with SPI communication, so it is possible that the below described problem is due to a lack of experience/knowledge on my part.
Based on the MCZ33996 datasheet, I drew the conclusion that this IC operates with clock polarity (CPOL) 0 and clock phase (CPHA) 1 for SPI communication, which would be SPI mode 1, expects the most significant bit to be passed first and can operate up to 6 MHz. Would anyone please be able to confirm that I have read this datasheet correctly? Relevant diagram is on page 8.
As an initial test, I have connected the Arduino to the IC (connections given below), I've connected LEDs to a few outputs in the range 0-7 and I have tried to pass a series of commands of the form 0x00 0x00 0x** where I've run * from 0 to F to try to get some sort of switching occurring, but no switching seems to occur. Additionally, when I check the bytes received back from the IC, the Arduino always reads 0xFF (so it looks like the MISO line is just being held high by the IC).
Info | IC | Arduino |
---|---|---|
External 5.3V | 3, 5 | VIN |
External Ground | 23, 24, 25, 26, 7, 8, 8, 10 | GND |
CS | 14 | 10 |
SCLK | 11 | 13 |
MISO | 22 | 12 |
MOSI | 19 | 11 |
LED - | 1, 5, 12, 16 | N/A |
4 separate LEDs, each with + side attached to external 5V terminal and through a 1k resistor.
I have tested this both with Arduino's own SPI library with frequencies ranging from 4 MHz down to 125 kHz, and a manual implementation with frequency around 500 kHz (I think). I have verified that the Arduino is communicating correctly over SPI as a master by connecting it to another Arduino acting as an SPI slave, which is able to receive bytes successfully over SPI.
I don't currently have access to an oscilloscope/logic analyser, but hopefully will in the near future.
Can anyone spot anything that I'm doing wrong or does anyone have any advice on where I can go next to debug the issue (without an oscilloscope)? I've attached the Arduino code I used for testing. Should we get an oscilloscope soon, I'll also attach the outputs I'm measuring from the Arduino & the IC.
Edit to add relevant parts of code. Firstly, using the Arduino SPI library:
#include <SPI.h>
#define baudRate 19200 // Serial debug baud rate
#define byteA 55 // Byte value for character A
#define spiClockDivider SPI_CLOCK_DIV8 // Highest within chip specification
#define spiMode SPI_MODE1 // Most standard mode?
#define spiBitOrder MSBFIRST // Most common order
byte received[3] = { 11 , 11 , 11 } ;
void setup()
{
// Deselect slave & pull clock low
digitalWrite( SS , HIGH ) ;
digitalWrite( SCK , LOW ) ;
// Set SPI settings
SPI.setClockDivider( spiClockDivider ) ;
SPI.setBitOrder( spiBitOrder ) ;
SPI.setDataMode( spiMode ) ;
// Ready SPI hardware
SPI.begin();
// Open serial for debug communications
Serial.begin( baudRate ) ;
}
void sendSPIBytes( byte data[3] ) {
// Make sure clock low
digitalWrite( SCK , LOW ) ;
// SS low so slave listens
digitalWrite( SS , LOW ) ;
// Transfer bytes
SPI.transfer( data , 3 ) ;
// Make sure clock low
digitalWrite( SCK , LOW ) ;
// SS high so slave stops listening
digitalWrite( SS , HIGH ) ;
}
void loop()
{
int i , j ;
byte message0[3] = { 0 , 0 , 0 } ;
// Transfer three different bytes with one second delay
// Write out received bytes to serial for debug (add byteA to make it easier to read)
for( i=0 ; i<256 ; i++ ) {
message0[3] = i ;
sendSPIBytes( message0 ) ;
for( j=0 ; j<3 ; j++ ) {
Serial.write( byteA + received[j] ) ;
}
delay( 500 ) ;
}
}
I realise that received
is not actually picking up the response here, this is a holdover from an earlier version of the code (where I was reading a response).
Secondly, my manual implementation
#define baudRate 19200 // Serial debug baud rate
#define byteA 55 // Byte value for character A
#define csPin 10 // Chip/Slave select pin
#define mosiPin 11 // Master data out, slave data in pin
#define misoPin 12 // Slave data in, master data out pin
#define clockPin 13 // Clock pin
#define delayTime 1 // Microseconds between SPI actions
void setup() {
// Initially / defaults
// SPI pins except miso are outputs
// Slave select pin is high
pinMode( csPin , OUTPUT ) ;
digitalWrite( csPin , HIGH ) ;
// Clock pin low
pinMode( clockPin , OUTPUT ) ;
digitalWrite( clockPin , LOW ) ;
// MOSI idles low
pinMode( mosiPin , OUTPUT ) ;
digitalWrite( mosiPin , LOW ) ;
// MISO is an input
pinMode( misoPin , INPUT ) ;
//DEBUG
Serial.begin( baudRate ) ;
}
void writeThenDelay( byte pinNumber , byte state ) {
digitalWrite( pinNumber , state ) ;
delayMicroseconds( delayTime ) ;
}
void spiInitialise() {
// To start SPI communication
// Check clock pin is low
writeThenDelay( clockPin , LOW ) ;
// Bring select pin low
writeThenDelay( csPin , LOW ) ;
}
void spiClose() {
// To stop sending bytes over SPI
// Check clock pin is low
writeThenDelay( clockPin , LOW ) ;
// Bring slave select high
writeThenDelay( csPin , HIGH ) ;
}
/*
* Applies a bit mask to a byte where a single bit is 1
* Byte is first argument
* Byte second argument between 1 and 8
* Specifies the bit to be 1
*/
byte bitMask( byte byteToMask , byte nonZeroBit ) {
switch ( nonZeroBit ) {
case 8 : // 00000001
return byteToMask & 1 ;
break ;
case 7 : // 00000010
return byteToMask & 2 ;
break ;
case 6 : // 00000100
return byteToMask & 4 ;
break ;
case 5 : // 00001000
return byteToMask & 8 ;
break ;
case 4 : // 00010000
return byteToMask & 16 ;
break ;
case 3 : // 00100000
return byteToMask & 32 ;
break ;
case 2 : // 01000000
return byteToMask & 64 ;
break ;
case 1 :
return byteToMask & 128 ;
break ;
}
}
/*
* Takes a byte and a bit position
* Returns HIGH if the bit in the byte at that position is 1
* Returns LOW if ^^^ 0
*/
byte bitMaskToBinary( byte byteToWrite , byte bitPosition ) {
if( bitMask( byteToWrite , bitPosition ) > 0 ) {
return HIGH ;
} else {
return LOW ;
}
}
// Clock idles low
// Data must be valid on rising edge of clock
void spiWriteByte( byte byteToWrite ) {
// For each bit in the byte
for( int i=0 ; i<8 ; i++ ) {
// Prepare bit on MOSI pin first
writeThenDelay( mosiPin , bitMaskToBinary( byteToWrite , i+1 ) ) ;
// Clock high
writeThenDelay( clockPin , HIGH ) ;
// TODO: READ THE BIT FROM MISO, DELAY IS PLACEHOLDER
delayMicroseconds( delayTime ) ;
// Clock low
writeThenDelay( clockPin , LOW ) ;
}
}
void loop() {
//DEBUG
Serial.write( 'l' ) ;
delay( 1000 ) ;
spiInitialise() ;
spiWriteByte( 0 ) ;
spiWriteByte( 0 ) ;
spiWriteByte( 0 ) ;
spiClose() ;
delay( 1000 ) ;
spiInitialise() ;
spiWriteByte( 0 ) ;
spiWriteByte( 0 ) ;
spiWriteByte( 1 ) ;
spiClose() ;
}
I've also tested with a loop like
for( int i=0 ; i<256 ; i++) {
delay( 1000 ) ;
spiInitialise() ;
spiWriteByte( 0 ) ;
spiWriteByte( 0 ) ;
spiWriteByte( i ) ;
spiClose() ;
}
to no avail.
Many Thanks
Scott Smith
spi-slave.ino (1.47 KB)