Original orangutan and SPI

Hi

I’m trying to interface the MicroMag3 from sparkfun with the O.

I created a cable for the MISO/MOSI/SCK pins from the MM3 to the programmer interface on the O.

I created a 3v power regulator for the MM3. The source power comes from the O.

Now, the programmer port has power and ground assigned to pins. But, I did not bring power/ground from the MM3 via the cable I built. I am leaving those pins without connection. Based on looking at the O and trying to decipher the schematic, I am assuming that since I already have power for the MM3, I don’t need those pins connected.

So, I’ve been programming it, shutting it off, switching programmer cable for MM3 cable, then powering back on.

Please advise if I have screwed up the above somehow.

If not, then I’ll move to the next section.

The MM3 requires 3 additional pins for its usage, SS, RESET, READY. I have SS=D0, RESET=D1, and READY=B7.
That should be ok. I’m not assuming I need to have them all on the same port. If so, I am going to jumper cable between a motor PD port and the MM3 READY.

If I’m still doing the right thing, next section is programming.

Here’s my code. lcd will work - even after the spi_init. But, if I try any of the other SPI stuff, there is nothing happening. I’m assuming its frozen at this line but cant figure out a way to verify:

      while(!(PIN_SPI & (1<<DD_RDY)));
/* begin */

#include "device.h"
#include "lcd.h"

#include <avr/io.h>
#include <string.h>
#include <util/delay.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>



#define DDR_SPI	DDRB
#define PORT_SPI	PORTB
#define PIN_SPI		PINB
#define DD_MOSI	DDB3
#define DD_MISO	DDB4
#define DD_SCK		DDB5
#define DD_RDY	DDB7

#define DDR_MM3	DDRD
#define PORT_MM3	PORTD
#define PIN_MM3	PIND
#define DD_SS		DDD0
#define DD_RST		DDD1



void init_spi()
{
// initialize SPI
	// Set MOSI, SCK, RST output, rest input
	DDR_SPI = (0<<DD_MISO) | (1<<DD_MOSI) | (1<<DD_SCK) | (1<<DD_RST);
	
	// Enable SPI, Master, set clock rate fck/64
	SPCR = (1<<SPE)|(1<<MSTR)| (1<<SPR1) | (1<<SPR0);
	
// initialize MM3
	//SS output
	DDR_MM3 = (1<<DD_SS);
	
	//SS high
	PORT_MM3 |= (1<<DD_SS);
	
	//RST low
	PORT_MM3 &= ~(1<<DD_RST);
}



int spi_comm(int data)
{
	
	// Start transmission
	SPDR = data;
	
	// Wait for transmission complete
	while(!(SPSR & (1<<SPIF)));
	
	return SPDR;
	
} 

void spi_disable(void)
{
	SPCR &= ~( 1 << SPI_SPE );
	SPI_DDR |= ( 1 << SPI_MISO );
}

void spi_enable(void)
{
	SPCR |= ( 1 << SPI_SPE );
	SPI_DDR &= ~( 1 << SPI_MISO );	
}





int main()
{
   char s[10];
   int delay;

   int x = 0;

   lcd_init();
   
   init_spi();

   spi_disable();


   for(;;)
   {
	  spi_enable();
	   
      // select mm3
          PORT_MM3 &= ~(1<<DD_SS);

      //Pulse RST
          PORT_MM3 |= (1<<DD_RST);
         _delay_ms(1);
          PORT_MM3 &= ~(1<<DD_RST);

      // get x axis
          spi_comm(0x01);
      
          while(!(PIN_SPI & (1<<DD_RDY)));
      
          x = (spi_comm(0) << 8) | spi_comm(0);

      // deselect mm3
          PORT_MM3 |= (1<<DD_SS);

	  spi_disable();
	  
	  lcd_gotoxy(0,0);
	  sprintf(s, "a:%d", x);
	  lcd_string(s);
	  

      //Delay between measurements
          for(delay = 0; delay < 500; delay++)
             _delay_ms(1);
   }
}

/* end */

Thoughts? Help? Hopefully a really dumb problem that I can’t see because I’ve been staring at it so long.

Hello.

