Debugging SPI

Go To Last Post
51 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

 

Ok, I am just doing my first SPI example on my ATmega328P with Amtel Studio 6.2 and I have run the debugging tool, but I can not get any value in the SPDR register.  I have added a watch on "data", and single stepped through the program using the "IO view window" and the SPDR register never gets a value.  Is this common when debugging none real time, or is this my fault.  Below is the function sending the data, and also the SPI function.

 

thanks in advance.  

 

Tuurbo46

 

spi_write(0x05); // Also tried spi_write('5');
                 // Both of these do not send a value to SPDR

 

 


void spi_write(byte data){       
    SPDR = data;                        // send the data
    while((SPSR & (1<<SPIF)) == 0);      // wait until transmission is complete    
}

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Tell what you mean by "debugging tool".  If the simulator, you'll need to jam in your own data AFAIK.

 

Tell what you mean by "not any value".  The read of the register will always have >>some<< value, right?

 

Indeed, upon examination you won't see your written value in SPDR.  Perhaps you might see the incoming response value.

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi theusch,

 

I am no guru on the debugging front, I am still working out what I can do.

 

I am using the ATmega328P xplained development board with on board mEDGB debugger.

 

I have tried the below code with rs232.  My SPI device is not working, so I am seeing if my values are getting in and out.

 

Any tips freely accepted. 

 

Thanks,

 

void spi_write(byte data){       
	write_rs232(data);
	_delay_ms(1000);
	SPDR = data;    				    // send the data
	while((SPSR & (1<<SPIF)) == 0);  	// wait until transmission is complete	
}

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

put a scope on the MOSI pin and the clock, do you see them wiggling? 

How have you setup the SS pin? 

 

Jim

 

Keys to wealth:

Invest for cash flow, not capital gains!

Wealth is attracted, not chased! 

Income is proportional to how many you serve!

If you want something you've never had...

...you must be willing to do something you've never done!

Lets go Brandon!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You want a salae logic. Indispensable.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

 

This is my code.  I think it is correct.  Kinda struggling now.

 

Thanks,

 


// File:  spi.c
   
  
#include <avr/io.h>
#include <util/delay.h>
//#include "spi.h"
#include "rs232.h"

typedef unsigned char byte;     

void initialize_spi(void) {
	DDRB |= (1<<2)|(1<<3)|(1<<5);    // CS (PB2), MOSI (PB3) and SCK (PB5) as outputs
	DDRB &= ~(1<<4);                 // MISO (PB4) as input

	SPCR |= (1<<MSTR);               // Set as Master
	SPCR |= (1<<SPR1)|(1<<SPI2X);    // divided clock by 32 (set to 500khz SCK)
	SPCR |= (1<<SPE);                // Enable SPI
	
	/* System FOSC is 16MhZ 
	    16Mhz/32 = 500Mhz  */
	
}

void cs_low(void){
	PORTB &= ~ (1 << PINB2);  // CS Low
	_delay_us(1);
}

void cs_high(void){
	PORTB |= (1 << PINB2);   // CS high
}

void spi_write(byte data){       
	//write_rs232(data);
	//_delay_ms(1000);
	SPDR = data;    		        // send the data
	while((SPSR & (1<<SPIF)) == 0);  	// wait until transmission is complete	
}

