Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

PIC18F25K42 a work in progress....

Status
Not open for further replies.

granddad

Well-Known Member
Most Helpful Member
After Mike-K8lH post
https://www.electro-tech-online.com/threads/fyi-feature-rich-pic18fxxk42-series.151942/#post-1305813
I read the data sheet, intro / features for the PIC18FxxK42 series, seemed a useful device , but has a lot of add-ons that required understanding … Having skipped the PIC18's for the 24F's seemed a interesting challenge.... a development project was born...
phase1.jpg Screenshot42_1.jpg
I am no programmer , and dislike libraries ( the soft sort ). so intend to code some device drivers.
A couple of hours and the PIC18F25K42 had voltage and ICSP connections. A minimum C 'main' file ...and the scope confirmed CLKOUT pin at 16Mhz ( 64 / 4).


Phase 1 , oscillator , delays and serial ….
 
Last edited:
Yeah! Ok! Please keep me (us) posted.

Just got my 18F26K42 DIP-28 samples and anxious to try one of 'em out but I have a couple other projects to finish first.

Cheerful regards, Mike
 
( Please post if you spot errors / any corrections )

Oscillator …Internal 64Mhz was set in configuration . Note ..Default seems to be LVP = ON so MCLRE = EXTMCLR
Code:
#pragma config FEXTOSC = OFF // External Oscillator Selection (Oscillator not enabled)
#pragma config RSTOSC = HFINTOSC_64MHZ// Reset Oscillator Selection (HFINTOSC with HFFRQ = 64 MHz and CDIV = 1:1)
// CONFIG1H
#pragma config CLKOUTEN = OFF // Clock out Enable bit (CLKOUT function is disabled)
#pragma config PR1WAY = ON // PRLOCKED One-Way Set Enable bit (PRLOCK bit can be cleared and set only once)
#pragma config CSWEN = ON // Clock Switch Enable bit (Writing to NOSC and NDIV is allowed)
#pragma config FCMEN = ON // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor enabled)

// CONFIG2L
#pragma config MCLRE = EXTMCLR // MCLR Enable bit (If LVP = 0, MCLR pin function is port defined function; If LVP =1, RE3 pin fuction is MCLR)
#pragma config PWRTS = PWRT_OFF // Power-up timer selection bits (PWRT is disabled)
#pragma config MVECEN = ON // Multi-vector enable bit (Multi-vector enabled, Vector table used for interrupts)
#pragma config IVT1WAY = ON // IVTLOCK bit One-way set enable bit (IVTLOCK bit can be cleared and set only once)
#pragma config LPBOREN = OFF // Low Power BOR Enable bit (ULPBOR disabled)
#pragma config BOREN = SBORDIS // Brown-out Reset Enable bits (Brown-out Reset enabled , SBOREN bit is ignored)
// CONFIG2H
#pragma config BORV = VBOR_2P45 // Brown-out Reset Voltage Selection bits (Brown-out Reset Voltage (VBOR) set to 2.45V)
#pragma config ZCD = OFF // ZCD Disable bit (ZCD disabled. ZCD can be enabled by setting the ZCDSEN bit of ZCDCON)
#pragma config PPS1WAY = ON // PPSLOCK bit One-Way Set Enable bit (PPSLOCK bit can be cleared and set only once; PPS registers remain locked after one clear/set cycle)
#pragma config STVREN = ON // Stack Full/Underflow Reset Enable bit (Stack full/underflow will cause Reset)
#pragma config DEBUG = OFF // Debugger Enable bit (Background debugger disabled)
#pragma config XINST = OFF // Extended Instruction Set Enable bit (Extended Instruction Set and Indexed Addressing Mode disabled)
// CONFIG3L
#pragma config WDTCPS = WDTCPS_31// WDT Period selection bits (Divider ratio 1:65536; software control of WDTPS)
#pragma config WDTE = OFF // WDT operating mode (WDT Disabled; SWDTEN is ignored)
// CONFIG3H
#pragma config WDTCWS = WDTCWS_7// WDT Window Select bits (window always open (100%); software control; keyed access not required)
#pragma config WDTCCS = SC // WDT input clock selector (Software Control)
// CONFIG4L
#pragma config BBSIZE = BBSIZE_512// Boot Block Size selection bits (Boot Block size is 512 words)
#pragma config BBEN = OFF // Boot Block enable bit (Boot block disabled)
#pragma config SAFEN = OFF // Storage Area Flash enable bit (SAF disabled)
#pragma config WRTAPP = OFF // Application Block write protection bit (Application Block not write protected)
// CONFIG4H
#pragma config WRTB = OFF // Configuration Register Write Protection bit (Configuration registers (300000-30000Bh) not write-protected)
#pragma config WRTC = OFF // Boot Block Write Protection bit (Boot Block (000000-0007FFh) not write-protected)
#pragma config WRTD = OFF // Data EEPROM Write Protection bit (Data EEPROM not write-protected)
#pragma config WRTSAF = OFF // SAF Write protection bit (SAF not Write Protected)
#pragma config LVP = ON // Low Voltage Programming Enable bit (Low voltage programming enabled. MCLR/VPP pin function is MCLR. MCLRE configuration bit is ignored)
// CONFIG5L
#pragma config CP = OFF // PFM and Data EEPROM Code Protection bit (PFM and Data EEPROM code protection disabled)

