News:

;) This forum is the property of Proton software developers

Main Menu

Wait for idle in I2C code

Started by broderic, Dec 24, 2022, 09:08 AM

Previous topic - Next topic

broderic

Hello.
I'm trying to replicate in Positron code this piece of code, that waits for idle before writing in SSPBUF:

void i2c_waitForIdle(){
    while((SSPSTAT&0x04)||(SSPCON2&0x1F));
    }

I wrote and run, with no success, this Positron code:
Sub i2c_waitForIdle()
    STAT_AND = SSPSTAT&0x04
    CONN2_AND = SSPCON2&0x1F
    STAT_CONN = STAT_AND | CONN2_AND
    While  STAT_CONN > 0x00 : Wend 
EndSub                                 ' stay here till ACKEN,RCEN,PEN,RSEN,SEN
                                       ' are all 0
It seems it stays forever in the while, even when the OR becomes 0.
Sorry for the silly question, but I don't understand why.
Thanks for any kind correction/explanation of my error.
Regards



tumbleweed

You have to evaluate the register settings inside the loop. You have it reading the registers once and then looping, so if the registers change it won't detect it.

Moving what you have inside the loop would look something like:
Sub i2c_waitForIdle()
  Dim STAT_AND As Byte
  Dim CONN2_AND As Byte
  Dim STAT_CONN As Byte
  Do
    STAT_AND = SSPSTAT&0x04
    CONN2_AND = SSPCON2&0x1F
    STAT_CONN = STAT_AND | CONN2_AND
  Loop While STAT_CONN > 0x00
EndSub                                 ' stay here till ACKEN,RCEN,PEN,RSEN,SEN



Pepe

#2
I could this
Sub i2c_waitForIdle()
    Do
    Loop While ((SSPSTAT&0x04)|(SSPCON2&0x1)) <> 0
EndSub

normnet

The following is the ChatGPT conversion from C to Proton Basic:

Here is the equivalent Proton Basic code for the C function i2c_waitForIdle:

Copy code
Sub i2c_waitForIdle()
  While (SSPSTAT And &H04) Or (SSPCON2 And &H1F)
  Wend
End Sub

This function waits until the I2C bus is idle, by continuously checking the status bits in the SSPSTAT and SSPCON2 registers. The And operator is used to mask off the relevant bits, and the Or operator is used to check if either of them are set. The While loop continues until both conditions are false.

Pepe

#4
It is totally wrong, in any case it would have to be so.

Sub i2c_waitForIdle()
  While (SSPSTAT & 0x04 | SSPCON2 & 0x1F) <> 0
  Wend
EndSub

Send the ChatGPT the right way for it to learn

broderic

Many thanks for your help tumbleweed,Pepe and normnet, but at the moment none of your suggestions works.
Furthermore And and Or are not recognized, I've been told from the compiler.

The only way it works is when I completely replace the whole Sub with a DelayUs 100.

But of course this is not the way.

It is very strange, because it seems it doesn't come out of the While loop as the condition is never unsatisfied.

But, with everything the same, the High tech C

void i2c_waitForIdle(){
    while((SSPSTAT&0x04)||(SSPCON2&0x1F));
    }
perfectly works.
Don't understand :(

Thanks a lot.

Pepe

If you publish the source in positron it could be revised

Stephen Moss

Quote from: broderic on Dec 24, 2022, 09:08 AMvoid i2c_waitForIdle(){
    while((SSPSTAT&0x04)||(SSPCON2&0x1F));
    }

I wrote and run, with no success, this Positron code:
Was that code written for the device you are using? If not then it would be worth investigating on the device you are using as to what the purpouse of the respective bits are, becasue if you are checking for the wrong thing the condition may not be met hence the code fails to operatre as expected.  

broderic

Here is the code.
In the ISR there are the instructions that write to the LCD driver, after reading the voltage in the potentiometer.
I was forced to "explose" the sub "Wait for Idle" because no Sub is allowed in ISR (as far as I know).

As I told it seems it doesn't come out from the While...Wend.
If I completely replace the 3 While...Wend with a Delayus 100 (for instance) it works properly.

I used this way due to some difficulties for me to use the I2Cout macro, since the PCF driver has address and subaddress, and I didn't know how to deal with them.

Thank you


'****************************************************************
'*  Name    : VIM332 LCD drive with PCF8566.BAS                 *
'*  Author  : [select VIEW...EDITOR OPTIONS]                    *
'*  Notice  : Copyright (c) 2022 [select VIEW...EDITOR OPTIONS] *
'*          : All Rights Reserved                               *
'*  Date    : 22/12/2022                                        *
'*  Version : 1.0                                               *
'*  Notes   :                                                   *
'*          :                                                   *
'****************************************************************
Device = 16F877
Declare Xtal = 4MHz
Declare I2C_Slow_Bus On
Config HS_OSC, WDT_OFF, PWRTE_ON, BODEN_OFF, LVP_OFF, WRTE_ON, CP_OFF, DEBUG_OFF
On_Hardware_Interrupt GoTo Timer_ISR
Dim i As Byte
Dim j As Byte
Dim segments As SByte
Dim lcd_segments[4] As SByte            '3x14 bit display buffer for LCD segments
Dim analog_in As Dword
Dim STAT_AND As Byte
Dim CONN2_AND As Byte
Dim STAT_CONN As Byte
Dim databuff As Byte
'bit pattern for 0 to 9 figure
Dim lcd_num_pa[10] As Byte = 0b11111010, 0b00001010, 0b10111100, 0b10011110, 0b01001110,0b11010110, 0b11110110, 0b10001010, 0b11111110, 0b11011110
Dim a As Dword
Dim b As Dword
Symbol DRIVER_ADDR = 0x7E               '7 bit address
Symbol PCF_CONTINUE = 1 << 7