I didn’t look at everything you posted in detail yet, but can you clarify a few things? First, do you have ground connected between the two boards, and do you have any indication that the MicroMag is working? Have you had SPI working on an AVR before? Do you have access to an oscilloscope or logic analyzer so that you could look at what the SPI lines are doing?

If you think the program is stuck at

while(!(PIN_SPI & (1<<DD_RDY)));

can’t you just look at the pin and see if it’s staying low? You could also manually make the pin high and see if your program continues. You can also print some data to the LCD to see how far your code is getting.

- Jan

This is my first attempt to use SPI with orangutan. I’m fairly sure my wiring is ok. I spent lots of time with that and checking things out with my multimeter. I don’t have oscilloscope at home, but will try to borrow one from work today. I’ll also try your suggestions with the manual pin sets to see if I get by that while statement. I was hoping there is something glaringly ugly in my code that someone would see right off.

I didn’t take a really thorough look at your code yet, but it doesn’t look like you’re doing anything with PB2, the ATMega168’s !SS line. In SPI master mode, this pin has to be either set as an output or pulled high. If it is set as an output its state doesn’t interfere with the SPI hardware operation, so its useful as a slave select pin for your slave device. If you leave it as an input and it isn’t pulled high, it can throw the SPI hardware back into slave mode. It’s designed to be used to let multiple master devices talk on a single SPI bus, and it’s described (somewhat cryptically) in section 17.3.2 of the ATMega168 datahseet.

I think you’ve got the clock phase and polarity configured properly, but the other thing you would want to think about is the SPI frequency. The comments in your code say you’re setting the SPI clock to fosc/64, but then you set the bits to run off of fosc/128. The MM3 datasheet specifies a max serial frequency of 1MHz, so you’re definitely not going too fast, but you can also probably speed up your serial communications. Something to think about after you get it working I guess. Which of the Orangutan’s do you have, and more importantly, do you know at what frequency the system clock is operating?

Also, is there a reason you want to run the MicroMag at 3V rather than 5? The spec sheet isn’t really clear on the normal operating range, but they list 5.25V as the absolute maximum, and I see lots of examples (including the wiring demo linked from SparkFun) of people running the MM3 at 5V. The only reason I bring this up is that you don’t mention level shifting components on your data lines (SparkFun has a tutorial on the subject here). The input pin absolute maximum voltage in your case is only 3.3V (VDD+0.3V). This probably isn’t related to your problem, but you’re out of spec in one way or another.

Anyway, good luck tracking down your problem.

-Adam

I’ve been confused and wondering about the master SS (pb2) - and what to do with it. I didn’t think I wanted to setup my slave device to use it as slave select because I have a second SPI device I want to integrate after getting the Micromag working. But, I will set it high tonight when I get home and see if it works. My understanding from the Micromag datasheet was that is a 3v device with absolute max rating at 5v (as you indicated). Since I already have two other 3v devices on my board, I went ahead and powered the micromag with the 3v. But, I have neglected the level shifting on all my 3v devices - but they are working! So, I will address that tonight as well. And btw, I am working with original orangutan168 (and trying to harness orangutan-lib for most device specific settings).
Thanks for the tips!

progress…

For those curious (and I’ll crosspost over on sparkfun)…

I implemented the inline resistor level shifter from the spark fun website. I also found a bunch of errors in my code that I had posted (port/channel mismatches).

So, using o-lib, I modified spi.h/.c so that there is a spi_disable/enable. I can actually get the output from to continue on the lcd now. Before anytime I tried to use the spi calls with the micromag, the lcd wouldn’t show anything, making me think the (while !drdy) code was blocking as that seemed to not be happening.

It is not 100% working yet, as the only output I am receiving on the lcd is -15. ??!!??

Any ideas on why that is happening?

code follows:

#include "device.h"
#include "lcd.h"

#include <avr/io.h>
#include <string.h>
#include <util/delay.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "spi.h"


#define DDR_SPI	DDRB
#define PORT_SPI	PORTB
#define PIN_SPI		PINB
#define O_SS		DDB2
#define DD_MOSI	DDB3
#define DD_MISO	DDB4
#define DD_SCK		DDB5
#define DD_RDY	DDB7

#define DDR_MM3	DDRD
#define PORT_MM3	PORTD
#define PIN_MM3	PIND
#define DD_SS		DDD0
#define DD_RST		DDD1



