PIC24F EPMP with EDS

General questions about Firewing...

PIC24F EPMP with EDS

Postby jmusselman64 » Wed Dec 06, 2017 5:13 am

First thanks all for the earlier replies to my question regarding the Firewing pin aliases. That part is working great, thanks to a few fine folks on this forum!
Now on to the next problem..

I'm using the PIC24F curiosity board, and am trying to use the EPMP to drive a standard character LCD.
I've come across some 'C' code that shifts the LCD operation into a timed que process, freeing up the processor for other tasks while dealing with the slow LCD. It maps the LCD command/Data registers in the Extended Data space of the processor's RAM.
Here's the snippet that defines the port:

Code: Select all
#define CS_BASE 0x2     UL   //start address for LCD registers in EDS
/*
   Address allocation for LCD registers
   First line defines LCD command register, third line defines LCD data register and the second line is used to align data register to 16-bit word boundary.
   This is done so that RS will clear while accessing LCDCMD and set while accessing LCDDATA – PIC24 is a 16-bit MCU and each memory address addresses 2-byte word.
*/
__eds__ uint8_t __attribute__((noload, section("epmp_cs1"), address(CS_BASE))) LCDCMD __attribute__((space(eds)));
__eds__ uint8_t __attribute__((noload, section("epmp_cs1"), address(CS_BASE))) LCDALIGN __attribute__((space(eds)));
__eds__ uint8_t __attribute__((noload, section("epmp_cs1"), address(CS_BASE))) LCDDATA __attribute__((space(eds)));


I need to convert this to Firewing. I'm guessing I'd do something like this:
Code: Select all
public LCDDAT as uinteger absolute &H28000
public LCDCMD as uinteger absolute &H28001

I don't quite get what the middle line of code with the 'LCDALIGN' definition does; Rather, I understand what it does, just not how to implement it in Firewing.

When I send something to that address, I'd also assume I have set the DSWPAG for writes and the DSRPAG for reads.

Trying to make sense of it all..

Thanks much,
Jerry
jmusselman64
 
Posts: 24
Joined: Thu Jan 22, 2015 1:01 am

Re: PIC24F EPMP with EDS

Postby Coccoliso » Wed Dec 06, 2017 9:55 am

Hi,

if you need to pilot a HD44780 it seems to me that you can find something already written in the Swordfish forum .. if I'm not mistaken :roll:
User avatar
Coccoliso
 
Posts: 177
Joined: Sat Sep 27, 2014 10:02 am

Re: PIC24F EPMP with EDS

Postby Jerry Messina » Wed Dec 06, 2017 11:12 am

The C code defines the EDS addresses as uint8_t, so you'd want to use 'byte' instead of 'uinteger' type.

The __eds__ mapping puts LCDCMD at CS_BASE+0, LCDALIGN at CS_BASE+1, and LCDDATA at CS_BASE+2, so adjust the firewing 'absolutes' to match.

For anyone interested the method is shown at https://www.circuitsathome.com/mcu/driv ... ster-port/
Jerry Messina
 
Posts: 280
Joined: Thu Feb 14, 2013 10:16 am

Re: PIC24F EPMP with EDS

Postby jmusselman64 » Wed Dec 06, 2017 3:47 pm

Coccoliso: Thanks for the reply. Yes, I've used 'standard' LCD routines in the past, and know they are effective. This particular routine is supposed to be very time-efficient. The application I'll be using it in will need all the processor time I can get.
And, it's always good to learn something outside your comfort zone!

Mr. Messina: Yes, that should have been a BYTE declaration....it slipped by. Thank you!
And, yes, (full disclosure) the link you posted is the code by Oleg Mazurov I'm trying to port. Very well documented, and if I was better in C and had more time, I'd probably just use it.

If/when I get it working I'll post it as a module for Firewing.
jmusselman64
 
Posts: 24
Joined: Thu Jan 22, 2015 1:01 am

Re: PIC24F EPMP with EDS

Postby jmusselman64 » Thu Dec 07, 2017 4:20 pm

