View previous topic :: View next topic |
Author |
Message |
PrinceNai
Joined: 31 Oct 2016 Posts: 478 Location: Montenegro
|
INT_EXT |
Posted: Sat Mar 09, 2019 7:57 am |
|
|
Hello,
I have a problem with external interrupt on 18f4520. I'm listening to I2C communication. The idea is to catch START and STOP conditions. My problem is that the interrupt fires regardless of the edge I set (or try to set), meaning that I should see a 0 on PIN_B0 when I come to START and SCK is high. Instead, it's fully random. Sometimes 0, sometimes 1. Probably my reasoning is wrong.
Code: |
#INT_EXT
void EXT_isr(void) { // we come here with interrupt edge set H_TO_L
clear_interrupt(INT_EXT);
delay_cycles(1);
switch(START_OR_STOP){
// wait I2C start condition (SDA HIGH TO LOW transition, SCK HIGH at that time)
case START:{
if(input_state(SCK) == 1){ // allowed only when START condition
Got_Start = 1;
Bit_Position = 8; // data is transfered MSB first, so we'll write 8th bit first
START_OR_STOP = STOP;
ext_int_edge(0,L_TO_H); // change the interrupt edge
ext_int_edge(0,L_TO_H); // change the interrupt edge
READ_DATA = IDLE;
delay_cycles(1);
}
break;
}
// wait I2C stop condition (SDA LOW TO HIGH transition, SCK HIGH at that time)
case STOP:{
if(input_state(SCK) == 1){ // allowed only when STOP condition
Got_Start = 0;
START_OR_STOP = START;
ext_int_edge(0,H_TO_L); // change the interrupt edge
ext_int_edge(0,H_TO_L); // change the interrupt edge
}
break;
}
}
}
|
|
|
|
PrinceNai
Joined: 31 Oct 2016 Posts: 478 Location: Montenegro
|
|
Posted: Sat Mar 09, 2019 7:59 am |
|
|
BTW, levels are correct. 5V all around. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9217 Location: Greensville,Ontario
|
|
Posted: Sat Mar 09, 2019 9:50 am |
|
|
comments.
1) do you preset the 'START_OR_STOP' variable before the interrupt occours?
2) how is 'SCK' defined ?
Either of these not being correct could cause random problems, especially if SCK is NOT correct !. |
|
|
PrinceNai
Joined: 31 Oct 2016 Posts: 478 Location: Montenegro
|
|
Posted: Sat Mar 09, 2019 10:11 am |
|
|
Code: |
#define SDA PIN_B0
#define SCK PIN_B4
#define START 0
#define STOP 1
int1 Got_Start = 0;
int8 START_OR_STOP = START;
#define CLK_LOW 0
#define SAMPLE_DATA 1
#define IDLE 2
int8 READ_DATA = CLK_LOW;
#define BUFFER_SIZE 64 //create 255 byte large buffer
char buffer[BUFFER_SIZE];
int8 next_in = 0;
int8 Bit_Position = 8;
int16 TMP = 0; // storage for sampled data
int16 counter = 0;
|
and interrupts:
Code: |
enable_interrupts(INT_TIMER0);
enable_interrupts(INT_TIMER1);
// enable_interrupts(INT_RDA);
enable_interrupts(GLOBAL);
clear_interrupt(INT_EXT); // prevent INT_EXT to fire on reset
ext_int_edge(0,H_TO_L); // init interrupt triggering for
ext_int_edge(0,H_TO_L);
enable_interrupts(INT_EXT);
|
|
|
|
PrinceNai
Joined: 31 Oct 2016 Posts: 478 Location: Montenegro
|
|
Posted: Sat Mar 09, 2019 10:24 am |
|
|
Right now, Bit_Position never comes to 6. I do have a logic analyzer, so I do see how SCL and SDA are moving. As I say, there must be some basic error with the way I designed this. And me being me, it'll take time to spot where I made a mistake.
Code: |
void main() {
enable_interrupts(INT_TIMER0);
enable_interrupts(INT_TIMER1);
// enable_interrupts(INT_RDA);
enable_interrupts(GLOBAL);
clear_interrupt(INT_EXT); // prevent INT_EXT1 to fire on reset
ext_int_edge(0,H_TO_L); // init interrupt triggering for button press
ext_int_edge(0,H_TO_L);
enable_interrupts(INT_EXT);
while(TRUE){
while(Got_Start){
switch(READ_DATA){
// ............................................................................
case CLK_LOW:{
delay_cycles(1);
while(input_state(SCK) == 0); // WAIT SCK high, then read data
READ_DATA = SAMPLE_DATA;
break;
}
// ............................................................................
case SAMPLE_DATA:{
delay_cycles(1);
if(input_state(SDA)){
bit_set(TMP, Bit_Position);
}
else{
bit_clear(TMP, Bit_Position);
}
Bit_Position--; // do it for 8 bits, ninth bit is ACK
if(Bit_Position == 6){
delay_cycles(1);
}
READ_DATA = IDLE; // go wait for SCK to be low
break;
}
// ............................................................................
case IDLE:{
delay_cycles(1);
while(input_state(SCK) == 1);
READ_DATA = CLK_LOW;
break;
}
// ............................................................................
} // switch
} // while(Got_Start)
} // while(TRUE)
} // MAIN
|
|
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9217 Location: Greensville,Ontario
|
|
Posted: Sat Mar 09, 2019 10:32 am |
|
|
OK if you're trying to code your own software I2C, then why not just let the compiler make one for you ? You can see how they do it by dumping the listing. |
|
|
PrinceNai
Joined: 31 Oct 2016 Posts: 478 Location: Montenegro
|
|
Posted: Sat Mar 09, 2019 10:33 am |
|
|
First mistake, I should start with IDLE state in main(), because I come there with SCK high and I need to see low before reading data. But it doesn't change anything regarding my interrupt problem. |
|
|
PrinceNai
Joined: 31 Oct 2016 Posts: 478 Location: Montenegro
|
|
Posted: Sat Mar 09, 2019 10:42 am |
|
|
I don't want to create I2C communication. The goal is to listen to an existing and working communication, and when I get the sequence I need, do something. The setup is like this: the main unit is sending data over I2C to the slave unit, that in turn writes stuff to an LCD display. It is a custom made thing, 18f67J10 is sending data to 18f2550 which in turn controls the LCD. My "sniffer" is just listening. Data protocol is simple, I can see the sequence that is written on LCD as ASCII bytes sent from master to slave. All I want to do is to record all bytes between START and STOP into the buffer and act if the right message appears on the line. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9217 Location: Greensville,Ontario
|
|
Posted: Sat Mar 09, 2019 10:55 am |
|
|
It'd be a lot simpler and more reliable if you'd use a PIC with I2C and use it as your 'sniffer'.From both HW and SW I don't see why this won't work. All data that the sniffer detects can go into a buffer, where you can 'process'( parse' to look for the 'sequence' you need. |
|
|
PrinceNai
Joined: 31 Oct 2016 Posts: 478 Location: Montenegro
|
|
Posted: Sat Mar 09, 2019 11:06 am |
|
|
How can I set my "sniffer" PIC to listen to everything? I have 5 units here and addresses that master sends are not the same. I don't know how they did it, but the slave is responding to 0x28 and 0x54 |
|
|
PrinceNai
Joined: 31 Oct 2016 Posts: 478 Location: Montenegro
|
|
Posted: Sat Mar 09, 2019 11:14 am |
|
|
That is the reason I went for software decoding. The communication is slow, 50kHz. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9217 Location: Greensville,Ontario
|
|
Posted: Sat Mar 09, 2019 1:08 pm |
|
|
You should be able to set the 'sniffer' (a slave..) to address '0x00' and it'll receive ALL transmissions. That's from my foggy, old memory. Pretty sure 00 was one of the special reserved addresses that Philips set aside. Other's will know for sure or just check an I2C handbook online. Heck I can't member who bought Philips....
edit Update: OK, NXP bought out Philips...so I downloaded the I2C pdf... address 0x00 is the 'broadcast' address. Master sends out TO every I2C device, you want to 'listen' to all I2C , so a 'receive all'. That leads me back to 'cut code for a CCS SW I2C' and dump the listing. ALL of the CCS code will appear....You just want the 'listen and decode' chunk BEFORE the PIC compares the incoming address and it's own. If you buffer that data, then YOU can parse/decode/act upon the data.
Jay
Last edited by temtronic on Sat Mar 09, 2019 3:43 pm; edited 1 time in total |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19481
|
|
Posted: Sat Mar 09, 2019 2:25 pm |
|
|
Your approach is basically not going to work.
First, the 'state' machine, would need to be able to handle 'restart'
conditions as well as just start and stop.
Then while 'start' is signalled by a drop on SDA, while SCL is high, 'stop'
involves SDA going high while SCL is high.
To decode in software, you need to use the INT_RB abaility to find
every edge on both lines, and then build a state machine based upon
these specific conditions to trigger the state changes, and then use the
rising edge of SCL to sample the data and build the byte, based upon
the machine state.
Currently your approach will be seeing every data bit as a stop or
start... |
|
|
PrinceNai
Joined: 31 Oct 2016 Posts: 478 Location: Montenegro
|
|
Posted: Sat Mar 09, 2019 10:07 pm |
|
|
This is the data I need to extract. Only ASCII bits (Check card). No restart condition, ever. I sampled 100s of data and it never happens.
https://imgur.com/a/P9WddMe |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19481
|
|
Posted: Sun Mar 10, 2019 3:30 am |
|
|
You still need to remember that the interrupt is going to be called on
every data edge, so will see the data bits as well as the start and stop.
The key thing with I2C, is you need to be always looking/responding
to _both_ lines.
Your current test will be seeing every SDA _data_ transition, as well as
the one that represents start and stop. Hence it's 'randomness'. |
|
|
|