|
|
View previous topic :: View next topic |
Author |
Message |
tanyamahen
Joined: 01 Jul 2004 Posts: 14
|
I2C Slave will not work on PIC16F690 |
Posted: Sun Nov 04, 2007 10:23 pm |
|
|
Hi
In my project I need to communicate with other hardware via I2C.
Currently I can't get I2C working. Interrupt naver happen.
Here is my code
Code: |
#include <16F690.h>
#device *=16 ICD=TRUE
#fuses INTRC, NOWDT, NOPROTECT
#use delay(clock=8000000)
#use rs232(baud=9600, xmit=PIN_B7, rcv=PIN_B5, ERRORS)
#LIST
#use i2c(SLAVE, SDA=PIN_B4, SCL=PIN_B6, address= 0x60, FORCE_HW)
#include <input.c>
#include <stdlib.h>
#include <string.h>
#byte SSPSTAT = 0x014
#byte SSPADD = 0x013
#byte SSPCON = 0x014
#byte STATUS = 0x003
#byte SSPBUF = 0x013
#byte PIE1 = 0x8c
#bit SSPOV1 = 0x014.6
#bit SSPSTART = 0x014.3
#define MY_I2C_ADDR 0x60
void I2C_Reset_Hardware();
//------------------- Synchronous Serial Port I2C ---------------------------
#define I2C_MASTER_MASK 0x0B // SSPCON.SSPM3-0 = 1011 firmware master mode, slave idle
#define I2C_SLAVE_MASK 0x0E // SSPCON.SSPM3-0 = 1110 - slave mode, 7-bit addr, S & P
// interrupts enabled
#define I2C_CLK_ENABLE 0x10
#define I2C_MODE_SETUP 0x20
// this is our current i2c address
static int8 I2C_Address = MY_I2C_ADDR;
//I2C Interrupt Service Routine
#INT_SSP
void ssp_interupt ()
{
BYTE incoming, state ;
// change the bank to 1
bit_set(STATUS, 6);
bit_clear(STATUS, 5);
output_low(PIN_A5);
output_high(PIN_A5);
output_low(PIN_A5);
state = i2c_isr_state();
if(state == 0x80) //address Master is sending data
{
// output_low(PIN_A5);
// output_high(PIN_A5);
// output_low(PIN_A5);
incoming = i2c_read();
// printf("Address %x\n\r", incoming );
}
else if(state < 0x80)
{
// output_low(PIN_A5);
// output_high(PIN_A5);
// output_low(PIN_A5);
// incoming = i2c_read();
// printf("i2c read %x\n\r", incoming );
}
// change the bank back to 0
bit_clear(STATUS, 6);
bit_clear(STATUS, 5);
}
// ***************************
// Main star here
// ***************************
void main ()
{
int addr1;
disable_interrupts(GLOBAL);
// ************** Intialise All Veriables ********************
setup_adc_ports(NO_ANALOGS);
setup_comparator(NC_NC_NC_NC);
// 1 = Input,0 = Output
set_tris_A ( 0x1B ); // Port A 00011011
set_tris_B ( 0x30 ); // Port B 00110000 ( Bit5/7 - RS232- Rx/tx)
set_tris_C ( 0x87 ); // Port C 00011111
printf( "\n\r\n\rInitialising \n\r");
setup_wdt(WDT_OFF);
output_float(PIN_B6);
output_float(PIN_B4);
I2C_Reset_Hardware();
// Change the Bank to set the address
bit_set(STATUS, 6);
bit_clear(STATUS, 5);
SSPADD = 0x60;
addr1 = SSPADD;
bit_set(PIE1,3);
bit_clear(STATUS, 6);
bit_clear(STATUS, 5);
printf(" get Address %X \n\r",addr1);
enable_interrupts(GLOBAL);
enable_interrupts(INT_SSP);
disable_interrupts(INT_RB);
bit_set(STATUS, 6);
bit_clear(STATUS, 5);
SSPADD = 0x60;
addr1 = SSPADD;
bit_set(PIE1,3);
bit_clear(STATUS, 6);
bit_clear(STATUS, 5);
while (TRUE)
{
;
}
}
void I2C_Reset_Hardware(void)
{
int tmp;
// Reset the I2C if we timeout, this should prevent us from ever
// locking up the I2C
SSPADD = I2C_Address;
SSPCON = (I2C_SLAVE_MASK);
SSPCON = (I2C_SLAVE_MASK | I2C_CLK_ENABLE | I2C_MODE_SETUP);
// Clear the buffer
tmp =SSPBUF;
SSPOV1 = 0;
clear_interrupt(INT_SSP);
enable_interrupts(INT_SSP);
return;
}
|
Did anyone had any issue with I2c using PIC16F690.
Can someone help me out here
Compiler Ver PCM 4.058
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Nov 04, 2007 10:59 pm |
|
|
Quote: | #byte SSPSTAT = 0x014
#byte SSPADD = 0x013
#byte SSPCON = 0x014
#byte STATUS = 0x003
#byte SSPBUF = 0x013
#byte PIE1 = 0x8c
#bit SSPOV1 = 0x014.6
#bit SSPSTART = 0x014.3
// Change the Bank to set the address
bit_set(STATUS, 6);
bit_clear(STATUS, 5);
SSPADD = 0x60;
addr1 = SSPADD;
bit_set(PIE1,3);
bit_clear(STATUS, 6);
bit_clear(STATUS, 5);
|
CCS automatically selects the correct bank if you specify the correct
address of the SSP registers. You don't need to manually switch banks
with CCS. |
|
|
tanyamahen
Joined: 01 Jul 2004 Posts: 14
|
|
Posted: Mon Nov 05, 2007 2:21 am |
|
|
I have chaged as you said but still problem |
|
|
rnielsen
Joined: 23 Sep 2003 Posts: 852 Location: Utah
|
|
Posted: Mon Nov 05, 2007 10:03 am |
|
|
I haven't compiled this code so I'm not 100% that it works but, try something like this:
Code: | //I2C Interrupt Service Routine
#INT_SSP
void ssp_interupt ()
{
unsigned int8 state;
state = i2c_isr_state();
output_toggle(PIN_A0);// indicate that the ISR has at least been entered
if(!state)
{
output_toggle(PIN_A1)// toggle output, assign whatever pin you want here
}
else if(state < 0x80)
{
output_toggle(PIN_A2)
}
else if(state == 0x80)
{
output_toggle(PIN_A3)
}
else if(state > 0x80)
{
output_toggle(PIN_A4)
}
}// end of ISR |
If you get my drift you'll see that we're simply trying to see if the PIC is responding to I2C commands. Assign what ever pins you want to toggle. I would suggest using LEDs for visual indication on what the slave is seeing. If none of the outputs toggle then there might be a hardware issue between the master and slave.
Make your master code very simple. Maybe something like:
Code: | i2c_start();
i2c_write(0x60);
i2c_stop(); |
This should cause the slave to ACK and toggle one of the outputs. Use this to start with and then expand the master code to write and read data. I've found it's best to start from the most simplest code, doing only one thing, and then adding to it.
Best of luck.
Ronald |
|
|
tanyamahen
Joined: 01 Jul 2004 Posts: 14
|
|
Posted: Mon Nov 05, 2007 11:59 pm |
|
|
Thanks Ronald
Today holiday in melbourne.... I will go to work tomorrow to check this out... I have CRO to check the toggled PIN (In my code PIN_A5).
Thanks for your help
Tanya |
|
|
Guest
|
|
Posted: Wed Nov 14, 2007 6:52 pm |
|
|
Thank you all
I have fix the problem. (Two Problem)
1) In PIC16f690 when you set "enable_interrupts(INT_SSP) " This interrupt setting goes and set the "RB interrupt" I have now set the interrupt in ASM and I get I2C interrupt.
2) Master send wrong address (0x10) My address is 0x60.
Once I finish coding I will post my code |
|
|
ymoona
Joined: 20 Mar 2005 Posts: 32 Location: Heiloo, the Netherlands
|
|
Posted: Sun Dec 30, 2007 12:26 pm |
|
|
Anonymous wrote: | Thank you all
I have fix the problem. (Two Problem)
1) In PIC16f690 when you set "enable_interrupts(INT_SSP) " This interrupt setting goes and set the "RB interrupt" I have now set the interrupt in ASM and I get I2C interrupt.
2) Master send wrong address (0x10) My address is 0x60.
Once I finish coding I will post my code |
currently I face the same problem. I've connected to 16F690 via I2C. Both line have 10k pullups to the +5V. The master source is:
Code: |
#include <16F690.h>
#device adc=8
#FUSES NOWDT //No Watch Dog Timer
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
#FUSES NOPROTECT //Code not protected from reading
#FUSES BROWNOUT //Reset when brownout detected
#FUSES MCLR //Master Clear pin enabled
#FUSES NOCPD //No EE protection
#FUSES NOPUT //No Power Up Timer
#FUSES IESO //Internal External Switch Over mode enabled
#FUSES FCMEN //Fail-safe clock monitor enabled
#use delay(clock=8000000)
#use rs232(baud=19200, xmit=PIN_B7, rcv=PIN_A5)
#use i2c(Master, Slow, sda=PIN_B4, scl=PIN_B6)
/*
Pin defines
*/
#define led PIN_A5
#int_SSP // i2c activty
SSP_isr()
{
return;
}
void main(){
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_DISABLED);
//setup_timer_2(T2_DIV_BY_1,255,1);
setup_timer_2(T2_DIV_BY_16, 124, 1); // 1000 Hz, the pwm frequention
setup_comparator(NC_NC_NC_NC);
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
setup_oscillator(OSC_8MHZ);
printf("I2C engine driver\n\r");
while(1){
output_high(led);
// Write the letter 'B' to the slave board.
i2c_start();
i2c_write(0xA0);
i2c_write(0x00);
i2c_write(0b01001111);
i2c_stop();
delay_ms(500);
output_low(led);
// Write the letter 'B' to the slave board.
i2c_start();
i2c_write(0xA0);
i2c_write(0x00);
i2c_write(0b10001111);
i2c_stop();
delay_ms(500);
}
}
|
This is the slave code:
Code: |
#include <16F690.h>
#device adc=8
#FUSES NOWDT //No Watch Dog Timer
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
#FUSES NOPROTECT //Code not protected from reading
#FUSES BROWNOUT //Reset when brownout detected
#FUSES MCLR //Master Clear pin enabled
#FUSES NOCPD //No EE protection
#FUSES NOPUT //No Power Up Timer
#FUSES IESO //Internal External Switch Over mode enabled
#FUSES FCMEN //Fail-safe clock monitor enabled
#use delay(clock=8000000)
#use rs232(baud=19200, xmit=PIN_A5, rcv=PIN_A4)
#use i2c(Slave, Slow, sda=PIN_B4, scl=PIN_B6, address=0xa0)
/*
Pin defines
*/
#define driver_a PIN_C0
#define driver_b PIN_C1
/*
Variables
*/
/*
Function prototypes
*/
// description: This funtion enables or disables the LM298 both H-bridges.
// arguments: Boolean enable
// return: None
void EnableDrivers( int1 enable );
// description: This funtion sets both the h-bridges to the desired value
// arguments: int8 config
// The four MBS configure the side and direction.
// The four LSB configure the duty cycle.
// return: None
void SetDrivers( int8 config );
// There are some issues with the PWM module, before changing pinout please clear.
void clear( void );
#int_SSP // i2c activty
SSP_isr()
{
printf("interrupt!\n\r");
return;
}
void main(){
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_DISABLED);
//setup_timer_2(T2_DIV_BY_1,255,1);
setup_timer_2(T2_DIV_BY_16, 124, 1); // 1000 Hz, the pwm frequention
setup_comparator(NC_NC_NC_NC);
enable_interrupts(INT_SSP);
//enable_interrupts(INT_RB);
enable_interrupts(GLOBAL);
setup_oscillator(OSC_8MHZ);
printf("I2C engine driver Updated with I2C\n\r");
EnableDrivers( true );
while(1){
// recht vooruit
SetDrivers( 0b10000111 );
delay_ms( 2000 );
// recht achteruit
SetDrivers( 0b01000111 );
delay_ms( 2000 );
// rechtsom
SetDrivers( 0b00100111 );
delay_ms( 2000 );
// linksom
SetDrivers( 0b00010111 );
delay_ms( 2000 );
// stop
SetDrivers( 0b00000111 );
delay_ms( 2000 );
}
}
/*
Funtion boddies
*/
void EnableDrivers( values ){
if( values ){
output_high( driver_a );
output_high( driver_b );
}
else{
output_low( driver_a );
output_low( driver_b );
}
}
void SetDrivers( int8 config ){
int8 settings = 0;
int8 duty = 0;
duty = config & 0b00001111;
duty = duty * 8;
set_pwm1_duty( duty );
settings = config & 0b11110000;
settings = settings >> 4;
if((settings & 0b1000) > 0 && // forward
(settings & 0b0100) == 0 &&
(settings & 0b0010) == 0 &&
(settings & 0b0001) == 0 ){
clear();
setup_ccp1(CCP_PWM | CCP_PWM_H_H | CCP_PULSE_STEERING_A | CCP_PULSE_STEERING_C );
output_low( PIN_C4);
output_low( PIN_C3);
}
else if((settings & 0b1000) == 0 &&
(settings & 0b0100) > 0 && // backward
(settings & 0b0010) == 0 &&
(settings & 0b0001) == 0 ){
clear();
setup_ccp1(CCP_PWM | CCP_PWM_H_H | CCP_PULSE_STEERING_B | CCP_PULSE_STEERING_D );
output_low( PIN_C5);
output_low( PIN_C2);
}
else if((settings & 0b1000) == 0 &&
(settings & 0b0100) == 0 &&
(settings & 0b0010) > 1 && // turn right
(settings & 0b0001) == 0 ){
clear();
setup_ccp1(CCP_PWM | CCP_PWM_H_H | CCP_PULSE_STEERING_A | CCP_PULSE_STEERING_D );
output_low( PIN_C4);
output_low( PIN_C2);
}
else if((settings & 0b1000) == 0 &&
(settings & 0b0100) == 0 &&
(settings & 0b0010) == 0 &&
(settings & 0b0001) > 0 ){ // turn
clear();
setup_ccp1(CCP_PWM | CCP_PWM_H_H | CCP_PULSE_STEERING_B | CCP_PULSE_STEERING_C );
output_low( PIN_C3);
output_low( PIN_C5);
}
else{
clear();
setup_ccp1(CCP_PWM | CCP_PWM_H_H );
output_low( PIN_C2);
output_low( PIN_C3);
output_low( PIN_C4);
output_low( PIN_C5); }
}
void clear( void ){
setup_ccp1(CCP_PWM | CCP_PWM_H_H | CCP_PULSE_STEERING_A | CCP_PULSE_STEERING_B | CCP_PULSE_STEERING_C | CCP_PULSE_STEERING_D );
output_low( PIN_C2);
output_low( PIN_C3);
output_low( PIN_C4);
output_low( PIN_C5);
}
|
The slave code is a bit big, but otherwise it doesn't compile.
The problem is that I get no interrupt.
I get this code on the slave in ASM
Code: |
.................... enable_interrupts(INT_SSP);
0226: BSF 03.5
0227: BSF 0C.3
.................... enable_interrupts(GLOBAL);
0228: MOVLW C0
0229: BCF 03.5
022A: IORWF 0B,F
|
Is this correct? _________________ checkout my site: www.ymoona.com/wiki |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Dec 30, 2007 2:26 pm |
|
|
It worked for me. Here's the output of the 16F690:
Quote: |
I2C engine driver Updated with I2C
interrupt!
interrupt!
interrupt!
interrupt!
|
I don't have two 16F690's, so I used a 16F877 for the master, but with
similar code. I tested this with vs. 4.064 of the compiler.
What is your compiler version ?
Also, you don't need SSP interrupts in the Master code. Remove the
#int_ssp routine, and remove the enable interrupts for INT_SSP and
GLOBAL. You only need SSP interrupts in the slave code.
Another thing is you don't need a 'return' statement in an interrupt
routine. The compiler puts in code to handle exiting from the routine.
Delete the 'return' statements. |
|
|
ymoona
Joined: 20 Mar 2005 Posts: 32 Location: Heiloo, the Netherlands
|
|
Posted: Sun Dec 30, 2007 2:40 pm |
|
|
PCM programmer wrote: | It worked for me. Here's the output of the 16F690:
Quote: |
I2C engine driver Updated with I2C
interrupt!
interrupt!
interrupt!
interrupt!
|
I don't have two 16F690's, so I used a 16F877 for the master, but with
similar code. I tested this with vs. 4.064 of the compiler.
What is your compiler version ?
Also, you don't need SSP interrupts in the Master code. Remove the
#int_ssp routine, and remove the enable interrupts for INT_SSP and
GLOBAL. You only need SSP interrupts in the slave code.
Another thing is you don't need a 'return' statement in an interrupt
routine. The compiler puts in code to handle exiting from the routine.
Delete the 'return' statements. |
I had a little break, an suddenly interrupts seem to work. The return statement in a function is a habbit, a function always returns. _________________ checkout my site: www.ymoona.com/wiki |
|
|
ymoona
Joined: 20 Mar 2005 Posts: 32 Location: Heiloo, the Netherlands
|
|
Posted: Sun Dec 30, 2007 4:49 pm |
|
|
I thought the problem was fixed, but still it isn't working as I expect.
This is my master code:
Code: |
while(1){
output_high(led);
i2c_start();
delay_ms(10);
i2c_write(0xA0);
delay_ms(10);
i2c_write(0b01001111);
delay_ms(10);
i2c_stop();
delay_ms(10);
delay_ms(500);
output_low(led);
i2c_start();
delay_ms(10);
i2c_write(0xA0);
delay_ms(10);
i2c_write(0b10001111);
delay_ms(10);
i2c_stop();
delay_ms(10);
delay_ms(500);
}
|
So I send a single byte (plus of course the address)
This is my slave code:
Code: |
#int_SSP // i2c activty
SSP_isr(){
output_high(led);
// you will only be interrupted if the address is correct.
// first check the mode: read or write
state = i2c_isr_state();
if(state < 0x80){ //Master is sending data
incoming = i2c_read();
if(state == 1){ //First received byte is byte I want
value = incoming;
}
}
output_low(led);
}
|
I expected the led to flash 2 times quick (I probably cannot see two pulses) per half second. But this is not what happens. The led flahes very irregularly, and it stays on for way too long sometimes. Up to 500 to 1500 ms. How does this happen?
Compiler version: 4.013
Edit:
I've done some tests, and this is the result: working code.
Code: |
#int_SSP // i2c activty
SSP_isr(){
output_high(led);
state = i2c_isr_state();
if(state < 0x80){ //Master is sending data
if(state == 0){
}
if(state == 1){ //First received byte is byte I want
incoming = i2c_read();
value = incoming;
}
}
output_low(led);
}
|
As you can see I ignore state 0 (the address byte) and then I don't read. Only the first data byte is read. Why is this working? _________________ checkout my site: www.ymoona.com/wiki |
|
|
|
|
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
|