Main:
    ' initialize all port pints as input
    TRISB  = 0x00
    TRISC  = 0xFF
    TRISD  = 0x00
    PORTB = 0x00
    PORTC = 0x00
    PORTD = 0x00
    TRISE  = 0x00
    PORTE = 0x00
    TRISA=0x09
    PORTA=0x00
    ADCON0 = 0b11000001               'Vref=1.1V, right-aligned, ADC4 input
    ADCON1 = 0b10000101               'Vref su RA3 (xxxx0101)
    DelayMS 20
    CCP2CON = 0b00001011              'adc, auto-trigger by Timer0 ovf, interrupt enabled, prescale = 1/2
    T1CON=0b00110001                  'initialize timer1
    TMR0 = 0
    TMR1H=0
    TMR1L=0
    CCPR2L=0XFE
    CCPR2H=0XFF
    OPTION_REG=0b10000011
    PIE1bits_ADIE=1
    INTCONbits_T0IE=1
    PIE1bits_TMR1IE=1
    PIR1bits_TMR1IF=0
    INTCONbits_GIE=1
    INTCONbits_PEIE=1
    DelayMS 50
    i2c_init()
    DelayMS 50
   
    While 1=1
    ' display voltage value to lcd
            DelayMS 40
            a = (analog_in * 2560/1024)
            'a=123
            b=a//10
            lcd_pr_num(0, b)
  a =a/ 10
  b= a//10
            lcd_pr_num(1, b )
            a =a/ 10
  b= a//10
            lcd_pr_num(2, b )
            a =a/ 10
  b= a//10
            lcd_pr_num(3, b )
            DelayMS 750
            DelayMS 750       
   Wend
   
   'Initialize the I2C module as a master mode
   'SCL - RC3
   'SDA - RC4
   '100kHz I2C click at 4Mhz crystal
   
   Sub i2c_init()                         
    TRISC.3 = 1                         'make SCL as input
    TRISC.4 = 1                         'make SDA as intput
   
    '---------- configs for SSPCON2 register-----------
    SSPCON2 = 0                         'initially no operations on the bus
    '--------------------------------------------------
   
    '----------configs for SSPSTAT register------------
    SSPSTAT = 0b10000000                'SMP = 1 [Slew rate control disabled for standard
                                        'speed mode (100 kHz and 1 MHz)]
    '--------------------------------------------------
   
    '----------configs for SSPCON register-------------
    'Synchronous Serial Port Mode Select bits
    'configs as I2C Master mode, clock = FOSC / (4 * (SSPADD+1))
    SSPCONbits_SSPM0 = 0
    SSPCONbits_SSPM1 = 0
    SSPCONbits_SSPM2 = 0
    SSPCONbits_SSPM3 = 1
    SSPCONbits_SSPEN = 1                'Enables the serial port and configures
                                        'the SDA and SCL pins as the source of the serial port pins
    SSPCONbits_SSPOV = 0                'No overflow
    SSPCONbits_WCOL = 0                 'No collision
 
    '--------------------------------------------------         
     SSPADD = 10                        '100kHz clock speed at 4Mhz cystal   
    '----------PIR1-----------
    PIR1bits_SSPIF = 0                  'clear  Master Synchronous Serial Port (MSSP) Interrupt Flag bit
    '-----------PIR2-----------
    PIR2bits_BCLIF = 0                  'clear Bus Collision Interrupt Flag bit
    SSPCON = 0b00101000
EndSub

'wait until I2C bus become idle
Sub i2c_waitForIdle()
    STAT_AND = SSPSTAT & 0x04
    CONN2_AND = SSPCON2 & 0x1F
    While  STAT_AND | CONN2_AND > 0 :  Wend 
EndSub                                 ' stay here till ACKEN,RCEN,PEN,RSEN,SEN
                                       ' are all 0
'send 8 bit(1 byte) through I2C bus
Proc i2c_write(databuff As Byte)

    STAT_AND = SSPSTAT&0x04            'replaces i2c_waitForIdle()
    CONN2_AND = SSPCON2&0x1F
    STAT_CONN = STAT_AND | CONN2_AND
    While  STAT_CONN > 0 :  Wend
    'i2c_waitForIdle()
   
    SSPBUF = databuff
EndProc 

'send start condition to I2C bus
Sub i2c_start()
    i2c_waitForIdle()
    SSPCON2bits_SEN = 1                 'Initiate Start condition on SDA and SCL pins.
EndSub                                  'Automatically cleared by hardware.   