byte spi_read(byte data){
	SPDR = data;			       // send the data
	while((SPSR & (1<<SPIF)) == 0);	       // wait until transmission is complete	 
	return SPDR;
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You've shown the components. Now show the code that ties this together. Are you remembering to call cs_low() before spi_write()?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok added the code below, main.c, LTC2983_functions.c, and spi.c.

 

I have written in the code what happens.  There is one line in main.c "" convert_channel(channel_number);"" that calls this function in LTC2983_functions.c.  This locks main.c up, I cannot send "146" to terminal every 1 second.  This is my way of debugging main in parallel to using the debugger. If I put ""convert_channel(channel_number);"" back in main.c and edit comment out the ""while (conversion_done()==0);"" in ""covert_channels functions"" the program runs again.  I do not know if its the SPI read/ write that is jamming this function up or wrong data types.  Kinda stuck.........

 


// main.c


#include <avr/io.h>
#include <stdint.h>
#include <stdbool.h>
#include <util/delay.h>
#include "stdio.h"
#include "math.h"

#include "LTC2983_configuration_constants.h"
#include "LTC2983_support_functions.h"
#include "LTC2983_table_coeffs.h"
#include "rs232.h"
#include "spi.h"

typedef unsigned char byte;
typedef int          boolean;


// Function prototypes
void configure_channels();
void configure_global_parameters();


// -------------- Configure the LTC2983 -------------------------------
void setup() {
	IO_configuration();
	initialize_rs232();
	initialize_spi();
	configure_channels();
	configure_global_parameters();
}

void configure_channels() {
		//byte channel_number;
		long channel_assignment_data;

		// ----- Channel 2: Assign Sense Resistor -----
		channel_assignment_data =
		(long) SENSOR_TYPE__SENSE_RESISTOR |
		(long) 0b101101011101101100000000000 << SENSE_RESISTOR_VALUE_LSB;		// sense resistor - value: 93110.
		assign_channel(2, channel_assignment_data);

		// ----- Channel 6: Assign RTD PT-100 -----
		channel_assignment_data =
		(long) SENSOR_TYPE__RTD_PT_100 |
		(long) RTD_RSENSE_CHANNEL__2 |
		(long) RTD_NUM_WIRES__2_WIRE |
		(long) RTD_EXCITATION_MODE__NO_ROTATION_SHARING |
		(long) RTD_EXCITATION_CURRENT__5UA |
		(long) RTD_STANDARD__EUROPEAN;
		assign_channel(6, channel_assignment_data);

}

void configure_global_parameters() {

	// -- Set global parameters
	write_single_byte(0xF0, TEMP_UNIT__C |
	REJECTION__50_60_HZ);
	// -- Set any extra delay between conversions (in this case, 0*100us)
	write_single_byte(0xFF, 0);

}

// -------------- Run the LTC2983 -------------------------------------
int main(void)
 {
	setup();
	byte rs232[] = "146";
	byte channel_number;
	float fdata;
	byte data;

	int channels_to_measure[] = {6};
	int num_measured_channels = sizeof(channels_to_measure)/sizeof(channels_to_measure[0]);


	while(1){
	for (int i = 0; i < num_measured_channels; i++) {
		channel_number = channels_to_measure[i];
//*********************************************************************************************************

		convert_channel(channel_number); // When this line is commented out main loops OK
                                         // This locks up in covert channel function on LTC2983_support_functions.c

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

		read_voltage_or_resistance_results((channel_number));
		fdata = read_temperature_results((channel_number));
		data = (byte)fdata;

	}
	write_rs232(rs232);
	_delay_ms(1000);
	}

}

 


// File: LTC2983_support_functions.c:
  

#include <avr/io.h>
#include <stdint.h>
#include <stdbool.h>
#include <util/delay.h>
#include "stdio.h"
#include "math.h"

#include "LTC2983_configuration_constants.h"
#include "LTC2983_table_coeffs.h"
#include "LTC2983_support_functions.h"
#include "spi.h"
#include "rs232.h"

typedef unsigned char byte;      
typedef int         boolean;


// -----------------------------------------------------------------
//                  Memory write functions
// -----------------------------------------------------------------
void assign_channel(int channel_number, long channel_assignment_data) {
	int bytes_per_coeff = 4;
	long start_address = 0x200+4*(channel_number-1);

	initialize_memory_write(start_address);
	write_coefficient(channel_assignment_data, bytes_per_coeff); 
	finish_memory_write();
}

void write_custom_table(struct table_coeffs coefficients[64], long start_address, long table_length) {
	int bytes_per_coeff = 3;
	
	initialize_memory_write(start_address);
	for (int i=0; i< table_length; i++) {
		// write_table multiplies i by 6 and adds 250 to start address
		write_coefficient(coefficients[i].measurement, bytes_per_coeff);
		write_coefficient(coefficients[i].temperature, bytes_per_coeff);
	}
	finish_memory_write();
}

void write_custom_steinhart_hart(long steinhart_hart_coeffs[6], long start_address) {
	int bytes_per_coeff = 4;
	
	initialize_memory_write(start_address);
	for (int i = 0; i < 6; i++) {
		write_coefficient(steinhart_hart_coeffs[i], bytes_per_coeff);
	}
	finish_memory_write();
}

void write_single_byte(long start_address, int single_byte) {
	initialize_memory_write(start_address);
	spi_write(single_byte);
	finish_memory_write();
}

void initialize_memory_write(long start_address) {
	cs_low(); // CS low
	spi_write(0x02); 
	//spi_write(highByte(start_address)); // Address MSB Byte
	spi_write((start_address & 0xFF00) >> 8); // Address MSB Byte
	//spi_write(lowByte(start_address)); // Address LSB Byte
	spi_write(0x00FF & start_address);   // Address LSB Byte	
}

void write_coefficient(long coeff, int bytes_per_coeff) {
	int data_byte;
	for (int i = bytes_per_coeff - 1; i >= 0; i--) {
		data_byte = coeff >> (i*8);
		spi_write(data_byte);
	}
}

void finish_memory_write() {
	cs_high(); // CS high
}

// -----------------------------------------------------------------
//                  Memory read functions
// -----------------------------------------------------------------
void initialize_memory_read(long start_address) {
	cs_low(); // CS low
	spi_write(0x03); // instruction Byte read
	//spi_write(highByte(start_address)); // Address MSB Byte
	spi_write ((start_address & 0xFF00) >> 8); // Address MSB Byte
	//spi_write(lowByte(start_address)); // Address LSB Byte
	spi_write(0x00FF & start_address);   // Address LSB Byte
}

void finish_memory_read() {
	cs_high();
}

// -----------------------------------------------------------------
// 					Channel conversion
// -----------------------------------------------------------------

void convert_channel(byte channel_number) {
	// initiate a new conversion
	initialize_memory_write(0);
	spi_write(0b10000000 | channel_number); // start a conversion
	finish_memory_write();

//******************** LOCKS UP HERE *********************************************************************************
	
	while (conversion_done()==0); // When this line is edited out, main.c loops and sends data to rs232
                                  // I am not sure if SPI Read/ Write is jamming the program up?????
                                  
//*********************************************************************************************************************

}

boolean conversion_done() {	
	initialize_memory_read(0x00); // was 0
	byte data = spi_read(0x00);   
	finish_memory_read();
	return(data & 0b01000000);
}

// -----------------------------------------------------------------
// 					Getting temperature results
// -----------------------------------------------------------------

float read_temperature_results(int channel_number) {
	unsigned char raw_results[4];
	get_raw_results(READ_CH_BASE, channel_number, raw_results);
	float signed_float = convert_to_signed_float(raw_results);
	float temperature_result = get_temperature(signed_float);
	//print_temperature_result(channel_number, temperature_result);
	//print_fault_data(raw_results[3]);
	return (temperature_result);
	return (byte)temperature_result;
}

float read_direct_adc_results(int channel_number) {
	unsigned char raw_results[4];
	get_raw_results(READ_CH_BASE, channel_number, raw_results);
	float signed_float = convert_to_signed_float(raw_results);
	float direct_adc_reading = get_direct_adc_reading(signed_float);
	print_direct_adc_reading(channel_number, direct_adc_reading);
	print_fault_data(raw_results[3]);
	return (direct_adc_reading);
}

void get_raw_results(long base_address, int channel_number, unsigned char results[4]) {
	long address = base_address+4*(channel_number-1);
	initialize_memory_read(address);

	results[3]=spi_read(0x00); // fault data
	results[2]=spi_read(0x00); // MSB result byte
	results[1]=spi_read(0x00); // 2nd result byte
	results[0]=spi_read(0x00); // LSB result byte
	
	finish_memory_read();
}

float convert_to_signed_float(unsigned char results[4]) {
	// Get the last 24 bits of the results (the first 8 bits are status bits)
	long x = 0L;
	x= x | ((unsigned long) results[2]<<16)
	| ((unsigned long) results[1]<<8)
	| ((unsigned long) results[0]);
	
	// Convert a 24-bit two's complement number into a 32-bit two's complement number
	boolean sign;
	if ((results[2]&0b10000000)==128) sign=true; else sign=false;
	if (sign) x=x | 0xFF000000;

	return (float)x;  // changed from float (x);
}

float get_temperature(float x) {
	// The temperature format is (14, 10) so we divide by 2^10
	return x/1024;
}

float get_direct_adc_reading(float x) {
	// The direct ADC format is (3, 21) so we divide by 2^21
	return x/2097152;
}

// -----------------------------------------------------------------
// 		Getting raw results -
//    voltage (for thermocouples), resistance (for RTDs/thermistors)
// -----------------------------------------------------------------

float read_voltage_or_resistance_results(int channel_number) {
	unsigned char raw_results[4];
	get_raw_results(VOUT_CH_BASE, channel_number, raw_results);
	float signed_float = convert_vr_to_signed_float(raw_results);
	float voltage_or_resistance_result = get_voltage_or_resistance(signed_float);
	//print_voltage_or_resistance_result(channel_number, voltage_or_resistance_result);
	return (voltage_or_resistance_result);
}

float convert_vr_to_signed_float(unsigned char results[4]) {
	long x = 0L;
	x= x | ((unsigned long) results[3]<<24)
	| ((unsigned long) results[2]<<16)
	| ((unsigned long) results[1]<<8)
	| ((unsigned long) results[0]);
	return (float)x;
}

float get_voltage_or_resistance(float x) {
	// The format is (14, 10) so we divide by 2^10
	return x/1024;
}

// -----------------------------------------------------------------

// Find out if a number is an element in an array
bool is_number_in_array(int number, int *array, int array_length) {
	int i;
	bool found = false;
	for (i=0; i< array_length; i++) {
		if (number == array[i]) {
			found = true;
		}
	}
	return found;
}

// File:  spi.c
   
  
#include <avr/io.h>
#include <util/delay.h>
//#include "spi.h"
#include "rs232.h"

typedef unsigned char byte;     

void initialize_spi(void) {
	DDRB |= (1<<2)|(1<<3)|(1<<5);    // CS (PB2), MOSI (PB3) and SCK (PB5) as outputs
	DDRB &= ~(1<<4);                 // MISO (PB4) as input

	SPCR |= (1<<MSTR);               // Set as Master
	SPCR |= (1<<SPR1)|(1<<SPI2X);    // divided clock by 32 (set to 500khz SCK)
	SPCR |= (1<<SPE);                // Enable SPI
	
	/* System FOSC is 16MhZ 
	    16Mhz/32 = 500Mhz  */
	
}

void cs_low(void){
	PORTB &= ~ (1 << PINB2);  // CS Low
	_delay_us(1);
}

void cs_high(void){
	PORTB |= (1 << PINB2);   // CS high
}

void spi_write(byte data){       
	//write_rs232(data);
	//_delay_ms(1000);
	SPDR = data;    		        // send the data
	while((SPSR & (1<<SPIF)) == 0);  	// wait until transmission is complete	
}

byte spi_read(byte data){
	SPDR = data;			       // send the data
	while((SPSR & (1<<SPIF)) == 0);	       // wait until transmission is complete	 
	return SPDR;
}

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You said you have a eDBG - surely you can step/breakpoint this to find out exactly where it is "stuck"? Also, as noted above, a wiggle watcher (scope or analyser) is invaluable when trying to debug SPI.

Last Edited: Thu. Jul 9, 2015 - 10:45 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
a wiggle watcher (scope or analyser) is invaluable when trying to debug SPI.

(Virtually) indispensable, I'd say!

 

And not just for SPI - for any external communications!

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So how would you debug.  I have put a watch on data variable in spi_write();.  Then I can see if commands are being send to SPI device.  I have tried to look at SPDR register and that has not been possible.  

 

Thanks,

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, can you point me in the direction of a good (cheap) reader in the UK.

 

Thanks for your help.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Google salae logic ebay

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

To debug just sprinkle a few breakpoints around in your code at places you expect execution to reach. If it doesn't reach one then the proble is before there. In fact if you simply run it until "stuck" then hit "||" to pause execution you should find the loop it is stuck in. Work back from there to find out why.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There are several "tricks" to learn when debugging.    For example,   copying an optimised register to a "global volatile variable".

 

You can't see the SPDR or UDR registers in a debugger.    But you can read their contents into a variable.

You have to do these things carefully.   The "read" will clear the SPIF or RXC flag bit.    You must study the data sheet before you dive into  these things.

 

Note that when you read the data sheet,  you will see why you only need a single uint8_t spi_xfer(uint8_t c); function.   

 

I suggest that you gain some familiarity with simple programs in the Simulator.    Then with real hardware using EDBG or ATMEL-ICE.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, When I add a break-point, then select "start debugging and break", the break point goes nearly invisible and says "The current selected device is unable to hit breakpoints at runtime".  I come out of debugging, and try again and it says the same.

 

Also the watch on data variable in "spi_write function" when debug is started and yellow cursor is on main, the data variable says 12, in yet the program is only just started.  On start up should this not be zero?

 

Thanks, 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The current selected device is unable to hit breakpoints at runtime

Silly question but you are building for mega328P?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yep, ATmega328P xplained development board, using Atmel Studio 6.2, and onboard eDBG debugger.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's not what I asked. I asked what MCU are you attempting to build and debug code for. Clearly the statement:

The current selected device is unable to hit breakpoints at runtime

is a lie when applied to a mega328P. Of course it can hit breakpoints at runtime - it has a debugWire interface.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am assuming an ATMEGA328P-XMINI board that looks like this

This should work seamlessly with AS6.2

I do not have one but I do have the ATMEGA168PB-XMINI which should behave in a very similar way.

 

As I said earlier.   Practice with the Simulator.    You can step, break, inspect ports,  memory, registers, ...

 

When you are happy,   use the EDBG to run the code on the actual chip via debugWIRE.

It is always easier to set a breakpoint and run to it.    Stepping is slow and boring.

Once you have got to the area of interest (a breakpoint), you can step, examine etc to your heart's content.

Copy "optimised" variables to (volatile) memory so that you can watch them easily.

 

From the code that you posted earlier,   you are trying stuff that is too complex for you.    Begin with simple Blinky and port manipulation.    We all started like this.

 

When you have a problem,   reduce down to the smallest compilable program.    Someone will probably test it out in real life and help you with the solution.    Believe me.   You will learn fast this way.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK,

 

So SPI read sends back a zero, which mean slave device was not read correctly.  Reading the datasheet the sensor takes 200mS to reply with result.  So does the SPI master time out, or just wait.  Is it possible I should be using the SPI on an interrupt, would this help my cause?

 

I am just bouncing ideas now.  Also I have used the pins on the outside of the above board in the picture, not the dedicated SPI header as I did not have the proper connector.  I was just wondering would this be connected different in anyway?

 

I have the watch and breakpoints going now.  

 

Thanks,

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The schematic will show you how everything is connected ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The spi does not wait or timeout. When the master sends something the data gets clocked out and the slave data gets clocked in at the bit rate you've specified. If the slave takes time to do something, there is usually a register that you read for status or a hardware bit for indication.
Interrupts are not going to help.

Is there an Arduino example for talking to this chip? Have a look at that.

I had a quick look at the dataheet. Do you have SDO connected to MISO and SDI to MOSI?

Last Edited: Thu. Jul 9, 2015 - 10:41 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hiya,

 

Yes kartman I have double checked I have all pins connected correctly - thanks.

 

Included below are my SPI logic analyzer screen shoots.  It looks like the MISO is stuck high, and the clock varies a bit. 

 

So the CS is working, SCK is working, MOSI is working, but MISO is stuck high.

 

I thought you want to keep updated, still trying to work out problem.  Cheers.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

scaled out pic also

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Give the URL of a link to the sensor datasheet.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

datasheet,

 

http://cds.linear.com/docs/en/da...

 

Thanks for your help clawson.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Most interesting. As it shows there, to read from within the device you send 0x03 then two bytes of address then a stuffing byte and as you do that reads the byte from the addressed location. I see this encompassed in your code as:

void initialize_memory_read(long start_address) {
	cs_low(); // CS low
	spi_write(0x03); // instruction Byte read
	//spi_write(highByte(start_address)); // Address MSB Byte
	spi_write ((start_address & 0xFF00) >> 8); // Address MSB Byte
	//spi_write(lowByte(start_address)); // Address LSB Byte
	spi_write(0x00FF & start_address);   // Address LSB Byte
}

which is then invoked by code such as:

boolean conversion_done() {	
	initialize_memory_read(0x00); // was 0
	byte data = spi_read(0x00);   
	finish_memory_read();
	return(data & 0b01000000);
}

but then in both your traces and log we see things like:

 

that is sending 0xE0 not 0x03 ?!?

 

I would suggest that is the place to start looking for problems. I would have said it was sending the bits "backwards" but that would be 0xC0 not 0xE0 so I'd try to track down what/why is sending 0xE0.

 

EDIT: Oh and why initialize_memory_read(0x00) ? For one thing the addresses are TWO byte (uint16).

 

Oh and I wouldn't use "long" all over the place. Use uint8_t, uint16_t, uint32_t. You are passing around 16 bit quantities as long for example.

 

Last Edited: Fri. Jul 10, 2015 - 09:08 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Farnell sells the LTC2983 chip for £28 and a demo board for £90.  

 

Do you have the demo board?   Do you have example code?

 

Using SPI is incredibly simple.   It is just a question of sending the appropriate commands and assembling the replies.

Your chip performance is going to be dependent on the type and quality of the physical sensor(s).

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:
 Do you have example code?

The LTC2983 product page is: http://www.linear.com/product/LT...

 

There's a load of "Linduino" files there - including headers and .cpp

 

Googling "Linduino" finds: http://www.linear.com/solutions/...

 

What is Linduino?

 

Linduino is Linear Technology’s Arduino compatible system for developing and distributing firmware libraries and example code for Linear Technology’s integrated circuits. The code is designed to be highly portable to other microcontroller platforms, and is written in C using as few processor specific functions as possible. The code libraries can be downloaded by clicking the Downloads tab above and used as-is in your project or individual code snippets may be viewed in the Code section of a supported part. The Linduino One board (Demonstration Circuit DC2026) allows you to test out the code directly, using the standard demo board for the particular IC.

 

The Linduino One board is compatible with the Arduino Uno, using the Atmel ATMEGA328 processor. This board features a 14-pin “QuikEval” connector that can be plugged into nearly 100 daughter boards for various Linear Technology parts, including Analog to Digital converters, Digital to Analog Converters, high-voltage power monitors, temperature measurement devices, RF synthesizers, battery stack monitors, and more.

 

 

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi, I have the full kit.

 

I have run there visual IDE with my 2 senors and the code is generated, but this is arduino code which I have modified for the ATmega328P.

 

Below Is my updated main.c. // I have moved while(1) up two lines the same as generated code and added my two sensors correctly.  The SPI screen shots match the main.c below, and the attached files.  See attached for generated code.

 

I think the initialization process, configure senors, then reads data.  This might be the  ""sending 0xE0 not 0x03 ?!?""

 

Thanks for your input everybody

 


// main.c

#include <avr/io.h>
#include <stdint.h>
#include <stdbool.h>
#include <util/delay.h>
#include "stdio.h"
#include "math.h"

#include "LTC2983_configuration_constants.h"
#include "LTC2983_support_functions.h"
#include "LTC2983_table_coeffs.h"
#include "rs232.h"
#include "spi.h"

typedef unsigned char byte;      
typedef int          boolean;


// Function prototypes
void configure_channels();
void configure_global_parameters();


// -------------- Configure the LTC2983 -------------------------------
void setup() {
	 _delay_ms(200);
	IO_configuration();
	initialize_rs232();
	initialize_spi();
	configure_channels();
	configure_global_parameters();	
}


void configure_channels() {
	byte channel_number;
	long channel_assignment_data;

	// ----- Channel 2: Assign Sense Resistor -----
	channel_assignment_data =
	(long) SENSOR_TYPE__SENSE_RESISTOR |
	(long) 0b101101011101101100000000000 << SENSE_RESISTOR_VALUE_LSB;		// sense resistor - value: 93110.
	assign_channel(2, channel_assignment_data);
	// ----- Channel 4: Assign RTD PT-100 -----
	channel_assignment_data =
	(long) SENSOR_TYPE__RTD_PT_100 |
	(long) RTD_RSENSE_CHANNEL__2 |
	(long) RTD_NUM_WIRES__2_WIRE |
	(long) RTD_EXCITATION_MODE__NO_ROTATION_SHARING |
	(long) RTD_EXCITATION_CURRENT__5UA |
	(long) RTD_STANDARD__EUROPEAN;
	assign_channel(4, channel_assignment_data);
	// ----- Channel 6: Assign RTD PT-100 -----
	channel_assignment_data =
	(long) SENSOR_TYPE__RTD_PT_100 |
	(long) RTD_RSENSE_CHANNEL__2 |
	(long) RTD_NUM_WIRES__2_WIRE |
	(long) RTD_EXCITATION_MODE__NO_ROTATION_SHARING |
	(long) RTD_EXCITATION_CURRENT__5UA |
	(long) RTD_STANDARD__EUROPEAN;
	assign_channel(6, channel_assignment_data);
}

void configure_global_parameters() {
	
	// -- Set global parameters
	write_single_byte(0xF0, TEMP_UNIT__C |
	REJECTION__50_60_HZ);
	// -- Set any extra delay between conversions (in this case, 0*100us)
	write_single_byte(0xFF, 0);
	
}

// -------------- Run the LTC2983 -------------------------------------
int main(void)
 {	
	setup();
	byte rs232[] = "146"; 
	float fdata;
	byte data;

	while(1){	
	byte channel_number; 
	int channels_to_measure[] = {4, 6};
	int num_measured_channels = sizeof(channels_to_measure)/sizeof(channels_to_measure[0]);

	for (int i = 0; i < num_measured_channels; i++) {
		channel_number = channels_to_measure[i];
		convert_channel(channel_number); 
										 
		write_rs232(rs232);
		_delay_ms(1000);

		read_voltage_or_resistance_results((channel_number));
		fdata = read_temperature_results((channel_number));    
		read_temperature_results((channel_number));  
		data = (byte)fdata;
		
	}
	
	}

}

 

 

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I can't see where your code sets CPHA in SPCR. From a brief read of the data, you want CPHA = 1.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Like this you mean,

 

I will add it, and see if it works.

 

thanks,

 

void initialize_spi(void) {
	DDRB |= (1<<2)|(1<<3)|(1<<5);    // CS (PB2), MOSI (PB3) and SCK (PB5) as outputs
	DDRB &= ~(1<<4);                 // MISO (PB4) as input

	SPCR |= (1<<MSTR);               // Set as Master
	SPCR |= (1<<SPR1)|(1<<SPI2X);    // divided clock by 32 (set to 500khz SCK)
	SPCR |= (1<<SPE);                // Enable SPI
	
	//************** ADDING CPHA = 1 ************
	 
	SPCR |= (1<<CPHA);
	
	//*******************************************
	
	/* System FOSC is 16MhZ 
	    16Mhz/32 = 500Mhz  */
	
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Kartman,

 

No, that change never worked.

 

What karts do you race.  Are you rotax max?

 

Cheers,

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

New screen shots with 

SPCR |= (1<<CPHA);

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

At least that has changed the 0xE0's into 0xC0's. They are supposed to be 0x03's so the bits would appear to be "backwards".

 

Have you tried DORD in SPCR?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, rotax max....heavy!

Why say it never worked? You've probably got a number of problems - you need to solve them one by one. According to the datasheets, should CPHA be 1?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Kartman,

 

I read datasheets as (CPOL=0, CPHA=0).  Which is the opposite to the good results below ( CPOL=0, CPHA=1 and DORD =1)

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If the master clocks out the data on rising (leading ) edge, the slave clocks it in on the falling and vice versa.
If in doubt, paste the relevant sections from both the datasheets here and have the others form an opinion.

Why do you have your analyser set for lsb first? Spi is usually msb first - thus the backward bits. If you posted the analyser pics with the actual signals, we could confirm the spi mode settings - turn off the spi analyser and show the logic traces.

Last Edited: Sat. Jul 11, 2015 - 01:10 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Kartman,

 

I have been studying datasheets hard the last 24 hours.  On the analyzer front I bought the analyzer, learned, then used, so to be honest I thought default mode was OK.  So are my bits correct, but the analyzer is showing in reverse?

 

Also the msb and lsb were revered because I am trying to replicate the commands below of the LTC2983 data sheet.  The initial analyzer screen shot came out sending 0xE0 not 0x03.  So changing to ( CPOL=0, CPHA=1 and DORD =1) gave me 0x03.  This was my reasoning. 

LTC2983 SENSOR DATASHEET - INSTRUCTION SPI INSTRUCTION BYTE DESCRIPTION - PAGE 14.

Read 0b00000011 See Figure 1
Write 0b00000010 See Figure 2
No Opp 0bXXXXXX0X

 

(RECEIVER SAMPLES DATA ON RISING EDGE) (TRANSMITTER TRANSITIONS DATA ON FALLING EDGE)

 

ATmega328P DATASHEET - SPI PAGE 165

 

SPI Mode Conditions Leading Edge Trailing Edge

0 CPOL=0, CPHA=0 Sample (Rising) Setup (Falling)
1 CPOL=0, CPHA=1 Setup (Rising) Sample (Falling)
2 CPOL=1, CPHA=0 Sample (Falling) Setup (Rising)
3 CPOL=1, CPHA=1 Setup (Falling) Sample (Rising)

 

So my conclusion is use ( CPOL=0, CPHA=0 and DORD =1) - unless analyzer is showing in reverse.  Also my clock frequency is 500khz and yet the screen shot shows 144khz? Attached is my screen shot not in SPI mode.

 

Thanks,   My internet is so slow uploading pics!!!!!!!!!!!!!!!!!!

 

 

 

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Does that clock signal look OK.  Looks a bit jittery to me.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Look at figure 1 in the ltc2983 datasheet. Does the bit pattern match yours? According to your picture, you are clocking out 11000000 instead of 00000011. Thus DORD must be set to 0. Msb first.

As for the clock signal being jittery, what sample rate is the analyser set to? Hopefully around 4MHz. Or higher. The analyser might be taking an average of the periods and getting the frequency wrong. Use cursor to measure the period of one clock.

Last Edited: Sat. Jul 11, 2015 - 10:35 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi, 

 

Yes I agree with your comments, ( CPOL=0, CPHA=0 and DORD =0), Below is the screenshots to these settings.

 

Also I was sampling at 1Mhz, so I have changed now to 5Mhz, thanks.

 

Also looking at the below 3 functions last night, "initialize_memory_write()", is only called once in the program, and they always send 0, so "start_address" which will always be 0.  What is the point of 

spi_write ((start_address & 0xFF00) >> 8); // Address MSB Byte

If start address is always 0???  Below are the 3 functions.  Also there original code I commented out and added mine.  Note sure if this is my problem.

 

You can see the full code further up the page.

 

Thanks in advance.

 

 

 

// -----------------------------------------------------------------
//                  Memory read functions
// -----------------------------------------------------------------
void initialize_memory_read(long start_address) {
	cs_low(); // CS low
	spi_write(0x03); // instruction Byte read
	//spi_write(highByte(start_address)); // Address MSB Byte
	spi_write ((start_address & 0xFF00) >> 8); // Address MSB Byte
	//spi_write(lowByte(start_address)); // Address LSB Byte
	spi_write(0x00FF & start_address);   // Address LSB Byte
}

void finish_memory_read() {
	cs_high();
}

// -----------------------------------------------------------------
// 					Channel conversion
// -----------------------------------------------------------------

void convert_channel(byte channel_number) {
	// initiate a new conversion
	initialize_memory_write(0);
	spi_write(0b10000000 | channel_number); // start a conversion
	finish_memory_write();

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Edit,

 

Sorry this is the code that calls initialize_merory_read()

 

void convert_channel(byte channel_number) {
	// initiate a new conversion
	initialize_memory_write(0);
	//_delay_us(1);
	spi_write(0b10000000 | channel_number); // start a conversion
	//_delay_us(1);
	finish_memory_write();
	//_delay_ms(300);
	
	while (conversion_done()==0); // wait for conversion to complete  ///////////////////////// Problem area.
}

boolean conversion_done() {	
	initialize_memory_read(0x00); 
	//_delay_ms(10);
	byte data = spi_read(0x00);   
	finish_memory_read();
	return(data & 0b01000000);
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

tuurbo - your avatar shows a supercharger - not a turbo!

 

Your spi send data now looks ok. It seems the chip doesn't want to talk - I'd be checking the hardware. Something is not quite right.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Kartman,

 

Yes you are correct it is a supercharger.  A turbo looks a bit boring, this supercharger looked quite nice all bling and polished.  Most people don't know the difference.

 

Will will have a serious Rotax Max chat when i get this darn sensor going.  

 

Also do you double wrap your exhaust, and run your oil have castor/ half synthetic to coke up your cylinder head to reduce head volume to make faster.  Your head volume is only checked at rebuild.  This is a UK go fast trick.  Most people just run 100% shell M.

 

This project is starting to wind me a little (a lot).

 

I have been on to manufacturer and they said maybe chip is faulty.

 

One last thing do you set I/O like this before a project or just setup is in SPI.c.  So I am doing twice for SPI and rs232?  I have tried with the below setup and without with no change :-(

 

Ok I will 11th time check the hardware connections.

 

Thanks again.

 

#include <avr/io.h>

void IO_configuration(void){
	
	// 1 = output, 0 = input
	
	// Initialize
	DDRB =  0b11111111;  // set all port pins as outputs
	DDRD =  0b11111111;  // set all port pins as outputs
	PORTB = 0b00000000;  // set all levels low
	PORTD = 0b00000000;  // set all levels low
	
	// Configure
	DDRB =  0b11101111;  // out, out, SCK, MISO, MOSI, SS, out, out
	DDRD =  0b11111110;  // out, out, out, out, out, out, Tx, Rx
	PORTB = 0b00000101; 
	PORTD = 0b00000000;
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I haven't raced in competition for many years. Even then i didn't do any go fast tricks. Half the fun was learning how to set the kart up. I began with jetting the rotax as i would a racing two stroke motorbike. It was interesting when i'd pass a front runner going down the straight, but the thing wouldn't pull out of corners. Nowadays, i just take it down the hire track for some fun with friends. The kart is over 10 years old now.

As for setting up i/o, there's many schools of thought. Doing it in one module is one. Considering most embedded stuff is tied to hardware that doesn't change, then this method works well. As long it is clear, well documented and works, then you've ticked the main boxes.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

All wires are checked again.

 

What you reckon chip is broke?

 

Cannot think of any other checks now.

 

Unless its a type change getting mixed up.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@Turboo46,

 

If you possess the full Demo kit,   you can run all the Demo programs to verify your hardware.

No,  I do not have the kit.   Nor have I read the kit documentation.   But if it is designed to work with Arduino Libraries,  I would be inclined to just modify the demo sketches until it does exactly what you want.

 

Regarding the protracted debate on which SPI mode# you should use,   just study the data sheet.   Select that mode# to write values into the device.   Then read these values back.

 

I do not recognise your Logic Analyser or software.    I recommend a $10 Saleae clone or a genuine Saleae.   Unless it is my old age,   I suspect that you will find it easier to understand and use.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

After many hours of being in a landing pattern, It now has an output, but an output I cannot read yet as I have to typecast a float to a byte to print with rs232.

 

The problem was hardware related, the development board had a dry joint on it on one of the SPI pins.

 

Wow what a hard learning curve. 

 

So because I want to finish tonight, how would I type cast a Float to a Byte.  I would like to use sprintf(), but we cannot do this in embedded.  What would be the next best solution.

 

Many thanks for every bodies help along the way.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think the jittery looking clock could be from the sample rate not being high enough in the logic analyzer.  I have seen that kind of thing.

 

The Saleae is a great little instrument.  I bought one when they first came out and before they had to increase the price $100.  I got a nice email from them when they had to raise the prices explaining why.  I got the 16 channel version for some reason.  I have never used more than 5 at a time.  8 channels would have been plenty.

 

I was having trouble with SPI and determined that when I set the SPI clock speed at half the system clock the first two bits of data didn't get transferred, but when I slowed it down to 1/4 the system clock it all worked fine.  The Saleae really saved me on that.  I recommend them highly.

 

good luck

Last Edited: Sun. Jul 12, 2015 - 05:25 PM