|
|
View previous topic :: View next topic |
Author |
Message |
sshahryiar
Joined: 05 May 2010 Posts: 94 Location: Dhaka, Bangladesh
|
AM2320 I2C Relative Humidity and Temperature Sensor Driver |
Posted: Sat May 16, 2015 9:08 pm |
|
|
AM2320.h
Code: | #define AM2320_address 0xB8
#define I2C_write_cmd 0x00
#define I2C_read_cmd 0x01
#define AM2320_read_sensor_data 0x03
#define AM2320_write_multiple_registers 0x10
#define AM2320_RH_hb 0x00
#define AM2320_RH_lb 0x01
#define AM2320_T_hb 0x02
#define AM2320_T_lb 0x03
unsigned char data_buffer[8];
#use I2C(MASTER, SDA = pin_B7, SCL = pin_B6)
void AM2320_init();
void AM2320_write(unsigned char *value, unsigned char length);
void AM2320_read(unsigned char *value, unsigned char length);
unsigned long CRC16(unsigned char *ptr, unsigned char length);
void get_sensor_data(unsigned char start_address, unsigned char input_length, unsigned char output_length);
void get_RH_and_temperature(unsigned long *data1, signed long *data2);
void get_CRC(unsigned long *CRC_data); |
AM2320.c
Code: | #include "AM2320.h"
void AM2320_init()
{
unsigned char s = 0;
for(s = 0; s< 8; s++)
{
data_buffer[s] = 0x00;
}
}
void AM2320_write(unsigned char *value, unsigned char length)
{
unsigned char s = 0x00;
I2C_Start();
I2C_Write(AM2320_address + I2C_write_cmd);
for(s = 0x00; s < length; s++)
{
I2C_Write(*value++);
}
I2C_Stop();
}
void AM2320_read(unsigned char *value, unsigned char length)
{
unsigned char s = 0x00;
I2C_Start();
I2C_Write(AM2320_address + I2C_read_cmd);
for(s = 0x00; s < length; s++)
{
value[s] = I2C_read(1);
}
I2C_Stop();
}
unsigned long CRC16(unsigned char *ptr, unsigned char length)
{
unsigned long crc = 0xFFFF;
unsigned char s = 0x00;
while(length--)
{
crc ^= *ptr++;
for(s = 0; s < 8; s++)
{
if((crc & 0x01) != 0)
{
crc >>= 1;
crc ^= 0xA001;
}
else
{
crc >>= 1;
}
}
}
return crc;
}
void get_sensor_data(unsigned char start_address, unsigned char input_length, unsigned char output_length)
{
unsigned char s = 0x00;
AM2320_write(0x00, 0x00);
delay_us(1600);
data_buffer[0] = AM2320_read_sensor_data;
data_buffer[1] = start_address;
data_buffer[2] = input_length;
AM2320_write(data_buffer, 0x03);
delay_us(1499);
for(s = 0x00; s < output_length; s++)
{
data_buffer[s] = 0x00;
}
AM2320_read(data_buffer, output_length);
}
void get_RH_and_temperature(unsigned long *data1, signed long *data2)
{
*data1 = ((unsigned long)((data_buffer[2] << 8) | data_buffer[3]));
*data2 = ((data_buffer[4] << 8) | data_buffer[5]);
}
void get_CRC(unsigned long *CRC_data)
{
*CRC_data = ((unsigned long)((data_buffer[7] << 8) | data_buffer[6]));
} |
_________________ https://www.facebook.com/MicroArena
SShahryiar |
|
|
nuclear__
Joined: 24 Jan 2015 Posts: 63
|
call from main |
Posted: Mon Mar 21, 2016 1:27 pm |
|
|
Hi
Can you give us an example on how you read values from main? |
|
|
sshahryiar
Joined: 05 May 2010 Posts: 94 Location: Dhaka, Bangladesh
|
|
|
nuclear__
Joined: 24 Jan 2015 Posts: 63
|
|
Posted: Tue Mar 22, 2016 5:48 am |
|
|
Ok, I will try to work with that. Thank you ! |
|
|
nuclear__
Joined: 24 Jan 2015 Posts: 63
|
|
Posted: Sun Mar 27, 2016 5:15 am |
|
|
Hi
I tried this but it doesn't work well.
Can you tell what is wrong?
Code: |
if (!strcmp(usb_buffer,sensors)) {
get_sensor_data(AM2320_RH_hb, 0x04, 0x08);
get_RH_and_temperature(*RH, *T);
get_CRC(*CRC_temp);
CRC_data = crc16(data_buffer, 6);
if(CRC_temp == CRC_data)
{
printf(usb_cdc_putc,"\n\rcrc fail\r\n");
}
else
{
printf(usb_cdc_putc,"\n\rcrc ok\r\n");
}
printf(usb_cdc_putc,"\n\rRH: %u T: %d crc: \r\n",*RH,*T);
clear_usb_buffer();
} |
|
|
|
sshahryiar
Joined: 05 May 2010 Posts: 94 Location: Dhaka, Bangladesh
|
|
|
nuclear__
Joined: 24 Jan 2015 Posts: 63
|
|
Posted: Sun Mar 27, 2016 7:28 am |
|
|
sshahryiar wrote: | Did you get any error message? What do you mean by "doesn't work well"? | no error message. I get negative temp and humidity value around 20.which are totally wrong ( they should be +20 and 60%). When i heat them i can see values change however. |
|
|
sshahryiar
Joined: 05 May 2010 Posts: 94 Location: Dhaka, Bangladesh
|
|
Posted: Sun Mar 27, 2016 8:11 am |
|
|
It could be due to wrong variable type.... You need to be careful with pointers too.... _________________ https://www.facebook.com/MicroArena
SShahryiar |
|
|
sshahryiar
Joined: 05 May 2010 Posts: 94 Location: Dhaka, Bangladesh
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Mar 27, 2016 8:22 am |
|
|
Quote: |
I get negative temp and humidity value around 20, which are totally wrong.
|
That's because you didn't follow his example code in this link:
http://libstock.mikroe.com/projects/download/1331/0/1331_am2320_relative_humidity_and_temperature_sensor_demo_v1.0.0.0.zip
You have used incorrect parameters in your code below:
Quote: |
if (!strcmp(usb_buffer,sensors)) {
get_sensor_data(AM2320_RH_hb, 0x04, 0x08);
get_RH_and_temperature(*RH, *T);
get_CRC(*CRC_temp);
|
His code is shown below. Notice how he passes the address of the
variables by putting an '&' in front of the variable name.
Code: | get_RH_and_temperature(&RH, &T);
get_CRC(&CRC_temp); |
Also in his MikroC code, he has these declarations:
Quote: |
signed int T = 0x0000;
unsigned int RH = 0x0000;
unsigned int CRC_data = 0x0000;
unsigned int CRC_temp = 0x0000;
|
Did you correctly translate these to CCS ? The correct translation is:
Code: |
signed int16 T = 0x0000;
unsigned int16 RH = 0x0000;
unsigned int16 CRC_data = 0x0000;
unsigned int16 CRC_temp = 0x0000;
|
That's because in the CCS PCH compiler an 'int' is 8-bits, but in MikroC
an 'int' is 16-bits, so you have to use 'int16' in CCS code.
MikroC data types:
http://www.mikroe.com/download/eng/documents/compilers/mikroc/pro/pic/help/arithmetic_types.htm
CCS data types are listed in the CCS manual.
In the code below, you are printing RH and T as if they are pointers to
8-bit values. That's wrong. They are variables holding the data.
Quote: |
printf(usb_cdc_putc,"\n\rRH: %u T: %d crc: \r\n",*RH,*T);
|
The correct line is shown below. This line uses %lu and %ld to print
16-bit values, and RH and T are given as the parameters:
Code: | printf(usb_cdc_putc,"\n\rRH: %lu T: %ld crc: \r\n", RH, T); |
Last edited by PCM programmer on Sat Apr 16, 2016 6:22 pm; edited 1 time in total |
|
|
nuclear__
Joined: 24 Jan 2015 Posts: 63
|
|
Posted: Sun Mar 27, 2016 9:37 am |
|
|
about & and not *, i had already test that too, but combining & with printf(usb_cdc_putc,"\n\rRH: %lu T: %ld crc: \r\n", RH, T); looks promising.
variables where correctly defined like you say.
however i get T up to 255 while heating which must be 25.5 .
i will try to figure out by myself some things and come back later.
Thanks at the moment |
|
|
sshahryiar
Joined: 05 May 2010 Posts: 94 Location: Dhaka, Bangladesh
|
|
Posted: Sun Mar 27, 2016 9:58 am |
|
|
You are getting 255 instead of 25.5 because in my MikroC example I avoided floating point math and instead used integral math to conserve RAM.... _________________ https://www.facebook.com/MicroArena
SShahryiar |
|
|
nuclear__
Joined: 24 Jan 2015 Posts: 63
|
|
Posted: Mon Mar 28, 2016 8:05 am |
|
|
Something is wrong again
Below are the measurements i get while heating it.
enviromental measurements are around 20C and 50%.
RH: 175 T: 205
RH: 199 T: 236
RH: 111 T: 12
RH: 43 T: 45
RH: 18 T: 68
RH: 230 T: 85
It looks like it lose high byte or if it was byte variable.
however they are not
Code: | unsigned int16 RH = 0x0000;
unsigned int16 CRC_data = 0x0000;
unsigned int16 CRC_temp = 0x0000;
signed int16 T = 0x0000;
...........
if (!strcmp(usb_buffer,sensors)) {
get_sensor_data(AM2320_RH_hb, 0x04, 0x08);
get_RH_and_temperature(&RH,&T);
get_CRC(&CRC_temp);
CRC_data = crc16(data_buffer, 6);
if(CRC_temp == CRC_data)
{
printf(usb_cdc_putc,"\n\rcrc fail\r\n");
}
else
{
printf(usb_cdc_putc,"\n\rcrc ok\r\n");
}
printf(usb_cdc_putc,"\n\rRH: %lu T: %ld crc: \r\n",RH,T);
clear_usb_buffer(); |
I'm not familiar with pointers and indicators and some of the programming technics you use to fully understand drivers. Any tip is appreciated! |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Mar 28, 2016 7:29 pm |
|
|
Quote: | Something is wrong again
Below are the measurements i get while heating it.
It looks like it lose high byte or if it was byte variable |
Sometimes the data can be garbled if the i2c protocol is not followed.
I have seen this happen with eeprom chips, in the following case:
The AM2320 data sheet says you should do a NACK on the last i2c read.
The AM2320_read() function posted in this thread is not doing the NACK.
Look at Figure 17 on page 18 of the AM2320 data sheet. On the right side
of the timing diagram it shows reading the last byte. It shows "0xFE + NACK".
https://akizukidenshi.com/download/ds/aosong/AM2320.pdf
See the modified read routine shown below. It checks inside the for()
loop for the last i2c_read(). On the last one, it does a NACK instead
of an ACK. Try substituting this routine and see if it solves your problem:
Code: |
void AM2320_read(unsigned char *value, unsigned char length)
{
unsigned char s = 0x00;
I2C_Start();
I2C_Write(AM2320_address + I2C_read_cmd);
for(s = 0x00; s < length; s++)
{
if(s == length-1) // Is this the last read ?
value[s] = I2C_read(0); // If so, do a Nack
else
value[s] = I2C_read(1); // If not, do an Ack
}
I2C_Stop();
}
|
For another example of this, look at the sample code here:
https://github.com/nodemcu/nodemcu-firmware/blob/dev/app/modules/am2320.c
Look at this section:
Quote: | // step 3: Read the data |
Part of the code is shown below. It reads bytes in the for() loop, then
it reads the two CRC bytes. It does a NACK on the last crc byte because
it's the last i2c read operation. You can see it's a NACK because the
2nd parameter on that byte is a 0:
Quote: | for(i=0; i<len+2; i++)
b[i] = platform_i2c_recv_byte(id,1);
crc = platform_i2c_recv_byte(id,1);
crc |= platform_i2c_recv_byte(id,0) << 8;
platform_i2c_send_stop(id); |
|
|
|
nuclear__
Joined: 24 Jan 2015 Posts: 63
|
|
Posted: Tue Mar 29, 2016 7:27 am |
|
|
Good catch, however it has exactly the same behaviour.
Readings rise up to 255 and then start from zero. If i still heat it, it goes again up to 255 and back to zero again.
I added 2 more print lines to get buffer[2..5] values, inside get_RH_and_temperature function. Problem is obvious now.
That's what i get while heating:
buf2:1 buf3:46 buf4:1 buf5:35
RH:46 T:35
buf2:0 buf3:151 buf4:1 buf5:147
RH:151 T:147
buf2:0 buf3:79 buf4:2 buf5:11
RH:79 T:11
So the problem is here
Code: |
void get_RH_and_temperature(unsigned long *data1, signed long *data2)
{
*data1 = ((unsigned long)((data_buffer[2] << 8) | data_buffer[3]));
*data2 = ((data_buffer[4] << 8) | data_buffer[5]);
printf(usb_cdc_putc,"\n\rbuf2: %u buf3: %u \r\n",data_buffer[2],data_buffer[3]);
printf(usb_cdc_putc,"\n\rbuf4: %u buf5: %u \r\n",data_buffer[4],data_buffer[5]);
}
|
These 2 lines
*data1 = ((unsigned long)((data_buffer[2] << 8) | data_buffer[3]));
*data2 = ((data_buffer[4] << 8) | data_buffer[5]);
I replaced them with these
Code: |
void get_RH_and_temperature(unsigned int16 *data1, signed int16 *data2)
{
*data1=make16(data_buffer[2], data_buffer[3]);
*data2=make16(data_buffer[4], data_buffer[5]);
}
|
and now works |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|