'send stop condition to I2C bus
Sub i2c_stop()
    i2c_waitForIdle()
    SSPCON2bits_PEN = 1                 'Initiate Stop condition on SDA and SCL pins.                                       'Automatically cleared by hardware.
EndSub

'timer0 overflow: it occurs every 2ms
Timer_ISR:
 Context Save
 If INTCONbits_T0IF & INTCONbits_T0IE = 1 Then
     
          STAT_AND = SSPSTAT&0x04        'replaces i2c_start()
          CONN2_AND = SSPCON2&0x1F
          STAT_CONN = STAT_AND | CONN2_AND
          While  STAT_CONN > 0 :  Wend             
          SSPCON2bits_SEN = 1
          'i2c_start()
         
          i2c_write(DRIVER_ADDR)
          i2c_write(0x60|PCF_CONTINUE)
          i2c_write(0x48|PCF_CONTINUE)
          i2c_write(0x60)
          i2c_write(lcd_segments[0])
          i2c_write(lcd_segments[1])
          i2c_write(lcd_segments[2])
          i2c_write(lcd_segments[3])
                     
          STAT_AND = SSPSTAT&0x04          'replaces i2c_stop()
          CONN2_AND = SSPCON2&0x1F
          STAT_CONN = STAT_AND | CONN2_AND
          While  STAT_CONN > 0 :  Wend
          SSPCON2bits_PEN = 1
          'i2c_stop()
         
          INTCONbits_T0IF=0
 EndIf
 If PIR1bits_ADIF & PIE1bits_ADIE = 1 Then
analog_in=(ADRESH<<8)+ADRESL
PIR1bits_ADIF=0
 EndIf
 Context Restore
 
'lcd_put_setments: put bit pattern on multiple segments
Proc lcd_put_segments(segline As SByte, pattern As SByte)
         lcd_segments[segline]=0x00
         lcd_segments[segline]=lcd_segments[segline] | pattern
EndProc       

'lcd_pr_num: put bit pattern for numbers on multiple segments
Proc lcd_pr_num(segline As Byte, num As Byte)
  If segline=3 Then
          If num = 1  Then
            lcd_put_segments(segline, 0b00000010)             '1 in fourth digit 4B,C
          ElseIf num = 0  Then
            lcd_put_segments(segline, 0b00000000)             'null in fourth digit
          EndIf
  Else
    lcd_put_segments(segline, lcd_num_pa[num])
  EndIf
EndProc









tumbleweed

#9
The code I posted back in post 1 is the long-handed equivalent of the C code. Are you sure you replaced all the inline code with the corrected waitforidle call?

I see you're writing to the lcd inside an ISR. I wouldn't recommend that, but if you do it, make sure that's the ONLY place that ever uses the I2C bus and/or the lcd, or disable interrupts around using the I2C.


tumbleweed

... and put the call to i2c_init before you enable the interrupts

broderic

Great suggestions tumbleweed! :)
Thanks a lot.
Your piece of code in post1 was not enough, but adding the call to i2c_init before the interrupt enables, made the job.
Following, I will also post the circuit with the connections (finding the right address and connections for PCF8566 was not easy too, for me...).
I know, my code is not too elegant, surely it can be improved and perhaps simplified, but it works properly and was all I could do with my current knowledge...
Here is the working code according to your suggestions.
Thanks to you and all others that showed interest and tried to help me.
Regards.

'****************************************************************
'*  Name    : VIM332 LCD drive with PCF8566.BAS                 *
'*  Author  : [select VIEW...EDITOR OPTIONS]                    *
'*  Notice  : Copyright (c) 2022 [select VIEW...EDITOR OPTIONS] *
'*          : All Rights Reserved                               *
'*  Date    : 22/12/2022                                        *
'*  Version : 1.1                                               *
'*  Notes   :                                                   *
'*          :                                                   *
'****************************************************************
Device = 16F877
Declare Xtal = 4MHz
Declare I2C_Slow_Bus On
Config HS_OSC, WDT_OFF, PWRTE_ON, BODEN_OFF, LVP_OFF, WRTE_ON, CP_OFF, DEBUG_OFF
On_Hardware_Interrupt GoTo Timer_ISR
Dim i As Byte
Dim j As Byte
Dim segments As SByte
Dim lcd_segments[4] As SByte            '3x14 bit display buffer for LCD segments
Dim analog_in As Dword
Dim STAT_AND As Byte
Dim CONN2_AND As Byte
Dim STAT_CONN As Byte
Dim databuff As Byte
'bit pattern for 0 to 9 figure
Dim lcd_num_pa[10] As Byte = 0b11111010, 0b00001010, 0b10111100, 0b10011110, 0b01001110,0b11010110, 0b11110110, 0b10001010, 0b11111110, 0b11011110
Dim a As Dword
Dim b As Dword
Symbol DRIVER_ADDR = 0x7E               '7 bit address
Symbol PCF_CONTINUE = 1 << 7