#include <xc.h>
#include <string.h>
#include <stdlib.h>
#define _XTAL_FREQ 64000000

void OSC_init() , was coded for perhaps future changes.
Delays... XC8 Has some built in delays , for example __delay_ms(100); __delay_us(100);
Xtal frequency has to be defined as above ... A led / 470R was put on RB5 and the delays were tested … all good.

Before establishing UART1 Serial, ( 2 available ) the PSS feature has to be understood , turns out is remarkably comprehensive and straight forward... and possibly more advanced than other PIC's (Note only digital can be multiplexed via PSS).

Note PPS registers have a locking feature as per data sheet.

PSS...Output..
Step 1 . Where do I want the U1TX pin , RB6, RB7 are ICSP and the LED was on RB5, so RB4 .
The PPS ( output ) has registers RxyPPS , for the 8 bit of the A,B,C ports so RB4PPS points to pin, to assign U1TX the value from the RxyPSS table ( Data Sheet * ) is coded …. ( any B or C port pins could have been used )
[ 6'b01 0011 UART1 (TX) - B C ]

PSS...Input.
From Data Sheet
-------------------------------------------------------------------------------------
REGISTER 19-1: xxxPPS: PERIPHERAL xxx INPUT SELECTION
bit 7-5 Unimplemented: Read as ‘0’
bit 4-3 xxxPPS<4:3>: Peripheral xxx Input PORTx Pin Selection bits
See Table 19-1 for the list of available ports and default pin locations.
11 = Reserved
10 = PORTC
01 = PORTB
00 = PORTA

bit 2-0 xxxPPS<2:0>: Peripheral xxx Input PORTx Pin Selection bits

111 = Peripheral input is from PORTx Pin 7 (Rx7)
110 = Peripheral input is from PORTx Pin 6 (Rx6)
101 = Peripheral input is from PORTx Pin 5 (Rx5)
100 = Peripheral input is from PORTx Pin 4 (Rx4)
011 = Peripheral input is from PORTx Pin 3 (Rx3)
010 = Peripheral input is from PORTx Pin 2 (Rx2)
001 = Peripheral input is from PORTx Pin 1 (Rx1)
000 = Peripheral input is from PORTx Pin 0 (Rx0)
---------------------------------------------------------------------------------------

PPS Inputs are logically reverse of outputs. The peripheral input has the register and the value assigns the pin via the xxxPPS table. U1 receive PPS register = b'01011 for RB3 pin.

The respective TRIS bits were set in the IO_init()
Result so far …
Code:
void OSC_init(){ 
 
OSCFRQ=0x08; // 8=64 Mhz----0=1Mhz

}

void IO_init(){
ANSELA=0; // all digital
ANSELB=0; // all digital
ANSELC=0; // all digital
TRISBbits.TRISB5=0; // led
TRISBbits.TRISB4=0; // U1TX
TRISBbits.TRISB3=1; // U1RX
LATBbits.LATB5=1; // Turns the led on
}

void PPS_init(){
RB4PPS=0x13; // RB4 = UART1 TX.
U1RXPPS=0x0B; // RB3 = UART1 RX
}
( I come from machine code so use HEX a lot ! )

Break here to solder on a FTDI and swat up on UART ....

Note , After reading the mountain of K42 'Features' it is obvious I will not be able to cover everything. The idea was more of a launch pad, to see the mcu's project possibilities.
 
The K42 UARTs are again comprehensive in the settings, lots of interrupt flags and, options , so this is just a basic serial test of TX . RX and its interrupt to follow...

Perhaps not the best 'C' way to code initialization for UART 1...

All the registers from the UART section of the data sheet ( Its a good idea to read the 'Register and bit naming conventions') were set initially at 0; and a clean/build to see if correct. Then the basic 8,N,1,19200 bits were set.

Code:
void UART_init(){
U1BRG=207; // SET BAUD OF 19200 for 64Mhz Fosc.
//U1CON0
U1BRGS=0; // Baud speed clock / bits
U1ABDEN=0; // en auto baud
U1TXEN=1; // TX enable
U1RXEN=1; // RX enable
U1CON0bits.MODE=0;// 8,N,1 mode
//U1CON1
U1ON=1; // U1 serial port enabled
U1WUE=0; // Wake up bit enable
U1RXBIMD=0; // RX break int mode
U1BRKOVR=0; // Break override
U1SENDB=0; // send break
//

//U1CON2
U1RUNOVF=0; // Run in overflow
U1RXPOL=0; // RX polarity
U1CON2bits.STP=0; // =1 Stop bit,
U1C0EN=0; // Check sum mode bits
U1TXPOL=0; // TX polarity
U1CON2bits.FLO=0; // Flow ctrl off
//
U1ERRIR=0x00; // error flags
//
// U1ERRIE // UART1 INTERRUPT ENABLE
U1TXMTIE=0; // TX empty
U1PERIE=0; // Parity err
U1ABDOVE=0; // Auto baud detect
U1CERIE=0; // Check sum err
U1FERIE=0; // Frame err
U1RXBKIE=0; // Break received
U1RXFOIE=0; // FIFO overflow
U1TXCIE=0; // TX collision
PIE3bits.U1RXIE=0; //RX interrrupt enable
PIE3bits.U1TXIE=0; //TX interrrupt enable
PIE3bits.U1EIE=0; //Frame err interrrupt enable
PIE3bits.U1IE=0; //UART1 interrrupt enable
}

void serial_print(unsigned char *Pline,int nl)
{
int x = 0;
for ( x = 0; Pline[x]!=0x00 ; x++)
{
while (U1TXMTIF == 0) {}
U1TXB = Pline[x];
}
while (U1TXMTIF == 0 ) {}
if(nl) U1TXB = 0x0D ; // new line
}

'main' to do a simple serial test ... Hyper terminal screen shot … all good :)

