|
|
View previous topic :: View next topic |
Author |
Message |
elcrcp
Joined: 11 Mar 2016 Posts: 62 Location: izmir / Turkey
|
#use i2c() in hardware mode with 18F2550 |
Posted: Fri Jun 29, 2018 2:34 am |
|
|
Hello guys, I migrated my oled application from picbasic to ccs and used Ttelmah's codes to drive the screen.
http://www.ccsinfo.com/forum/viewtopic.php?t=54453
I'm using 18f2550 and 1.3" ssd1306. There are 3 vertical lines on screen I couldn't solve yet but works fine other than these lines. Oh by the way, Thank you for your hard work Ttelmah.
So, why I'm writing this is that I faced a situation while my code migration, I couldn't make my code work without "force_sw" option whatever I tried, then I got stubborn and wanted "hw" option but nothing worked then "noinit" option solved my stuation =) here I listed my working and not working options for people who might face same stuation later.
Not Working I2C Initialization lines
Code: |
#USE I2C(MASTER,I2C1, FAST=400000, FORCE_HW)
#USE I2C(MASTER,I2C1, FAST=400000, FORCE_HW)
#USE I2C(MASTER,I2C1, FAST=400000)
#USE I2C(MASTER,I2C1, FORCE_HW)
#USE I2C(MASTER,I2C1, FAST, FORCE_HW)
#USE I2C(MASTER,I2C1, FAST)
#USE I2C(MASTER,SDA=PIN_B0,SCL=PIN_B1, FAST=400000, FORCE_HW)
#USE I2C(MASTER,SDA=PIN_B0,SCL=PIN_B1, FAST=400000)
#USE I2C(MASTER,SDA=PIN_B0,SCL=PIN_B1, FORCE_HW)
#USE I2C(MASTER,SDA=PIN_B0,SCL=PIN_B1, FAST, FORCE_HW)
#USE I2C(MASTER,SDA=PIN_B0,SCL=PIN_B1, FAST)
#USE I2C(MASTER,SDA=PIN_B0,SCL=PIN_B1)
#USE I2C(MASTER,I2C1)
|
"FORCE_SW" options were all working, I was using:
Code: | #USE I2C(MASTER,SDA=PIN_B0,SCL=PIN_B1, FAST=400000, FORCE_SW) |
At last, NOINIT option worked for HW, Now its is working with HW option:
Code: | #USE I2C(MASTER,I2C1, FAST=400000,NOINIT, FORCE_HW)
.
.
.
void SetupMCU()
{
.
.
.
i2c_init(TRUE);
}
|
Note : I'm trying to figure out and solve the 3 vertical lines on screen which are not appearing with my other code and I'll post it when I found but I'm all ears to all ideas while working on this =) _________________ There is nothing you can't do if you try |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Jun 29, 2018 6:13 pm |
|
|
Please post your compiler version. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19622
|
|
Posted: Sat Jun 30, 2018 1:05 am |
|
|
There is a basic and very simple reason it won't work.
Signal levels.
The SSD1306, is a 3.3v device. Though a lot of the boards have an internal 5v regulator, the signalling is 3.3v.
The hardware I2C, requires the inputs to go up to at least 0.8* the supply voltage of the PIC. So on a 2550 at 5v, they need to go to 4v. The 3.3v device does not allow this to happen.
Note my comment:
Quote: |
Have had it going successfully off a 5v device, by setting this to use SMBUS levels (many PIC's support this), and having the pull-ups to 3.3v. Or if you pull-up to 3.3v, and use _software_ I2C, on a port that supports TTL levels it also works OK.
|
You need to enable SMBUS mode to use the hardware port:
#USE I2C(MASTER,I2C1, FAST=400000, FORCE_HW, SMBUS)
Why it works with the bodge syntax, is 'interesting'. May accidentally be selecting SMBUS mode by not setting the bit controlling this when it is configured in software. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9299 Location: Greensville,Ontario
|
|
Posted: Sat Jun 30, 2018 6:00 am |
|
|
I see MR. T posted what I was thinking !! 5 volt PIC, 3 volt peripheral...quite common these days as most peripherals are low voltage devices.
As for the 3 vertical lines...does the display powerup properly with no lines? If so, I suspect a code problem, say a miscast variable or wrong counter value. I don't use that display or driver, just 'thinking out loud' here.
Is the power supply good for say 1 amp or more? Perhaps the LCD module draws more power than you think(or listed), the Vdd drops a bit and corrupts the LCD data ? Adding say a 470 or 1000 mfd cap on VDD may help.
Are the 3 lines always the same ones on the display, if so which x values ?? that may be a clue as to what's happening. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19622
|
|
Posted: Sat Jun 30, 2018 9:00 am |
|
|
I must admit a number of bits offset is quite common if you are not resetting the display correctly. I had one of 4 bits on one particular display board, if I didn't have a capacitor on the reset line ensuring the OLED chip reset correctly on power on (and then also delayed long enough for the module to have finished waking, before sending anything to the display). |
|
|
elcrcp
Joined: 11 Mar 2016 Posts: 62 Location: izmir / Turkey
|
|
Posted: Thu Jul 05, 2018 2:52 am |
|
|
Uh, I thought I did post a reply to Ttelmah's topic, I didn't intend to post a new topic sorry about that.
PCM programmer wrote: | Please post your compiler version. |
my compiler version is: 5.070
Ttelmah wrote: | You need to enable SMBUS mode to use the hardware port:
#USE I2C(MASTER,I2C1, FAST=400000, FORCE_HW, SMBUS) |
Thanks Ttelmah, I actually totally forgot about signal levels' difference a simple but big mistake of me. However, SMBUS option isn't working either. I tried quickly after reading your answer, I also tried bit banging registers directly for smbus (I don't trust built in functions sometimes) but still not working without noinit option. I'll upgrade my hardware later and use 18LF2550 so there won't be any signal lvl problem but thing is; I didn't use smbus with my previous code which is written with picbasic and I didn't get any problem. I also applied same oled and pic initialization lines with previous one since I'm migrating the code, but results are not same
temtronic wrote: | I see MR. T posted what I was thinking !! 5 volt PIC, 3 volt peripheral...quite common these days as most peripherals are low voltage devices. |
As I said, I use same hardware with my old code without any problem, I don't think this is something about hardware. But can't see the difference between codes either. I'll post my init lines. _________________ There is nothing you can't do if you try |
|
|
elcrcp
Joined: 11 Mar 2016 Posts: 62 Location: izmir / Turkey
|
|
Posted: Thu Jul 05, 2018 3:19 am |
|
|
This is my schematics, https://ibb.co/eyppxJ, these are my main code;
Code: | #include <18F2550.h>
#fuses HSPLL //High Speed Crystal/Resonator with PLL enabled
#fuses PLL5 //Divide By 5(20MHz oscillator input)
#fuses CPUDIV3 //System Clock by 3
#fuses USBDIV //USB clock source comes from PLL divide by 2
#fuses NOFCMEN //Fail-safe clock monitor disabled
#fuses NOIESO //Internal External Switch Over mode disabled
#fuses PUT //Power Up Timer
#fuses NOBROWNOUT //No brownout reset
#fuses VREGEN //USB voltage regulator enabled
#fuses NOWDT //Watch Dog Timer
#fuses WDT512 //Watch Dog Timer uses 1:512 Postscale
#fuses CCP2C1 //CCP2 input/output multiplexed with RC1
#fuses NOPBADEN //PORTB pins are configured as digital I/O on RESET
#fuses NOLPT1OSC //Timer1 configured for higher power operation
#fuses MCLR //Master Clear pin enabled
#fuses STVREN //Stack full/underflow will cause reset
#fuses NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#fuses NOXINST //Extended set extension and Indexed Addressing mode disabled (Legacy mode)
#use delay(clock=32MHz,crystal=20MHz) //INTERNAL 32MHZ CLOCK
#USE I2C(MASTER,I2C1, FAST=400000,NOINIT, FORCE_HW) //SETTING UP I2C
//DEFINING SOME OF REGISTERS FOR DIRECT ACCESS
#BYTE UCFG=0xF6F
#BYTE ADCON0=0xFC2
#BYTE ADCON1=0xFC1
#BYTE CMCON=0xFB4
#BYTE CVRCON=0xFB5
#BYTE T0CON=0xFD5
#BYTE INTCON2=0xFF1
#BYTE T2CON=0xFCA
#BYTE TRISA=0xF92
#BYTE TRISB=0xF93
//display dimensions - the physical LCD
#define S_LCDWIDTH 128
#define S_LCDHEIGHT 64
//If you want to use the SH1106, add this #define
//#define SH1106
#define TEXT_ONLY //If this is defined, gives a smaller text driver only
#define SSDADDR 0x78 //address for the chip - usually 0x7C or 0x78.
#include <string.h>
#include "ssd1306.h" //The OLED driver
//RTC variables
#define XTAL_FREQUENCY 32000000
#define TIMER1_FREQUENCY (XTAL_FREQUENCY / 4) // 1 clock tick = 1 instr. cycle = crystal frequency / 4
int32 Ticker;
int1 ScreenUpdate;
unsigned int8 mSeconds=0,intTimes=0,Seconds=0,Minutes=0,Hours=0,Days=0,Month=0,Year=0;
//optional:
// int8 Year=11, Month=1, Days=1, Hours=0, Minutes=0; // 2011-01-01 00:00
////////////////////////////////////////////////////////////////////////////////
// Test whether a given year is a leap year.
// This optimized version only works for the period 1901 - 2099
////////////////////////////////////////////////////////////////////////////////
#define IS_LEAP(year) (year%4 == 0)
////////////////////////////////////////////////////////////////////////////////
// Initialize RTC
////////////////////////////////////////////////////////////////////////////////
void Initialize_RTC(void)
{
Ticker = TIMER1_FREQUENCY; // initialize clock counter to number of clocks per second
setup_timer_1( T1_INTERNAL | T1_DIV_BY_1 ); // initialize 16-bit Timer1 to interrupt
// exactly every 65536 clock cycles
// (about 76 times per second)
enable_interrupts( INT_TIMER1 ); // Start RTC
}
////////////////////////////////////////////////////////////////////////////////
// -=Process Zero Drift Real Time Clock Information=-
//
// Most algorithms configure the timer to generate an interrupt every 100ms, and
// then count the number of interrupts. The problem in that approach is that most
// clock frequencies can't be divided by 256 and you don't get an exact 100ms.
// The small errors will add up to an error of several seconds a day.
//
// The algorithm presented here is exact in the long run because it doesn't
// count the number of interrupts but counts the number of clock cycles.
////////////////////////////////////////////////////////////////////////////////
#int_TIMER1
void TIMER1_isr()
{
Ticker -= 65536; // Decrement ticker by clocks per interrupt
intTimes++;
mSeconds=(int8)((intTimes/122.0703125)*100);
ScreenUpdate=1;
if ( Ticker < 65536 ) // If second has expired
{ Ticker += TIMER1_FREQUENCY; // Increment ticker by clocks per second
Seconds++; // Increment number of seconds
}
//--- Optional part start ---
if (Seconds == 60) {Minutes++; Seconds=0;
if (Minutes == 60) {Hours++; Minutes=0;
if (Hours == 24) {Days++; Hours=0;
if ( (Days == 29 && Month==2 && !IS_LEAP(Year)) // February in leap year
|| (Days == 30 && Month==2) // February in normal years
|| (Days == 31 && (Month==4 || Month==6 || Month==9 || Month==11 )) // All months with 30 days
|| (Days == 32) // All months with 31 days
) {Month++; Days=1;}
if (Month == 13) {Year++; Month=1;}
}}}
//--- Optional part end ---
}
void SetupMCU()
{
bit_set(UCFG,2);
bit_clear(ADCON0,0);
ADCON1=0b00001111;
CMCON=0b00000111;
bit_clear(CVRCON,7);
T0CON=0b01010111;
bit_clear(INTCON2,4);
T2CON=0b11111011;
TRISA=0b00000000;
TRISB=0b00000101;
Initialize_RTC();
enable_interrupts( GLOBAL );
i2c_init(TRUE);
}
void main()
{
SetupMCU();
//OLED_commands(init_sequence,sizeof(init_sequence)); //initialise the OLED
ssd_init();
unsigned int8 prev_second;
char text[15]; //temporary text buffer
OLED_CLS(); //clear the physical screen
while(TRUE)
{
if(ScreenUpdate==1)
{
ScreenUpdate=0;
OLED_gotoxy(1,0);
printf(OLED_putc,"%02u",Hours);
OLED_gotoxy(3,0);
strcpy(text,":");
OLED_text(text,strlen(text));
OLED_gotoxy(4,0);
printf(OLED_putc,"%02u",Minutes);
OLED_gotoxy(6,0);
OLED_text(text,strlen(text));
OLED_gotoxy(7,0);
printf(OLED_putc,"%02u",Seconds);
OLED_gotoxy(9,0);
OLED_text(text,strlen(text));
OLED_gotoxy(10,0);
printf(OLED_putc,"%02u",mSeconds);
}
if(prev_second!=Seconds)
{
prev_second=Seconds;
intTimes=0;
}
}
} |
and these are my OLED init codes (added to Ttelmah's h file, Ttelmah's original init lines are also working same, no big difference between )
Code: | void ssd_init()
{
i2c_start ();
i2c_write (SSDADDR); //select the display
i2c_write (COMMAND_ONLY); //we are sending a command
i2c_write(S_DISPLAYOFF);
i2c_stop();
i2c_start ();
i2c_write (SSDADDR); //select the display
i2c_write (COMMAND_ONLY); //we are sending a command
i2c_write(S_CHARGEPUMP);
i2c_write(0x14);
i2c_stop();
i2c_start ();
i2c_write (SSDADDR); //select the display
i2c_write (COMMAND_ONLY); //we are sending a command
i2c_write(S_MEMORYMODE);
i2c_write(0x10);
i2c_stop();
i2c_start ();
i2c_write (SSDADDR); //select the display
i2c_write (COMMAND_ONLY); //we are sending a command
i2c_write(0xA1);
i2c_stop();
i2c_start ();
i2c_write (SSDADDR); //select the display
i2c_write (COMMAND_ONLY); //we are sending a command
i2c_write(S_COMSCANDEC);
i2c_stop();
i2c_start ();
i2c_write (SSDADDR); //select the display
i2c_write (COMMAND_ONLY); //we are sending a command
i2c_write(S_SETCOMPINS);
i2c_write(0x12);
i2c_stop();
i2c_start ();
i2c_write (SSDADDR); //select the display
i2c_write (COMMAND_ONLY); //we are sending a command
i2c_write(S_SETCONTRAST);
i2c_write(0x1F);
i2c_stop();
i2c_start ();
i2c_write (SSDADDR); //select the display
i2c_write (COMMAND_ONLY); //we are sending a command
i2c_write(S_DISPLAYALLON_RESUME);
i2c_stop();
i2c_start ();
i2c_write (SSDADDR); //select the display
i2c_write (COMMAND_ONLY); //we are sending a command
i2c_write(S_NORMALDISPLAY);
i2c_stop();
i2c_start ();
i2c_write (SSDADDR); //select the display
i2c_write (COMMAND_ONLY); //we are sending a command
i2c_write(S_DISPLAYON);
i2c_stop();
} |
and here this is my screen with old code; https://ibb.co/fcZa4y
and this is the screen with new code https://ibb.co/jnQnHJ[/img] _________________ There is nothing you can't do if you try |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19622
|
|
Posted: Thu Jul 05, 2018 4:27 am |
|
|
The standard PICBASIC I2C code uses software I2C, not hardware.
The pin thresholds are fine for software I2C (which is why it works for you with this selected in CCS). It's only hardware I2C that introduces the 0.8*Vdd threshold.
Why worry?. Software I2C will be almost as fast (in fact may actually match the speed required), and works....
The SMBUS options do not work on all chips, so this may be the reason for yor problem. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9299 Location: Greensville,Ontario
|
|
Posted: Thu Jul 05, 2018 5:39 am |
|
|
As Mr. T says 'why worrry'. Most LCD modules are snail slow in operation compared to any PIC, so I doubt you really can benefit from using the internal I2C peripheral. As you've got a SW RTC, I'll assume you're updating the LCD module no more than once per second, so again the SW I2C will be fine.
Jay |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19622
|
|
Posted: Thu Jul 05, 2018 7:01 am |
|
|
Ignoring the other stuff, there is another 'screaming' thing:
Code: |
void main()
{
SetupMCU();
//OLED_commands(init_sequence,sizeof(init_sequence)); //initialise the OLED
ssd_init();
|
At 32mHz, you have just a few uSec between power being applied and sending the init to the display. The displays take some time to wake up. Typically quite a few mSec. In my example code I have:
Code: |
void main()
{
int8 temp=1;
delay_ms(250); //OLED takes time to wake
|
I suspect the start of the initialisation is being sent before the LED is ready to take it. Add a delay before trying to initialise the chip.
Update.
Just checked the Densitron data sheet for their displays based on this chip and they require 100mSec after Vcc. |
|
|
elcrcp
Joined: 11 Mar 2016 Posts: 62 Location: izmir / Turkey
|
|
Posted: Tue Jul 10, 2018 4:32 pm |
|
|
Ttelmah wrote: | The standard PICBASIC I2C code uses software I2C, not hardware.
The pin thresholds are fine for software I2C (which is why it works for you with this selected in CCS). It's only hardware I2C that introduces the 0.8*Vdd threshold.
Why worry?. Software I2C will be almost as fast (in fact may actually match the speed required), and works....
The SMBUS options do not work on all chips, so this may be the reason for yor problem. |
temtronic wrote: | As Mr. T says 'why worrry'. Most LCD modules are snail slow in operation compared to any PIC, so I doubt you really can benefit from using the internal I2C peripheral. As you've got a SW RTC, I'll assume you're updating the LCD module no more than once per second, so again the SW I2C will be fine. |
Thank you for your concern guys, it's nice to have your opinions as I see you very knowledgable. I was already ok with sw IIC , also noinit option already helped HW IIC, I was just curious and wanted to share my curiosity that why I wasn't able to run HW IIC any other way =)
I found the problem with 3 vertical lines and it's a bit embrassing I bought my oled from aliex. and it was said ssd1306 on the sellers' page after inspecting my old code I figure out that I altered driver codes to work it then I realised that these alterations are already defined as sh1106 in Ttelmah's driver code so my oled wasn't a ssd1306 but a sh1106
So, declaring sh1106 in my code solved vertical lines problem _________________ There is nothing you can't do if you try |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9299 Location: Greensville,Ontario
|
|
Posted: Tue Jul 10, 2018 5:54 pm |
|
|
I'm happy you got to the bottom of them 3 lines !!! It'd be nice if these itty, bitty PCBs had BIG readable info on them, might have saved you a few sleepless nights, 5-6 coffee pots....... we've all been there, so welcome to the 'club' ! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19622
|
|
Posted: Tue Jul 10, 2018 11:24 pm |
|
|
I think the phrase 'aaargh' applies....
The two pixel offset sounded familiar for the SH1106, but you seemed certain you had SSD1306's, so it didn't occur to me to suggest trying this!.
Glad you have it sorted.
Normally the SSD1306, is used for the smallest 0.9" displays. The SH1106 for the slightly larger ones (about 1.2"), then there is a higher power version of the SSD1306 the SSD1309 used for the really large displays (2.4" etc.). |
|
|
2xhp
Joined: 14 Jan 2019 Posts: 39
|
|
Posted: Tue Aug 20, 2019 10:38 pm |
|
|
Ttelmah wrote: | There is a basic and very simple reason it won't work.
Signal levels.
The SSD1306, is a 3.3v device. Though a lot of the boards have an internal 5v regulator, the signalling is 3.3v.
The hardware I2C, requires the inputs to go up to at least 0.8* the supply voltage of the PIC. So on a 2550 at 5v, they need to go to 4v. The 3.3v device does not allow this to happen.
|
Thank you Ttelmah and the original poster for providing this information. It helped end frustration I was having with a 18F25K80 that I couldn't get working with FORCE_HW and without having FORCE_SW. What's odd is that it worked for a while without either. Then, it's like I changed/added/removed something in the code and then i2c stopped working. Adding FORCE_SW got it working again. BTW, I'm running the device at 5 V.
Thanks again. This forum is the reason why I steer people to use the CCS compiler and what often keeps me happy. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19622
|
|
Posted: Wed Aug 21, 2019 12:42 am |
|
|
Quite a few of the hardware peripherals offer the option 'SMBUS'. This lowers
the input thresholds (amongst other things), allowing the hardware to
support a chip like this at 3.3v.
So try adding 'SMBUS' to your #use I2C setup. I do mention this in the
driver thread.
By 'the device' I presume you mean the PIC?. If you run the SSD1306 at
5v. Bang... 5v SSD1306 'modules', have a LDO voltage regulator on them and
run the chip at 3.3v. |
|
|
|
|
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
|