Menu

Using SSD1306 with I2C speed of 400Khz PIC18F16Q41

Help
2026-09-12
2026-09-13
  • Fabrice Engel

    Fabrice Engel - 2026-09-12

    Hello GCBASIC Community,

    I am working on a new project using a display SSD1306. For that project I initially used a PIC16F17146, but the program grows so fast and big, that I needed to replace the PIC with a bigger one. So, I selected a PIC18F16Q41. I managed finally most conversion issues (WDT, ADC, Interrupt, Config,...), but I struggle with I2C speed.
    I2C Baud of 400Khz was fine with PIC16F17146, but something make not possible to reach that speed with PIC18F16Q41. The 16F uses a MSSP, and the 18F an I2C module. So I believe the commands are not the same.

    In the ASM file of the test screen, should we not have an instruction defining the I2C bus speed ?
    Here what I have in the PIC18F asm file:

    ;Dir HI2C_DATA out
    bcf TRISB,4,ACCESS
    ;Dir HI2C_CLOCK out
    bcf TRISB,6,ACCESS
    ;I2C1CON1 = I2C1I2C1CON1Default
    movlw 128
    banksel I2C1CON1
    movwf I2C1CON1,BANKED
    ;I2C1CON2 = I2C1I2C1CON2Default
    movlw 33
    movwf I2C1CON2,BANKED
    ;I2C1CLK = I2C1CLOCKSOURCE
    movlw 3
    movwf I2C1CLK,BANKED
    ;I2C1CON0 = I2C1I2C1CON0Default
    movlw 132
    movwf I2C1CON0,BANKED
    ;I2C1PIR = 0 ;Clear all the error flags
    clrf I2C1PIR,BANKED
    ;I2C1ERR = 0
    clrf I2C1ERR,BANKED
    ;I2C1CON0.EN=1
    bsf I2C1CON0,EN,BANKED
    ;Commence I2C protocol
    ;I2C1CON2.ACNT = 0
    bcf I2C1CON2,ACNT,BANKED
    ;I2C1CON2.ABD=0
    bcf I2C1CON2,ABD,BANKED
    ;I2C1CON0.MDR=1
    bsf I2C1CON0,MDR,BANKED

    According to the PIC18F16Q41 Datasheet at page 683 we have the register I2C1BAUD to be defined for the I2C bus speed, and I do not find that register in the assembled file (sub SI2CINIT).

    Is something missing in the library ? For me I2C and also PIC18F16Q41 are new, have never used these materials until that project. Maybe I made another mistake, but this is the state I am right now.

    Thank for any advice

     
  • Anobium

    Anobium - 2026-09-12

    Can you post the GCB source? Just enough to show the issue.

     
    • Fabrice Engel

      Fabrice Engel - 2026-09-12

      Oops, sure yes, my mistake ! Please find attached the related source code.
      The screen displays correctly. But much slower than with the PIC16F17146 at 400.

       
  • Anobium

    Anobium - 2026-09-12

    Now this is a wierd one. I will later today.

    :-(

     
    • Fabrice Engel

      Fabrice Engel - 2026-09-12

      Thank,

       
  • Anobium

    Anobium - 2026-09-12

    The setting of the I2C is not really discussed in the Help as I had not been asked. :-)

    So here goes. If this code works for you then I will move into the main library.

    I2C1 baud rate setup for the PIC18FxxQxx and PIC18FxxK8x I2C peripheral

    The BACKGROUND:

    These chips have the redesigned I2C module (I2C1CON0/1/2, I2C1CLK,
    I2C1BAUD) instead of the classic MSSP module (SSPCON1/SSPADD).
    GCBASIC's HI2CMode (called automatically by glcd.h's SSD1306 driver) is written
    for the old MSSP module and is there is a no-op on these chips - it's wrapped in
    "#ifdef var(SSPCON1)", which this chip doesn't have. Likewise
    HI2C_BAUD_RATE is read by that same legacy MSSP code
    path, so on its own it has no effect on these chip's actual bus speed.

    So, we used the default clock in the demo, and, this is a fixed frequency.

    The PROPER WAY:

    The script in the program attached now reads HI2C_BAUD_RATE proprtly and does the equivalent job for the new module: pick a clock source and compute I2C1BAUD to hit that
    requested speed.

    Formula (confirmed against the datasheet - I2CxCON2,
    I2CxCLK, I2CxBAUD register definitions, and the "Host Clock Timing"
    section 36.4.2.2):
    FSCL = FI2CxCLK / (4 * (I2C1BAUD + 1)) when FME = 1
    FSCL = FI2CxCLK / (5 * (I2C1BAUD + 1)) when FME = 0
    FME is bit 5 of I2C1CON2. GCBASIC's own default (I2C1I2C1CON0Default
    in hwi2c.h) sets I2C1CON2 = 0x21, which has bit 5 set, so FME = 1 and
    the divisor is 4 - that's the formula used below.

    Clock source choice:

    I2C1CLOCK_MFINTOSC is a fixed 500kHz source, independent of Fosc. At
    BAUD=0 that gives FSCL_max = 500,000/4 = 125kHz - it cannot go any
    faster, so it can only serve Standard-mode targets (<=125kHz), but it
    has the advantage of not changing if Fosc ever does (Doze, clock
    switching, sleep/wake). Above 125kHz (Fast mode, e.g. this file's
    400kHz), only FOSC (or another fast source) can reach it, so this
    script picks I2C1CLOCK_FOSC automatically once HI2C_BAUD_RATE > 125.

    Usage

    Just change HI2C_BAUD_RATE above to the target speed in kHz;
    this block recalculates the clock source and I2C1BAUD automatically.
    Watch the GCBASIC compiler output for the "I2C1: source=... FSCL=..."
    Warning line below to see the actual achieved frequency versus what
    was requested (they can differ slightly since I2C1BAUD is an integer
    divider - see the I2C1BAUDReload > 255 clamp/Warning case too).

    It may not be perfect but it will be very close.

    Let me know have you get on.

     
  • Anobium

    Anobium - 2026-09-12

    This program also shows use of INFO within scripts. :-)

     
  • Fabrice Engel

    Fabrice Engel - 2026-09-13

    Hi,
    Works well, program calculated the value (39 for 64Mhz) and displayed following message during compilation:
    Info: I2C1: source= 64000000 Hz HI2C_BAUD_RATE= 400 kHz(requested) => I2C1BAUD= 39 FSCL= 400000 Hz(actual)

    It added in the asm:
    ;**************

    ;Source: TestHardware18FV1.1TestScreen.gcb (168)
    SETI2C1BAUDRATE
    ;I2C1BAUD = I2C1BAUDReload
    movlw 39
    banksel I2C1BAUD
    movwf I2C1BAUD,BANKED
    banksel 0
    return

    ;**************

    Hardware test also shows a better display speed

    So, i used it for my project. But then I get strange reaction of the whole program, hang at some place. I reversed the implementation, compiled and programmed the PIC, then was fine again.
    I tried again by reducing frequency from 400 to 200, seems to better react...

    Need to make some more tests. Very weird ...

    Thank for your help so far. Let me know if I can help more by tests.

     

    Last edit: Fabrice Engel 2026-09-13
  • Fabrice Engel

    Fabrice Engel - 2026-09-13

    I made some more tests, I assume Evan, your fix is ok.

    My issues now can be from collisions between my ISR (it is not really lean now) and I2C requests.

    Will work now on improving the Interrupt routine.

    Will give more feedback later (right now my I2C bus speed is set to 125 to have no issues - issues happen when i set time in mm:ss on screen, too much computation if I turn too quick the rotary encoder).

     

    Last edit: Fabrice Engel 2026-09-13
  • Anobium

    Anobium - 2026-09-13

    This is great news. I will adpat the HWI2C.h library. When I post please remove the script from your program and test with the new library. And, of course, report back. :-)

    GLCDs do not like high speed but do try increasing the constant HI2CITSCLWaitPeriod this is very important. I set to 100 as a minimum but this may be too low for your GLCD.

     
  • Fabrice Engel

    Fabrice Engel - 2026-09-13

    I made good progress. I2C Bus is now at 200KHz, and I have no issues (until yet) with the SSD display. Thank for your help.

     
  • Anobium

    Anobium - 2026-09-13

    Good, I am reving the .h now. I do this now as you can test. :-)

     
  • Anobium

    Anobium - 2026-09-13

    See attached, Remove the script and that little sub and replace your existing .h

    Enjoy!

     
  • Fabrice Engel

    Fabrice Engel - 2026-09-13

    Hi Anobium, let me report successfully implementation of new hwi2c.h library.
    I made multiple I2C speed test compilations, and it works. Also on my big project it was correctly added with a speed of 200Khz:

    ;Source: hwi2c.h (1147)
    SI2CINIT
    ;This method sets the MSSP modules for K-mode family chips
    ;Dir HI2C_DATA out
        bcf TRISB,4,ACCESS
    ;Dir HI2C_CLOCK out
        bcf TRISB,6,ACCESS
    ;I2C1CON1 = I2C1I2C1CON1Default
        movlw   128
        banksel I2C1CON1
        movwf   I2C1CON1,BANKED
    ;I2C1CON2 = I2C1I2C1CON2Default
        movlw   33
        movwf   I2C1CON2,BANKED
    ;I2C1CLK =  I2C1CLOCKSOURCE
        movlw   1
        movwf   I2C1CLK,BANKED
    ;I2C1CON0 = I2C1I2C1CON0Default
        movlw   132
        movwf   I2C1CON0,BANKED
    ;I2C1BAUD = NEWI2C_BAUD_RATE_TEMP
        movlw   79
        movwf   I2C1BAUD,BANKED
    ;I2C1PIR = 0    ;Clear all the error flags
        clrf    I2C1PIR,BANKED
    ;I2C1ERR = 0
        clrf    I2C1ERR,BANKED
    ;I2C1CON0.EN=1
        bsf I2C1CON0,EN,BANKED
    ;Commence I2C protocol
    ;I2C1CON2.ACNT = 0
        bcf I2C1CON2,ACNT,BANKED
    ;I2C1CON2.ABD=0
        bcf I2C1CON2,ABD,BANKED
    ;I2C1CON0.MDR=1
        bsf I2C1CON0,MDR,BANKED
    

    Thank a lot for your quick help !

     
  • Anobium

    Anobium - 2026-09-13

    BRILLIANT!!!

    I will be in the 1640 build - out soon.

     

Log in to post a comment.