Code:
int main(void) {
OSC_init();
IO_init();
PPS_init();
UART_init();
__delay_ms(100);
serial_print(" The answer to Life the Universe and Everything ? ",1);
serial_print("= K42 ",0);

while(1){}
return ;
}

I have always found I2C useful, either saving IO pins , or linking boards.
I have a small 2x16, I2C LCD wired up in my parts box, so next is K42 and I2C, the aim is to send from HyperTerminal to the LCD . I have a load of I2C code written for other PIC's so will try to import and see how it goes...
K42ftdi.jpg ht42.jpg
 
Fun stuff-- will definitely keep an eye on this thread. I too tend to have an aversion to libraries when I have the option to set the registers myself. Perhaps more due to stubbornness in doing things myself than any belief that I am a better programmer or less prone to mistakes than the people making the libraries. :p

Not sure if this would be of much use to anyone, but I did a quick-and-dirty development board layout in Eagle a little while back for the PIC16F1778, which just so happens to have an identical pinout to the PIC18F2#K42 chips. I recently ordered a couple 26K42 TSSOP chips, so I may give one a try on a leftover test board to see if it works.
 

Attachments

  • PIC1778.brd
    103.6 KB · Views: 232
  • PIC1778.sch.txt
    566.7 KB · Views: 230
  • board.png
    board.png
    94.5 KB · Views: 276
  • schematic.png
    schematic.png
    47.3 KB · Views: 289
A quick update to the post above: I soldered up a couple of 26K42s onto my 16F1778 boards, and I can confirm that they seem to work and program just fine. I haven't checked all the pins, but they should be the same. I was able to bang out a crude blink program using a quick-and-dirty delay function on timer 2.

I'm sure my code is likely to have errors or just generally be sloppy, so feel free to suggest corrections where necessary. I am using an 8MHz crystal with the x4 PLL for 32MHz, because the 16F1778s I was using with these boards were only rated to 32MHz instead of 64MHz, so you will need to change T2CON to 0x70 for a 64MHz Fosc.

Code:
// CONFIG1L
#pragma config FEXTOSC = HS     // External Oscillator Selection (HS (crystal oscillator) above 8 MHz; PFM set to high power)
#pragma config RSTOSC = EXTOSC_4PLL// Reset Oscillator Selection (EXTOSC with 4x PLL, with EXTOSC operating per FEXTOSC bits)

// CONFIG1H
#pragma config CLKOUTEN = OFF   // Clock out Enable bit (CLKOUT function is disabled)
#pragma config PR1WAY = ON      // PRLOCKED One-Way Set Enable bit (PRLOCK bit can be cleared and set only once)
#pragma config CSWEN = ON       // Clock Switch Enable bit (Writing to NOSC and NDIV is allowed)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor enabled)

// CONFIG2L
#pragma config MCLRE = EXTMCLR  // MCLR Enable bit (If LVP = 0, MCLR pin is MCLR; If LVP = 1, RE3 pin function is MCLR )
#pragma config PWRTS = PWRT_OFF // Power-up timer selection bits (PWRT is disabled)
#pragma config MVECEN = ON      // Multi-vector enable bit (Multi-vector enabled, Vector table used for interrupts)
#pragma config IVT1WAY = ON     // IVTLOCK bit One-way set enable bit (IVTLOCK bit can be cleared and set only once)
#pragma config LPBOREN = OFF    // Low Power BOR Enable bit (ULPBOR disabled)
#pragma config BOREN = SBORDIS  // Brown-out Reset Enable bits (Brown-out Reset enabled , SBOREN bit is ignored)