Main:
    ' initialize all port pints as input
    TRISB  = 0x00
    TRISC  = 0xFF
    TRISD  = 0x00
    PORTB = 0x00
    PORTC = 0x00
    PORTD = 0x00
    TRISE  = 0x00
    PORTE = 0x00
    TRISA=0x09
    PORTA=0x00
    ADCON0 = 0b11000001               'Vref=1.1V, right-aligned, ADC4 input
    ADCON1 = 0b10000101               'Vref su RA3 (xxxx0101)
    DelayMS 20
    CCP2CON = 0b00001011              'adc, auto-trigger by Timer0 ovf, interrupt enabled, prescale = 1/2
    T1CON=0b00110001                  'initialize timer1
    TMR0 = 0
    TMR1H=0
    TMR1L=0
    CCPR2L=0XFE
    CCPR2H=0XFF
    OPTION_REG=0b10000011
    DelayMS 50
    i2c_init()
    DelayMS 50
    PIE1bits_ADIE=1
    INTCONbits_T0IE=1
    PIE1bits_TMR1IE=1
    PIR1bits_TMR1IF=0
    INTCONbits_GIE=1
    INTCONbits_PEIE=1
   'DelayMS 50
   'i2c_init()
   'DelayMS 50
   
    While 1=1
    ' display voltage value to lcd
            DelayMS 40
            a = (analog_in * 2560/1024)
            'a=123
            b=a//10
            lcd_pr_num(0, b)
  a =a/ 10
  b= a//10
            lcd_pr_num(1, b )
            a =a/ 10
  b= a//10
            lcd_pr_num(2, b )
            a =a/ 10
  b= a//10
            lcd_pr_num(3, b )
            DelayMS 750
            DelayMS 750       
   Wend
   
   'Initialize the I2C module as a master mode
   'SCL - RC3
   'SDA - RC4
   '100kHz I2C click at 4Mhz crystal
   
   Sub i2c_init()                         
    TRISC.3 = 1                         'make SCL as input
    TRISC.4 = 1                         'make SDA as intput
   
    '---------- configs for SSPCON2 register-----------
    SSPCON2 = 0                         'initially no operations on the bus
    '--------------------------------------------------
   
    '----------configs for SSPSTAT register------------
    SSPSTAT = 0b10000000                'SMP = 1 [Slew rate control disabled for standard
                                        'speed mode (100 kHz and 1 MHz)]
    '--------------------------------------------------
   
    '----------configs for SSPCON register-------------
    'Synchronous Serial Port Mode Select bits
    'configs as I2C Master mode, clock = FOSC / (4 * (SSPADD+1))
    SSPCONbits_SSPM0 = 0
    SSPCONbits_SSPM1 = 0
    SSPCONbits_SSPM2 = 0
    SSPCONbits_SSPM3 = 1
    SSPCONbits_SSPEN = 1                'Enables the serial port and configures
                                        'the SDA and SCL pins as the source of the serial port pins
    SSPCONbits_SSPOV = 0                'No overflow
    SSPCONbits_WCOL = 0                 'No collision
 
    '--------------------------------------------------         
     SSPADD = 10                        '100kHz clock speed at 4Mhz cystal   
    '----------PIR1-----------
    PIR1bits_SSPIF = 0                  'clear  Master Synchronous Serial Port (MSSP) Interrupt Flag bit
    '-----------PIR2-----------
    PIR2bits_BCLIF = 0                  'clear Bus Collision Interrupt Flag bit
    SSPCON = 0b00101000
EndSub

'wait until I2C bus become idle
Sub i2c_waitForIdle()
    STAT_AND = SSPSTAT & 0x04
    CONN2_AND = SSPCON2 & 0x1F
    While  STAT_AND | CONN2_AND > 0 :  Wend 
EndSub                                 ' stay here till ACKEN,RCEN,PEN,RSEN,SEN
                                       ' are all 0
'send 8 bit(1 byte) through I2C bus
Proc i2c_write(databuff As Byte)
    'STAT_AND = SSPSTAT&0x04           
    'CONN2_AND = SSPCON2&0x1F
    'STAT_CONN = STAT_AND | CONN2_AND
    'While  STAT_CONN > 0 :  Wend
    Do                                  'replaces i2c_waitForIdle()
    STAT_AND = SSPSTAT&0x04
    CONN2_AND = SSPCON2&0x1F
    STAT_CONN = STAT_AND | CONN2_AND
    Loop While STAT_CONN > 0x00
    'i2c_waitForIdle()
     
    SSPBUF = databuff
   
EndProc 

'send start condition to I2C bus
Sub i2c_start()
    i2c_waitForIdle()
    SSPCON2bits_SEN = 1                 'Initiate Start condition on SDA and SCL pins.
EndSub                                  'Automatically cleared by hardware.   


'send stop condition to I2C bus
Sub i2c_stop()
    i2c_waitForIdle()
    SSPCON2bits_PEN = 1                 'Initiate Stop condition on SDA and SCL pins.                                       'Automatically cleared by hardware.
EndSub

