SMbus managed by i2C (Peter Fleury) - ATMEGA1284 - READ BYTE trouble

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

Hi all,

 

I have a strange behaviour in my application: ATMEGA 1284 + EMC 2305 fan controller.

 

I am using the i2C as TWI hardware interface (twimaster.c) coming from Peter Fleury already used in the past with satisfaction.

i2C set at 100KHz frequency. 

 

The EMC2305 fan controller must be interfaced by SMbus.

I set the CONFIGURATION REGISTER of the EMC2305 so to be "almost fully" i2C compliant (really the SMBus timeout function is disabled).

 

When I write/read a byte to/from the EMC2305 I do not receive any "error" from i2c_start i2c_start_wait i2c_rep_start i2c_write i2c_readAck i2c_readNak.

So when I write or read a register I have returned always a "valid" byte.

 

It seems that the EMC2305 fan controller is working "well".

It seems I can set all the parameters properly because the fans run as expected, tested both Direct Setting Mode (I impose the duty cycle) and the Fan Speed Control Mode (the inner algorithm of the EMC2305 set the duty cycle so to have the wanted RPM of the fans).

It is possible to set and change the speed; I checked by oscilloscope and the duty cycle is varying as expected.

All the fans are at the same speed.

 

Impossible to read properly the registers so to monitor the fans running.

What it is wrong is what I read from any register inside the EMC2305.

The values read have no sense.

 

I did a straight/direct test without compliments:

write into a register, echo at the LCD, read the same register, echo at the LCD.

The byte write and the byte read are totally different.

 

And this for any register.

What I write is not what I read.

 

So when I read a inner register to have the RPM of the fans the data is totally a no sense. 

 

Any suggestion? 

All the functions I can implement reading the EMC2305 registers are not working.

 

Any help is really welcome. After 4 days I do not know what else I have to check.

Best regards.

 

oiram revenac

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

I ave never used EMC2305.

 

Some devices require i2c_stop(), i2c_start() and other devices require i2c_restart().

Most devices don't care.

 

I am too idle to trawl through the data sheet.

 

I would just try to  read the ID with both styles.

Then write/read a volatile register.

 

David.

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

Please post some clear pictures of your setup, then some schematics.

 

Fans can be noisy, so some details on power sources and bypassing would help, did I say schematic.... yes I did.

 

 

Jim

 

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

canever wrote:
The byte write and the byte read are totally different.

Seems like it's time to put a logic analyser on your wires.

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com

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

@ david.pretince

tried both start() alternatives in the middle the read() cycle; I mean a restart() (really stop() immediately followed by a start()) and a start() only.

Nothing changed. The volatile register wrote and read immediately after exhibit different values.

 

I attach the read byte from he Datasheet. There is a mistake in it, where they declare START in the middle, and shows 0>1. It is mistake. ... and the meaning is not a restart() made of a stop() +start(). Tried this option without success.  

 

What I have tried follows.

I placed into the main_loop a loop where I write all the possible value for Mask Interrupt Register, together the chance to change the duty cycle pressing 2 keys only (up and down of 100 RPM), not elegant, not optimized, only to check immediately the results.

When I write into the duty cycle register I can check at the oscilloscope that it is set in the right way.

What I write into the Interrupt Mask Register is not what I read immediately after.  

 

for(i=0; i<=0x1E; i++) {

writeEMC2305Reg(EMC2305_FAN_INTERRUPT_ENABLE,i); // FAN5 FAN4 noFAN3 FAN2 FAN1 generates an ALERT if drive/spin/stall fault

EMC2305reg = readEMC2305Reg(EMC2305_FAN_INTERRUPT_ENABLE);

utoa(i+0x100,LCDbuffer,2); lcd_gotoxy(0,1); lcd_puts(LCDbuffer); lcd_gotoxy(0,1); lcd_putc(' '); // print a binary value

utoa(EMC2305reg+0x100,LCDbuffer,2); lcd_gotoxy(0,2); lcd_puts(LCDbuffer); lcd_gotoxy(0,2); lcd_putc(' '); // print a binary value

_delay_ms(1000);

}

 

whole code below

#include <avr/io.h>
#include <inttypes.h>
#include <stdlib.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>
#include <avr/wdt.h>
#include <stdint.h>

#include <i2cmaster.h>
#include <lcd.h>

#define false 0
#define true 1
#define LOW 0
#define HIGH 1
#define lcd_on()				PORTD |= _BV(6)


/*§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§*/
#define ReadSETUPbutton()		(PINA & _BV(6)) >> 6	// PORTA.6 is the pin for LEFT push button
#define ReadCHANGEbutton()		(PINC & _BV(7)) >> 7	// PORTC.7 is the pin for RIGHT push button

// #define F_CPU 8000000UL	
// in Project->Properties->Toolchain->AVR/GNU C Compiler->Symbols: added F_CPU=8000000

#define		K_RPM							3932160.0
#define		NOCTUA_NF_A9x14_MAXRPM			2500
#define		NOCTUA_NF_A9x14_MINRPM			600		
#define		DIRECT_SETTING_MODE				1			//	Direct Setting Mode is Active is 1, Fan Speed Control Mode is ACTIVE if 0

/* EMC2305 i2C identifier */
#define EMC2305_ADD							0b01011000	

// global registers
#define	EMC2305_CONFIGURATION				0x20		// Configures the fault monitor, clocking and watchdog. Clocking and Watchdog must be changed from DEFAULT