OK.. Here's what I've got so far. It's far from complete, and needs to be tidied up a lot. I just converted as it was written.
The interrupt call isn't correctly implemented yet; I'm just trying to get it to compile.
Using Microchip XC16, v1.34 and the latest Firewing

I'm getting this error:
[ERROR] : [GCC] inconsistent operand constraints in an 'asm'

If I comment out line 378: (Which I now see isn't numbered in this code insertion..sorry)
Code: Select all
LCDCMD = lcd_init_seq(lcd_init_p)   //place a byte directly on the LCD bus

..it compiles.

Any ideas where to look, or where to find more debugging info?

Here's the entire program:

Code: Select all
/* PIC24FJ LCD DRIVER USING EPMP (FIREWING VERSION)
/* 12/6/17 - J. MUSSELMAN
/*
/*PORTED FROM 'C' CODE BY OLEG MAZUROV AT  https://www.circuitsathome.com/mcu/driving-a-character-lcd-using-pic24-enhanced-parallel-master-port/
/*Comments from the article have been copied into the program
/*
/* HD44780-compatible LCD timing tuning routine. Coded for PIC24F MCU with EPMP peripheral */
/* uses external 12MHz crystal to produce 32MHz clock */
/* Many other oscillators can be used with coresponding fuse change */
/* uses timer interrupt to consume the LCD queue */
/* compiled/tested using Microchip C30 compiler ver.3.31 with no optimization */
/* The LCD pin to MCU pin assignment identical to Microchip Explorer 16 board */

/*
When developing for HD44780 we need to deal with 3 different times. First is the timing of the display part – the screen we see. LCD glass is very slow.
When we attempt to update the screen faster than say twice a second the symbols become blurry and pale.
The fastest display in my collection still looks OK when updated at 4Hz rate (250ms), while most others are twice as slow.

On the other hand, display data bus timing is many times faster.
In order to write to the display we first need to set RS, RW and data lines, wait a little, then assert E line, wait some more and then de-assert it.
If we are reading from the display we will also need to wait a little more after de-asserting E before we can read the data on the bus.
Total bus cycle length is ~2.5us, which is 200 000 times less than the update rate of typical LCD glass.
This time is pretty short but the MCU is still faster – a PIC24F clocked at 32MHz has an instruction cycle of 62.5ns and in 2.5us it will be able to execute 40 instructions.
Therefore, no matter how simple it looks, it is preferable not to bit-bang the bus.

The third timing we need to deal with is command execution time. All but two LCD commands have stated execution time of 40us.  Two slow commands – Clear and Home require 1.64ms to finish.
Those are datasheet numbers, in reality the fast command on a modern display may finish in as low as 10us and slow commands on an old display can take as much as 3.5ms,
depending on the age and the particular “HD44780-compatible” controller used. It is about 100 times faster than the glass.
*/

'module LCD_PIC24F_EPMP
Device = 24FJ128GA204           'Select processor

clock = 32               'Define system clock

config CONFIG4 = {DSWDTPS_DSWDTPS1F,
                  DSWDTOSC_LPRC,
                  DSBOREN_OFF,
                  DSWDTEN_OFF,
                  DSSWEN_OFF,
                  PLLDIV_PLL4X,
                  I2C1SEL_DISABLE,
                  IOL1WAY_OFF}   
                 
Config CONFIG3 = {WPFP_WPFP127,
                  SOSCSEL_ON,
                  WDTWIN_PS25_0,
                  PLLSS_PLL_FRC,
                  BOREN_ON,
                  WPDIS_WPDIS,
                  WPCFG_WPCFGDIS,
                  WPEND_WPENDMEM}                 

Config CONFIG2 = {POSCMD_NONE,
                  WDTCLK_LPRC,
                  OSCIOFCN_OFF,
                  FCKSM_CSDCMD,
                  FNOSC_FRCPLL,
                  ALTCMPI_CxINC_RB,
                  WDTCMX_WDTCLK,
                  IESO_OFF}

Config CONFIG1 = {WDTPS_PS32768,
                  FWPSA_PR128,
                  WINDIS_OFF,
                  FWDTEN_OFF,
                  ICS_PGx1,
                  LPCFG_OFF,
                  GWRP_OFF,
                  GCP_OFF,
                  JTAGEN_OFF}
                                                         
/*
   The program can me modified for differnt CPUs and crystal speeds. Also, it is possible to fine tune the times.
   Simply change the intervals for short/long commands and see if the screen still looks good. The definitions look like this:
*/

//timer period for fast and slow commands
const BSP_TMR3_PER_SHORT = 799     //Timer period for fast commands
//#define BSP_TMR3_PER_SHORT = 2000
const BSP_TMR3_PER_LONG = 35000   //Timer3 period for slow commands
//#define BSP_TMR3_PER_LONG = 55000

/*
   To feed the data to LCD I’m using a simple one-way circular buffer AKA queue, defined like this:
   To make queue management easier the size must be a power of 2. The size of 256 saves couple instruction cycles; if memory size is more important the buffer size can be decreased.
   LcdTx_Head is moved by a producer of the data and LcdTx_Tail is moved by the consumer, as will be explained later in the article. LcdTx_Buf is the buffer itself.
*/

//LCD buffer size - must be power of 2
#define LCD_TX_BUFSIZE =256
#define LCD_TX_BUFMASK =(LCD_TX_BUFSIZE - 1)

#if (LCD_TX_BUFSIZE and LCD_TX_BUFMASK) = 1
   #error "LCD Tx Buffer size is not a power of 2"
#endif

//LCD buffer
dim LcdTx_Buf(LCD_TX_BUFSIZE) as byte
dim LcdTx_Head as byte
dim LcdTx_Tail as byte

const CMDFLAG as byte = &H0f        //a character code inserted before a command in LCD queue
const CS_BASE as ulong = &H02      //start address for LCD registers in EDS

/*
   Address allocation for LCD registers
   First line defines LCD command register, third line defines LCD data register and the second line is used to align data register to 16-bit word boundary.
   This is done so that RS will clear while accessing LCDCMD and set while accessing LCDDATA – PIC24 is a 16-bit MCU and each memory address addresses 2-byte word.
*/
'__eds__ uint8_t __attribute__((noload, section("epmp_cs1"), address(CS_BASE))) LCDCMD __attribute__((space(eds)));
'__eds__ uint8_t __attribute__((noload, section("epmp_cs1"), address(CS_BASE))) LCDALIGN __attribute__((space(eds)));
'__eds__ uint8_t __attribute__((noload, section("epmp_cs1"), address(CS_BASE))) LCDDATA __attribute__((space(eds)));
public LCDCMD as byte absolute &H8000
public LCDALIGN as Byte absolute &H8001
public LCDDAT as byte absolute &H8002

// commands
const LCD_CLEARDISPLAY =        &H01
const LCD_RETURNHOME =          &H02
#define LCD_ENTRYMODESET =        &H04
#define LCD_DISPLAYCONTROL =      &H08
#define LCD_CURSORSHIFT =         &H10
#define LCD_FUNCTIONSET =         &H20
#define LCD_SETCGRAMADDR =        &H40
#define LCD_SETDDRAMADDR =        &H80

// flags for display entry mode
#define LCD_ENTRYRIGHT=          &H00
#define LCD_ENTRYLEFT=           &H02
#define LCD_ENTRYSHIFTINCREMENT= &H01
#define LCD_ENTRYSHIFTDECREMENT= &H00

// flags for display on/off control
#define LCD_DISPLAYON=           &H04
#define LCD_DISPLAYOFF=          &H00
#define LCD_CURSORON=            &H02
#define LCD_CURSOROFF=           &H00
#define LCD_BLINKON=             &H01
#define LCD_BLINKOFF=            &H00

// flags for display/cursor shift
#define LCD_DISPLAYMOVE=         &H08
#define LCD_CURSORMOVE=          &H00
#define LCD_MOVERIGHT=           &H04
#define LCD_MOVELEFT=            &H00

// flags for function set
#define LCD_8BITMODE=            &H10
#define LCD_4BITMODE=            &H00
#define LCD_2LINE =              &H08
#define LCD_1LINE =              &H00
#define LCD_5x10DOTS=            &H04
#define LCD_5x8DOTS =            &H00

/*
I will now explain the timer interrupt service routine (ISR) which consumes the queue and sends data to LCD using EPMP. The routine gets called each time the timer overflows.
Two distinctive time intervals are used – one for fast commands and data and the second one for slow commands Clear and Home.
When the queue is empty the timer is stopped since there is no reason to run it anymore. When data is placed into the queue the timer is started again.
Commands in the queue are preceded by a special “flag”: the ISR tracks that and sends data to either command or data register.
The following (rather long) listing demonstrates how all this is coded. The interesting lines are explained after the listing.
 
    Line 2 The interrupt priority set to the lowest value. We don’t need to serve it very fast. This definition is used during timer initialization shown later in the article
    Line 3 Compiler instruction stating that the function is the ISR
    Line 5 In order to “remember” that the previous byte in the queue was a command flag the ISR is written as a simple two state state machine. This variable holds the state
    Line 9 Advance the “tail” of the buffer
    Line 15 The state machine
    Line 19 Checking if the next byte in the queue is a command
    Lines 21-23 If yes, change state to the next one. Also, since nothing has been sent to the LCD we don’t need to wait and can read the next byte immediately. An interrupt occurs when TMR3 equals PR3 and the timer counter runs all the time until stopped explicitly. Therefore if number of counts in TMR3 is less than the number of instruction cycles necessary to finish the ISR the interrupt will occur immediately after return. It is also possible that the byte just read was the last one in the queue – in this case the application will never have a chance to place another byte in the queue. For this reason, the number of remaining timer counts should be assigned generously
    Lines 25-30 If no, then write the byte to LCD data register and set the timer period to “short command” interval
    Line 31 Jump past the closing brace of the switch statement
    Line 33 Next state, which sends the command
    Line 35 Sending the command
    Line 38 Checking if the command is long or short. Long commands have command codes 1 and 2, the next command code is 4
    Lines 40-47 Load the period register with the value necessary for the command delay. This is the beauty of 16-bit timers – when clocked directly from the system clock the range for such timer is from 62.5ns to 4ms
    Line 49 Switching to the previous state to analyze next byte in the queue
    Lines 55-59 Stop the timer if the queue is empty. Next time it starts the counting will continue from where it was turned on so even if the next character will be placed in the queue immediately after returning from the ISR the delay condition will be satisfied
*/

//Timer interrupt
 
const TIMER3_ISR_PRIO = 1

sub _T3Interrupt()
'void  __attribute__((__interrupt__, auto_psv)) _T3Interrupt(void)

 dim state as byte = 0

    IFS0.8 = 0    //clear T3 interrupt flag

    LcdTx_Tail +=1

#if LCD_TX_BUFMASK < 255
    LcdTx_Tail = (LcdTx_Tail and LCD_TX_BUFMASK)
#endif

   DSWPAG = CS_BASE                    //SET EXTENDED DATA SPACE PAGE

   select case state
     
      case 0:    //read byte, send data
         if(LcdTx_Buf(LcdTx_Tail) = CMDFLAG) then     //next byte is a command
            TMR3 = (PR3 - 20)                   //shorter cycle. Must be set longer than the execution time of the rest of the ISR
            state = 1
         else
           
            LCDDAT = LcdTx_Buf(LcdTx_Tail)    //send data
            PR3 = BSP_TMR3_PER_SHORT
         end if

      case 1:    //send command
            LCDCMD = LcdTx_Buf(LcdTx_Tail)    //send command
           
            if(LcdTx_Buf(LcdTx_Tail) < 4 ) then  //slow command
               PR3 = BSP_TMR3_PER_LONG
            else
               PR3 = BSP_TMR3_PER_SHORT
            end if

            state = 0
   end select

    if(LcdTx_Head = LcdTx_Tail ) then    //stop the timer
        T3CON.15= 0
    end if
end sub

sub MCU_init()
   
   //REFOCONbits.ROEN = 1;  //uncomment to observe clock output on RS - breaks LCD interface

   /* All pins output */
    // PIC24FJ256GB206 doesn't have port A
  TRISB = 0
  TRISC = 0
   
  /* Turn off analog */
  ANSB = 0
  ANSC = 0
 
  //reset LCD buffer head and tail
  LcdTx_Head = 0   
  LcdTx_Tail = 0
 
  //The following piece of code shows the initialization of this timer:
   //Setup timer 3 for LCD
  T3CON  = &H0000                     /* Use Internal Osc (Fcy), 16 bit mode, no prescaler */
  PR3    = BSP_TMR3_PER_SHORT         /* set the period */
  TMR3   = PR3 - 1                    /* one count before interrupt */
  IPC2 = (IPC2 and &HFFF8) and TIMER3_ISR_PRIO            /* set Timer 3 interrupt priority */
  IFS0.8  = 0                          /* clear the interrupt for Timer 2 */
  IEC0.8  = 1                          /* enable interrupt for Timer 2 */
  //we don't want to start this timer yet..
  //The timer will be turned on when the first byte is placed into the queue and since at this time the LCD is ready to take it the TMR3 is set one cycle less than PR3 so the interrupt will happen almost immediately.
 
  /*
   Set PMP Registers
   PMCON2 uses default settings
  */
  PMCON2 = &H0000

  // register bits are set separately for better presentation
  // in case space is needed they can be combined in a single assignment per register
  PMCON1 = &H0300
  'PMCON1bits.ADRMUX = 0         // address is not multiplexed
  'PMCON1bits.MODE = 3        // master mode
  'PMCON1bits.CSF = 0         // PMCS1 pin used for chip select 1, PMCS2 pin used for chip select 2
  'PMCON1bits.ALMODE = 0      // "smart" address strobes are not used
  'PMCON1bits.BUSKEEP = 0     // bus keeper is not used
  'PMCON1bits.IRQM = 0        //interrupt at the end of of rd/wr cycle

  /*
   Here I enable READ and WRITE signals and set address latch signal (which is not used) delay to the minimum.
   Later, I will combine READ and WRITE to a single RW and make E(NABLE) out of WRITE.
  */
  PMCON3 = &HC000
  'PMCON3bits.PTWREN = 1      // enable write(rd/WR) strobe port
  'PMCON3bits.PTRDEN = 1      // enable read(enable) strobe port
  'PMCON3bits.AWAITM = 0      // set address latch pulses width to 1/2 Tcy
  'PMCON3bits.AWAITE = 0      // set address hold time to 1/4 Tcy
 
  // Here I enable a single address line which will serve as LCD RS signal switching between command and data registers.
  PMCON4 = &H0001            // PMA0 address line is enabled
 
  /*
   The PMCS1CF register defines the behaviour of lines used as LCD RW and E lines.
   They are tied to EPMP CS signal so we need it active even though we don’t need CS to drive an LCD.
   Even if CS signal is not used we still need to configure it since READ/WRITE and ENABLE depend on it.
   SM combines READ and WRITE into a single pin and sets separate ENABLE.
*/
  PMCS1CF = &H4700
  'PMCS1CFbits.CSDIS = 0      // enable CS function
  'PMCS1CFbits.CSP = 1           // CS1 polarity
  'PMCS1CFbits.CSPTEN = 0     // disable CS port
  'PMCS1CFbits.BEP = 1           // byte enable polarity
  'PMCS1CFbits.WRSP = 1          // write strobe polarity - enable active high
  'PMCS1CFbits.RDSP =1           // read strobe polarity, READ high, WRITE low
  'PMCS1CFbits.SM = 1            // read/write and enable strobes
  'PMCS1CFbits.PTSZ = 0          // data bus width is 8 bit
 
  /*
   PMCS1BS sets starting extended memory address for EPMP. An attentive reader may have noticed that this is the same address used in LCDCMD and LCDDADA definitions. I’m using the &H20000 address which is the default;
   Technically, in this case I don’t need to set PMCS1BS since it contains the same value at power-on – I just wanted to show how it’s done.
  */
  PMCS1BS = (CS_BASE>>8)     // CS1 start address
 
 /*
   Configure timing of all important signals.
   These times were chosen conservatively. In my experience, for modern displays they can be made much shorter and even set to zero. The speed advantage is very small and only appears when LCD reads are performed.
   The necessary bits are contained in PMCS1MD register, as follows:
*/
   PMCS1MD = &H00E3
  'PMCS1MDbits.ACKM = 0       // PMACK is not used
  // The device timing parameters. Set the proper timing
  // according to the device used (the timing macros are defined in the hardware profile)
  //time in Tcy. One cycle is 62.5ns for 32MHz clock
  'PMCS1MDbits.DWAITB = 3      // time from RS,RW to E
  'PMCS1MDbits.DWAITM = &H08   //E strobe length - 450ns by spec
  'PMCS1MDbits.DWAITE = 3          //time from E to valid data
  /**/
   
   PMCON1.15 = 1        // enable the module
end sub

//Place a byte in the LCD queue. This is done by LcdSendByte()function:
/* Places a byte to the LCD queue. Can be used to send data */
/*
    Line 3 This is done to be able to “see” the tail index of the buffer
    Line 9 If the buffer is full, wait
    Line 11 Place a byte into the queue
    Line 13 Advance the buffer “head”
    Line 15 Turn on the timer in case it was turned off by ISR
*/
sub LcdSendByte( sendbyte as char)
   dim tmphead as byte = LcdTx_Head + 1

   #if LCD_TX_BUFMASK < 255
      tmphead = tmphead and LCD_TX_BUFMASK
   #endif

   while(tmphead = LcdTx_Tail)   //this line blocks - keep buffer large enough
      LcdTx_Buf(tmphead) = sendbyte
      LcdTx_Head = tmphead
      T3CON.15 = 1    //start the timer in case it was stopped
   end while
   
end sub

//This function can be used to send character data to the LCD. To send a command we need to insert a flag before it. The LcdSendCmd() function does just that:
/* Places a command flag to the LCD queue followed by a byte */

'void LcdSendCmd(uint8_t cmd) {
sub LcdSendCmd(cmd as byte)   
   LcdSendByte(CMDFLAG)   //insert command flag symbol
   LcdSendByte(cmd)         
end sub

// We now have everything necessary to use the LCD. The following is a main() routine which first initializes both MCU and LCD and then fills first four screen positions with BSD-stype “rolling stick” character.
// Note that initialization commands are placed directly into LCDCMD – this is because during initialization wait time between commands must be made much larger.
sub main()

   //initialization commands for standard 16x2 LCD
   const FUNC_SET  = LCD_FUNCTIONSET or LCD_8BITMODE or LCD_2LINE or LCD_5x8DOTS
   const DISP_CTRL = LCD_DISPLAYCONTROL or LCD_DISPLAYON or LCD_CURSOROFF or LCD_BLINKOFF
   const ENTRY_MODE  = LCD_ENTRYMODESET or LCD_ENTRYLEFT

   const  lcd_init_seq(4) as byte  = {FUNC_SET, DISP_CTRL, LCD_CLEARDISPLAY, ENTRY_MODE}   //initialization sequence
   dim lcd_init_p as byte   //pointer to the first element
   const rollchar as string = "/-\|"
   dim roll_idx as byte
 
   const ROLL_IDX_MASK = &H03
 
   MCU_init()
   
   For lcd_init_p = 0 to 3       
      delayms(30)
      LCDCMD = lcd_init_seq(lcd_init_p)   //place a byte directly on the LCD bus
   next lcd_init_p
   
/*   ....bypassed for testing....
   while(true)   //output rolling characters in the first 4 posirions of the display
     
         dim i as byte
     
         LcdSendCmd(LCD_RETURNHOME) //Home the screen - slow command
     
         for i = 0 to 4
            LcdSendByte(cbyte(rollchar(roll_idx))) //fast command
         next i         
         
         roll_idx +=1
         roll_idx = roll_idx and ROLL_IDX_MASK
         delayms(1000)
     
  end while //while( 1 )
*/     
end sub //main

'end module


Thanks for any replies..
Jerry
jmusselman64
 
Posts: 24
Joined: Thu Jan 22, 2015 1:01 am

Re: PIC24F EPMP with EDS

Postby Jerry Messina » Thu Dec 07, 2017 6:34 pm

It doesn't seem to like assigning a const array element to LCDCMD.

It doesn't seem to mind using a single byte or a normal array of bytes in ram...
Code: Select all
   dim b as byte
   dim ix as byte
   dim init_array(4) as byte
   
   // copy const to ram array
   for ix = 0 to 3
      init_array(ix) = lcd_init_seq(ix)
   next     
   
   For lcd_init_p = 0 to 3       
      delayms(30)
      'LCDCMD = lcd_init_seq(lcd_init_p)   //place a byte directly on the LCD bus
      // this seems to work... copy byte from const array and use that
      b = lcd_init_seq(lcd_init_p)
      LCDCMD = b
      // this also seems to work... use ram copy of const array
      LCDCMD = init_array(lcd_init_p)
   next lcd_init_p


I didn't look at the actual code to see if this does what's intended, but it did compile (using the firewing16 toolsuite)...
Jerry Messina
 
Posts: 280
Joined: Thu Feb 14, 2013 10:16 am

Re: PIC24F EPMP with EDS

Postby jmusselman64 » Sat Dec 09, 2017 10:01 pm

Stumped again..

I'm getting this error in Firewing now:
ITEM_0=ERROR, ||, 0, 0, |ERROR - One or more errors have been reported during linking, build sourcefile in MPLAB to identify cause (LCD_PIC24F_EPMP.c)|

So I did, and I got 3 errors from MPLAB:

1)
Compiled:
Code: Select all
LCD_PIC24F_EPMP.c:249:51: error: expected '=', ',', ';', 'asm' or '__attribute__' before '.' token

