Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

PIC 16F877A I2C Compass Module

Status
Not open for further replies.

TSmooth

Newbie level 4
Newbie level 4
Joined
Nov 24, 2014
Messages
5
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Visit site
Activity points
63
Hello I have been trying to get a I2C compass module to talk to a PIC16F877A. I can repeatedly send the slave address with the R/W bit low and the address I want to read from and receive acknowledgement every time (I have a bus pirate logic analyser which I am able to view the signal on). However, as soon as I ask the slave to transmit (R/W bit high) I see nothing on the logic analyser. Was wondering if anyone can see anything obvious I have missed? Thanks


Code dot - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
configp
        clrf PORTB
        clrf PORTA              ;reset input and outputs
    
        bank1                   ;select bank1
 
        movlw b'00000110'       ;configure all pins 
        movwf ADCON1            ; as DIGITAL 
        movlw b'000000'         ;porta is 6 bits wide
        movwf TRISA             ;porta RA5 to RA0 input
        movlw b'00000000'       ;a 0 in ctrl register make the I/O pin an output 
        movwf TRISB 
        movlw b'00000000'
        movwf TRISD         ;portb  is all output 
        movlw b'11111111'
        movwf TRISC
        movlw b'00000111'       ;divide clock frquency by 256
        movwf OPTION_REG        ;timer PRESCALAR
    
        bank0                   ;select bank0
    
        movlw b'00000000'       ;w = 00h
        movwf INTCON            ;clear interrupts
        retlw 0                 ;return from subroutine
;;------------Delay
delayp
        movlw time
        movwf count1            ; save time factor in gpr
loop3
        clrf rtcc
loop1
        btfsc rtcc,0            ; use bit 1 under simulation
        goto loop2              ; if rtcc timer delay done goto loop2
        clrwdt
        goto loop1              ; keep checking rtcc bit for a 1
loop2
        decfsz count1,1         ; has time factor reached 0? 
        goto loop3              ; repeat rtcc timer  delay
        retlw 0                 ; delay done
;---------i2c subroutines----------------------------------
i2c_config
        banksel SSPADD          ; select SFR bank
        movlw   b'00110001'     ; 20mhz clock, 100khz = 49
        movwf   SSPADD          ; initialize I2C baud rate
        
        bcf     SSPSTAT,6       ; select I2C input levels
        bsf     SSPSTAT,7       ; disable slew rate
        bcf     STATUS,RP0      ; select SFR bank
        movlw   b'00101000'     ; 
        movwf   SSPCON          ; Master mode, SSP enable
        return 
 
i2c_send  
        banksel SSPBUF          ;select SSPBUF bank   
        movwf   SSPBUF          ;copy the content from W register to SSPBUF    
        banksel PIR1            ;select PIR1 bank  
sndwait   
        btfss   PIR1,SSPIF      ;check whether transmission is complete?   
        goto    sndwait         ;wait until it completes.   
        bcf     PIR1,SSPIF      ;clear SSPIF bit   
        banksel SSPCON2         ;select SSPCON2 bank for checking acknowledgement from slave.   
        nop
        btfsc   SSPCON2,ACKSTAT;check for acknldge, If ACKSTAT=0,acknowledged. 
        goto not_ack        ;if not acknowledge send error signal 
        banksel PIR1            
        bcf     PIR1,SSPIF      ;Clear SSPIF bit
        bank0
        return  
 
i2c_receive   
        banksel SSPCON2
        bsf SSPCON2,RCEN ; Enable Receive Mode (I2C)
        bsf SSPCON2,ACKDT ; ACK DATA to send is 1, which is NACK.
        call i2c_wait
        banksel SSPBUF
        movf SSPBUF, w
        banksel SSPCON2
        bsf SSPCON2,ACKEN ; Send ACK DATA now.
ait   
        btfss   PIR1,SSPIF      ;check whether transmission is complete?   
        goto    ait         ;wait until it completes.   
        return
startb 
 
        banksel SSPCON2         ;Select SSPCON2 Bank 
        bsf     SSPCON2,SEN     ;Initiaite Start Sequence  
        call    i2c_wait        ;Call i2c_wait 
        return 
        
    
rstartb
        banksel SSPCON2         ;Select SSPCON2 Bank 
        bsf     SSPCON2,RSEN    ;Initiaite Restart Sequence
        call    i2c_wait        ;Call i2c_wait 
        return  
        
stopb 
        banksel SSPCON2         ;Select SSPCON2 Bank       
        bsf     SSPCON2,PEN     ;Initiaite Stop Sequence
        call    i2c_wait        ;Call i2c_wait 
        return 
i2c_wait 
        banksel PIR1 
iiwait
        btfss   PIR1,SSPIF      ;Operation Completed succesfully?     
        goto    iiwait          ;No, Keep Checking     
        bcf     PIR1,SSPIF      ;Clear SSPIF bit  
        return
        
not_ack 
        banksel PORTB
        addlw d'1'
        addwf PORTB, 1
        banksel PIR1            ;select PIR1 bank  
        bcf PIR1,SSPIF
        bcf PIR1, BCLIF
        return
 
    ;;; -----main program------------------------------------------
start
        call configp
        call delayp
        call i2c_config
        
        call delayp
        call delayp
        
 