int main()
{
   char s[10];
   int delay;

   int x = 0;

   lcd_init();
   
   spi_init();

   // mm3 init
   
   DDR_SPI |= (1 << O_SS); // master SS output
   
   //SS high
   PORT_MM3 |= (1<<DD_SS);
   DDR_MM3 |= (1 << DD_SS); // mag3 SS output
   
   // MOSI, SCK, RST output, rest input
   DDR_SPI |=  (1<<DD_MOSI) | (1<<DD_SCK);
   DDR_MM3 |= (1<<DD_RST);
   
   // Enable SPI, Master, set clock rate
   SPCR = (1<<SPE)|(1<<MSTR)| (1<<SPR1) | (1<<SPR0);      
   
   spi_disable();

   for(;;)
   {
      spi_enable();
	   
// bring ss_low
      PORT_MM3 &= ~(1<<DD_SS);

//Pulse RST  0,1,0
     PORT_MM3 &= ~(1<<DD_RST);
     _delay_ms(1);
     PORT_MM3 |= (1<<DD_RST);
     _delay_ms(1);
     PORT_MM3 &= ~(1<<DD_RST);
     _delay_ms(1);
    
// X axis  
     spi_transfer(0x01);
     
// wait for ready 
     while(!(PIN_SPI & (1<<DD_RDY)));

// combine two reads
     x = (spi_transfer(0x00) << 8) | spi_transfer(0x00);

// SS high
     PORT_MM3 |= (1<<DD_SS);
	   
     spi_disable();

     lcd_gotoxy(0,0);
     sprintf(s, "a:%d", x);
     lcd_string(s);	  

//Delay between measurements
     for(delay = 0; delay < 500; delay++)
         _delay_ms(1);
   }
}

Glad to hear you’re making progress.

I probably won’t have time to look at your code today, but you might want to take a look at this thread: SPI and LCD with Orangutan. Its from about a year and a half ago when I was first trying to interface an SPI sensor (SCP1000 pressure sensor breakout board from SparkFun) with an Orangutan, while displaying the results to the LCD. At the beginning I was pretty lost, but there’s some working code near the end. Keep in mind that (I’m pretty sure) the SCP1000 uses the opposite SPI clock phase and polarity as your MicroMag3, so I don’t think you should be setting the CPHA and CPOL bits like I do in my code.

-Adam

Could my issue be 16bit vs 8bit reads?

I am including my latest code below. I can get one reading from the device. I know its stuck at the while !DRDY line. I have used all the period selects to try and figure it out. If the period select in the command is >=512, all input is 0. If any of the options <=256, I can see data. And though it doesn’t read more than once, if I change the orientation of my device and reset the Orangutan, I get different numbers. My assumption is the first reading I’m getting is somewhat reasonable. I cannot see anything in my code that would prevent continuous reads. I do the CS as they expect. I do the reset pulse 0-1-0. Is it the orangutan and lcd setup?


#include "device.h"
#include "lcd.h"

#include <avr/io.h>
#include <string.h>
#include <util/delay.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "spi.h"

#define DDR_SPI		DDRB
#define PORT_SPI	PORTB
#define PIN_SPI		PINB
#define O_SS		DDB2
#define DD_MOSI		DDB3
#define DD_MISO		DDB4
#define DD_SCK		DDB5
#define DD_RDY		DDB7

#define DDR_MM3		DDRD
#define PORT_MM3	PORTD
#define PIN_MM3		PIND
#define DD_SS		DDD0
#define DD_RST		DDD1

#define MM3_X_AXIS	0x01
#define MM3_Y_AXIS	0x02
#define MM3_Z_AXIS	0x03

#define MM3_PS32	0x00
#define MM3_PS64	0x10
#define MM3_PS128	0x20
#define MM3_PS256	0x30
#define MM3_PS512	0x40
#define MM3_PS1024	0x50
#define MM3_PS2048	0x60
#define MM3_PS4096	0x70