..which is...
Code: Select all
asm("__DSWPAG = 0x000034;"); extern volatile WORD _DSWPAG;



2) (This one I can probably figure out or workaround...just a string thing)
Firewing:
Code: Select all
const rollchar as string = "/-\|"

'C' code...
Code: Select all
LCD_PIC24F_EPMP.c:291:70: warning: unknown escape sequence: '\|'

Compiled:
Code: Select all
__prog__ const char _DB_StrConst_00[] __attribute__((space(prog))) = "/-\|";



3)
Firewing:
Code: Select all
 DSWPAG  = &H0002                    //SET EXTENDED DATA SPACE PAGE

Compiled:
Code: Select all
LCD_PIC24F_EPMP.c:475:11: error: request for member 'val' in something not a structure or union

..which is..
Code: Select all
 _DSWPAG.val = 2;


It doesn't seem to like the DSWPAG register.
If I comment out that line, it compiles.

Am I not specifying the EDS memory right?
Jerry
jmusselman64
 
Posts: 24
Joined: Thu Jan 22, 2015 1:01 am

Re: PIC24F EPMP with EDS

Postby Jerry Messina » Sun Dec 10, 2017 1:11 am

The fw line
Code: Select all
DSWPAG = CS_BASE                    //SET EXTENDED DATA SPACE PAGE
generates this C expression:
Code: Select all
   _DSWPAG.val = 2;


