|
|
View previous topic :: View next topic |
Author |
Message |
Sensor
Joined: 04 Nov 2004 Posts: 33
|
I2C Communication Problem using PIC16F877A |
Posted: Wed May 31, 2006 10:14 am |
|
|
Compiler: PCWH version 3.236
PIC = PIC16F877A
Voltage = 5V
Osc Speed = 4MHz
I am trying to comminicate with a HMC6352 compass by Honeywell. I have my PIC16F877A configured as the master. I'm just trying to read the heading output from the compass. No matter what I try, heading_a and heading_b are always 255. Also, if I don't have the "Force_HW" in the #use i2c(...) in the *.h file, then my i2c clock is only 37khz. If I put the Force_HW in, then my i2c clock is 100khz. Below is a link to the compass datasheet and my code.
http://www.ssec.honeywell.com/magnetic/datasheets/HMC6352.pdf
Code: |
/***** Start of HMC6352.h *****/
#include <16F877A.h>
#device adc=8
#FUSES NOWDT //No Watch Dog Timer
#FUSES XT
#FUSES NOPUT //No Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming
#FUSES NOCPD //No EE protection
#FUSES NOWRT //Program memory not write protected
#use delay(clock=4000000)
#use i2c(Master,sda=PIN_C4,scl=PIN_C3,Force_HW)
/***** End of HMC6352.h *****/
/***** Start of HMC6352.c *****/
#include "HMC6352.h"
#include "lcd_driver.c"
int heading_a;
int heading_b;
void main()
{
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_OFF);
setup_psp(PSP_DISABLED);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
heading_a = 0;
heading_b = 0;
while(TRUE)
{
i2c_start();
i2c_write(0x43); //slave address of HMC6352
i2c_write('A'); //have also tried i2c_write(0x41);
//i've tried with and without the delay
delay_ms(6); //requires 6mS before data is ready to be read
heading_a = i2c_read();
heading_b = i2c_read();
i2c_stop();
lcd_clear();
lcd_putch("Heading:");
lcd_XY(2,0);
printf(lcd_putch, "%u, %u", heading_a, heading_b);
delay_ms(3000);
}
}
/***** End of HMC6352.c *****/
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed May 31, 2006 1:10 pm |
|
|
I read the data sheet and the appnote. They don't really have a very
good explanation of the i2c operations required to talk to the chip.
But as far as I can tell, the test program shown below has a chance to
work. They don't show a NAK on the last i2c read, but I've added it
below, because that's the standard way of doing things.
Code: | #include <16F877A.H>
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
#use i2c(Master, sda=PIN_C4, scl=PIN_C3, FORCE_HW)
#define HMC6352_I2C_WRITE_ADDRESS 0x42
#define HMC6352_I2C_READ_ADDRESS 0x43
#define HMC6352_GET_DATA_COMMAND 0x41
// Read the compass heading. This is in units
// of 1/10's of a degree, from 0 to 3599.
int16 HMC6352_read_heading(void)
{
int8 lsb;
int8 msb;
i2c_start();
i2c_write(HMC6352_I2C_WRITE_ADDRESS);
i2c_write(HMC6352_GET_DATA_COMMAND);
i2c_stop();
delay_ms(7); // Allow 1 ms extra for safety
i2c_start();
i2c_write(HMC6352_I2C_READ_ADDRESS);
msb = i2c_read();
lsb = i2c_read(0);
i2c_stop();
return((int16)msb | ((int16)lsb << 8));
}
//===================================
void main()
{
int16 heading;
while(1)
{
heading = HMC6352_read_heading();
printf("Heading in degrees = %lu\n\r", heading/10);
delay_ms(1000);
}
} |
|
|
|
rnielsen
Joined: 23 Sep 2003 Posts: 852 Location: Utah
|
|
Posted: Wed May 31, 2006 1:37 pm |
|
|
Yes, the spec sheet really is written quite poorly. It's hard to understand just how to talk to the sensor.
One thing that could help is the fact that i2c_write() returns the ACK bit. You could write your routine to let you know if the part is responding properly. If it's not, it could be giving you the result of 255. Try something like this:
i2c_start();
if(i2c_write(address))
{
puts("NO ACK");// sensor did not ack back
}
else
{
// put the rest of the code in here to talk to the sensor
}
i2c_stop();
or something like that. This will let you know if the sensor is acknowledging the Master's signals.
Make sure you have pullups on each line, too. Between 2K - 4.7K.
Ronald |
|
|
Sensor
Joined: 04 Nov 2004 Posts: 33
|
|
Posted: Thu Jun 01, 2006 8:16 am |
|
|
I think something is wrong with my I2C bus. Whether, I have the compass plugged in or not, the bus acts the exact same. I always get a NACK on the 9th clock pulse. I double checked, I have the pin 42 of the PIC going to the SDA line on the compass (pin 7) and I have pin 37 of the PIC going to the CLK line on the compass (pin 10). I've got 3k pullups on each of those lines. Why does the bus act the exact same whether the compass is plugged in or not? |
|
|
rnielsen
Joined: 23 Sep 2003 Posts: 852 Location: Utah
|
|
Posted: Thu Jun 01, 2006 9:21 am |
|
|
That probably means that the sensor is not 'talking' to the Master. If you have a scope hooked up to the SDA and SCL lines you will notice that the 9th clock pulse is the ACK/NACK pulse. If the SDA line is High during the 9th clock pulse then the sensor is not talking. If the sensor is out of the circuit then the SDA line will be held High by the pull up resistor. This will give you a value of 255 ( all bits high) and the ACK pulse will be high also, causing a NOACK. Try puting the sensor in place and then only send an i2c_start(), i2c_write(address) and then an i2c_stop(). Put delays between each command. This will let you know if the sensor can detect it's own address. Monitor the ACK pulse to see if the sensor actually responded to the address call. If it doesn't, then you might not be sending the correct address to the sensor. Make sure the sensor's address is configured correctly and the Master is sending the same address.
It could be possible that the sensor is dead too.
Ronald |
|
|
Sensor
Joined: 04 Nov 2004 Posts: 33
|
|
Posted: Thu Jun 01, 2006 1:24 pm |
|
|
I placed some other device on the bus and was able to talk to them so I know my I2C bus is working. So, I then sent only i2c_start(), i2c_write(address) and i2c_stop() and monitored the lines on the oscilloscope. I wrote the code to try every address between 00 and FF. Each time through the loop I would increment the address and delay so I could see what was happening on the oscilloscope. I left the compass and one more device on the bus. The other device has an address of 40. When I ran the program, I would only see an ACK pulse when the address of 40 was sent on the bus. The compass never responded to any address. I have tried multiple compasses and they all behave the same. I am now waiting on an engineer from Honeywell to get back to me. |
|
|
Sensor
Joined: 04 Nov 2004 Posts: 33
|
|
Posted: Mon Jun 12, 2006 1:11 pm |
|
|
I just received two new compasses from Honeywell and they work fine. The two that I received before were from a "bad batch". Thanks for all the help. |
|
|
charlie Guest
|
|
Posted: Sat Dec 09, 2006 11:07 pm |
|
|
I'm not a cracker-jack programmer, but I got the code PIC Programmer posted to work by changing the return line of the HMC6352_read_heading function to the following:
return((int16)lsb | ((int16)msb << 8));
Just in case other people come on this thread...
The i2c part of the code works great. Thanks for posting it. |
|
|
lost Guest
|
|
Posted: Sat Dec 30, 2006 12:12 pm |
|
|
can anyone help me to convert the program code for hmc6352 to assembly language?? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sat Dec 30, 2006 1:11 pm |
|
|
If you're lost, your first resources should be the forum's search page
or http://www.google.com
To find an assembly language program, go to Google and type in
the name of the chip and a very common ASM instruction.
For example, to find PIC assembly language for the HMC6352,
use these keywords: HMC6352 MOVLW
I did that and got one hit. It's Mr. Wilkinson's page, which has
sample code on it. I don't know if it works, but there's a lot of it.
Then just edit the URL a little, refresh, and you get his homepage,
which is a large project page dedicated to the HMC6352 with
several levels of test code. |
|
|
lost Guest
|
|
Posted: Sat Dec 30, 2006 1:22 pm |
|
|
currently i'm doing the program code for hmc6352 in assembly language. but i cant make it... so i'm lost .... |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
|
lost Guest
|
|
Posted: Sun Dec 31, 2006 4:15 am |
|
|
it really helps me alot... thanks... |
|
|
almost Guest
|
|
Posted: Thu Jan 04, 2007 2:25 pm |
|
|
how can i get a proper hmc6352 code that send out the heading through rs232?? how can i edit from this soucr code??
title "PIC16F877A Honeywell communication"
list p = 16F877A
include "P16F877A.inc" ; This �header file� contains all
; the PIC16F876A special function
; register names and addresses.
;define input/output pins_____________________________________________________________________
#define UNA PORTB,0 ;define unused pin
#define SDA PORTB,1 ;define SDA in pin 1
#define SCL PORTB,2 ;define SCL in pin 2
#define ACK PORTB,3 ;define ACK in pin 3
#define SYN PORTB,4 ;define SYN in pin 4
#define HED PORTB,5 ;define HED in pin 5
#define UNB PORTB,6 ;define unused pin
#define UNC PORTB,7 ;define unused pin
;define variables and locations_______________________________________________________________
HEAD_LO equ 0x20 ;8 LSB's of heading
HEAD_HI equ 0x21 ;8 MSB's of heading
COUNT equ 0x22 ;COUNT 1 used for Byte output
COUNTB equ 0x23
COUNTC equ 0x24
DATA_O equ 0x25 ;Data to output
IN_BIT equ 0x26 ;used to read single bits
DATA_I equ 0x27 ;containsd input byte words
org 0
goto START
;long pause (1s), used to allow for startup time______________________________________________
LONGWAIT movlw 0x49 ;W = 49
movwf COUNT ;COUNT = d'73'
LPB movlw 0xFF ;W = FF
movwf COUNTB ;COUNTB = d'255'
LPA call PAUSE ;54us pause
decfsz COUNTB
goto LPA
decfsz COUNT
goto LPB
retlw 0
PAUSE clrf TMR2 ;reset timer 2
movlw 0x00 ;w = F2
movwf TMR2 ;TMR2 = FA
bcf PIR1, TMR2IF ;clear timeout flag
bsf T2CON, TMR2ON ;timer 2 on
WAIT_LOOPA btfss PIR1, TMR2IF ;test timeout flag
goto WAIT_LOOPA ;loop until timeout flag
bcf T2CON, TMR2ON ;timer 2 off
retlw 0
;5.4us pause, used for clock timing___________________________________________________________
CLKWAIT clrf TMR2 ;reset timer 2
movlw 0xF2 ;w = F2
movwf TMR2 ;TMR2 = FA
bcf PIR1, TMR2IF ;clear timeout flag
bsf T2CON, TMR2ON ;timer 2 on
WAIT_LOOPB btfss PIR1, TMR2IF ;test timeout flag
goto WAIT_LOOPB ;loop until timeout flag
bcf T2CON, TMR2ON ;timer 2 off
retlw 0
;2.6us pause, used for reading bits___________________________________________________________
BITWAIT clrf TMR2 ;reset timer 2
movlw 0xFC ;w = FA
movwf TMR2 ;TMR2 = FA
bcf PIR1, TMR2IF ;clear timeout flag
bsf T2CON, TMR2ON ;timer 2 on
WAIT_LOOPC btfss PIR1, TMR2IF ;test timeout flag
goto WAIT_LOOPC ;loop until timeout flag
bcf T2CON, TMR2ON ;timer 2 off
retlw 0
;PORT B for SDA output________________________________________________________________________
PORTB_OUT bsf STATUS, 5 ;now in bank 1
clrf TRISB ;All port B is now an output
bcf STATUS, 5 ;now in bank 0
retlw 0 ;return
;PORT B for SDA input_________________________________________________________________________
PORTB_IN bsf STATUS, 5 ;now in bank 1
movlw 0x02 ;W=02
movwf TRISB ;SDA input, rest output
bcf STATUS, 5 ;now in bank 0
retlw 0 ;return
;start sequence (SHOULD START SDA = 1, SCL = 1 'free')________________________________________
;_______________(ENDS SDA = 0, SCL = 0)_______________________________________________________
;a start sequence is represented by:
;1) SDA GOES HIGH (if not allready, but should be as released at end of STOP)
;2) HOLD SCL HIGH (if not allready, but shopld be as set at end of STOP)
;3) MAKE PORT OUTPUT
;4) WAIT 5 MICROSECONDS
;5) PULL SDA LOW (this signifies START)
;6) WAIT 5 MICROSECONDS
;7) PULL SCL LOW
START_SEQ bcf SCL
bcf SDA
call PORTB_OUT ;make port 3 output keeping both lines high
bsf SDA ;SDA goes high
bsf SCL ;SCL goes high
call CLKWAIT ;5us wait
bcf SDA ;SDA low while SCL high = start
call CLKWAIT ;5us wait
bcf SCL ;SCL goes low (SDA still low)
retlw 0 ;return
;stop sequence (SHOULD START SDA = BIT, SCL =)________________________________________________
;______________(ENDS SDA = 1, SCL = 1)________________________________________________________
;a stop seqeunce is represented by:
;1) SDA GOES LOW (if not allready)
;2) WAIT 5 MICROSECONDS
;3) SCL HIGH
;4) WAIT 5 MICROSECONDS
;5) SDA HIGH (this signifies STOP)
;6) WAIT 5 MICROSECONDS
;7) MAKE PORT INPUT THEREFORE RELEASING SDA
STOP_SEQ bcf SDA ;SDA goes low
call CLKWAIT ;5us wait
bsf SCL ;SCL goes high
call CLKWAIT ;5us wait
bsf SDA ;SDA high while SCL high = stop
call CLKWAIT ;wait 5us
call PORTB_IN ;make port 3 input therefore release
retlw 0 ;return
;send a byte of data and get ACK (STARTS SDA = X, SCL = 0)____________________________________
;________________________________(ENDS SDA = LSB, SCL = 0)____________________________________
BYTE_OUT movlw 0x08 ;initialise Counter
movwf COUNT ;COUNT = 8
BYTE_OUT_LOOP call BIT_OUT ;output bit
decfsz COUNT, f ;Decrement count + test
goto BYTE_OUT_LOOP ;get next bit
call PORTB_IN ;make port 3 input to get ACK
;get the (N)ACK
call BIT_IN ;get ack bit
bcf ACK ;assume ACK and output on ACK flag pin (note on port 2)
btfsc IN_BIT, 0 ;test successfull ACK (test LSB IN_BIT, ACK = 0)
bsf ACK ;NACK detected and output
call PORTB_OUT ;make port 3 an output again
retlw 0 ;return to main code
;read in a single bit of data for (N)ACK (STARTS SDA = N(ACK), SCL = 0)_______________________
;________________________________________(ENDS SDA = X, SCL = 0)______________________________
BIT_IN movlw 0x01 ;w = 00000001
movwf IN_BIT ;set LSB of IN_BIT = 00000001
call CLKWAIT ;5us wait
bsf SCL ;SCL high
call BITWAIT ;2.5us wait
movlw 0x00 ;w = 00000000
btfss SDA ;test to see if data is 1
clrf IN_BIT ;assumption wrong IN_BIT = 00000000
call BITWAIT ;2.5us wait
bcf SCL ;SCL low
retlw 0 ;return
;send a single bit of data____________________________________________________________________
BIT_OUT btfss DATA_O, 7 ;test MSB of data to output,this is shifted each cycle
goto BIT_OUT_LOW ;bit is 0
bsf SDA ;data bit is 1
call CLKWAIT ;5us wait
bsf SCL ;clock goes high
call CLKWAIT ;5us wait
bcf SCL ;clock goes low
rlf DATA_O, f ;shift data in DATA_O
retlw 0 ;return
BIT_OUT_LOW bcf SDA ;data bit is 0
call CLKWAIT ;5us wait
bsf SCL ;clock goes high
call CLKWAIT ;5us wait
bcf SCL ;clock goes low
rlf DATA_O, f ;shift data in DATA_O
retlw 0 ;return
;read in a byte of data (STARTS SDA = BIT, SCL = 0)___________________________________________
;_______________________(ENDS SDA = 0 (ACK), SCL = 0)_________________________________________
BYTE_IN call PORTB_IN ;make PORT 3 an input
movlw 0x08 ;initialise Counter
movwf COUNT ;COUNT = 8
clrf DATA_I ;clear data input register
BYTE_IN_LOOP call BIT_IN_B ;read in single bit
decfsz COUNT, f ;decrement and test COUNT
goto BYTE_IN_LOOP ;process next bit
;send ACK
call PORTB_OUT ;make PORT 3 an output
call CLKWAIT ;5us wait
bcf SDA ;pull SDA low for ACK
call CLKWAIT ;5us wait
bsf SCL ;SCL goes high
call CLKWAIT ;5us wait
bcf SCL ;SCL goes low
retlw 0 ;return
;read in a single bit and amend to byte_______________________________________________________
BIT_IN_B movlw 0x01 ;w = 00000001
movwf IN_BIT ;IN_BIT = 00000001, i.e. assume bit is 1
call CLKWAIT ;5us wait
rlf DATA_I, f ;shift data in DATA_I left (more signinifcant)
bsf SCL ;SCL high
call BITWAIT ;2.5us wait
btfss SDA ;test to see if data is 1
clrf IN_BIT ;assumption wrong, IN_BIT = 00000000 as data is 0
call BITWAIT ;2.5us wait
bcf SCL ;SCL low
movlw IN_BIT ;W:LSB = data: 00000000 or 00000001
iorwf DATA_I, 1 ;inclusive or IN_BIT and DATA_I to amend data word
retlw 0 ;return
;start of program_____________________________________________________________________________
;_____________________________________________________________________________________________
START bcf STATUS,6 ;move to bank 0
bcf STATUS,5
bcf INTCON, GIE ;disable interupts at this time
;initialise Values
clrf HEAD_LO ;clear register
clrf HEAD_HI ;clear register
bcf UNA ;clear all output pins
bcf SDA
bcf SCL
bcf ACK
bcf SYN
bcf HED
bcf UNB
bcf UNC
;Setup Honeywell HMC6352 to be in continuous Mode 5Hz at startup
;with periodic reset pulses. In comments Chip will be refered to
;as HMC.
call BITWAIT
call CLKWAIT
call LONGWAIT ;long wait for startup
movlw 0x42 ;Put write address into W
movwf DATA_O ;Set as data out
call START_SEQ ;Initiate start sequnce
call BYTE_OUT ;send address + get ACK
movlw 0x47 ;write to RAM command
movwf DATA_O ;set as data out
call BYTE_OUT ;send data + get ACK
movlw 0x74 ;RAM memory location
movwf DATA_O ;set as data out
call BYTE_OUT ;send data + get ACK
movlw 0x32 ;Operational mode setup
movwf DATA_O ;set as data out
call BYTE_OUT ;send data + get ACK
call STOP_SEQ ;initialise stop sequence
call LONGWAIT ;give HMC time to perform command
;Copy operational mode into EEPROM so it becomes the default
;mode of operation
movlw 0x42 ;Put write address into W
movwf DATA_O ;set as data out
call START_SEQ ;initialise start sequence
call BYTE_OUT ;send address + get ACK
movlw 0x4C ;command to write OP mode to EEPROM
movwf DATA_O ;set as data out
call BYTE_OUT ;send command + get ACK
call STOP_SEQ ;initialise stop sequence
call LONGWAIT ;give HMC time to perform command
;Section of code that reads data from HMC
HEADING_IN movlw 0x43 ;put read address into W
movwf DATA_O ;set as data out
call START_SEQ ;initilise start segeunce
call BYTE_OUT ;send address + get ACK
call BYTE_IN ;read first heading byte
clrw ;clear W
addwf DATA_I, w ;W = DATA_I
movlw HEAD_HI ;HEAD_HI is MSByte
call BYTE_IN ;read second heading byte
clrw ;clear W
addwf DATA_I, w ;W = DATA_I
movlw HEAD_LO ;HEAD_LO is LSByte
call STOP_SEQ ;initialise stop sequence
;Section of code that outputs the two heading bytes on PORTB
movlw 0x07 ;W = 7
movwf COUNT ;COUNT = 7
HEAD_HI_BITS bsf SYN ;sync pulse
bcf SYN ;sync pulse
btfss HEAD_HI, 7 ;test bit of output
bcf HED ;clear output bit
bsf HED ;set output bit
call CLKWAIT ;5 microsecond wait
rlf HEAD_HI, f ;shift data in HEAD_HI
decfsz COUNT ,f ;decrement and test COUNT
goto HEAD_HI_BITS ;output mext bit
movlw 0x07 ;W = 7
movwf COUNT ;COUNT = 7
HEAD_LO_BITS bsf SYN ;sync pulse
bcf SYN ;sync pulse
btfss HEAD_LO, 7 ;test bit of output
bcf HED ;clear output bit
bsf HED ;set output bit
call CLKWAIT ;5 microsecond wait
rlf HEAD_LO, f ;shift data in HEAD_HI
decfsz COUNT, f ;decrement and test COUNT
goto HEAD_LO_BITS ;output mext bit
call CLKWAIT ;5 microsecond wait
bsf HED ;toggle output a few times
bcf HED ;so that you know all bits
bsf HED ;have been output
bcf HED ;successfully
bsf HED
bcf HED
bsf HED
bcf HED
;loop infinately around the get heading and output heading code (300us pause)
movlw 0x32 ;d'50'
movwf COUNTC
ABOVE call CLKWAIT ;5us wait
decfsz COUNTC
goto ABOVE
goto HEADING_IN ;get new heading data
end |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Jan 04, 2007 2:44 pm |
|
|
This forum is not about Microchip Assembly language. That's what
your program is written in. This forum is about the C language.
We don't usually like to work on ASM programs.
Here is a tutorial web page on the using PIC assembly language.
Read "Tutorial 7" which is about using the serial port:
http://www.winpicprog.co.uk/pic_tutorial.htm
If you have further questions about PIC assembly language
programming, try to ask them on the Microchip forum.
Here's the main forum page:
http://forum.microchip.com/
Try this forum for information on the serial port:
SCI/USART/EUSART |
|
|
|
|
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
|