// FAULTS registers
#define	EMC2305_FAN_INTERRUPT_ENABLE		0x29		// allow the activation of PIN ALERT# in case of fault - Controls the masking of interrupts on all fan related channels
#define	EMC2305_PWM_POLARITY_CONFIG			0x2A		// Configures Polarity of all PWM drivers

// FAN#1 control registers
#define	EMC2305_1_FAN_SETTING				0x30
#define	EMC2305_1_FAN_CONFIGURATION_1		0x32
#define	EMC2305_1_TACH_TARGET_LOW			0x3C
#define	EMC2305_1_TACH_TARGET_HIGH			0x3D
#define	EMC2305_1_TACH_READ_HIGH			0x3E
#define	EMC2305_1_TACH_READ_LOW				0x3F

// FAN#2 control registers
#define	EMC2305_2_FAN_SETTING				0x40
#define	EMC2305_2_FAN_CONFIGURATION_1		0x42
#define	EMC2305_2_TACH_TARGET_LOW			0x4C
#define	EMC2305_2_TACH_TARGET_HIGH			0x4D
#define	EMC2305_2_TACH_READ_HIGH			0x4E
#define	EMC2305_2_TACH_READ_LOW				0x4F

// FAN#4 control registers
#define	EMC2305_4_FAN_SETTING				0x60
#define	EMC2305_4_FAN_CONFIGURATION_1		0x62
#define	EMC2305_4_TACH_TARGET_LOW			0x6C
#define	EMC2305_4_TACH_TARGET_HIGH			0x6D
#define	EMC2305_4_TACH_READ_HIGH			0x6E
#define	EMC2305_4_TACH_READ_LOW				0x6F

// FAN#5 control registers
#define	EMC2305_5_FAN_SETTING				0x70
#define	EMC2305_5_FAN_CONFIGURATION_1		0x72
#define	EMC2305_5_TACH_TARGET_LOW			0x7C
#define	EMC2305_5_TACH_TARGET_HIGH			0x7D
#define	EMC2305_5_TACH_READ_HIGH			0x7E
#define	EMC2305_5_TACH_READ_LOW				0x7F

/*§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§*/
uint8_t		EMC2305reg;											// to read or write an EMC2305 register
uint8_t		EMC2305_KO;
uint16_t	GlobalRPM;

uint8_t		SETUPbutton;										// PORTA6 pin poition of the LEFT SWITCH
uint8_t		CHANGEbutton;										// PORTC7 pin position of the RIGHT SWITCH
uint8_t		Setup_Detected					=false;
uint8_t		Change_Detected					=false;
 
char		LCDbuffer[21];

uint8_t		i;


/*§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§*/							/*§§§§§§§  EMC2305   §§§§§§§§*/
/*             	   READ a Register of the FAN controller by I2C bus (TWI) of the Atmega328P       */
/*§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§*/
uint8_t readEMC2305Reg(uint8_t regAddr) {
	
	uint8_t buffer;

	// set a start condition and wait until the device is no longer busy from the previous operation
	i2c_start_wait(EMC2305_ADD+I2C_WRITE);     					// set device address and write mode

	EMC2305_KO =i2c_write(regAddr);								// write address
	if (EMC2305_KO) {
		i2c_stop();												// failed to issue start condition
		return -1;}												// return 0xFF - verify it is not a valid data
	else {
		i2c_rep_start(EMC2305_ADD+I2C_READ);					// i2c_rep_start() and i2c_start() are exactly the same - set device address and read mode
		buffer = i2c_readNak();									// read one byte and inform the slave there are no other byte to transfer
		i2c_stop();
		return buffer; }									
}

/*§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§*/
/*           WRITE a DAC Register by I2C bus (TWI) of the Atmega328P                              */
/*§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§*/
void writeEMC2305Reg(uint8_t regAddr, uint8_t regVal) {

	// set a start condition and wait until the device is no longer busy from the previous operation
	i2c_start_wait(EMC2305_ADD+I2C_WRITE); 		// set device address and WRITE mode

	EMC2305_KO =i2c_write(regAddr); 			// Specifying the address of register
	if (EMC2305_KO) {
		i2c_stop();								// release the bus
		while(1);}

	EMC2305_KO =i2c_write(regVal);		        // Writing the value into the register
	if (EMC2305_KO) {
		i2c_stop();								// release the bus
		while(1); }
		
	i2c_stop();									// Release the I2C bus	
}

/*§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§*/
void InitEMC2305() {
	
	writeEMC2305Reg(EMC2305_CONFIGURATION,       0b11000001);			// ALERT pin OFF + i2s_compliant + WatchDog OFF + null + null + null + CLK as input pin + use EXTERNAL CLK//	EMC2305reg= readEMC2305Reg(EMC2305_CONFIGURATION)&0b11100011; 

	lcd_gotoxy(0,2); 
	if(DIRECT_SETTING_MODE) {	
		lcd_puts("Direct Setting Mode "); 
		writeEMC2305Reg(EMC2305_PWM_POLARITY_CONFIG,  0b00011111);	// PWM polarity is inverse: drive set at 0x00 means 100% duty cycle (really == OFF)
	}
	else {			// PWM_POLARITY_CONFIG stay at default value
		lcd_puts("Fan Speed Ctrl Mode ");
		writeEMC2305Reg(EMC2305_1_FAN_CONFIGURATION_1,0b10001011);	// EN_ALGO + 500RPM range + (2 poles FAN & 5 edges used) + update every 400mS
		writeEMC2305Reg(EMC2305_2_FAN_CONFIGURATION_1,0b10001011);	// EN_ALGO + 500RPM range + (2 poles FAN & 5 edges used) + update every 400mS
		writeEMC2305Reg(EMC2305_4_FAN_CONFIGURATION_1,0b10001011);	// EN_ALGO + 500RPM range + (2 poles FAN & 5 edges used) + update every 400mS
		writeEMC2305Reg(EMC2305_5_FAN_CONFIGURATION_1,0b10001011);}	// EN_ALGO + 500RPM range + (2 poles FAN & 5 edges used) + update every 400mS
}