There seems to be a conflicting C definition that I think is causing the problem. In the C header file p24FJ128GA204.h:
Code: Select all
/* DSWPAG */
#define _DSWPAG DSWPAGbits.DSWPAG


One way to get around this is to add a new definition for the DSWPAG register to your code that doesn't conflict.
Code: Select all
// select one or the other... which ever you prefer
'public system _DSWPAG as ushort absolute &H0034
public system _DSWPAG as ushort absolute DSWPAG

// this should then compile
sub main()
   _DSWPAG = CS_BASE                    //SET EXTENDED DATA SPACE PAGE
   LCDCMD = FUNC_SET
end sub

That should generate a C symbol for the register with two leading underscores
Jerry Messina
 
Posts: 280
Joined: Thu Feb 14, 2013 10:16 am

Re: PIC24F EPMP with EDS

Postby jmusselman64 » Sun Dec 10, 2017 11:19 pm

Thanks Jerry...that did the trick! It compiled and with a few more fixes, I was able to see the correct signals on the LCD port pins with a logic analyzer. Of course, nothing appears on the actual LCD screen yet..

I wanted to make sure the Timer3 interrupt was working, so I make a small test program that blinks a couple LED's...one in the main loop, and one using Timer3.
It compiles and runs just fine. I wanted to see the inner workings of the interrupt...both in the test program and the LCD24 program...so I thought I would run MPLAB SIM

