View previous topic :: View next topic |
Author |
Message |
bdeb
Joined: 05 Nov 2010 Posts: 42 Location: Sweden
|
18F16Q41 HW I2C trouble |
Posted: Wed Mar 16, 2022 10:39 am |
|
|
uP=PIC18F16Q41 (DeviceID=7560 rev=A005)
CCS=5.107
IDE=MPLAB X 5.50
Dear code gods, once again I need your help.
Just switched from a 16F18346 to the 18F16Q41 (64k ROM, 4k RAM, 3xUART, but same pin-out)
Timers are timing, LED's are blinking, and UART's are uarting happily.
Only thing not working is hardware I2C master:
#use i2c(MASTER, I2C1, FAST, CLOCK_SOURCE=FOSC, STREAM=I2CM)
#pin_select is done OK + even tried setting I2C pins open drain (+ set as inputs of course)
Tried other pins, but now using "default" B4/B6
Tried explicitly turning (otherwise unused) SPI2 off
Nothing in errata except "Start/StopFlags may be set when I2C enabled"
Tried different speeds, different clock sources, but nothing helps - uP stalls at first i2c_transfer_out() and I2C pins stay high
If commenting out pin_select and using bit-banged I2C on same pins, everything works...
I'm completely new to this uP (and non-MSSP I2C module), so before checking the assembler against registers, I hope to get some hints to what I've missed..
All the best:
/Björn |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19500
|
|
Posted: Wed Mar 16, 2022 10:58 am |
|
|
You need to use i2c_transfer, rather than i2c_start, read/write, stop.
The default is for the compiler to set up this I2C peripheral to support
this rather than allowing the older syntax.
A search here will find some examples of using this. |
|
|
bdeb
Joined: 05 Nov 2010 Posts: 42 Location: Sweden
|
|
Posted: Wed Mar 16, 2022 11:06 am |
|
|
Thank you Ttelmah,
Done that, that's why I stated using "i2c_transfer_out()"
All the best:
/Björn |
|
|
bdeb
Joined: 05 Nov 2010 Posts: 42 Location: Sweden
|
|
Posted: Wed Mar 16, 2022 11:14 am |
|
|
By the way, as a comment older discussions,
I use the exact number of bytes as "wCount" - adding 1 for address byte is not working.
Maybe different in this CCS version, or it comes from reading EEPROM where you actually have to send an internal address.
/Björn |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19500
|
|
Posted: Thu Mar 17, 2022 2:50 am |
|
|
No, 'adding 1' was only right at the start with this function.
Basically when initially released, the count number was the number put
into the actual chip's register. If you read the data sheet this is the total
bytes for the transaction. Quite quickly CCS changed this and made it the
number of bytes to send.
Post what you are actually using, and what chip you are trying to talk to.
Classic things would be using a 7bit address, not an 8bit address, but you
say that bit banged works, which suggests this is not the problem.
Are you sure your pull-ups are small enough to support 400KHz?.
Try with a slower rate. If it then works, this is the problem.
I2c_transfer_out, will only hang if you are not getting a NACK from the
device being talked to. Suggests something is wrong with the syntax
you are using. |
|
|
bdeb
Joined: 05 Nov 2010 Posts: 42 Location: Sweden
|
|
Posted: Thu Mar 17, 2022 3:25 am |
|
|
Thank you Ttelmah,
Pull-ups are excellent, since SCL/SDA never goes low if HW I2C..
Talking to SSD1306 which;
A-Worked with 16F18346 on same PCB
B-Works with software I2C
Code below hangs after setting LED on
Code: |
void ssd1306Command(uint8_t cmd)
{
uint8_t data[2];
data[0] = 0x00; //Co=0, D/C=0
data[1] = cmd;
output_bit(P_LEDRED,1);
I2C_Transfer_Out(I2CM, _i2caddr, data, 2);
output_bit(P_LEDRED,0);
} |
For good measure also made "empty" project, but scope still shows "flat-lining" pins.
Code: |
#include <18F16Q41.h> //Must come before other includes
#device ADC=10, *=16, WRITE_EEPROM=ASYNC, PASS_STRINGS=IN_RAM
//General
//#fuses DEBUG //ICD debugger
//Oscillator
#fuses PUT_64MS //Power Up Timer (PWRT)
#fuses noCLKOUT
//Resets
#fuses noMCLR //Master Clear pin enabled
#fuses WDT //Watch Dog Timer set in Setup()
#fuses noBROWNOUT //Brownout reset
#fuses noLVP
#fuses STVREN //Reset on stack over/under
//Protection
#fuses noPROTECT //CP Code read protect
#fuses noPPS1WAY
//Programming
#fuses NOLVP //No low Voltage Programming
//New 18xxQ
//#fuses noJTAG //JTAG DISabled
#fuses noBOOTBLOCK
#fuses ZCDDIS //Zero-cross detect circuit is disabled at POR
#fuses noMVECEN //Vector tables used for interrupts
//#FUSES IVT1WAY //IVTLOCKED bit can be cleared and set only once
#use delay(internal = 16MHZ)
//Syntax: #pin_select function=pin
//BIDIRECTIONAL - IN+OUT TO SAME PIN! (I2C can be moved - but will only have ST/TTL-levels on other pins)
#pin_select SCL1IN=PIN_B6
#pin_select SCL1OUT=PIN_B6
#pin_select SDA1IN=PIN_B4
#pin_select SDA1OUT=PIN_B4
#use i2c(MASTER, I2C1, FAST, CLOCK_SOURCE=FOSC, CLOCK_DIVISOR=4, STREAM=I2CM)
void main()
{
//I2C-pins must be set as inputs!
set_tris_b(0b11111111); //4=SDA1, 5=SDA2, 6=SCL1, 7=SCL2
//set_open_drain_b(0b01010000);
while(1)
{
int8 data[10] = {1,2,3,4,5,6,7,8,9};
I2C_Transfer_Out(I2CM, 0x78, data, 9);
delay_ms(1000);
}
}
|
Also tried different, speeds, clock sources, ad nauseam.
All the best:
/Björn |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19500
|
|
Posted: Thu Mar 17, 2022 5:35 am |
|
|
Key thing is that the software operation will be significantly slower.
Now two things may cause problems when you go faster. The first is the
pull-ups. A pull up that is entirely adequate at 100KHz, will not be low enough
for 400KHz. You are specifying 'FAST', which tells the hardware to select
400KHz. For a 3.3v system, I'd recommend 1.2KR for the pull ups.
The way to test the hardware at slow speed, is not to fiddle with the
clock sources, but select 'SLOW'.
Second is the chip's slew rate limiting. By default this in enabled. This will
limit the speeds that the outputs actually support.
Add the line
set_slow_slew_b(FALSE);
Then I'm not actually sure that this is a legitimate command sequence
for the SSD1306. You seem to be setting the page offsets, before the
chip has actually been started. Now software I2C, and hardware I2C
are very different in what will happen if the slave doesn't send a good
ACK on a byte.
Try sending something legitimate like 0xAF, which is the power on
command. |
|
|
bdeb
Joined: 05 Nov 2010 Posts: 42 Location: Sweden
|
|
Posted: Thu Mar 17, 2022 9:44 am |
|
|
Valid tips!
Tried with "SLOW" and no clock source/divider
Tried without "STREAM"
Tried set_slow_slew_b(FALSE)
Have 1.5k as pull-ups
Tried a correct SSD1306-command
Tried with complete setup that works on SW-I2C
Tried without display (only scope on SCL)
Still pins are stable high without even the shortest spike.
IMHO pins should show 8-9 clocks even if display does not "understand" what is sent?
Before sacrificing live goats to the I2C-gods, I've tried to study the listing.
Many years since last ASM, but found nothing obvious.
Don't know if it's good practice to post it here. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19500
|
|
Posted: Thu Mar 17, 2022 10:48 am |
|
|
OK.
I just tried compiling this dummy project.
Selected 'symbolic' and then looked at the listing.
A huge amount of the generated code is talking to SPI2, rather than the
I2C peripheral!...
It is testing status bits in the wrong peripheral.
Talk to CCS. Ask them for an example using the I2C peripheral, and
point out this oddity.
I suspect there is actually a fault in the implementation on this chip, and
the code is incorrect. |
|
|
bdeb
Joined: 05 Nov 2010 Posts: 42 Location: Sweden
|
|
Posted: Thu Mar 17, 2022 10:51 am |
|
|
Wow!
Felt something missing in disassembled listings after all..
Changed I2C pins to *outputs* and then it suddenly works!
Call me stupid, but I'm sure you normally should set I2C pins as inputs, so the CCS code or MSSP can handle direction?
Thanks for many excellent tips anyway!
/Björn |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19500
|
|
Posted: Thu Mar 17, 2022 11:06 am |
|
|
On 90% of PPS chips, you never have to touch TRIS. The peripheral
takes over complete control of the pins.
However this peripheral says:
The SDA and SCL pins must be configured as open-drain outputs.
The CCS code is correctly setting this in the initialisation,
but the TRIS for this is being overwritten by your TRIS line.
000D0: BSF 00A.6
000D2: BSF 00A.4 //These are the open collector settings
000D4: BCF TRISB.TRISB6
000D6: BCF TRISB.TRISB4
At the start of main.
But then your TRIS line sets the TRIS bits.
So you have shot yourself in the foot on this one.... |
|
|
bdeb
Joined: 05 Nov 2010 Posts: 42 Location: Sweden
|
|
Posted: Thu Mar 17, 2022 12:07 pm |
|
|
My feet are bullet-ridden..
What is safe and correct way for me to set TRIS for normal I/O pins on same port as any PPS peripheral?
Moving TRIS before any #use statement?
Did you find the info of setting to open drain output in any CCS doc's?
Excellent info - thank's again! |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9222 Location: Greensville,Ontario
|
|
Posted: Thu Mar 17, 2022 2:01 pm |
|
|
generally speaking, you do not need to use 'tris' or 'fast_io' statements.
The compiler's pretty smart and it'll handle the 'details' for you. The default should be 'standard_io().
In over 2 decades of using PICs, I've only needed them for very time sensitive, propriatory equipment and back then 20MHz was a real fast PIC. |
|
|
dyeatman
Joined: 06 Sep 2003 Posts: 1933 Location: Norman, OK
|
|
Posted: Thu Mar 17, 2022 3:18 pm |
|
|
I second what temtronic says. I almost always use default Standard_IO.
I have needed TRIS statements only twice. Once in an interrupt driven real
time application communicating with "factory floor" equipment and once
in a Rockwell PLC simulator I wrote for training equipment.
BTW, During development I also rarely specify more than 3-5 fuses (NOLVP,
NOWDT, etc.) since most fuses default to settings you would normally use.
Try to keep things simple and let the compiler do all the work! _________________ Google and Forum Search are some of your best tools!!!! |
|
|
bdeb
Joined: 05 Nov 2010 Posts: 42 Location: Sweden
|
|
Posted: Thu Mar 17, 2022 4:04 pm |
|
|
Thank you all for good advice - will make use of them.
All the best:
/Björn |
|
|
|