/*§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§*/
void setRPM_EMC2305(uint16_t rpm) {
	uint8_t HighByte;
	uint8_t LowByte;
	uint16_t TachCounter;										
	#define DutyCycle LowByte									
	
	if(DIRECT_SETTING_MODE) {
		DutyCycle =(uint8_t)((rpm*255.0)/NOCTUA_NF_A9x14_MAXRPM);	// duty cycle in 256 steps
		writeEMC2305Reg(EMC2305_1_FAN_SETTING, DutyCycle);			//FAN#1		
		writeEMC2305Reg(EMC2305_2_FAN_SETTING, DutyCycle);			//FAN#2
		writeEMC2305Reg(EMC2305_4_FAN_SETTING, DutyCycle);			//FAN#4
		writeEMC2305Reg(EMC2305_5_FAN_SETTING, DutyCycle);	}		//FAN#5
	else {
		TachCounter =(uint16_t)(K_RPM/rpm);							// see datasheet abou the formula to apply		
		TachCounter =TachCounter << 3;							 
		HighByte = (uint8_t)(TachCounter>>8);					
		LowByte = (uint8_t)(TachCounter & 0b11111000);
		writeEMC2305Reg(EMC2305_1_TACH_TARGET_LOW,LowByte); writeEMC2305Reg(EMC2305_1_TACH_TARGET_HIGH,HighByte);	//FAN#1
		writeEMC2305Reg(EMC2305_2_TACH_TARGET_LOW,LowByte); writeEMC2305Reg(EMC2305_2_TACH_TARGET_HIGH,HighByte);	//FAN#2
		writeEMC2305Reg(EMC2305_4_TACH_TARGET_LOW,LowByte); writeEMC2305Reg(EMC2305_4_TACH_TARGET_HIGH,HighByte);	//FAN#4
		writeEMC2305Reg(EMC2305_5_TACH_TARGET_LOW,LowByte); writeEMC2305Reg(EMC2305_5_TACH_TARGET_HIGH,HighByte); }	//FAN#5
}


/*§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§*/
void FANmonitor(void) {

	uint16_t RPM;
	uint16_t COUNT;
	uint8_t HighByte, LowByte;

	HighByte =readEMC2305Reg(EMC2305_1_TACH_READ_HIGH);
	LowByte =readEMC2305Reg(EMC2305_1_TACH_READ_LOW);	//FAN#1 - read the TACH value
	COUNT = ( ( (uint16_t)HighByte <<8 ) + (uint16_t)LowByte );											//count of the 32.768KHz clock
	COUNT = COUNT >> 3;
	RPM	= (uint16_t)(K_RPM/COUNT);
	lcd_gotoxy(15,0); utoa(RPM, LCDbuffer, 10); lcd_puts(LCDbuffer);
	
	HighByte =readEMC2305Reg(EMC2305_2_TACH_READ_HIGH);	
	LowByte =readEMC2305Reg(EMC2305_2_TACH_READ_LOW); 	//FAN#2 - read the TACH value
	COUNT = ( ( (uint16_t)HighByte <<8 ) + (uint16_t)LowByte );											//count of the 32.768KHz clock
	COUNT = COUNT >> 3;
	RPM	= (uint16_t)(K_RPM/COUNT);
	lcd_gotoxy(15,1); utoa(RPM, LCDbuffer, 10); lcd_puts(LCDbuffer);
	
	HighByte =readEMC2305Reg(EMC2305_4_TACH_READ_HIGH);	
	LowByte =readEMC2305Reg(EMC2305_4_TACH_READ_LOW); 	//FAN#4 - read the TACH value
	COUNT = ( ( (uint16_t)HighByte <<8 ) + (uint16_t)LowByte );											//count of the 32.768KHz clock
	COUNT = COUNT >> 3;
	RPM	= (uint16_t)(K_RPM/COUNT);
	lcd_gotoxy(15,2); utoa(RPM, LCDbuffer, 10); lcd_puts(LCDbuffer);
	
	HighByte =readEMC2305Reg(EMC2305_5_TACH_READ_HIGH);	
	LowByte =readEMC2305Reg(EMC2305_5_TACH_READ_LOW); 	//FAN#5 - read the TACH value
	COUNT = ( ( (uint16_t)HighByte <<8 ) + (uint16_t)LowByte );											//count of the 32.768KHz clock
	COUNT = COUNT >> 3;
	RPM	= (uint16_t)(K_RPM/COUNT);
	lcd_gotoxy(15,3); utoa(RPM, LCDbuffer, 10); lcd_puts(LCDbuffer);
}