However, when I load the .hex and .cof files into MPLAB 8.92 for debugging, It crashes when it comes upon a 'CLRWDT' instruction. I have the WDT disabled (FWDTEN_OFF in config1), so I thought it shouldn't matter. When it stops I get a 'CORE-W0003 Watchdog Timer event occurred. Break in execution requested' error in the MPLAB output window, and a 'Simulator status indicates that the SIM stopped because the WDT expired...' pop-up. I can choose to have the warning not displayed again, but it still won't move past the 'CLRWDT' instruction.

What is putting the CLRWDT instructions in...Firewing or XC-16?

I can load the .hex file into the newer MPLABX but then I lose all the debugging info, so I'm kind of stuck.
jmusselman64
 
Posts: 24
Joined: Thu Jan 22, 2015 1:01 am

Re: PIC24F EPMP with EDS

Postby jmusselman64 » Mon Dec 11, 2017 12:01 am

Forgot to attach the Timer3 Test program..
Code: Select all
// PIC24FJ T3 Interrupt test
// 12/10/17 - J. MUSSELMAN

Device = 24FJ128GA204    'Select processor
clock = 16               'Define system clock (Fosc/2)

config CONFIG4 = {DSWDTPS_DSWDTPS1F,
                  DSWDTOSC_LPRC,
                  DSBOREN_OFF,
                  DSWDTEN_OFF,
                  DSSWEN_OFF,
                  PLLDIV_PLL4X,
                  I2C1SEL_DISABLE,
                  IOL1WAY_OFF}   
                 