'timer0 overflow: it occurs every 2ms
Timer_ISR:
 Context Save
 If INTCONbits_T0IF & INTCONbits_T0IE = 1 Then
          'STAT_AND = SSPSTAT&0x04           
          'CONN2_AND = SSPCON2&0x1F
          'STAT_CONN = STAT_AND | CONN2_AND
          'While  STAT_CONN > 0 :  Wend
          Do                             'replaces i2c_start()
          STAT_AND = SSPSTAT&0x04
          CONN2_AND = SSPCON2&0x1F
          STAT_CONN = STAT_AND | CONN2_AND
          Loop While STAT_CONN > 0x00           
          SSPCON2bits_SEN = 1
          'i2c_start()
       
          i2c_write(DRIVER_ADDR)
          i2c_write(0x60|PCF_CONTINUE)
          i2c_write(0x48|PCF_CONTINUE)
          i2c_write(0x60)
          i2c_write(lcd_segments[0])
          i2c_write(lcd_segments[1])
          i2c_write(lcd_segments[2])
          i2c_write(lcd_segments[3])
         
          'STAT_AND = SSPSTAT&0x04           
          'CONN2_AND = SSPCON2&0x1F
          'STAT_CONN = STAT_AND | CONN2_AND
          'While  STAT_CONN > 0 :  Wend           
          Do                               'replaces i2c_stop()
          STAT_AND = SSPSTAT&0x04
          CONN2_AND = SSPCON2&0x1F
          STAT_CONN = STAT_AND | CONN2_AND
          Loop While STAT_CONN > 0x00
          SSPCON2bits_PEN = 1
          'i2c_stop()
          INTCONbits_T0IF=0
 EndIf
 If PIR1bits_ADIF & PIE1bits_ADIE = 1 Then
analog_in=(ADRESH<<8)+ADRESL
PIR1bits_ADIF=0
 EndIf
 Context Restore
 
'lcd_put_setments: put bit pattern on multiple segments
Proc lcd_put_segments(segline As SByte, pattern As SByte)
         lcd_segments[segline]=0x00
         lcd_segments[segline]=lcd_segments[segline] | pattern
EndProc       

'lcd_pr_num: put bit pattern for numbers on multiple segments
Proc lcd_pr_num(segline As Byte, num As Byte)
  If segline=3 Then
          If num = 1  Then
            lcd_put_segments(segline, 0b00000010)             '1 in fourth digit 4B,C
          ElseIf num = 0  Then
            lcd_put_segments(segline, 0b00000000)             'null in fourth digit
          EndIf
  Else
    lcd_put_segments(segline, lcd_num_pa[num])
  EndIf
EndProc


top204

Below is a procedure I created a couple of years ago to wait for the MSSP1 peripheral operating as I2C, to wait for idle. It seemed to work well, and the timeout is a must to stop code blocking if something goes wrong with the I2C peripheral.

'-----------------------------------------------------------------------------------------------
' Wait for the I2C bus to become idle
' Input     : None
' Output    : Returns 0 if no acknowledge, else 1
' Notes     : None
'
Proc Hbus1_Wait_For_Idle(), Bit
    Dim wTimeout As Word                        ' A timeout loop counter
    wTimeout = $FFFF   

    Result = 1                                  ' Default to Acknowledge received
    Repeat
        DelayUS 1                               ' A small delay      
        If SSPCON2 & %00011111 = 0 Then ExitProc ' Zero so I2C bus is ready, so exit with result set to 1
        Dec wTimeout
    Until wTimeout = 0
    Result = 0                                  ' Result 0 to signal no Acknowledge received
EndProc

Pepe

Then it would have to work like that too.
'****************************************************************
'*  Name    : VIM332 LCD drive with PCF8566.BAS                 *
'*  Author  : [select VIEW...EDITOR OPTIONS]                    *
'*  Notice  : Copyright (c) 2022 [select VIEW...EDITOR OPTIONS] *
'*          : All Rights Reserved                               *
'*  Date    : 22/12/2022                                        *
'*  Version : 1.0                                               *
'*  Notes   :                                                   *
'*          :                                                   *
'****************************************************************
Device = 16F877
Declare Xtal = 4MHz
Declare I2C_Slow_Bus On
Declare Create_Coff On

Config HS_OSC, WDT_OFF, PWRTE_ON, BODEN_OFF, LVP_OFF, WRTE_ON, CP_OFF, DEBUG_OFF
On_Hardware_Interrupt GoTo Timer_ISR

Dim i As Byte
Dim j As Byte
Dim segments As SByte
Dim lcd_segments[4] As SByte            '3x14 bit display buffer for LCD segments
Dim analog_in As Dword
Dim STAT_AND As Byte
Dim CONN2_AND As Byte
Dim STAT_CONN As Byte
Dim databuff As Byte
'bit pattern for 0 to 9 figure
Dim lcd_num_pa[10] As Byte = 0b11111010, 0b00001010, 0b10111100, 0b10011110, 0b01001110,0b11010110, 0b11110110, 0b10001010, 0b11111110, 0b11011110
Dim a As Dword
Dim b As Dword
Symbol DRIVER_ADDR = 0x7E               '7 bit address
Symbol PCF_CONTINUE = 1 << 7