/*§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§*/
int main( void ) {
	
	DDRA &= ~_BV(5);		// PORTA.5 ALERT from FAN controller
	DDRA &= ~_BV(6);		// PORTA.6 left button
	DDRC &= ~_BV(7);		// PORTC.7 right button
		
	DDRD |= _BV(6);			// PORTD.6 is the GND for the LCD through the MOS. 
	
	lcd_init(LCD_DISP_ON);		// LCD on, Cursor OFF
	lcd_on();
	sei();						// enable interrupts

	i2c_init();				
	
	lcd_gotoxy(0,1);	lcd_puts("Init FANs controller");
	
	EMC2305_KO =i2c_start(EMC2305_ADD+I2C_WRITE); 										// set device address on I2C bus + WRITE
	if(EMC2305_KO) {
		i2c_stop();
		lcd_gotoxy(0,2);	lcd_puts("FAN  controller KO  ");
		while(1); }		
	
	InitEMC2305(); 
	lcd_gotoxy(0,2);	lcd_puts("FAN CTRL online     "); _delay_ms(1000);
	
	
	GlobalRPM =NOCTUA_NF_A9x14_MINRPM; setRPM_EMC2305(GlobalRPM); _delay_ms(2000);
	GlobalRPM= NOCTUA_NF_A9x14_MAXRPM; setRPM_EMC2305(GlobalRPM); _delay_ms(2000);
	
	lcd_clrscr();
		
	while(1) {	
		
		for(i=0; i<=0x1E; i++) {
			writeEMC2305Reg(EMC2305_FAN_INTERRUPT_ENABLE,i);				// FAN5 FAN4 noFAN3 FAN2 FAN1 generates an ALERT if drive/spin/stall fault
			EMC2305reg = readEMC2305Reg(EMC2305_FAN_INTERRUPT_ENABLE);
			utoa(i+0x100,LCDbuffer,2); lcd_gotoxy(0,1); lcd_puts(LCDbuffer); lcd_gotoxy(0,1); lcd_putc(' ');				// print a binary value
			utoa(EMC2305reg+0x100,LCDbuffer,2); lcd_gotoxy(0,2); lcd_puts(LCDbuffer); lcd_gotoxy(0,2); lcd_putc(' ');		// print a binary value
			_delay_ms(1000);
		}	
		
		FANmonitor();														// always shows the RPM from the inner registers (tachometer counter) of EMC2305 

		SETUPbutton =ReadSETUPbutton();							
		if( SETUPbutton==HIGH && !Setup_Detected) Setup_Detected=	true;
		if( Setup_Detected && SETUPbutton==LOW) {							// wait for the SETUPbutton release and check if (SIMPLE MENU) or (RESET) or (FULL MENU)
			GlobalRPM +=100;  if (GlobalRPM > NOCTUA_NF_A9x14_MAXRPM ) GlobalRPM =NOCTUA_NF_A9x14_MINRPM;
			setRPM_EMC2305(GlobalRPM);
			lcd_gotoxy(0,0); lcd_puts("     "); lcd_gotoxy(0,0); utoa(GlobalRPM,LCDbuffer,10); lcd_puts(LCDbuffer);
			Setup_Detected = false; }
		
		CHANGEbutton =ReadCHANGEbutton();									
		if(CHANGEbutton==HIGH && !Change_Detected) Change_Detected = true; 
		if(Change_Detected && CHANGEbutton==LOW) {							// sync to the High>Low front
			GlobalRPM -=100;  if (GlobalRPM < NOCTUA_NF_A9x14_MINRPM ) GlobalRPM =NOCTUA_NF_A9x14_MAXRPM;		
			setRPM_EMC2305(GlobalRPM);
			lcd_gotoxy(0,0); lcd_puts("     "); lcd_gotoxy(0,0); utoa(GlobalRPM,LCDbuffer,10); lcd_puts(LCDbuffer);
			Change_Detected = false; }
	} 
} 

the Peter Fleury TWI packet is used like it is, with 100KHz for the i2C SM bus.

Only tried a different restart(): as said before with one start() only (original) and with a stop() before the start().

For this I don't add the Peter Fleury TWI solution. I only change its name in i2cmaster.c and i2cmaster.h

Same story for lcd.c and lcd.h

________________________________________________________________________________________________

 

@ ki0bk

the behavior is the same with the fans connected or not connected.

Without fans the EMC2305 PWM output (open collector) is loaded with only a 10K pull-up resistor, nothing else.

I don't want the tachometer algorithm works. It cannot work when there aren't the fans. 

I want to write into a volatile register and read immediately after what i wrote.

Anyway, even if I connect the fans the behavior is the same. 

 

Anyway I prepare a schematic shortly with the basic setup I organized today.

I have isolated the case and I am mounting a board with only the MCU, the EMC2305 plus a level translator from 5V to 3V3, and two digital reostat AD5272-20K.

________________________________________________________________________________________________

 

@ Paulvdh. Logic analyser KO. It will come back within this week and I check what's into the bus

 

 

 

 

   

Cause I can change successfully the Duty Cycle, that means (I think) I can write in the right way into the register.

Why I cannot read from a register? The EMC2305 replies to the "stimuluses" of the MCU, there are no error.

Why i cannot address the right register when I have to read it? 

I am loosing something so big that I don't see it.

First time I have a behavior like this.

 

 

Any help is appreciated. It is a week I am on this trouble. It is becoming a nightmare.

 