// CONFIG2H
#pragma config BORV = VBOR_2P45 // Brown-out Reset Voltage Selection bits (Brown-out Reset Voltage (VBOR) set to 2.45V)
#pragma config ZCD = OFF        // ZCD Disable bit (ZCD disabled. ZCD can be enabled by setting the ZCDSEN bit of ZCDCON)
#pragma config PPS1WAY = ON     // PPSLOCK bit One-Way Set Enable bit (PPSLOCK bit can be cleared and set only once; PPS registers remain locked after one clear/set cycle)
#pragma config STVREN = ON      // Stack Full/Underflow Reset Enable bit (Stack full/underflow will cause Reset)
#pragma config DEBUG = OFF      // Debugger Enable bit (Background debugger disabled)
#pragma config XINST = OFF      // Extended Instruction Set Enable bit (Extended Instruction Set and Indexed Addressing Mode disabled)

// CONFIG3L
#pragma config WDTCPS = WDTCPS_31// WDT Period selection bits (Divider ratio 1:65536; software control of WDTPS)
#pragma config WDTE = OFF       // WDT operating mode (WDT Disabled; SWDTEN is ignored)

// CONFIG3H
#pragma config WDTCWS = WDTCWS_7// WDT Window Select bits (window always open (100%); software control; keyed access not required)
#pragma config WDTCCS = SC      // WDT input clock selector (Software Control)

// CONFIG4L
#pragma config BBSIZE = BBSIZE_512// Boot Block Size selection bits (Boot Block size is 512 words)
#pragma config BBEN = OFF       // Boot Block enable bit (Boot block disabled)
#pragma config SAFEN = OFF      // Storage Area Flash enable bit (SAF disabled)
#pragma config WRTAPP = OFF     // Application Block write protection bit (Application Block not write protected)

// CONFIG4H
#pragma config WRTB = OFF       // Configuration Register Write Protection bit (Configuration registers (300000-30000Bh) not write-protected)
#pragma config WRTC = OFF       // Boot Block Write Protection bit (Boot Block (000000-0007FFh) not write-protected)
#pragma config WRTD = OFF       // Data EEPROM Write Protection bit (Data EEPROM not write-protected)
#pragma config WRTSAF = OFF     // SAF Write protection bit (SAF not Write Protected)
#pragma config LVP = ON         // Low Voltage Programming Enable bit (Low voltage programming enabled. MCLR/VPP pin function is MCLR. MCLRE configuration bit is ignored)

// CONFIG5L
#pragma config CP = OFF         // PFM and Data EEPROM Code Protection bit (PFM and Data EEPROM code protection disabled)

// CONFIG5H

#include <xc.h>
#include <PIC18F26K42.h>

volatile int t = 0;

void init(){
 
    TRISB = 0x7E; //B7 as output
    LATB = 0x80;
 
    T2CLK = 0x01; //clock source = Fosc/4, Fosc = 32MHz = 8MHz*4xPLL
    //T2HLT = 0x00; //rising edge triggered, free running with software gate
    T2CON = 0x60; //T2 off, /64 prescaler, 1:1 postcaler (125KHz)
    T2PR = 125; //125KHz/125=1KHz or 1ms delay
 
    T2TMR=0;     //clear TMR2 counter
    TMR2IF = 0; //clear TMR2 interrupt flag
    GIE = 1; //global interrupts enabled
    TMR2IE = 1; //T2 interrupt enabled
 
}

void delay_millis(int time){
    t=time;
    T2TMR=0;
    T2ON=1;
    while(t>0);
    T2ON=0;
}

void main(void) {
 
    init();
 
    while(1){
        delay_millis(1000);
        LATB ^= 0x80;
    }
 
    return;
}

void __interrupt (irq(IRQ_TMR2)) TMR2_ISR(void){
    TMR2IF = 0;
    t--;
}
 

Attachments

  • IMG_0597_resized.jpg
    IMG_0597_resized.jpg
    225.1 KB · Views: 272
Hi JLNY , that is a nifty little board. I have not achieved I2C yet ( still having to do other things ) . Was wondering how to do Interrupt on K42 , looks like I have more stuff to learn . on PIC24 and XC16 was "simple-ish " ....
 
Hehe, thanks! Yeah, I don't have much experience with vectored interrupts on PICs, so the syntax is still a bit alien to me. I mostly just copied the example on the datasheet.
 