repeat      
        call startb
        movlw b'11000000'
        call i2c_send
        movlw b'00000001'
        call i2c_send
        call stopb
        call delayp     
        call startb 
        movlw b'11000001'
        call i2c_send
        call i2c_receive
        call stopb
        goto repeat
 
 
end

 
Last edited by a moderator:

It is difficult to say what's wrong from your assembly code, but have you somewhere in your code after asking for data from slave, you provide clock so that slave can transmit data back? Master in I2C always provides clock. I also suggest have a look at: https://microchip.wikidot.com/i2c:chip-select and following pages that explains how to use I2C using free Microchip C.
 

Yeah I know that but I assumed that is what is achieved when enabling receive mode by setting RCEN in SSPCON2. I have read a lot on I2C and have been through many examples but I can't see where I am going wrong. The only thing I am unsure of is that when the device is asked to send data (the read address is sent), with it being a compass it needs to fetch the data. Whilst it is doing this it holds the clock line low telling the master it is not ready to transmit and when ready it releases it. I am not sure on how to implement this feature or whether the MSSP module handles that. Thanks for your reply.
 

Just out of curiosity, why are you using ASM code? There's is no real space advantage over C-code, but it makes the code hard to read for most people that might help you. ASM also considerably increases the risk of careless mistakes being unnoticed.

Good ASM reading capability is still required to debug code or optimize time critical applications, but I won't waste time by coding trivial problems in assembly.
 

What Compass are you using ? What clock frequency are you using for PIC ?


Implement this code in ASM.


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/ Function Purpose: Configure I2C module
void InitI2C(void)
{   
    SDA_DIR = 1;        // Make SDA and 
    SCK_DIR = 1;        // SCK pins input
 
    SSPADD  = ((_XTAL_FREQ/4000)/I2C_SPEED) - 1;    
    SSPSTAT = 0x80;     // Slew Rate control is disabled
    SSPCON  = 0x28;     // Select and enable I2C in master mode
}
 
 
// Function Purpose: I2C_Start sends start bit sequence
void I2C_Start(void)
{
    SEN = 1;            // Send start bit
    while(!SSPIF);      // Wait for it to complete
    SSPIF = 0;          // Clear the flag bit
}
 
 
// Function Purpose: I2C_ReStart sends start bit sequence
void I2C_ReStart(void)
{
    RSEN = 1;           // Send Restart bit
    while(!SSPIF);      // Wait for it to complete
    SSPIF = 0;          // Clear the flag bit
}
 
 
//Function : I2C_Stop sends stop bit sequence
void I2C_Stop(void)
{
    PEN = 1;            // Send stop bit
    while(!SSPIF);      // Wait for it to complete
    SSPIF = 0;          // Clear the flag bit
}
 
 
 
//Function : I2C_Send_ACK sends ACK bit sequence
void I2C_Send_ACK(void)
{
    ACKDT = 0;          // 0 means ACK
    ACKEN = 1;          // Send ACKDT value
    while(!SSPIF);      // Wait for it to complete
    SSPIF = 0;          // Clear the flag bit
}
 
 
//Function : I2C_Send_NACK sends NACK bit sequence
void I2C_Send_NACK(void)
{
    ACKDT = 1;          // 1 means NACK
    ACKEN = 1;          // Send ACKDT value
    while(!SSPIF);      // Wait for it to complete
    SSPIF = 0;          // Clear the flag bit
}
 
 
// Function Purpose: I2C_Write_Byte transfers one byte
bit I2C_Write_Byte(unsigned char Byte)
{
    SSPBUF = Byte;      // Send Byte value
    while(!SSPIF);      // Wait for it to complete
    SSPIF = 0;          // Clear the flag bit
 
    return ACKSTAT;     // Return ACK/NACK from slave
}
 
 
// Function Purpose: I2C_Read_Byte reads one byte
unsigned char I2C_Read_Byte(void)
{
    RCEN = 1;           // Enable reception of 8 bits
    while(!SSPIF);      // Wait for it to complete
    SSPIF = 0;          // Clear the flag bit
 
    return SSPBUF;      // Return received byte
}

 
Last edited:

It's a CMPS10. I am using assembly because that is what I have been taught. I have been slowly learning C but I am pretty fast with assembly anyway. If I compare my code with that it is pretty much the exact same.
 

although i have not worked on I2C mode yet, but i ll like to take one small point in note, in your code you are using
movlw time
movwf count1 ; save time factor in gpr
loop3
clrf rtcc
loop1
btfsc rtcc,0 ; use bit 1 under simulation
goto loop2 ; if rtcc timer delay done goto loop2
clrwdt
goto loop1 ; keep checking rtcc bit for a 1
loop2
decfsz count1,1 ; has time factor reached 0?
goto loop3 ; repeat rtcc timer delay
retlw 0
for delay.. if u hav successfully done that technique before then it seems ok, but in my coding, to move a file in accumulator (w) we need to use movf command, so i think you can write the commands as movf time,w and then further where ever u wanna use it... there are 2 or 3 places where you used this method, i hpe it hlps a bit..

in your whole code there is nothing tthat is fillinf the variable time.. so what is the time anyway???? if that is the mistake then it cud be in your timing routine.. plz chk it...
 

Yeah sorry, the number is defined previously in the program. time equ .100. I have used this delay in a lot of my programs. Thanks
 

I have solved it now. Clearing the BF bit in SSPSTAT sorted everything. Thanks for your help.
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top