I ordered another bunch of 10 chips because playing with them I burn the 3 received at the beginning.

 

best regards.

 

 

 

 

 

Attachment(s): 

oiram revenac

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

I don't like your logic in main().   It will be fine on most devices.   Until you are sure of the behaviour I would say:

    EMC2305_KO =i2c_start(EMC2305_ADD+I2C_WRITE);    // set device address on I2C bus + WRITE
    if(EMC2305_KO) {
        i2c_stop();
        lcd_gotoxy(0,2);
        lcd_puts("FAN  controller KO  ");
        while(1); 
    }		
    i2c_stop();     //.kbv ensure a fresh start instead of restart.
    InitEMC2305();
    ... 

But the first step is to read the device ID.  e.g.

uint8_t Manufacturer_ID = readEMC2305Reg(0xFE);
uint8_t Product_ID = readEMC2305Reg(0xFD);
if (Manufacturer_ID != 0x5D || Product_ID != 0x34) panic();

If that fails,  you know you have a problem.

 

David.

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

tomorrow I check. Thanks.

 

I upload a "simplified" schematic.

Frankly speaking the load of the SMbus is quite high. 3K3 instead of 10K as usual. 

I don't think this is the cause of the trouble. Anyway tomorrow I change the pull-up of the SMbus to the "canonic" value.

 

I have designed only what is under test now in the main board.  

 

Attachment(s): 

oiram revenac

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

You've drawn 2 pullup resistors near "dip40" and 2 near the AD5372 ic's.

6k6//6k6 = 3k3

Still only a few mA and probably not a problem.

But to be sure you should check the signal quality with ans oscilloscope.

Only after you have verivied proper signal levels with an oscilloscope you can expect to have meaningfull output from a logic analyser.

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com

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

hi all, I am back, not disappeared.

 

New board, new EMC2305 chip. 

 

Changed the pull-up after the PCA9617A level translator, SMbus side, from 3K3 to 10K so to be within the SMbus specifications (not below 8K5 at 3V3 power supply) 

The i2C bus, ATMEGA1284 side, has the pull-up resistors unchanged. So 3K3 are the total pull-up resistors for the bus.

 

Attached 3 screenshots of the SCL and SDA signals.

Pictures #1 and #2 about the level and the fronts. There is a little oscillation. I will tune in the future working on the load.

Anyway seems OK. Please note the SCL (above trace) is LOW before to pulse (see 2) below).  

 

Anyway the matter is always the same. I read a wrong value. 

 

This is this code used to test the Read of the Registers. Before to try to write any register I read some register with a precise and known value inside

	........
        EMC2305_KO =i2c_start(EMC2305_ADD+I2C_WRITE);

	lcd_gotoxy(0,1);
	if(EMC2305_KO) { 
		lcd_puts("no FAN controller"); while(1); }						
	
	for(;;) { 
		lcd_gotoxy(0,0);
		EMC2305reg =readEMC2305Reg(Manufacturer_ID); 
		sprintf(LCDbuffer, "%.2X MFG ID (5D) \n", EMC2305reg); lcd_puts(LCDbuffer); 
		EMC2305reg = readEMC2305Reg(Product_ID);
		sprintf(LCDbuffer, "%.2X PRD ID (34) \n", EMC2305reg); lcd_puts(LCDbuffer);
		EMC2305reg = readEMC2305Reg(Revision_ID);
		sprintf(LCDbuffer, "%.2X RVS ID (80) \n", EMC2305reg); lcd_puts(LCDbuffer);
		wdt_reset();
         }

1) in this way the 3 values read are 0xFF 0xFF 0xFF  instead of 0x5D 0x34 and 0x80 (80 should be not correct because the datasheet is old).

    With this code the SCL bus is always LOW, means the SCL bus is not released even if there is the i2c_readNak() before to return.

uint8_t readEMC2305Reg(uint8_t regAddr) {
	
	i2c_start_wait(EMC2305_ADD+I2C_WRITE);     					

	EMC2305_KO =i2c_write(regAddr);								
	if (EMC2305_KO) {
		i2c_stop();												
		lcd_puts("FanWrite_KO\n");  
		return -1;}												
	else {
		i2c_rep_start(EMC2305_ADD+I2C_READ);					
		return i2c_readNak(); /*i2c_stop() already inside*/ }	
}

 

2) if I insert one i2c_stop() in the middle of the for(;;) the values showed are 0x01 0x04 0x00 ... at least something is read, and the SCL signal is "better"

    Look the pictures #2, the upper trace is related the SCL signal, before the last burst it is LOW, then the burst, and the effect of the i2C_stop() just said.

 

3) if I use the code with the for(;;), without any i2c_stop() in the middle, and I modify the i2c_readNack() from Peter Fleury inserting a i2c_stop() before the end of the module