int main()
{
   char s[10];
   int delay;

   uint16_t x = 0;   
   uint16_t y = 0;

   int i = 0;

   int j = 0;
   
   int i0 = 0;
   int i1 = 0;
   
   lcd_init();
   
   spi_init();

   
   // mm3 init
   
   DDR_SPI |= (1 << O_SS); // output
   
   //SS high
   PORT_MM3 |= (1<<DD_SS);
   DDR_MM3 |= (1 << DD_SS); // output
   
   // Set SS, MOSI, SCK, RST output, rest input
   DDR_SPI |=  (1<<DD_MOSI) | (1<<DD_SCK);
   DDR_MM3 |= (1<<DD_RST);
   
   // Enable SPI, Master
   SPCR = (1<<SPE)|(1<<MSTR)| (1<<SPR1) | (1<<SPR0);   
   
   spi_disable();

   
   

   for(;;)
   {
	  SPCR &=~(1<<CPOL);	
	  SPCR &=~(1<<CPHA);
	
	  spi_enable();
	   
      PORT_MM3 &= ~(1<<DD_SS);

      //Pulse RST
	  PORT_MM3 &= ~(1<<DD_RST);
	  _delay_ms(1);
      PORT_MM3 |= (1<<DD_RST);
      _delay_ms(1);
      PORT_MM3 &= ~(1<<DD_RST);
      
	  spi_transfer(MM3_PS32 | MM3_X_AXIS);
  	  
	  while(!(PIN_SPI & (1<<DD_RDY)));

	  i0 = spi_transfer(0x00);
	  i1 = spi_transfer(0x00);
	  
      x = (i0 << 8) | i1;    
	  
	  PORT_MM3 |= (1<<DD_SS);
	   
	  spi_disable();

	   
	  lcd_gotoxy(0,0);
	  sprintf(s, "%d %d", i0, i1);
	  lcd_string(s);
	  
	  lcd_gotoxy(0,1);
	  sprintf(s, "%d %d", i++, x);
	  lcd_string(s);
	  
      //Delay between measurements
      for(delay = 0; delay < 500; delay++)
         _delay_ms(1);
   }
}

Well after three days of hair-greying debugging, it was not the code at all. My devboard had a SCP1000 barometric pressure sensor from Sparkfun tied into the SPI miso,mosi,sck. This morning I removed that device and I can continuously query data from the micromag without issue.

Unfortunately, the SCP1000 is needed in my project. So, I will now build the code to use that (with the help of the posts on this board), and if I get that to work, the battle will be to integrate the two devices together.

I’m running out of ports on the orangutan, so don’t think I can do the bit-banging technique.

Glad to hear you tracked down the problem.

What revision of the SCP1000 do you have? SparkFun now carries the C revision, which you can use on the same SPI bus as other devices so long as you hold the CSB pin high. Older breakout boards made by SparkFun have the B revision, which drives the MISO line high by default so you don’t need an external pull up resistor. The problem is that revisoin B SCP1000’s will drive the MISO line high even when the CSB pin is externally driven high, which will interfere with other devices on the SPI bus.

You can tell which revision your sensor is by looking (carefully) at the letters on the top of the sensor next to the bar code. If your sensor says “P01” you have revision B, but if it says “D01” you have revision C:

If you have revision B (“P01”) you have a couple of options. You can add a resistor (probably 1Kohm-10Kohm) between the SCP1000 and the MISO line, which would effectively make the SCP1000 your pull-up resistor. You can also configure the SCP1000 to open-drain mode by writing 0x17 to the CFG2 register. Unfortunately you’ll need to do this every time you power the sensor up, before trying to talk to any other devices on the SPI bus.

The SCP1000 really is a great little sensor, I’m sure you’ll be able to get yours working. These links might help:
SCP1000 Revision B Specification
SCP1000 Revision C Specification
SCP1000 Revision Differences
Sample SCP1000 Code from VTI for ATMega16L (WAY more commented than my code, and portable to the ATMega168)

-Adam

P.S. If you do have revision C, you can skip the low noise configuration part of my code and/or the VTI demo code, but it doesn’t hurt to leave it in.

I have the B version. A question about the resistor. I already am doing the level shifting resistor trick for the micromag (and I would assume any 3v SPI going into the O). So, there are resistors between micromag MISO,MOSI,SCK and the O. If the SCP1000 is inline with this device (these three channels shared), a resistor is in place already. I’ll read more on those links and try some stuff tonight. It will be my insomnia exercise for tonight.

thanks!