Main:
    ' initialize all port pints as input
    TRISB  = 0x00
    TRISC  = 0xFF
    TRISD  = 0x00
    PORTB = 0x00
    PORTC = 0x00
    PORTD = 0x00
    TRISE  = 0x00
    PORTE = 0x00
    TRISA=0x09
    PORTA=0x00
    ADCON0 = 0b11000001               'Vref=1.1V, right-aligned, ADC4 input
    ADCON1 = 0b10000101               'Vref su RA3 (xxxx0101)
    DelayMS 20
    CCP2CON = 0b00001011              'adc, auto-trigger by Timer0 ovf, interrupt enabled, prescale = 1/2
    T1CON=0b00110001                  'initialize timer1
    TMR0 = 0
    TMR1H=0
    TMR1L=0
    CCPR2L=0XFE
    CCPR2H=0XFF
    OPTION_REG=0b10000011
    DelayMS 50
    i2c_init()
    DelayMS 50
    PIE1bits_ADIE=1
    INTCONbits_T0IE=1
    PIE1bits_TMR1IE=1
    PIR1bits_TMR1IF=0
    INTCONbits_GIE=1
    INTCONbits_PEIE=1
   
    Do
    ' display voltage value to lcd
            DelayMS 40
            a = (analog_in * 2560/1024)
            'a=123
            b=a//10
            lcd_pr_num(0, b)
  a =a/ 10
  b= a//10
            lcd_pr_num(1, b )
            a =a/ 10
  b= a//10
            lcd_pr_num(2, b )
            a =a/ 10
  b= a//10
            lcd_pr_num(3, b )
            DelayMS 750
            DelayMS 750       
   Loop
   
   'Initialize the I2C module as a master mode
   'SCL - RC3
   'SDA - RC4
   '100kHz I2C click at 4Mhz crystal
   
   Proc i2c_init()                         
    TRISC.3 = 1                         'make SCL as input
    TRISC.4 = 1                         'make SDA as intput
   
    '---------- configs for SSPCON2 register-----------
    SSPCON2 = 0                         'initially no operations on the bus
    '--------------------------------------------------
   
    '----------configs for SSPSTAT register------------
    SSPSTAT = 0b10000000                'SMP = 1 [Slew rate control disabled for standard
                                        'speed mode (100 kHz and 1 MHz)]
    '--------------------------------------------------
   
    '----------configs for SSPCON register-------------
    'Synchronous Serial Port Mode Select bits
    'configs as I2C Master mode, clock = FOSC / (4 * (SSPADD+1))
    SSPCONbits_SSPM0 = 0
    SSPCONbits_SSPM1 = 0
    SSPCONbits_SSPM2 = 0
    SSPCONbits_SSPM3 = 1
    SSPCONbits_SSPEN = 1                'Enables the serial port and configures
                                        'the SDA and SCL pins as the source of the serial port pins
    SSPCONbits_SSPOV = 0                'No overflow
    SSPCONbits_WCOL = 0                 'No collision
 
    '--------------------------------------------------         
     SSPADD = 10                        '100kHz clock speed at 4Mhz cystal   
    '----------PIR1-----------
    PIR1bits_SSPIF = 0                  'clear  Master Synchronous Serial Port (MSSP) Interrupt Flag bit
    '-----------PIR2-----------
    PIR2bits_BCLIF = 0                  'clear Bus Collision Interrupt Flag bit
    SSPCON = 0b00101000
EndProc

'wait until I2C bus become idle
Proc i2c_waitForIdle()
    Do                                  'replaces i2c_waitForIdle()
    STAT_AND = SSPSTAT&0x04
    CONN2_AND = SSPCON2&0x1F
    STAT_CONN = STAT_AND | CONN2_AND
    Loop While STAT_CONN <> 0
EndProc                                 ' stay here till ACKEN,RCEN,PEN,RSEN,SEN
                                       ' are all 0
'send 8 bit(1 byte) through I2C bus
Proc i2c_write(databuff As Byte)
    i2c_waitForIdle()
    SSPBUF = databuff
EndProc

'send start condition to I2C bus
Proc i2c_start()
    i2c_waitForIdle()
    SSPCON2bits_SEN = 1                 'Initiate Start condition on SDA and SCL pins.
EndProc                                  'Automatically cleared by hardware.   


'send stop condition to I2C bus
Proc i2c_stop()
    i2c_waitForIdle()
    SSPCON2bits_PEN = 1                 'Initiate Stop condition on SDA and SCL pins.                                       'Automatically cleared by hardware.
EndProc

'timer0 overflow: it occurs every 2ms
Timer_ISR:
 Context Save
 If INTCONbits_T0IF & INTCONbits_T0IE = 1 Then
         
          i2c_start()
       
          i2c_write(DRIVER_ADDR)
          i2c_write(0x60|PCF_CONTINUE)
          i2c_write(0x48|PCF_CONTINUE)
          i2c_write(0x60)
          i2c_write(lcd_segments[0])
          i2c_write(lcd_segments[1])
          i2c_write(lcd_segments[2])
          i2c_write(lcd_segments[3])
         
          i2c_stop()
         
          INTCONbits_T0IF=0
 EndIf
 If PIR1bits_ADIF & PIE1bits_ADIE = 1 Then
analog_in=(ADRESH<<8)+ADRESL
PIR1bits_ADIF=0
 EndIf
 Context Restore
 