unsigned char i2c_readNak(void) {
	TWCR = (1<<TWINT) | (1<<TWEN);
	while(!(TWCR & (1<<TWINT)));
	i2c_stop();    // >>>>>> TRIAL ONLY inserted by me <<<<<<<< 
	return TWDR;

}/* i2c_readNak */

  the SCL at the oscilloscope becomes "classic" (see pictures #3), at pull-up when there is "inactivity".

  The value showed when I read the registers are again 0x01 0x04 0x00 instead of 0x5D 0x34 0x80.

 

I tried with 100KHz SMbus, and with 60KHz too. Same.

 

 

Tomorrow (I hope) I will have back the logic analyzer so to have more details but seems clear that the i2c_read() is not synchronized.

What I read is always exactly the same so it is not a matter of timing. The i2c_read() is not synchronized. 

 

Any suggestion?

For sure the Peter Fleury's TWI interface is OK. I already tested and used in other application.

What's wrong with my solution?

 

Thanks for any help.

Attachment(s): 

oiram revenac

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

The datasheet describes the Read sequence in 3.2 SMBus Protocols.

It shows a i2c_repstart() and NOT a i2c_stop(), i2c_start().

 

My main query is : Why do you use a PCA9617?

The datasheet implies that SDA, SCL are 5V Tolerant.   So you can put the EMC2305 on the regular 5V I2C bus.  Just supply 3.3V to VDD.

 

I really don't like the idea of reading TWDR after i2c_stop().   I am fairly certain that it will be safe but it seems more natural to read TWDR before i2c_stop()

I would stick closely to the Fleury library.   It provides i2c_start() and i2c_repstart() functions.   And it does everything in a logical order.

 

I see no problem with typical I2C pullup values e.g. 4k7 for 5V bus or 2k2 for 3.3V bus.    Anything from 820R to 10k should be ok if there is only moderate capacitance on the bus.

 

Yes, it is wise to have an error return for EMC2305_readReg().    Make the function return an int.   Then positive is ok,  negative is error.

 

David.

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

Hi David,

 

I am 100% with you. After two weeks stopped on this troubles the risk is to lose the right way trying exotic alternativ

 

So:

 

1) I went back to Peter Fleury original code. No i2c_stop() in a no sense position as I did (before the TWDR ... very stupid idea).

For sure the Peter Fleury code is OK because it is used by hundred people and used by me with satisfaction other times.

 

 

2) I implemented the Read and Write function in the canonical way, with an i2c_stop() at the end of the cycle (exactly as in the example by Peter Fleury)

uint8_t readEMC2305Reg(uint8_t regAddr) {
	
	uint8_t buff;
	
	i2c_start_wait(EMC2305_ADD+I2C_WRITE);     					

	EMC2305_KO =i2c_write(regAddr);
	if (EMC2305_KO) {
		i2c_stop();
		buff =0xFF; }
	else {
		i2c_rep_start(EMC2305_ADD+I2C_READ);
		buff =i2c_readNak(); }				 		

	i2c_stop();		
	
	return buff;
}

and the WRITE still in the canonical way 

void writeEMC2305Reg(uint8_t regAddr, uint8_t regVal) {

	i2c_start_wait(EMC2305_ADD+I2C_WRITE); 		

	EMC2305_KO =i2c_write(regAddr); 			
	
	if (EMC2305_KO) { 
		lcd_puts("FanWrite_KO_1\n"); _delay_ms(300); }
	else{
		EMC2305_KO =i2c_write(regVal);
		if (EMC2305_KO) {
			lcd_puts("FanWrite_KO_2\n"); _delay_ms(300); }}
	
	i2c_stop();										
}

 

 

3) in the main.c, where I leave the EMC2305 at the default setup (I don't try any write), I tried a first loop 

	for(;;) { 

		lcd_gotoxy(0,0);
		
		EMC2305reg =readEMC2305Reg(Manufacturer_ID);
		sprintf(LCDbuffer, "%.2X MFG ID (5D) \n", EMC2305reg); lcd_puts(LCDbuffer);

		EMC2305reg = readEMC2305Reg(Product_ID);
		sprintf(LCDbuffer, "%.2X PRD ID (34) \n", EMC2305reg); lcd_puts(LCDbuffer);	

		EMC2305reg = readEMC2305Reg(Revision_ID);
		sprintf(LCDbuffer, "%.2X RVS ID (80) \n", EMC2305reg); lcd_puts(LCDbuffer); 

	}

the signals at the oscilloscope are OK, levels, edges, timing.

The values at the LCD are "wrong": read 0x01 0x04 and 0x00 instead of 0x5D 0x34 and 0x80. 

The values read are stable and always the same (it is not a matter of noise). Nothing change with the time.

 

 

4) I tried a 2nd loop with inside a WRITE to a volatile register (Config Register, default value at power on is 0x40)

	for(;;) { 

		lcd_gotoxy(0,0);
		
		EMC2305reg =readEMC2305Reg(Manufacturer_ID);
		sprintf(LCDbuffer, "%.2X MFG ID (5D) \n", EMC2305reg); lcd_puts(LCDbuffer);

		EMC2305reg = readEMC2305Reg(Product_ID);
		sprintf(LCDbuffer, "%.2X PRD ID (34) \n", EMC2305reg); lcd_puts(LCDbuffer);	

		EMC2305reg = readEMC2305Reg(Revision_ID);
		sprintf(LCDbuffer, "%.2X RVS ID (80) \n", EMC2305reg); lcd_puts(LCDbuffer); 

		writeEMC2305Reg(EMC2305_CONFIGURATION, 0x40); 
		EMC2305reg = readEMC2305Reg(EMC2305_CONFIGURATION);
		sprintf(LCDbuffer, "%.2X CONFIG (40) \n", EMC2305reg); lcd_puts(LCDbuffer);

		_delay_ms(1500); wdt_reset();
	}

I write 0x40 into the configuration register (it is also the default value) and I read it immediately after.