Attachments

  • datasheet examples.png
    datasheet examples.png
    32.6 KB · Views: 283
:facepalm: I just now see there are 19 registers associated with I2C ..... See you next year :eek:

Edit... Well , I2C has all changed from my previous pic stuff with the K42 , no waits just load I2C TX the buffer and that's it ... or is it ...
 
Last edited:
In my experience I2C has always been about timing ... likewise LCD has always been about timing , so my I2C + Lcd mix is ... you guessed it... Timing x 2 !
I have a result on the display of (1) sending the I2C Lcd init string ... and (2) sending a message string .....

CEG_2.jpg


And I have a timing issue as I sent "ABCDE1234" . Init appears to work... characters not..

It took some time to get here , the MC document TB3159 I2C Communications with Hardware Protocol Acceleration on 8-Bit PIC® Microcontrollers , was a help as it has examples , ( K42 data sheet = contradictions about I2C port pins open collector settings ). But think I need to find a slower I2C clock.... this is @ 500Khz all other options I think are faster ...( wrong)

Edit since realised 500khz = 125khz SCL Have reached a sometimes it does then it does not...
 
Last edited:
Result !
For a 'C' amateur like me the K42 does I2C somewhat easier than previous PIC I2C peripherals , re checking ACKSTAT , and start bits etc...

My eventual result.... to initialise a I2C-2x16 LCD and send a message.
I2C initialise... Lots of bits here .. cut it down to the very minimum .

Code:
void I2C_init(){    // ---7---|---6---|---5---|---4---|---3---|---2---|---1---|---0---| 
                   //   --      --      --      --      --          BTO<2:0>           
I2C1BTO =0;         //    0       0       0       0       0       0       0       0
                   //   --      --      --      --      --          CLK<2:0>         
I2C1CLK =0x03;      //    0       0       0       0       0       0       1       1   
                   //  CNTIE   ACKTIE   --     WRIE    ADRIE   PCIE    RSCIE    SCIE
I2C1PIE =0;         //    0       0       0       0       0       0       0       0
                   //  CNTIF   ACKTIF          WRIF    ADRIF   PCIF    RSCIF    SCIF
I2C1PIR =0;         //    0       0       0       0       0       0       0       0
                   //   --     BTOIF   BCLIF  NACKIF    --     BTOIE   BCLIE   NACKIE
I2C1ERR =0;         //    0       0       0       0       0       0       0       0
                   //  BFRE     SMA     MMA      R       D      --      --      --
I2C1STAT0 =0;       //    0       0       0       0       0       0       0      0
                   //  TXWE     --     TXBE     --      RXRE   CLRBF    --      RXBF
I2C1STAT1 =0;       //    0       0       0       0       0       0       0      0
                   //   EN      RSEN     S     CSTR      MDR        MODE<2:0>
I2C1CON0 =0x04;     //    0       0       0       0       0       1       0      0
                   // ACKCNT   ACKDT  ACKSTAT  ACKT     --      RXOV     TXU     CSD
I2C1CON1 =0x80;     //    1       0       0       0       0       0       0      0
                   //  ACNT    GCEN     FME     ADB*     SDAHT<3:2>     BFRET<1:0>
I2C1CON2  =0x31;    //    0       0       1       1       0       0       0      1
I2C1ADR0  =0;       // ADR<7:0>                *addr buf disabled
I2C1ADR1  =0;       // ADR<7:1> 
I2C1ADR2  =0;       // ADR<7:0>
I2C1ADR3  =0;       // ADR<7:1> 
I2C1ADB0  =0;       // ADB<7:0>
I2C1ADB1 =0;        // ADB<7:0>
I2C1CNT  =0;        // CNT<7:0>
I2C1RXB  =0;        // RXB<7:0>
I2C1TXB =0;         // TXB<7:0>
I2C1CON0bits.EN = 1; // enable I2C module
}

If the auto address buffer is enabled * I2C1CON2 then start bit sequence is different to my code , I could not get this to work, so went with it disabled . I2C clock is derived from a 500khz osc. /4 = 125khz.

Also not sure to load TX buffer then wait on empty flag or wait then load after start bit . As the address is still in TX buffer , did the later.