Config CONFIG3 = {WPFP_WPFP127,
                  SOSCSEL_ON,
                  WDTWIN_PS25_0,
                  PLLSS_PLL_FRC,
                  BOREN_ON,
                  WPDIS_WPDIS,
                  WPCFG_WPCFGDIS,
                  WPEND_WPENDMEM}                 

Config CONFIG2 = {POSCMD_NONE,
                  WDTCLK_LPRC,
                  OSCIOFCN_OFF,
                  FCKSM_CSDCMD,
                  FNOSC_FRCPLL,
                  ALTCMPI_CxINC_RB,
                  WDTCMX_WDTCLK,
                  IESO_OFF}

Config CONFIG1 = {WDTPS_PS32768,
                  FWPSA_PR128,
                  WINDIS_OFF,
                  FWDTEN_OFF,
                  ICS_PGx1,
                  LPCFG_OFF,
                  GWRP_OFF,
                  GCP_OFF,
                  JTAGEN_OFF}

interrupt OnTimer3(Pic.T3Interrupt)
 
   TOGGLE( PORTA.10)
   IFS0.8 = 0          //clear T3 interrupt flag

end interrupt

sub MCU_init()
   // Set port direction
  TRISA = 0
  TRISB = 0
  TRISC = 0
   
  // Turn off analog
  ANSA = 0
  ANSB = 0
  ANSC = 0
 

  //The following piece of code shows the initialization of this timer:
  //Setup timer 3 for LCD
  T3CON  = &H8000                        //Use Internal Osc (Fcy), 16 bit mode, no prescaler, timer on
  PR3    = 800                           // set the period for 100 uS
  IFS0.8  = 0                          // clear the interrupt for Timer 3
  IEC0.8  = 1                          // enable interrupt for Timer 3
 
  //Enable the timer interrupt
  enable(OnTimer3)
 
end sub

sub main()

   MCU_init()

   while(true)   
      PORTA.9 = 1
      delayms(30) 
      PORTA.9 = 0
      delayms(30)
   end while
   
end sub
jmusselman64
 
Posts: 24
Joined: Thu Jan 22, 2015 1:01 am

Next

Return to Questions

Who is online

Users browsing this forum: No registered users and 1 guest

cron

x