Wrote 0x40 and I read 0x00 ... and (SURPRISE!!) after 1.5sec ... when I read again the MFG DEV and REV registers, I read 0x00 0x00 0x00

 

So, double confirmed, what I read is not what I am thinking is addressed. The space address I refer too is wrong.

Now I am with 100% Fleury Code. No one change. Somewhere the addresses are shifted.  

 

 

5) I removed the PCA9617. Now it is not needed. In the future, when I will share the bus with another peripheral no 5V tolerant I have to install again but now can be bypassed.

Even if without PCA9617 nothing change in the results: same values with the same code implemented.

 

 

 

.... where do I corrupt the address of the registers?

where will you look first knowing that:

1) I am 100% Peter Fleury original

2) PCA9617 is removed and substituted by 2 jumpers, so there is any delay, phase rotation or bla bla bla ... 

3) the #define used for the registers are correct

 

oiram revenac

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

I am a little gobsmacked by your problem.

 

As a general rule:

1.  Use external pullup resistors.

2.  Use correct Slave address

3.  Use Error return values from library functions.

 

You appear to be doing everything correctly.

4.  Change/omit the PCA9617

5.  Change the EMC2305.

6.  Check Ground Plane and all soldered joints.     Avoid any mechanical connectors especially breadboards.

 

I would expect all the I2C/SMBUS comms to work 100% whether the external fan electronics is connected or not.

 

It is a lot easier to debug with a Logic Analyser.   But since you have a scope,  you can inspect the bus cycle.   Which means one clock pulse or 9 clocks in one bus cycle.

You can calculate bus capacitance,  measure risetimes etc.    But only when in "human view".   (your posted photos are impossible to read - by me)

 

David.

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

... you cannot imagine my frustration... and the serviced logic analyzer is in delay ...

oiram revenac

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

finally I found the force to collect the traces by the oscilloscope only, and by cut & past I have verified one "bunch" of bits.

I forgot how precious is the Logic Analyzer. I went back in one hour at the time of the school.

 

The device address is 0b01011000 where the last bit is 1/0 Read/write.

At the oscilloscope this is correct.

 

I want to READ the Register Manufacturer ID: it is the REG 0xFE.

At the oscilloscope it is OK. 

 

Ack and Nak are in the right sequence and position.

 

 

The Reg content released by the EMC2305 (check the oscilloscope trace) is 0x00 and it is what I see at the display.

 

So what the firmware is doing is correct.

The problem is the EMC2305 that doesn't reply as expected. 

What I show at the display is really the result of the communication between the ATMEGA <> EMC2305.

 

The problem is that the manufacturer ID should be 0x5D and not 0x00.

 

Seems that I am no able to address the right position of the address space inside the EMC2305. 

 

 

Now what can I do?

Tomorrow I will change for the 4th time the EMC2305 chip.

But I scare to have a double confirmation only of the problem.

If you were in my place, what would you do?

 

 

P.S.: Next week I hope to receive the Logic Analyzer so to investigate deeply more than one state.

Attachment(s): 

oiram revenac

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

solved!

 

was the double pull-up on the i2C bus, before the EMC2305 and before the AD5274.

 

Even if both pull-up are referred to the same +5V and to the same ground plane.

It is a parallel of resistors placed 15 cm faraway each other.

 

And it is not because the the 6K6//6K6 loads too much the SMbus.

SMbus would like a pull-up > 8K5(14K) for a 3V3(5V) bus.

Indeed the trouble was present also when the PCA9617 was in position and the SMbus was electrically separated from the i2C bus.  

 

Why I don't know .... thinking to the open collector configuration of the SCL and SDA pins it is not clear for me why a double pull-up (it is a parallel only, with the resistors not close) ruins the driving capability when the slave has to drive the bus.

 

Anyway finally an happy week-end to me first and to david.prentice too.

Finally is thanks him if I investigated all the details excluding one by one all the possibilities till the physical level.

 

 

oiram revenac

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#include <avr/io.h>
#include <inttypes.h>
#include <stdlib.h>
#include <avr/eeprom.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>
#include <avr/wdt.h>
#include <stdint.h>		

#include <i2cmaster.h>
#include <lcd.h>

//-------------------------------------------------------------------------------------------------------------------------	
#define EMC2305_ADD				0b01011000			// 0b00101100* from the data sheet  
#define Manufacturer_ID			0xFE				// read only register. 0x5D (data sheet rev. 1.3 05-18-11)
#define Product_ID				0xFD				// read only register. 0x34 (data sheet rev. 1.3 05-18-11)
#define Revision_ID				0xFF				// read only register. 0x80 (data sheet rev. 1.3 05-18-11)
#define	EMC2305_CONFIGURATION	0x20				// default at power on is 0x40

//-------------------------------------------------------------------------------------------------------------------------	
uint8_t		EMC2305reg;											// to read or write an EMC2305 register
uint8_t		EMC2305_KO;

char		LCDbuffer[21];

//-------------------------------------------------------------------------------------------------------------------------	
void InitTimers() {

	TCCR0A |= (1<<WGM01);	
	OCR0A = 99;				
	TCCR0B |= (1 << CS01);	
	TIMSK0 |= (1<<OCIE0A );	
}

