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:
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.
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.
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).
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)
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
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
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
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
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.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
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:
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
Can you post the GCB source? Just enough to show the issue.
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.
Now this is a wierd one. I will later today.
:-(
Thank,
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.
This program also shows use of INFO within scripts. :-)
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
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
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.
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.
Good, I am reving the .h now. I do this now as you can test. :-)
See attached, Remove the script and that little sub and replace your existing .h
Enjoy!
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:
Thank a lot for your quick help !
BRILLIANT!!!
I will be in the 1640 build - out soon.