HTS221 Humidity and Temperature sensing problem with MikroC and PIC
Does anyone work with HTS221 of ST using MikroC? I used I2C to communicate with that chip but when I read the sensing signals from HTS221, I think I have errors. I always read value 79 from any register .
I used RB0, RB1 and configure them as SCK and SDA in I2C. I uploaded whole of my codes and the signals that I captured by an oscilloscope. Any recommendations and suggestions will be high appreciated. Many thanks.
#define W_SAD 0xBE #define R_SAD 0xBF sbit SDA at RB0_bit; sbit SCL at RB1_bit; sbit SDA_D at TRISB0_bit; void GPIO_Init(); void HTS221_Init(); void HTS221_Start(); void HTS221_Stop(); void HTS221_ACK(); void HTS221_NACK(); void HTS221_Write(unsigned char address); unsigned char HTS221_uRead(unsigned char address); char HTS221_cRead(unsigned char address); void main() { unsigned char data0_temp[8], data0_humid[8]; unsigned char ucSensor; char data1_temp[8], data1_humid[8]; char Temperature[2], Humidity[2]; int H0=0, H1=0, H2=0, H3=0; int T0=0, T1=0, T2=0, T3=0; int temp=0, humi=0; float fTemp=0, fHumidity=0; int raw=0; GPIO_Init(); HTS221_Init(); while(1) { PORTB.B5 = 1; //Reading calibration values //H0_rH_x2 0x30 register address - u8 data0_humid[0] = HTS221_uRead(0x30); H0 = data0_humid[0]/2; //H1_rH_x2 0x31 register address - u8 data0_humid[1] = HTS221_uRead(0x31); H1 = data0_humid[1]/2; //H0_T0_OUT 0x36 0x37 - s16 data1_humid[0] = HTS221_cRead(0x36); //Lower Byte data1_humid[1] = HTS221_cRead(0x37); //Higher Byte H2 = data1_humid[1]*256+data1_humid[0]; //H1_T0_OUT 0x3A 0x3B - s16 data1_humid[2] = HTS221_cRead(0x3A); //Lower Byte data1_humid[3] = HTS221_cRead(0x3B); //Higher Byte H3= data1_humid[3]*256+data1_humid[2]; //T0_degC_x8 0x32 register address - u8 data0_temp[0] = HTS221_uRead(0x32); T0 = data0_temp[0]; //T1_degC_x8 0x33 register address - u8 data0_temp[1] = HTS221_uRead(0x33); T1 = data0_temp[1]; //T1/T0 msb 0x35 - u2 data0_temp[2] = HTS221_uRead(0x35); raw = data0_temp[2]; T0 = ((raw & 0x03)*256) + T0; T1 = ((raw & 0x0C)*64) + T1; //T0_OUT 0x3C 0x3D - s16 data1_temp[0] = HTS221_cRead(0x3C); //Lower Byte data1_temp[1] = HTS221_cRead(0x3D); //Higher Byte T2 = data1_temp[1]*256 + data1_temp[0]; //T1_OUT 0x3E 0x3F - s16 data1_temp[2] = HTS221_cRead(0x3E); //Lower Byte data1_temp[3] = HTS221_cRead(0x3F); //Higher Byte T3 = data1_temp[3]*256 + data1_temp[2]; //Reading Humidity and Temperature output //H_OUT 0x28 0x29 - s16 Humidity[0] = HTS221_cRead(0x28); Humidity[1] = HTS221_cRead(0x29); //T_OUT 0x2A 0x2B - s16 Temperature[0] = HTS221_cRead(0x2A); Temperature[1] = HTS221_cRead(0x2B); humi = Humidity[1]*256+Humidity[0]; temp = Temperature[1]*256+Temperature[0]; if(temp > 32767) { temp-= 65536; } fHumidity = ((1.0 * H1) - (1.0 * H0)) * (1.0 * humi - 1.0 * H2) / (1.0 * H3 - 1.0 * H2) + (1.0 * H0); fTemp = ((T1 - T0) / 8.0) * (temp - T2) / (T3 - T2) + (T0 / 8.0); // Temperature in Celcius UART1_Write(Humidity[1]); } } //Initialize GPIO and I2C protocol for measuring sensing signal void GPIO_Init() { ANSELC = 0; ANSELB = 0; TRISB = 0; //PORTB = 0; TRISC = 0; //PORTC = 0; OSCCON = 0b01011011; //Internal Oscillator 1Mhz //I2C1_Init(100000); //Initialize I2C communication //I2C1_Start(); UART1_Init(9600); delay_ms(300); } //HTS221 Start I2C communication void HTS221_Start() { SDA_D = 0; // Data output SDA = 1; SCL = 1; delay_us(10); SDA = 0; //delay_us(10); } //HTS221 Stop I2C communication void HTS221_Stop() { SDA_D = 0; SDA = 0; SCL = 1; delay_us(10); SDA = 1; //delay_us(100); } //HTS221 Ackowledgement void HTS221_ACK() { SDA_D = 0; SDA = 0; SCL = 0; delay_us(1); SCL = 1; delay_us(2); SCL = 0; delay_us(1); SDA = 1; } //HTS221 Not Ackowledgement void HTS221_NACK() { SDA_D = 0; SDA = 1; SCL = 0; delay_us(1); SCL = 1; delay_us(2); SCL = 0; delay_us(1); SDA = 0; } //Write data to HTS221 based on register address void HTS221_Write(unsigned char CMD) { unsigned char i; unsigned char j = CMD; SDA_D = 0; //Write function so SDA_D is OUTPUT for(i=0x80; i>0; i/=2) { SCL = 0; //delay_us(1); if(i & j) //AND SCL and DATA if { SDA = 1; //SDA = 1 value } else { SDA = 0; //SDA = 0 value } SCL = 1; //delay_us(1); } } //Read data from HTS221 based on register address, return unsigned char value unsigned char HTS221_uRead(unsigned char CMD) { unsigned char ucData=0; int i=0; HTS221_Start(); HTS221_Write(W_SAD); HTS221_Write(CMD); HTS221_Start(); //Repeat Start HTS221_Write(R_SAD); SDA_D = 1; SCL = 0; delay_us(1); SCL = 1; while(SDA==1); delay_us(200); for(i=0; i<8; i++) { ucData <<= 1; SCL = 0; if(SDA == 1) ucData = ucData | 0x01; SCL = 1; } HTS221_Stop(); return ucData; } //Read data from HTS221 based on register address, return unsigned char value char HTS221_cRead(unsigned char CMD) { char cData=0; int i=0; HTS221_Start(); HTS221_Write(W_SAD); HTS221_Write(CMD); HTS221_Start(); //Repeat Start HTS221_Write(R_SAD); SDA_D = 1; SCL = 0; delay_us(1); SCL = 1; while(SDA==1); delay_us(200); for(i=0; i<8; i++) { cData <<= 1; SCL = 0; if(SDA == 1) cData = cData | 0x01; SCL = 1; } HTS221_Stop(); return cData; } //Initialize HTS211 Humidity and Temperature sensor void HTS221_Init() { HTS221_Start(); HTS221_Write(W_SAD); HTS221_Write(0x10); //AV_CONF HTS221_Write(0x1B); //Temperature average samples = 16; Humidity average samples = 32 HTS221_Stop(); //Configure Active Mode, 1Hz @ODR, not continuous update 0x20 HTS221_Start(); HTS221_Write(W_SAD); HTS221_Write(0x20); HTS221_Write(0x85); HTS221_Stop(); }
Attachment 136108
References:
C language: https://github.com/ControlEverything...ter/C/HTS221.c
Arduino : https://github.com/ControlEverything...ino/HTS221.ino
Hi,
Maybe a timing problem.
The conversion data rate is low. Maybe you need to wait until conversion is finished.
Klaus
@KlauST: you mean in the Read function I should wait until the conversion is finished. May I can use an interrupt?
Hi,
No, not "in the READ function",
but you should READ - wait - READ -
***
What debugging have you done so far?
--> does the sensor send ACK?
--> Read status byte: and show H_DA and T_DA.
--> show raw (HEX) data read from the I2C
Your scope picture shows 8 clock cycles only. I expect 9. ( 8 data + 1 ACK)
Why the bit bang mode? Doesn′t your uC has I2C perifieral?
Klaus
Your I2C handling seems basically wrong. At least SDA should be operated as open drain, means you will be toggling the direction bit SDA_D while SDA is programmed to 0.
Fix the I2C handling and check that you get correct waveforms according to the Philips/NXP I2C protocol specification. The waveform in the screenshot is completely wrong.
@FvM: I really appreciate your suggestions but could you tell me more in detail. Which are parts in the waveforms wrong?
In my circuit, I used pull-up resistor for both SCK and SDA and I change the direction of SDA_D from Write to Read depending on my purpose.
@KlausSt: my uC I used that is PIC16F1513 and it has I2C function but the same pin was used for SPI so I took another pin and try to configure them as same as I2C communication. I will do more experiments later.
Hi,
SDA and SCL are never driven actively HIGH by the controller. Either high_impedance or driven_LOW. It is open_collector or open_drain style .
Details:
Did you read I2C specification? If not, then I strongly recommend to do so.
--> Where is the ninth clock pulse?
Klaus
Hi BiNa2605;
As mentoined above, your I2C routines are completely wrong. Study this site for example:
http://www.robot-electronics.co.uk/i2c-tutorial
Or, try to use the Software I2C Library in mikroC (see the help).
@KlausST: may you check for me?
I write 0xBE(W_Add), 0x30 (Register Add), 0xBF(R_Add) and then read 8 bit value.
Hi,
Look at typical timing diagrams.
Especially the ACK handling...and the timing of SCK...and the signal until first bit of next byte...
After transmitting the 8th bit
.. SCK is pulled low to begin ACK handling
.. during ACK_Low period the ACK (= Low) is asserted, either by master or slave
.. (keeping timing) the master detects the ACK and answers with SCK_low (this is what I miss in your scope picture
.. SCK remains LOW between two transmitted bytes.
Klaus
@KlausST: Actually I understood what you said but I did still not solve my problem.
this is my Write function:
//Write data to HTS221 based on register address void HTS221_Write(unsigned char CMD) { unsigned char i; unsigned char j = 128; SDA_D = 0; //Write function so SDA_D is OUTPUT for(i=1; i<=8; i++) { SCL = 0; if(CMD & j) { SDA = 1; //SDA = 1 value } else { SDA = 0; //SDA = 0 value } SCL = 1; CMD <<= 1; } //Checking acknowledgement pulse SDA_D = 1; SCL = 0; while(!SDA); SCL = 1; }
Hi,
It seems you still refuse to accept I2C specifications.
From your code it seems you are driving sometimes SDA actively HIGH. This is not allowed! (Already mentioned in post#7)
Short circuit currents may occur (if you keep on specification, then short circuit is impossible). This may lead to IC damage.
And it seems the short circuit really happens inyour case.
See SDA line at about 2.6ms after trigger. It is clean low, then it reaches about 0.4V then it goes back to zero.
At the point wher it reaches 0.4V I assume you try to drive SDA line HIGH, but the slave forces it LOW.
Btw.: This is also at the beginning of your communication.. before trigger event.
--> Don′t be surprised if an IC gets damaged.
Klaus
@Klaus: I tried again just only a write function with a captured screen. I get problem with a read function. How stupid am I. I will post a read function and a figure later. Could you check for me?
And for Read function and a capture:
I read all 0,0 values.
Write function:
//Write data to HTS221 based on register address void HTS221_Write(unsigned char CMD) { unsigned char i; unsigned char j = 128; for(i=1; i<=8; i++) { SCL = 0; if(CMD & j) { SDA = 1; //SDA = 1 value } else { SDA = 0; //SDA = 0 value } SCL = 1; CMD <<= 1; } SDA = 1; SCL = 0; i2c_dly(); while(SDA_IN); SCL = 1; }
//I2C read function unsigned char i2c_rx(char ack) { char x, d=0; SDA = 1; for(x=0; x<8; x++) { d <<= 1; do { SCL = 0; } while(SCL_IN==0); // wait for any SCL clock stretching i2c_dly(); if(SDA_IN==1) d |= 1; SCL = 1; } if(ack) SDA = 0; else SDA = 1; SCL = 0; i2c_dly(); // send (N)ACK bit SCL = 1; SDA = 1; return d; }
//Read data from HTS221 based on register address, return unsigned char value unsigned char HTS221_uRead(unsigned char CMD) { unsigned char ucData=0; int i=0; i2c_start(); HTS221_Write(W_SAD); HTS221_Write(CMD); i2c_start(); //Repeat Start HTS221_Write(R_SAD); ucData = i2c_rx(0); i2c_stop(); return ucData; delay_ms(250); }
Hi,
again: Read I2C specification.
There are goode internet pages, that explain everything in detail.
Example: http://www.i2c-bus.org/addressing/
Compare your waveforms with the specified waveforms.
Just two issues, maybe there are more....
* Why is there no ACK after addressing?
* Correct the bad timing after seinding the first byte
* add an ACK cycle after the first byte.
The same as I did with your scope picture... you could do also. Just comparing pictures...
Klaus
I changed my code to receive an ACK after write byte (after 8 bits).
//Write data to HTS221 based on register address void HTS221_Write(unsigned char CMD) { unsigned char i; unsigned char j = 128; for(i=1; i<=8; i++) { SCL = 0; if(CMD & j) { SDA = 1; //SDA = 1 value } else { SDA = 0; //SDA = 0 value } SCL = 1; CMD <<= 1; } //SDA = 0; SCL = 0; i2c_dly(); SCL = 1; while(SDA_IN); }
But I still receive a wrong data. I will try the next step.
@KlausST: That is right or wrong Sir? I changed another IC chip.
Hi,
check this diagram
https://www.i2c-bus.org/repeated-start-condition/
with your diagram.
And find that the start / repeated_start is missing.
Klaus
The latest Write routine is wrong in so far that it doesn't release SDA before reading acknowledge.
It's also not right to wait for acknowledge. The write transaction will be either acknowledged or not acknowledged, but SDA won't change any more while SCL is high.
Finally, unless you redefined SDA in your code (you only showed a definition in post #1)
sbit SDA at RB0_bit;
@FvM: I redefined SDA, thanks Sir
#define SCL TRISB.F1 // I2C bus #define SDA TRISB.F0 // #define SCL_IN RB1 // #define SDA_IN RB0 //
@KlausST: in post #16, I have a problem with my code. For the ACK checking I programmed wrong codes.
For checking ACK pulse after LSB bit.
SDA = 1; SCL = 0; i2c_dly(); while(SDA_IN == 0); // SCL = 1;
Hi,
Although I am not familiar with PIC and MicroC...
I assume
SDA = 1..is driving active HIGH.
If this is the case ... and you are not willing to correct this...we can not help anymore.
SDA must never be driven high --> instead it should be released, = high Z
SCL must never be driven high --> instead it should be released, = high Z
Klaus