//-------------------------------------------------------------------------------------------------------------------------	
uint8_t readEMC2305Reg(uint8_t regAddr) {
	
	uint8_t buff;
	
	i2c_start_wait(EMC2305_ADD+I2C_WRITE);     					

	EMC2305_KO =i2c_write(regAddr);
	if (EMC2305_KO) {
		i2c_stop();
		buff =0xFF; }
	else {
		i2c_rep_start(EMC2305_ADD+I2C_READ);
		buff =i2c_readNak(); }				 		

	i2c_stop();										
	
	return buff;
}

//-------------------------------------------------------------------------------------------------------------------------	
void writeEMC2305Reg(uint8_t regAddr, uint8_t regVal) {

	i2c_start_wait(EMC2305_ADD+I2C_WRITE); 		

	EMC2305_KO =i2c_write(regAddr); 			
	
	if (!EMC2305_KO) { 
		lcd_puts("FanWrite_KO_1\n"); _delay_ms(300); }
	else{
		EMC2305_KO =i2c_write(regVal);
		if (EMC2305_KO) {
			lcd_puts("FanWrite_KO_2\n"); _delay_ms(300); }}
	
	i2c_stop();										
}

//#############################################################################################################################	
int main( void ) {
	
	DDRD |= _BV(6);				// it is used for the GND of the LCD through the MOSFET
	PORTD |= _BV(6); 			// lcd_on()
	lcd_init(LCD_DISP_ON);		// LCD on, Cursor OFF
	
	InitTimers();				
	
	sei();						// enable interrupts
	
	i2c_init();				

	//-------------------------------------------------------------------------------------------------------------------------	
	EMC2305_KO =i2c_start(EMC2305_ADD+I2C_WRITE);
	i2c_stop();			
	if(EMC2305_KO) { lcd_puts("no FAN controller\n"); while(1); }
	//-------------------------------------------------------------------------------------------------------------------------	

	for(;;) { 

			lcd_gotoxy(0,0);
		
			EMC2305reg =readEMC2305Reg(Manufacturer_ID);
			sprintf(LCDbuffer, "%.2X MFG ID (5D) \n", EMC2305reg); lcd_puts(LCDbuffer);

			EMC2305reg = readEMC2305Reg(Product_ID);
			sprintf(LCDbuffer, "%.2X PRD ID (34) \n", EMC2305reg); lcd_puts(LCDbuffer);	

			EMC2305reg = readEMC2305Reg(Revision_ID);
			sprintf(LCDbuffer, "%.2X RVS ID (80) \n", EMC2305reg); lcd_puts(LCDbuffer); 

//			writeEMC2305Reg(EMC2305_CONFIGURATION, 0x40); 
//			EMC2305reg = readEMC2305Reg(EMC2305_CONFIGURATION);
//			sprintf(LCDbuffer, "%.2X CONFIG (40) \n", EMC2305reg); lcd_puts(LCDbuffer);

			_delay_ms(1500); wdt_reset();
		}

} // end of main

/*§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§*/
ISR(TIMER0_COMPA_vect) {
}

 

Attachment(s): 

oiram revenac

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

I have never read the SMBus spec.    I would expect Slave devices to cope with sinking up to 8mA e.g. when it does an ACK.   An 8k5 pullup to 3.3V is only 0.39mA.  14k pullup to 5V is 0.36mA

 

However your Slave is a 400kHz device.   It might be getting reflections on the bus with the distributed pullups.   Only a fast scope would see this.

And it is also the reason why you should only have a SINGLE pullup on SDA, SCL.

 

I am still a little gobsmacked.    The AVR has slew-limiting on its TWI.   I would expect this to cope with any nasties.

The Slave is Acking as expected.   If it was receiving  glitches/reflections it would not recognise its own i2c_start() address.

 

David.

Last Edited: Sat. Jan 13, 2018 - 10:36 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Now I am working with 100KHz i2C/SMbus, anyway when I tried to solve the matter I tried the 20 40 60 and 80 KHz too, without any positive result. 

 

The slave always recognized the i2C_start because the slave ACK first and the started driving the bus trying to send the register content.

Anyway it is not a matter of a too heavy pull-up because the problem was exactly the same even if the SMbus was separated from the i2C bus by the PCA9617 and pulled-up with only 10K.

 

So I vote for a reflection ... but a reflection with a 20KHz rate is quite difficult. 

So the origin of the problem is really a mystery for me.

 

Mario

 

oiram revenac

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

this morning I try to work with the EMC2305 and the pair of AD5272.

 

With a double pull-up towards 5V nothing works.

With a pull-up towards 5V close the EMC2305 only the EMC2305 works properly.

With a pull-up towards 5V close the pair of AD5272 only the AD5272 work properly.

 

I introduced again the bus separator PCA9617.

Experimented exactly the 3 cases just described.

 

10cm bus length, 100KHz frequency rate.

 

I set up another post because the matter is not firmware buy hardware and Peter Fleury ot the TWI management is out of the case.

http://www.avrfreaks.net/node/ad...

 

Any suggestion?

oiram revenac

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

Post a photo of your actual hardware.   And the current schematic.

 

You have a scope.   I would look closely at what you see on any "GND" pins.

 

David.

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

.... 

AD5272 from Analog Device and EMC2305 from SMSC (Microchip), can have the same i2C address!

And UNFORTUNATELY for me I configured the HW so to have one of the AD5272 at 0x58 ... exactly the same address of the EMC2305, still 0x58.

Changed the pull-up of the ADDR pin of the EMC2305 and now everything is fine.

....

 

matter explained. 

Attachment(s): 

oiram revenac