'lcd_put_setments: put bit pattern on multiple segments
Proc lcd_put_segments(segline As SByte, pattern As SByte)
         lcd_segments[segline]=0x00
         lcd_segments[segline]=lcd_segments[segline] | pattern
EndProc       

'lcd_pr_num: put bit pattern for numbers on multiple segments
Proc lcd_pr_num(segline As Byte, num As Byte)
  If segline=3 Then
          If num = 1  Then
            lcd_put_segments(segline, 0b00000010)             '1 in fourth digit 4B,C
          ElseIf num = 0  Then
            lcd_put_segments(segline, 0b00000000)             'null in fourth digit
          EndIf
  Else
    lcd_put_segments(segline, lcd_num_pa[num])
  EndIf
EndProc

tumbleweed

Quote from: top204 on Dec 24, 2022, 06:04 PMBelow is a procedure I created a couple of years ago to wait for the MSSP1 peripheral operating as I2C, to wait for idle.
One condition that proc doesn't wait for is the master mode R/W bit to be low (SSPSTAT.2), which is set high for the duration of an I2C write.

That could cause issues with routines like the OP's i2c_write, or initiating a stop after writing the last byte.

broderic

Pepe's code now obviously runs.

To make it run with Les' Proc, I changed it as follows:
Proc Wait_For_Idle(), Bit
    Dim wTimeout As Word                        ' A timeout loop counter
    wTimeout = $FFFF     
    Result = 1                                  ' Default to Acknowledge received
    Repeat
        DelayUS 1                              ' A small delay     
        If SSPCON2 & %00011111 = 0 Then
          If SSPSTAT & %00000100 = 0 Then ExitProc ' Zero so I2C bus is ready, so exit with result set to 1
        EndIf
        Dec wTimeout
    Until wTimeout = 0
    Result = 0                                  ' Result 0 to signal no Acknowledge received
EndProc   

Below, I post the revision 3 with all the suggestions of tumbleweed, Les,Pepe.
It looks smarter.
Learned a lot!
With your contributions.
Many thanks.

I will also try a version using I2Cout (but I'm not sure how to manage "control" and "address" parameters, with this component...let's see)

'****************************************************************
'*  Name    : VIM332 LCD drive with PCF8566_rev3                *
'*  Author  : Magic contribution of thumbleweed, Les, Pepe      *
'*  Notice  : Copyright (c) 2022 [select VIEW...EDITOR OPTIONS] *
'*          : All Rights Reserved                               *
'*  Date    : 25/12/2022                                        *
'*  Version : 3.0                                               *
'*  Notes   : - MSSP registers check in "wait for idle" must be *
'*          : within the loop                                   *
'*          : - i2c_init must be before interrupt enable        *
'****************************************************************
 Device = 16F877
Declare Xtal = 4MHz
Declare I2C_Slow_Bus On
Declare Create_Coff On

Config HS_OSC, WDT_OFF, PWRTE_ON, BODEN_OFF, LVP_OFF, WRTE_ON, CP_OFF, DEBUG_OFF
On_Hardware_Interrupt GoTo Timer_ISR

Dim lcd_segments[4] As SByte            '3x14 bit display buffer for LCD segments
Dim analog_in As Dword
Dim databuff As Byte
'bit pattern for 0 to 9 figure
Dim lcd_num_pa[10] As Byte = 0b11111010, 0b00001010, 0b10111100, 0b10011110, 0b01001110,0b11010110, 0b11110110, 0b10001010, 0b11111110, 0b11011110
Dim a As Dword
Dim b As Dword
Symbol DRIVER_ADDR = 0x7E               '7 bit address
Symbol PCF_CONTINUE = 1 << 7