HW , set up.. I have 2k2 pull-ups on data and clock but could have had internal .
I also mistakenly called IO_init before PPS_init. This gave a short spike to lines before I2C took control, not good :(

Code:
void IO_init(){
   ANSELA=0; // all digital
   ANSELB=0; // all digital
   ANSELC=0; // all digital
   TRISBbits.TRISB5=0;
   LATBbits.LATB5=1;   //led on
   // Configure the pins as Open-drain
   ODCONCbits.ODCC3 = 1;
   ODCONCbits.ODCC4 = 1;
   // Set the I2C levels
   RC3I2Cbits.TH = 1;
   RC4I2Cbits.TH = 1;
   // Configure the pins as Outputs
   TRISCbits.TRISC3 = 0;
   TRISCbits.TRISC4 = 0;
 }

void PPS_init(){   // do PPS before IO_init();
   //UART1
   RB4PPS=0x13; // RB4 = UART1 TX.
   U1RXPPS=0x0B; // RB3 = UART1 RX
   // Set RC4 for SDA1
   RC4PPS = 0x22;
   I2C1SDAPPS = 0x14;
   // Set RC3 for SCL1
   RC3PPS = 0x21;
   I2C1SCLPPS = 0x13;

}

Basic K42 sending of I2C data... ( address buffer disabled )
load count with bytes to send.
Load device address for write ! to the TX buffer
Set the start bit.
loop
Wait for the TX buffer empty
load the data byte
loop if count not zero
when count is zero and buffer empty stop bit is automatically sent

Any errors will set a interrupt flag for an error routine .

This Blue / white Midas I2C display has extended controls similar to the standard lcd HD44780 chip .

Code:
void LCD_init()
{
  unsigned int x;
  I2C1CNT=0x08;     // load I2C1CNT with number of INIT bytes   
  I2C1TXB = 0x7C;   // load addr
  I2C1CON0bits.S=1; // set start
  for(x=0;x<8;x++){
  while(!I2C1STAT1bits.TXBE){}  // wait for buffer empty
  I2C1TXB = LCD_init_data[x];
  }
  __delay_ms(200);
}   

void send_I2C_lcd(char *message, unsigned char Lpos)  // send message at line + location
{
  LCD_loc(Lpos) ; 
  unsigned int x;
  unsigned int sendlen;
  sendlen= strlen(message);
  I2C1CNT=sendlen + 1;        // load I2C1CNT with number of bytes   
  I2C1TXB=0x7C;               // load I2C1TXB with device address
  I2C1CON0bits.S=1;     // set start
  while (!I2C1STAT1bits.TXBE);  // wait for buffer empty
  I2C1TXB = 0x40;               // load control byte
  for(x=0;x<sendlen;x++){
  while (!I2C1STAT1bits.TXBE); // wait for buffer empty
  I2C1TXB = message[x];        // load next message byte
  __delay_ms(1);
  } 
}

void LCD_loc(unsigned char Lpos) // line 1 0x80 to 0x8F
{                                                      // line 2 0xC0 to 0xCF   
  unsigned int x;
  I2C1CNT=0x02;      //  load I2C1CNT with number of bytes   
  I2C1TXB=0x7C;       // load I2C1TXB with device address
  I2C1CON0bits.S=1; // set start
  while (!I2C1STAT1bits.TXBE); // wait for buffer empty
  I2C1TXB = 0x00;        // load next byte
  while (!I2C1STAT1bits.TXBE); // wait for buffer empty
  I2C1TXB = Lpos;        // load next byte
  __delay_ms(1);
}

K42 Phase 2...or is it 5 !

RX from UART , this requires an interrupt..... more new ground .
 
The UART interrupt stumped me for some time , seems you cannot clear the U1RX interrupt flag without first reading the receive buffer , then it all behaves as it should , and keyboard input from the PC, gets via FTDI chip into PIC.. then out to I2C display... job done …K42 UART's have a FIFO but not looked at that yet, I2C reading has not been included either . I have a DS1338 RTC module , so Phase 6 = to put the time / date on the display...

phase5.jpg

Interrupt stuff....

Code:
void __interrupt(irq(IRQ_U1RX)) U1RX_ISR()
{
LATBbits.LATB5=1;   //led on
Serial_in=1;        // flag main to send to lcd
byte_in=U1RXB;     / get the byte
PIR3bits.U1RXIF = 0; // Clear the interrupt flag
}

void __interrupt(irq(default)) DEFAULT_ISR()
{
while(1); // Unhandled interrupts go here
}

void INTERRUPT_Initialize(void)
{
 INTCON0bits.GIEH=1; // Global int enable
 INTCON0bits.GIEL=0; //   
 INTCON0bits.IPEN=0; // Interrupt priority not used

 PIE3bits.U1RXIE=1;  // Enable U1RX INTERRUPT
 IPR3bits.U1RXIP=0;  //   

 IVTBASEU=0x00;     // default 0x000008
 IVTBASEH=0x00;     // 
 IVTBASEL=0x08;     // 
}
 
...seems you cannot clear the U1RX interrupt flag without first reading the receive buffer , then it all behaves as it should lag...

I think that behavior is pretty common across the PIC lines. For example, RCIF in the 16F versions is read only and is cleared by reading the buffer, i.e., clearing the interrupt event.

John
 
john.... agree, I think I have been coding [ read RX then reset flag ] (pic16,18, 24 ) that way and not realised it .... the MC example I pasted was a TMR0 interrupt and it reset the flag on first line, I just changed the names etc hence my difficulty...
 
Time and Date from DS1338 module. (This has battery backup)

Adding the DS1338 module to the board, caused a load of problems. And it took me some time with the Bus Pirate and a laptop and scope , to realise how dopey , slipshod I can be ! I have always used 2k2 pullup on I2C , I have seen other “standards” like 4k7 ,10k etc , I am thinking it depend on clock speed and line capacitance , to cut this post short the DS... ACK to write data was sometimes being seen as NACK by the PIC, (scope showed a ACK at a highish level )and resulted in all sorts of automatic stops / restarts etc... because I actually had two sets of pullups installed ! A pair of 2k2 on my board and 10k's on the RTC … result 1k7 ish. After the penny dropped , I had loads of fun sending control and data back and forth....Had to change the I2C init to address buffer enabled , as below.

The smart C guys will see lots of better code solutions , but this was my no fancy definitions time, to make sure it worked as required. The signal out from the DS will be used to trigger an interrupt to update the display (60 sec) . This could be done several ways , but as K42 has TxCLKIPPS ( timer x clock input to a pin ), thinking be a good way to achieve it with timer 3 on port RB2. Edit decided better with timer 0..

Note to self … assume nothing !


phase61.jpg





Code:
// info
//char RTC_I[]={0x00,0x00,0x38,0x15,0x04,0x18,0x10,0x17,0x10};
//Ds1338  start-reg   sec  min  hrs  day# Day  Mth  Year out

void RTC_get_DateTime(char *Time_date)
{
  unsigned int x;
  I2C1CNT=0x01;     // load I2C1CNT with number of bytes
  I2C1ADB1 = 0xD0; //RTC_write;   // load addr
  I2C1TXB = 0x00;   // write 0x00 start addr and tx byte
  while(!I2C1PIRbits.PCIF); // WAIT FOR STOP.
  I2C1CNT=0x08; // load I2C1CNT with number of bytes
  I2C1ADB1= 0xD1;  // RTC_read;     // load addr
  I2C1CON0bits.S=1; // set start
  for(x=0;x<8;x++){  // loop while reading
  while(!I2C1STAT1bits.RXBF); // RX test
   *Time_date = I2C1RXB; // update data
   Time_date++; // advance pointer
   }
}

{
 // fast mode , address buffer enabled , T2 is bus timeout

void I2C_init(){    // ---7---|---6---|---5---|---4---|---3---|---2---|---1---|---0---|
                   //   --      --      --      --      --          BTO<2:0>          
I2C1BTO =0x01;      //    0       0       0       0       0       0       0       1
                   //   --      --      --      --      --          CLK<2:0>        
I2C1CLK =0x03;      //    0       0       0       0       0       0       1       1  
                   //  CNTIE   ACKTIE   --     WRIE    ADRIE   PCIE    RSCIE    SCIE
I2C1PIE =0;         //    0       0       0       0       0       0       0       0
                   //  CNTIF   ACKTIF          WRIF    ADRIF   PCIF    RSCIF    SCIF
I2C1PIR =0;         //    0       0       0       0       0       0       0       0
                   //   --     BTOIF   BCLIF  NACKIF    --     BTOIE   BCLIE   NACKIE
I2C1ERR =0;         //    0       0       0       0       0       0       0       0
                   //  BFRE     SMA     MMA      R       D      --      --      --
I2C1STAT0 =0;       //    0       0       0       0       0       0       0      0
                   //  TXWE     --     TXBE     --      RXRE   CLRBF    --      RXBF
I2C1STAT1 =0;       //    0       0       0       0       0       0       0      0
                   //   EN      RSEN     S     CSTR      MDR        MODE<2:0>
I2C1CON0 =0x04;     //    0       0       0       0       0       1       0      0
                   // ACKCNT   ACKDT  ACKSTAT  ACKT     --      RXOV     TXU     CSD
I2C1CON1 =0x80;     //    1       0       0       0       0       0       0      0
                   //  ACNT    GCEN     FME     ADB*     SDAHT<3:2>     BFRET<1:0>
I2C1CON2  =0x21;    //    0       0       1       0       0       0       0      1
I2C1ADR0  =0;       // ADR<7:0>                *enabled
I2C1ADR1  =0;       // ADR<7:1>
I2C1ADR2  =0;       // ADR<7:0>
I2C1ADR3  =0;       // ADR<7:1>
I2C1ADB0  =0;       // ADB<7:0>
I2C1ADB1 =0;        // ADB<7:0>
I2C1CNT  =0;        // CNT<7:0>
I2C1RXB  =0;        // RXB<7:0>
I2C1TXB =0;         // TXB<7:0>
I2C1CON0bits.EN = 1; // enable I2C module
}


void LCD_init()
{
  unsigned int x;
  I2C1ADB1 = 0x7C;   // load addr  7c
  I2C1CNT=0x08;     // load I2C1CNT with number of INIT bytes  
  for(x=0;x<8;x++){
  I2C1TXB = LCD_init_data[x];  // send data
   while(!I2C1STAT1bits.TXBE); // wait for buffer empty
   }
  while(!I2C1PIRbits.PCIF); // WAIT FOR STOP.
  __delay_ms(100);
}  


void DS1338_init()
{
  unsigned int x;
  I2C1CNT=0x09;     // load I2C1CNT with number of INIT bytes
  I2C1ADB1 = 0xD0;   // load addr  7c
  for(x=0;x<9;x++){
  I2C1TXB = RTC_I[x];
   while(!I2C1STAT1bits.TXBE); // wait for buffer empty
   }
  while(!I2C1PIRbits.PCIF); // WAIT FOR STOP.
  __delay_ms(1);
}  


void send_I2C_lcd(char *message,unsigned char Lpos)  /// send message at line + location
{
  LCD_loc(Lpos);
  unsigned int x;
  unsigned int sendlen;
  sendlen= strlen(message);
  I2C1ADB1=0x7C;               // load I2C1TXB with device address
  I2C1CNT=sendlen + 1;        // load I2C1CNT with number of bytes  
  while (!I2C1STAT1bits.TXBE); // wait for buffer empty
  I2C1TXB = 0x40;               // load control byte
  for(x=0;x<sendlen;x++){
  while (!I2C1STAT1bits.TXBE); // wait for buffer empty
  I2C1TXB = message[x];        // load next message byte
  }
  while(!I2C1PIRbits.PCIF); // WAIT FOR STOP.
  __delay_ms(1);
}

void LCD_loc(unsigned char Lpos) // line 1 0x80 to 0x8F
{                                // line 2 0xC0 to 0xCF  
  unsigned int x;
  I2C1ADB1=0x7C;       // load I2C1TXB with device address
  I2C1CNT=0x02;      //  load I2C1CNT with number of bytes  
  while (!I2C1STAT1bits.TXBE); // wait for buffer empty
  I2C1TXB = 0x00;        // load next byte
  while (!I2C1STAT1bits.TXBE); // wait for buffer empty
  I2C1TXB = Lpos;        // load next byte
  __delay_ms(1);
}
 
Last edited:
I have learnt quite a bit trying to get the time to roll over from a T0 (timer 0 ) interrupt ,
Scenario
I2C , IO , T0 , display Interrups all initialised OK
Date and time fetched OK
Displayed OK
T0 interrupt roll over set to match count of 59 DS is output 1 Hz ( PIC weak pull up )
60 second later T0 Interrupt set ..
Date and time fetched OK

Display I2C Crash......... K42 has stop condition flag set writing the LCD address.
Lots of tweaking ... depression... midnight oil...
Previous post .... I had pullups too low , Realised now 10k pullup not good either , with a mpu running at 16 MIPS.
Conclusion After I2C fetch of date time , with 10k installed bus needs several u_sec to recover to high level , put in a __delay_us(100) at end of ... void RTC_get_DateTime(char *Time_date) now works fine. Scope also shows very poor square SCL / SDA so going back to 2k2 .

Edit well decided to try 4k7 pullups as i had a couple of smd , the display seems to have trouble in pulling the Data low for the ACK... I also reduced the delay to 50 usec ...anyway is working. Read from RTC
DS1Z_2.jpg

Write to display
DS1Z_1.jpg
 
Last edited:
I am aware I have coded I2C for what 'Should' happen broke the rules by not coding for what 'Could' happen. Given the quantity of I2C condition interrupt flags it will be possible to write a function(s) to cover all of the 'Coulds " and recover from errors.

I have since realised it may have been better on this occasion to read the LCD Busy flag before sending to it, as timing seem to vary quiet a bit with this display .

Phase 7 I will attack K42's AD , I have a "Dfrobot" moisture sensor , still waiting for some volts to be applied ,
 
I'm a bit confused as to why you're using a timer interrupt? - is it just to read the clock chip at regular intervals?.

Easiest solution, and what I do, is configure the DS to output a 1 sec pulse, and feed that to an I/O interrupt pin - this makes it simple to automatically update the display every time the seconds tick over.
 
Nigel, Fair point , I am not displaying the seconds , and the project is just to evaluate the K42 series, int from T0 was just an exercise , the PPS and the 7 (4) timers with this pic are feature rich , I am usually 'playing' with Pic24 trying to find a a pin or a clock frequency. One thing I will add , I have this project / IDE v4.0 etc running on linux mint , and it seems way better than on windows. point 2 Do Rigol really charge 180 quid to licence I2c trigger and decode, My 30 hours nearly up :( ....
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top