|
|
View previous topic :: View next topic |
Author |
Message |
thefloyd
Joined: 02 Sep 2009 Posts: 46
|
PIC18F46K22 & DS3234/SPI woes |
Posted: Fri Aug 09, 2013 8:17 pm |
|
|
OK folks, I've got a good one (I'm good and stumped!).
Setup:
PIC18F46K22 with an SPI EEPROM, SPI RTC (DS3234), SPI ethernet (Wiz820io). 16mhz external crystal with 4xPLL (64mhz system clock).
In SPI mode 0 I can speak to the EEPROM and Wiz820io as expected. I can set registers on the wiznet module and read them back. I can read things from my EEPROM. So I know SPI works..
The RTC doesn't speak mode 0, only 1 or 3. So when I'm getting ready to drive the RTC's SS line low, I switch to SPI mode 1 (or 3 - doesn't matter) and delay for some time (currently 50ms, but have used more/less with no difference). I've made all sorts of attempts at getting reliable communication working but to no avail.
Out comes the old logic analyzer - and this is where it gets fun.
Following the datasheet, I write 0x80 to the DS3234, followed by seconds, minutes, hours. For testing, I used values 2, 5, 3. I can see these go to the RTC just fine.
I then read back the values - write 0x00, then 3 spi_read(0) calls. I see my test values on my logic analyzer (2, 5, 3) on the MISO line - which is connected DIRECTLY to the pins on my PIC. But reading back and printing my values gives me 3, 4, 3. Wait, what?!
What could I be missing here? I'm literally snooping the hardware right at the PIC pins and see the values just fine. My SPI setup works just fine with other peripherals. This seems to rule hardware out. The code is as simple as can be, see below..
Code: |
void ds3234_test(void) {
unsigned int8 h,m,s;
setup_spi(SPI_MASTER | SPI_MODE_1 | SPI_CLK_DIV_16);
delay_ms(50);
output_low(RTC_CS);
s = 2;
m = 5;
h = 3;
spi_write(0x80);
spi_write(bin2bcd(s));
spi_write(bin2bcd(m));
spi_write(bin2bcd(h));
output_high(RTC_CS);
delay_ms(100);
output_low(RTC_CS);
spi_write(0x00);
s = spi_read(0) & 0x7F;
printf("s: %u ", s);
s = bcd2bin(s);
m = spi_read(0) & 0x7F;
printf("m: %u ", m);
m = bcd2bin(m);
h = spi_read(0) & 0x3F;
printf("h: %u\r\n", h);
h = bcd2bin(h);
printf("%u:%u:%u\r\n",h,m,s);
output_high(RTC_CS);
setup_spi(SPI_MASTER | SPI_MODE_0 | SPI_CLK_DIV_16);
}
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Sat Aug 10, 2013 2:05 am |
|
|
Try disabling the SPI, then changing mode.
I think you will find some options can't be switched, while the peripheral is enabled. Hence you end up sampling on the wrong edge.
So:
Code: |
setup_SPI(SPI_DISABLED);
delay_cycles(1);
setup_spi(SPI_MASTER | SPI_MODE_1 | SPI_CLK_DIV_16);
delay_ms(1); //really shouldn't need to be more than a few clock times
output_low(RTC_CS);
|
and the same when going the other way.
If you look at the 'sequence' recommended in the data sheet, enabling is always the last thing done. The CCS code assumes it is being called at the beginning with the peripheral disabled.
Best Wishes |
|
|
thefloyd
Joined: 02 Sep 2009 Posts: 46
|
|
Posted: Sat Aug 10, 2013 7:02 am |
|
|
Ttelmah wrote: | Try disabling the SPI, then changing mode.
I think you will find some options can't be switched, while the peripheral is enabled. Hence you end up sampling on the wrong edge.
So:
Code: |
setup_SPI(SPI_DISABLED);
delay_cycles(1);
setup_spi(SPI_MASTER | SPI_MODE_1 | SPI_CLK_DIV_16);
delay_ms(1); //really shouldn't need to be more than a few clock times
output_low(RTC_CS);
|
and the same when going the other way.
If you look at the 'sequence' recommended in the data sheet, enabling is always the last thing done. The CCS code assumes it is being called at the beginning with the peripheral disabled.
Best Wishes |
Thanks for your prompt reply and explanation. I read through it this morning with my coffee and immediately had a "d'oh - why didn't I think of that?" moment.
I came downstairs and hooked my hardware up and reprogrammed everything and... same result. I've tried both mode 1 and mode 3, tried disabling with various ms and cycle delays immediately after.. same thing, no joy. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Sat Aug 10, 2013 7:12 am |
|
|
Rats.
It was the only 'obvious' idea I had!....
I'd suggest taking a 'step back'.
First just setup the SPI to talk to the DS3254. Prove that this works without the other SPI settings, It could be that there is something simply wrong with the mode being used. It looks like the PIC is reading the data on the wrong clock edge.
Then, once this is working, use something like MPLAB SIM, and put a breakpoint at the place the first DS3254 transaction occurs. Note down all the SPI config register settings at this point.
Now add the other SPI setup, and repeat the test.
Are the register settings the same?.
If not, then the problem becomes working out how to get them the same.
Best Wishes |
|
|
thefloyd
Joined: 02 Sep 2009 Posts: 46
|
|
Posted: Sat Aug 10, 2013 7:21 am |
|
|
Good idea. This is a new piece of hardware and I've been testing each piece bit by bit (first the eeprom, then the wiz820io, finally the RTC). What I didn't do was disable each piece (the first two worked so well, so I didn't need to!) when moving on to the next.
I'll create a fresh project and target only the RTC and then go from there if it works.. Thanks for the advice! |
|
|
thefloyd
Joined: 02 Sep 2009 Posts: 46
|
|
Posted: Sat Aug 10, 2013 7:47 am |
|
|
OK, so stripped down to just RTC access I'm still seeing the same thing:
sample main function:
Code: |
void main()
{
unsigned int8 i;
BYTE hr,min,sec;
output_high(W5200_RS);
output_high(W5200_CS);
output_high(RTC_CS);
output_high(EEPROM_CS);
// setup_spi(SPI_MASTER | SPI_MODE_0 | SPI_CLK_DIV_16);
setup_spi(SPI_MASTER | SPI_MODE_1 | SPI_CLK_DIV_16);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256); //1.0 s overflow
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);
delay_ms(100);
printf("Hello, world!\r\n");
while(true) {
ds3234_test();
delay_ms(1000);
}
}
|
ds3234_test() function:
Code: |
void ds3234_test(void) {
unsigned int8 h,m,s;
// setup_spi(SPI_DISABLED);
// delay_cycles(1);
// setup_spi(SPI_MASTER | SPI_MODE_3 | SPI_CLK_DIV_16);
// delay_ms(50);
output_low(RTC_CS);
s = 2;
m = 5;
h = 3;
spi_write(0x80);
spi_write(bin2bcd(s));
spi_write(bin2bcd(m));
spi_write(bin2bcd(h));
output_high(RTC_CS);
delay_ms(100);
output_low(RTC_CS);
spi_write(0x00);
s = spi_read(0) & 0x7F;
printf("s: %u ", s);
s = bcd2bin(s);
m = spi_read(0) & 0x7F;
printf("m: %u ", m);
m = bcd2bin(m);
h = spi_read(0) & 0x3F;
printf("h: %u\r\n", h);
h = bcd2bin(h);
printf("%u:%u:%u\r\n",h,m,s);
output_high(RTC_CS);
// setup_spi(SPI_DISABLED);
// delay_cycles(1);
// setup_spi(SPI_MASTER | SPI_MODE_0 | SPI_CLK_DIV_16);
}
|
I *really* want to blame the hardware but I can't. My PIC is a 40 pin PDIP and I've literally got the logic analyzer on the clk/miso/mosi/cs pins of the PIC itself. I see the right values. Other SPI hardware works.
Anyone have any other suggestions before I throw my prototype out the window? :D |
|
|
thefloyd
Joined: 02 Sep 2009 Posts: 46
|
|
Posted: Sat Aug 10, 2013 7:51 am |
|
|
.. and as quickly as that I'm on to something..
Change SPI MODE to 3 and it looks like it may be working.. I'll keep quiet for now ;) |
|
|
thefloyd
Joined: 02 Sep 2009 Posts: 46
|
|
Posted: Wed Aug 14, 2013 9:32 am |
|
|
So, after taking a couple of days off and fiddling around (rebuilt a whole new circuit on a breadboard) I've finally gotten somewhere (and I found out my hardware isn't faulty .
First - it seems 4mhz is too fast to talk to the DS3234. Other peripherals on my SPI bus work at 4mhz but the DS3234, even when re-done on a breadboard with just the PIC and RTC is unreliable. The datasheet lists 4mhz as the max, but since I don't NEED 4mhz for my RTC and my communications seem reliable at 1mhz I'm not inclined to pursue this too much further.
NOW here is where the fun begins. Both my prototype PCB as well as my breadboard setup are PIC18F46K22, PDIP 40 pin, 16mhz external crystal. A working setup is:
#use delay(clock=16MHz,crystal=16MHz)
setup_spi(SPI_MASTER | SPI_MODE_3 | SPI_CLK_DIV_16);
However, no matter WHAT I do, this does not work:
#use delay(clock=64MHz,crystal=16MHz)
setup_spi(SPI_MASTER | SPI_MODE_3 | SPI_CLK_DIV_64);
What happens? Well, on the latter setup my code freezes at my calls to spi_write/spi_read. If I change the second setup to SPI_CLK_DIV_16 all of my SPI peripherals work and I just read wrong data from the RTC (both on the isolated breadboard setup as well as my prototype pcb).
So what gives? Why does a 1mhz SPI clock derived from a 16mhz system clock and SPI_CLK_DIV_16 work, whereas a 1mhz SPI clock derived from a 64mhz system clock and SPI_CLK_DIV_64 freezes? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Wed Aug 14, 2013 10:59 am |
|
|
If you are not using T2, try setting the SSP to clock from this, and programming that for 1MHz.
I'd traditionally suspect an erratum at this point, but none listed on this chip about this.
The symptom suggests it is not actually clocking.
What compiler version are you on?. The current releases seem to set the right values into the SSP registers, but it might be that yours puts a corrupted number into these.
Best Wishes |
|
|
thefloyd
Joined: 02 Sep 2009 Posts: 46
|
|
Posted: Wed Aug 14, 2013 11:08 am |
|
|
Can't get T2 to clock slow enough.. the slowest is T2_DIV_BY_16 which, at 64mhz, is no different than SPI_CLK_DIV_16.
I've tried compiler version 5.011 and 4.141.
Same thing both times. My program freezes at spi_write() calls. |
|
|
thefloyd
Joined: 02 Sep 2009 Posts: 46
|
|
Posted: Wed Aug 14, 2013 11:18 am |
|
|
An interesting tidbit - I seem to hang when I call spi_write(0) or spi_write(0x00).
I recompiled and ran under the debugger. I can get as far as spi_write(0x8E) and the next spi_write(0x00) I never return from.
Code: |
printf("\r\ninit!\r\n");
output_low(RTC_CS);
spi_write(0x8E);
spi_write(0x00);
output_high(RTC_CS);
delay_ms(10);
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Wed Aug 14, 2013 2:52 pm |
|
|
It's the output of T2 used.
It is fed with Fosc/4, so you can do /4, /16, then maximum of /256 to give a maximum possible division of 16384.....
To give 500KHz, you'd want:
setup_timer_2(T2_DIV_BY_1,31,1);
1MHz
setup_timer_2(T2_DIV_BY_1,15,1);
Clock/4, over the second number+1.
4KHz
setup_timer_2(T2_DIV_BY_16,255,1);
Clock /4, /16, /256 = 3906Hz.
Best Wishes |
|
|
thefloyd
Joined: 02 Sep 2009 Posts: 46
|
|
Posted: Wed Aug 14, 2013 3:11 pm |
|
|
Aha. Thanks! That makes more sense..
Sadly, it doesn't seem to help much. Changing my setup_timer_2 statement to EACH of the examples you gave yields the same results as I was seeing with a 4mhz clock - garbage data. Polling the RTC once a second yields several seconds with the same number followed by a 2-3 second jump (same for minutes! seconds wrap around and minutes stay the same and then jump after a 2nd or 3rd cycle!).
I've found a few things that get reliable communication:
- changing the system voltage to 5V (from 3.3v), this yields stable communication with the RTC all the way up to 4mhz (DS3234 datasheet says 4mhz should be fine > 2.7v, so something isn't working here..)
- disabling the 4xPLL and using the system at 16mhz, with SPI_CLK_DIV_16 yielding a 1mhz SPI clock
What should work - 64mhz system clock with SPI_CLK_DIV_64. It doesn't. I'm dumbfounded. |
|
|
thefloyd
Joined: 02 Sep 2009 Posts: 46
|
|
Posted: Wed Aug 14, 2013 8:22 pm |
|
|
Out came the logic analyzer again and double checked everything.
The RTC is definitely returning the expected/proper data. The different SPI clock frequencies are definitely working.
I think at this point I can narrow this down to the 18F46K22 operating at 64mhz and 3.3v. Raising the system voltage works. Lowering the clock speed works. What I don't understand is WHY this isn't working and why it's only affecting the PIC's ability to get data from the DS3234 RTC. My PCB prototype has an SPI eeprom and a wiz820io ethernet module and they all work just fine. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Thu Aug 15, 2013 12:33 am |
|
|
I'm having a 'niggling' little memory....
What you are describing, is exactly what happens on some of the faster chips that have an erratum regarding loading the buffer immediately after the flag says the last transfer has completed.
A typical example is:
<http://ww1.microchip.com/downloads/en/DeviceDoc/80387a.pdf>
Basically on these, where you have:
Code: |
spi_write(0x80);
spi_write(bin2bcd(s));
|
You technically have to add a tiny delay, and then try writing, then test the WCOL bit, and if this is set, write again... Rather complex.
However it has in the past worked for me, by adding a slightly longer delay between writes. So something like:
Code: |
spi_write(0x80);
delay_cycles(16);
spi_write(bin2bcd(s));
|
It only happens when you run off /64, or the T2 clock, which would agree with what you are seeing.
Try with the delay, between any adjacent pairs of transfers, and see what happens.
Best Wishes |
|
|
|
|
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
|