Main:
    ' initialize all port pints as input
    TRISB  = 0x00
    TRISC  = 0xFF
    TRISD  = 0x00
    PORTB = 0x00
    PORTC = 0x00
    PORTD = 0x00
    TRISE  = 0x00
    PORTE = 0x00
    TRISA=0x09
    PORTA=0x00
    ADCON0 = 0b11000001               'Vref=1.1V, right-aligned, ADC4 input
    ADCON1 = 0b10000101               'Vref su RA3 (xxxx0101)
    DelayMS 20
    CCP2CON = 0b00001011              'adc, auto-trigger by Timer0 ovf, interrupt enabled, prescale = 1/2
    T1CON=0b00110001                  'initialize timer1
    TMR0 = 0
    TMR1H=0
    TMR1L=0
    CCPR2L=0XFE
    CCPR2H=0XFF
    OPTION_REG=0b10000011
    DelayMS 50
    i2c_init()
    DelayMS 50
    PIE1bits_ADIE=1
    INTCONbits_T0IE=1
    PIE1bits_TMR1IE=1
    PIR1bits_TMR1IF=0
    INTCONbits_GIE=1
    INTCONbits_PEIE=1
   
    Do
    ' display voltage value to lcd
            DelayMS 40
            a = (analog_in * 2560/1024)
            b=a//10
            lcd_pr_num(0, b)
  a =a/ 10
  b= a//10
            lcd_pr_num(1, b )
            a =a/ 10
  b= a//10
            lcd_pr_num(2, b )
            a =a/ 10
  b= a//10
            lcd_pr_num(3, b )
            DelayMS 750
            DelayMS 750       
   Loop
   
   'Initialize the I2C module as a master mode
   'SCL - RC3
   'SDA - RC4
   '100kHz I2C click at 4Mhz crystal
   
   Proc i2c_init()                         
    TRISC.3 = 1                         'make SCL as input
    TRISC.4 = 1                         'make SDA as intput
   
    '---------- configs for SSPCON2 register-----------
    SSPCON2 = 0                         'initially no operations on the bus
    '--------------------------------------------------
   
    '----------configs for SSPSTAT register------------
    SSPSTAT = 0b10000000                'SMP = 1 [Slew rate control disabled for standard
                                        'speed mode (100 kHz and 1 MHz)]
    '--------------------------------------------------
   
    '----------configs for SSPCON register-------------
    'Synchronous Serial Port Mode Select bits
    'configs as I2C Master mode, clock = FOSC / (4 * (SSPADD+1))
    SSPCONbits_SSPM0 = 0
    SSPCONbits_SSPM1 = 0
    SSPCONbits_SSPM2 = 0
    SSPCONbits_SSPM3 = 1
    SSPCONbits_SSPEN = 1                'Enables the serial port and configures
                                        'the SDA and SCL pins as the source of the serial port pins
    SSPCONbits_SSPOV = 0                'No overflow
    SSPCONbits_WCOL = 0                 'No collision
 
    '--------------------------------------------------         
     SSPADD = 10                        '100kHz clock speed at 4Mhz cystal   
    '----------PIR1-----------
    PIR1bits_SSPIF = 0                  'clear  Master Synchronous Serial Port (MSSP) Interrupt Flag bit
    '-----------PIR2-----------
    PIR2bits_BCLIF = 0                  'clear Bus Collision Interrupt Flag bit
    SSPCON = 0b00101000
EndProc

'-----------------------------------------------------------------------------------------------
' Wait for the I2C bus to become idle
' Input     : None
' Output    : Returns 0 if no acknowledge, else 1
' Notes     : None
'
Proc Wait_For_Idle(), Bit
    Dim wTimeout As Word                        ' A timeout loop counter
    wTimeout = $FFFF     
    Result = 1                                  ' Default to Acknowledge received
    Repeat
        DelayUS 1                              ' A small delay     
        If SSPCON2 & %00011111 = 0 Then
          If SSPSTAT & %00000100 = 0 Then ExitProc ' Zero so I2C bus is ready, so exit with result set to 1
        EndIf
        Dec wTimeout
    Until wTimeout = 0
    Result = 0                                  ' Result 0 to signal no Acknowledge received
EndProc   

'send 8 bit(1 byte) through I2C bus
Proc i2c_write(databuff As Byte)
    Wait_For_Idle()
    SSPBUF = databuff
EndProc

'send start condition to I2C bus
Proc i2c_start()
    Wait_For_Idle()
    SSPCON2bits_SEN = 1                 'Initiate Start condition on SDA and SCL pins.
EndProc                                  'Automatically cleared by hardware.   


'send stop condition to I2C bus
Proc i2c_stop()
    Wait_For_Idle()
    SSPCON2bits_PEN = 1                 'Initiate Stop condition on SDA and SCL pins.                                       'Automatically cleared by hardware.
EndProc

'timer0 overflow: it occurs every 2ms
Timer_ISR:
 Context Save
 If INTCONbits_T0IF & INTCONbits_T0IE = 1 Then
         
          i2c_start()
       
          i2c_write(DRIVER_ADDR)
          i2c_write(0x60|PCF_CONTINUE)
          i2c_write(0x48|PCF_CONTINUE)
          i2c_write(0x60)
          i2c_write(lcd_segments[0])
          i2c_write(lcd_segments[1])
          i2c_write(lcd_segments[2])
          i2c_write(lcd_segments[3])
         
          i2c_stop()
         
          INTCONbits_T0IF=0
 EndIf
 If PIR1bits_ADIF & PIE1bits_ADIE = 1 Then
    analog_in=(ADRESH<<8)+ADRESL
    PIR1bits_ADIF=0
 EndIf
 Context Restore
 
'lcd_put_setments: put bit pattern on multiple segments
Proc lcd_put_segments(segline As SByte, pattern As SByte)
         lcd_segments[segline]=0x00
         lcd_segments[segline]=lcd_segments[segline] | pattern
EndProc       

'lcd_pr_num: put bit pattern for numbers on multiple segments
Proc lcd_pr_num(segline As Byte, num As Byte)
  If segline=3 Then
          If num = 1  Then
            lcd_put_segments(segline, 0b00000010)             '1 in fourth digit 4B,C
          ElseIf num = 0  Then
            lcd_put_segments(segline, 0b00000000)             'null in fourth digit
          EndIf
  Else
    lcd_put_segments(segline, lcd_num_pa[num])
  EndIf
EndProc

broderic

As promised, enclosed the circuit layout.
I also added the versions using I2Cout or Busout commands.
Also added how it would appear in high tech C (sorry for this intrusion but the only purpose is to show how Positron code is smarter, and that's why I like it so much... thank you Les for your work)

Regards