Subversion Repositories HomeAutomation

Rev

Rev 80 | Rev 91 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | SVN | RSS feed

  1. /*********************************************************************
  2.  *
  3.  *                  Main application
  4.  *
  5.  *********************************************************************
  6.  * FileName:        $HeadURL: https://svn.auml.se/svn/HomeAutomation/trunk/EmbeddedSoftware/PIC/canBoot/Source/Main.c $
  7.  * Last changed:    $LastChangedDate: 2006-11-14 11:44:27 +0100 (Tue, 14 Nov 2006) $
  8.  * By:              $LastChangedBy: johboh $
  9.  * Revision:        $Revision: 88 $
  10.  ********************************************************************/
  11.  
  12. #include <compiler.h>
  13. #include <stackTasks.h>
  14. #include <CANdefs.h>
  15. #include <CAN.h>
  16. #include <boot.h>
  17.  
  18. /** V E C T O R  R E M A P P I N G *******************************************/
  19.  
  20. #pragma code _HIGH_INTERRUPT_VECTOR = 0x000008
  21. void _high_ISR (void)
  22. {
  23.     _asm goto RM_HIGH_INTERRUPT_VECTOR _endasm
  24. }
  25.  
  26. #pragma code _LOW_INTERRUPT_VECTOR = 0x000018
  27. void _low_ISR (void)
  28. {
  29.     _asm goto RM_LOW_INTERRUPT_VECTOR _endasm
  30. }
  31.  
  32. #pragma code
  33.  
  34.  
  35. #ifdef USE_CAN
  36.     #include <CAN.h>
  37. #endif
  38.  
  39. static CAN_PROTO_MESSAGE cm;
  40. static BYTE ful=0;
  41. static DWORD cnt =0;
  42.  
  43. static void mainInit(void);
  44. static BOOL canGetPacket(void);
  45. void TBLWT_Func(void);
  46. void TBLWTPOSTINC_Func (void);
  47. void TBLRD_Func(void);
  48.  
  49. #define TICK_SUB_COUNTER_10MS 312
  50. #define PACKET_TIMEOUT 14
  51. #define BOOTLOADER_INIT_TIMOUT 500
  52.  
  53. DWORD tickCounter = 0;
  54. DWORD tickSubCounter = 0;
  55.  
  56. DWORD tickGet(void);
  57.  
  58. void main()
  59. {
  60.     static PROGRAM_STATE pgs = pgsWATING_START;
  61.     static DWORD t = 0;
  62.     static WORD programmerSid=0;
  63.    
  64.     static BYTE errMsg = ERR_NO_ERROR;
  65.     static PROGRAM_STATE afterAckPgs = pgsWATING_START;
  66.     static DWORD pgmAddress = 0;
  67.     static BYTE bytesReceived = 0;
  68.  
  69.     DWORD tBoot;
  70.    
  71.     static BYTE pgmData[64];
  72.  
  73.     static BYTE tmp;
  74.  
  75.     // Inits
  76.     mainInit();
  77.  
  78.     #ifdef USE_CAN
  79.         canInit();
  80.     #endif
  81.  
  82.     tBoot = tickGet();
  83.    
  84.  
  85.     for(;;)
  86.     {
  87.         BYTE i;
  88.         CAN_PROTO_MESSAGE outCm;
  89.         BOOL hasPacket = canGetPacket();
  90.  
  91.         tickSubCounter++;
  92.         if (tickSubCounter>TICK_SUB_COUNTER_10MS)
  93.         {
  94.             tickCounter++;
  95.             tickSubCounter=0;
  96.             ClrWdt();
  97.         }
  98.  
  99.         switch(pgs)
  100.         {
  101.             case pgsWATING_START:
  102.                 // Check if there is bootloader start packet
  103.                 if (hasPacket==TRUE && cm.funct==FUNCT_BOOTLOADER && cm.funcc==FUNCC_BOOT_INIT && ((((unsigned int)cm.data[RID_HIGH_INDEX])<<8)+cm.data[RID_LOW_INDEX])==MY_ID && cm.nid==MY_NID)
  104.                 {
  105.                     // Has new start packet
  106.                     pgmAddress      = (((DWORD)cm.data[ADDRU_INDEX])<<16)+(((DWORD)cm.data[ADDRH_INDEX])<<8)+((DWORD)cm.data[ADDRL_INDEX]);
  107.                     programmerSid   = cm.sid;
  108.                     t               = tickGet();
  109.                     errMsg          = ERR_NO_ERROR;
  110.                     afterAckPgs     = pgsWAIT_FIRST_PGM_PACKET;
  111.                     pgs             = pgsSEND_ACK;
  112.                     // save pgm adress.
  113.                    
  114.                 }
  115.  
  116.                 // Bootloader timeout
  117.                 if ((tickGet()-tBoot)>BOOTLOADER_INIT_TIMOUT)
  118.                 {
  119.                     pgs = pgsDONE;
  120.                 }
  121.             break;         
  122.  
  123.             case pgsSEND_ACK:
  124.                 // send ack
  125.                 outCm.funct             = FUNCT_BOOTLOADER;
  126.                 outCm.funcc             = FUNCC_BOOT_ACK;
  127.                 outCm.nid               = MY_NID;
  128.                 outCm.sid               = MY_ID;
  129.                 outCm.data_length       = 8;
  130.                 outCm.data[ERR_INDEX]   = errMsg;
  131.                 t                       = tickGet();
  132.                 pgs                     = afterAckPgs;
  133.  
  134.                 while(!canSendMessage(outCm,PRIO_HIGH));
  135.             break;
  136.  
  137.  
  138.             case pgsWAIT_FIRST_PGM_PACKET:
  139.                 // wait for first pgm packet.
  140.                 if ((tickGet()-t)>PACKET_TIMEOUT)
  141.                 {
  142.                     // Timeout, resend ack.
  143.                     errMsg          = ERR_NO_ERROR;
  144.                     afterAckPgs     = pgsWAIT_FIRST_PGM_PACKET;
  145.                     pgs             = pgsSEND_ACK;
  146.                 }
  147.  
  148.                 // If programming packet...
  149.                 if (hasPacket==TRUE && cm.funct==FUNCT_BOOTLOADER && (cm.funcc & 0x3)==FUNCC_BOOT_PGM && cm.nid==MY_NID && cm.sid==programmerSid)
  150.                 {
  151.                     BYTE localIndex = (BYTE)((cm.funcc & 0x1C)>>2)*8;
  152.  
  153.                     // Save bytes in memory, setup start adress etc..
  154.                     bytesReceived   = 8;       
  155.                     pgmData[localIndex+0]=cm.data[0]; pgmData[localIndex+1]=cm.data[1]; pgmData[localIndex+2]=cm.data[2]; pgmData[localIndex+3]=cm.data[3];
  156.                     pgmData[localIndex+4]=cm.data[4]; pgmData[localIndex+5]=cm.data[5]; pgmData[localIndex+6]=cm.data[6]; pgmData[localIndex+7]=cm.data[7];
  157.                
  158.                     pgs             = pgsWAIT_PGM_PACKET;
  159.                 }
  160.  
  161.  
  162.             break;
  163.  
  164.  
  165.             case pgsWAIT_PGM_PACKET:
  166.        
  167.            
  168.                 // If programming packet...
  169.                 if (hasPacket==TRUE && cm.funct==FUNCT_BOOTLOADER && (cm.funcc & 0x3)==FUNCC_BOOT_PGM && cm.nid==MY_NID && cm.sid==programmerSid)
  170.                 {
  171.                     BYTE localIndex = (BYTE)((cm.funcc & 0x1C)>>2)*8;
  172.                     // Save bytes in memory, inc received bytes.
  173.                     pgmData[localIndex+0]=cm.data[0]; pgmData[localIndex+1]=cm.data[1]; pgmData[localIndex+2]=cm.data[2]; pgmData[localIndex+3]=cm.data[3];
  174.                     pgmData[localIndex+4]=cm.data[4]; pgmData[localIndex+5]=cm.data[5]; pgmData[localIndex+6]=cm.data[6]; pgmData[localIndex+7]=cm.data[7];
  175.                    
  176.                     bytesReceived   += 8;
  177.                     pgs              = pgsWAIT_PGM_PACKET;
  178.                 }
  179.  
  180.                 // If adress packet (control packet)..
  181.                 if (hasPacket==TRUE && cm.funct==FUNCT_BOOTLOADER && cm.funcc==FUNCC_BOOT_ADDR && cm.nid==MY_NID && cm.sid==programmerSid)
  182.                 {
  183.                     // Send ack and goto wait first packet.
  184.                     // save adress.
  185.                     errMsg          = ERR_NO_ERROR;
  186.                     afterAckPgs     = pgsWAIT_FIRST_PGM_PACKET;
  187.                     pgs             = pgsSEND_ACK;
  188.                 }
  189.                
  190.                 // If end packet...
  191.                 if (hasPacket==TRUE && cm.funct==FUNCT_BOOTLOADER && cm.funcc==FUNCC_BOOT_DONE && cm.nid==MY_NID && cm.sid==programmerSid)
  192.                 {
  193.                     // write program.
  194.                     // If no bytes received at all, assume end.
  195.                     // If not 64 bytes received, send error ack and wait for first packet.
  196.                     // If 64, write.
  197.  
  198.                     if (bytesReceived==0)
  199.                     {
  200.                         // No received, done.
  201.                         errMsg          = ERR_NO_ERROR;
  202.                         afterAckPgs     = pgsDONE;
  203.                         pgs             = pgsSEND_ACK;
  204.                     }
  205.                     else if (bytesReceived!=64)
  206.                     {
  207.                         errMsg          = ERR_ERROR;
  208.                         afterAckPgs     = pgsWAIT_FIRST_PGM_PACKET;
  209.                         pgs             = pgsSEND_ACK;
  210.                     }
  211.                     else
  212.                     {
  213.                         pgs              = pgsWRITE_PROGRAM;
  214.                         afterAckPgs     = pgsDONE;
  215.                     }
  216.                 }
  217.  
  218.                 // If bytes received 64, write..
  219.                 if (bytesReceived==64)
  220.                 {
  221.                     LED0_IO=~LED0_IO;
  222.                     pgs              = pgsWRITE_PROGRAM;
  223.                     afterAckPgs     = pgsWAIT_PGM_PACKET;
  224.                     // TODO! Send ack more than once..
  225.                 }
  226.  
  227.  
  228.             break;
  229.  
  230.  
  231.             case pgsWRITE_PROGRAM:
  232.  
  233.                 ClrWdt();
  234.  
  235.                 // Write program and send ack.
  236.  
  237.                 // Check addr limit
  238.                 if (pgmAddress<RM_RESET_VECTOR)
  239.                 {              
  240.                     errMsg          = ERR_ERROR;
  241.                     pgs             = pgsSEND_ACK;
  242.                     //Not allowed to write here!
  243.                    
  244.                 }
  245.  
  246.                 //Load Table pointer registers with base address to erase.
  247.                 TBLPTRU = (BYTE)((pgmAddress & 0xFF0000)>>16);
  248.                 TBLPTRH = (BYTE)((pgmAddress & 0xFF00)>>8);
  249.                 TBLPTRL = (BYTE)(pgmAddress & 0xFF);
  250.  
  251.                 // point to Flash program memory
  252.                 EECON1bits.EEPGD = 1;
  253.                 // access Flash program memory
  254.                 EECON1bits.CFGS = 0;
  255.                 // enable write to memory
  256.                 EECON1bits.WREN = 1;
  257.                 // enable Row Erase operation
  258.                 EECON1bits.FREE = 1;
  259.                 // disable interrupts
  260.                 INTCONbits.GIE = 0;
  261.  
  262.                 // Do it!
  263.                 EECON2 = 0x55;
  264.                 EECON2 = 0xAA;
  265.                 EECON1bits.WR = 1;
  266.  
  267.                 // enable interrupts
  268.                 INTCONbits.GIE = 1;
  269.                
  270.                 //Load Table pointer registers with base address to program.
  271.                 TBLPTRU = (BYTE)((pgmAddress & 0xFF0000)>>16);
  272.                 TBLPTRH = (BYTE)((pgmAddress & 0xFF00)>>8);
  273.                 TBLPTRL = (BYTE)(pgmAddress & 0xFF);
  274.  
  275.                 // Load bytes
  276.                 for(i=0;i<63;i++)
  277.                 {
  278.                     TABLAT=pgmData[i];
  279.                     TBLWTPOSTINC_Func();
  280.                 }
  281.                 TABLAT=pgmData[63];
  282.                 TBLWT_Func();
  283.  
  284.  
  285.                 // point to Flash program memory
  286.                 EECON1bits.EEPGD = 1;
  287.                 // access Flash program memory
  288.                 EECON1bits.CFGS = 0;
  289.                 // enable write to memory
  290.                 EECON1bits.WREN = 1;
  291.                 // disable interrupts
  292.                 INTCONbits.GIE = 0;
  293.                
  294.                 // Do it!
  295.                 EECON2 = 0x55;
  296.                 EECON2 = 0xAA;
  297.                 EECON1bits.WR = 1;
  298.  
  299.                 // enable interrupts
  300.                 INTCONbits.GIE = 1;
  301.  
  302.                 // disable write to memory
  303.                 EECON1bits.WREN = 0;
  304.  
  305.                 pgmAddress     += 64;
  306.                 bytesReceived   = 0;
  307.                 errMsg          = ERR_NO_ERROR;
  308.                 pgs             = pgsSEND_ACK;
  309.  
  310.             break;
  311.  
  312.  
  313.             case pgsDONE:
  314.                 _asm  goto RM_RESET_VECTOR _endasm
  315.             break;
  316.  
  317.        
  318.  
  319.  
  320.             default:
  321.                 pgs = pgsWATING_START;
  322.             break;
  323.         }
  324.    
  325.  
  326.     }
  327. }
  328.  
  329.  
  330. /*
  331. *   Function: mainInit
  332. *
  333. *   Input:  none
  334. *   Output: none
  335. *   Pre-conditions: none
  336. *   Affects: Se code
  337. *   Depends: none.
  338. */
  339. void mainInit()
  340. {
  341.     LED0_TRIS=0;   
  342.  
  343.     // Enable internal PORTB pull-ups
  344.     INTCON2bits.RBPU = 0;
  345.    
  346.     // Enable Interrupts
  347.     INTCONbits.GIEH = 1;
  348.     INTCONbits.GIEL = 1;
  349.  
  350.     // Enable interrupt prirority
  351.     RCONbits.IPEN=1;
  352.    
  353.  
  354. }
  355.  
  356.  
  357.  
  358. /// CAN-------------------------------------------------------
  359.  
  360.  
  361. #ifdef USE_CAN
  362.  
  363.  
  364. /*
  365. *   Function: canInit
  366. *
  367. *   Input:  none
  368. *   Output: none
  369. *   Pre-conditions: none
  370. *   Affects: Changes can registers.
  371. *   Depends: none.
  372. */
  373. void canInit()
  374. {
  375.     CANCON = 0x80; //request config mode
  376.     while ((CANSTAT & 0xE0) != 0x80 ); //wait for config mode request
  377.  
  378.     // BAUD AND TQS
  379.  
  380.     BRGCON1=0;
  381.    
  382.     // Sync_Seq = 2 x TQ = 1
  383.     BRGCON1bits.SJW1=0;
  384.     BRGCON1bits.SJW0=1;
  385.    
  386.     // Setting BRP.
  387.     BRGCON1=BRGCON1|CAN_BRP;
  388.    
  389.     // Maximum PHEG1
  390.     BRGCON2bits.SEG2PHTS = 1;
  391.    
  392.     // Phase_Seg1 = 2 x TQ = 1
  393.     BRGCON2bits.SEG1PH2 = 0;
  394.     BRGCON2bits.SEG1PH1 = 0;
  395.     BRGCON2bits.SEG1PH0 = 1;
  396.    
  397.     // Prop_Seq = 2 x TQ = 1
  398.     BRGCON2bits.PRSEG2 = 0;
  399.     BRGCON2bits.PRSEG1 = 0;
  400.     BRGCON2bits.PRSEG0 = 1;
  401.    
  402.     //  Enableing bus wake-up
  403.     BRGCON3bits.WAKDIS = 0;
  404.    
  405.     // Dont use filter when wakeup
  406.     BRGCON3bits.WAKFIL = 0;
  407.    
  408.     // Phase_Seg2 = 2 x TQ = 1
  409.     BRGCON3bits.SEG2PH2 = 0;
  410.     BRGCON3bits.SEG2PH1 = 0;
  411.     BRGCON3bits.SEG2PH0 = 1;
  412.  
  413.     ECANCON=0;
  414.     ECANCONbits.MDSEL1 = 0;
  415.     ECANCONbits.MDSEL0 = 1;
  416.  
  417.  
  418.     // FILTERS AND MASKS
  419.     RXB0CONbits.RXM1=1;
  420.     RXB0CONbits.RXM0=0;
  421.  
  422.  
  423.     // INTERRUPTS
  424.    
  425.     // Diable transmit interrupt
  426.     TXBIE = 0;
  427.  
  428.     //rx no interrupt
  429.     PIR3 = 0;
  430.     PIE3 = 0;
  431.  
  432.  
  433.     // Programmable no buffers interrupt
  434.     BIE0  = 0x00;  
  435.  
  436.  
  437.     //loopback mode or not
  438.     #ifdef CAN_LOOPBACK_MODE
  439.         CANCON = 0x40;//request loopback mode
  440.         while ((CANSTAT & 0xE0) != 0x40 );//wait for loopback mode
  441.     #else
  442.         CANCON = 0x00;//request normal mode
  443.         while ((CANSTAT & 0xE0) != 0x00 );//wait for normal mode
  444.     #endif
  445.  
  446. }
  447.  
  448.  
  449.  
  450.  
  451. /*
  452. *   Function: canGetPacket
  453. *
  454. *   Input:  none
  455. *   Output: none
  456. *   Pre-conditions: a call to canISR and an packet is available and banked to RXB0
  457. *   Affects: Calls canParse() function in main for parsing received packet.
  458. *   Depends: none.
  459. */
  460. BOOL canGetPacket()
  461. {
  462.  
  463.         // Get which buffer that is ful.
  464.              if (RXB0CONbits.RXFUL) { ECANCON=(ECANCON&0b00000)|0b10000;  }
  465.         else if (RXB1CONbits.RXFUL) { ECANCON=(ECANCON&0b00000)|0b10001;  }
  466.         else if (B0CONbits.RXFUL)   { ECANCON=(ECANCON&0b00000)|0b10010;  }
  467.         else if (B1CONbits.RXFUL)   { ECANCON=(ECANCON&0b00000)|0b10011;  }
  468.         else if (B2CONbits.RXFUL)   { ECANCON=(ECANCON&0b00000)|0b10100;  }
  469.         else if (B3CONbits.RXFUL)   { ECANCON=(ECANCON&0b00000)|0b10101;  }
  470.         else if (B4CONbits.RXFUL)   { ECANCON=(ECANCON&0b00000)|0b10110;  }
  471.         else if (B5CONbits.RXFUL)   { ECANCON=(ECANCON&0b00000)|0b10111;  }
  472.         else return FALSE;
  473.  
  474.  
  475.         //RXB0SIDH 28 27 26 25 24 23 22 21
  476.         //RXB0SIDL 20 19 18 xx xx xx 17 16
  477.         //RXB0EIDH 15 14 13 12 11 10 09 08
  478.         //RXB0EIDL 07 06 05 04 03 02 01 00
  479.  
  480.         //funct xx xx xx xx 28 27 26 25
  481.         //funcc xx xx xx xx xx xx 24 23 22 21 20 19 18 17 16 15
  482.         //nid   xx xx 14 13 12 11 10 09
  483.         //sid   xx xx xx xx xx xx xx 08 07 06 05 04 03 02 01 00
  484.  
  485.  
  486.         cm.funct=((RXB0SIDH & 0xF0)>>4);
  487.         cm.funcc=(((WORD)(RXB0SIDH & 0x0F))<<6)+(((WORD)(RXB0SIDL & 0xE0))>>2)+(((WORD)(RXB0SIDL & 0x03))<<1)+(((WORD)(RXB0EIDH & 0x80))>>7);
  488.         cm.nid=((RXB0EIDH & 0x7E)>>1);
  489.         cm.sid=(((WORD)(RXB0EIDH & 0x01))<<8)+RXB0EIDL;
  490.  
  491.  
  492.         // Data length
  493.         cm.data_length=(RXB0DLCbits.DLC3<<3)+(RXB0DLCbits.DLC2<<2)+(RXB0DLCbits.DLC1<<1)+RXB0DLCbits.DLC0;
  494.  
  495.         // Data one bye one, for speed
  496.         cm.data[0]=RXB0D0;  cm.data[1]=RXB0D1;
  497.         cm.data[2]=RXB0D2;  cm.data[3]=RXB0D3;
  498.         cm.data[4]=RXB0D4;  cm.data[5]=RXB0D5;
  499.         cm.data[6]=RXB0D6;  cm.data[7]=RXB0D7;
  500.  
  501.         // Clear ful status
  502.         RXB0CONbits.RXFUL=0;
  503.  
  504.         return TRUE;
  505. }
  506.  
  507.  
  508. /*
  509. *   Function: canSendMessage
  510. *
  511. *   Input:  CAN_MESSAGE to send
  512. *   Output: True if packet scheduled sucess, false otherwise.
  513. *   Pre-conditions: canInit
  514. *   Affects: ..
  515. *   Depends: ..
  516. */
  517. BOOL canSendMessage(CAN_PROTO_MESSAGE cm,CAN_PRIORITY prio)
  518. {
  519.    
  520.     if ( TXB0CONbits.TXREQ == 0 )  { ECANCON=(ECANCON&0b00000)|0b00011; }
  521.     if ( TXB1CONbits.TXREQ == 0 )  { ECANCON=(ECANCON&0b00000)|0b00100; }
  522.     if ( TXB2CONbits.TXREQ == 0 )  { ECANCON=(ECANCON&0b00000)|0b00101; }
  523.     // None of the transmit buffers were empty.
  524.     else { return FALSE;}
  525.  
  526.     if (prio<0 || prio>3) prio = 0;
  527.  
  528.  
  529.         //RXB0SIDH 28 27 26 25 24 23 22 21
  530.         //RXB0SIDL 20 19 18 xx xx xx 17 16
  531.         //RXB0EIDH 15 14 13 12 11 10 09 08
  532.         //RXB0EIDL 07 06 05 04 03 02 01 00
  533.  
  534.         //funct xx xx xx xx 28 27 26 25
  535.         //funcc xx xx xx xx xx xx 24 23 22 21 20 19 18 17 16 15
  536.         //nid   xx xx 14 13 12 11 10 09
  537.         //sid   xx xx xx xx xx xx xx 08 07 06 05 04 03 02 01 00
  538.  
  539.         RXB0SIDH = (BYTE)((cm.funct & 0x0F)<<4)+(BYTE)((cm.funcc & 0x03C0)>>6);
  540.         RXB0SIDL = (BYTE)((cm.funcc & 0x003F)<<2)+(BYTE)((cm.funcc & 0x0006)>>1);
  541.         RXB0EIDH = (BYTE)((cm.funcc & 0x0001)<<7)+(BYTE)((cm.nid & 0x3F)<<1)+(BYTE)((cm.sid & 0x0100)>>8);
  542.         RXB0EIDL = (BYTE)((cm.sid & 0x00FF));
  543.  
  544.         // Data length
  545.         RXB0DLC = 0;
  546.         if (cm.data_length>8) RXB0DLC=8; else RXB0DLC=cm.data_length;
  547.        
  548.         // NOT Remote request
  549.         _asm
  550.             bcf RXB0DLC, 6, 0
  551.         _endasm
  552.  
  553.  
  554.         // Data one bye one, for speed
  555.         RXB0D0=cm.data[0];  RXB0D1=cm.data[1];
  556.         RXB0D2=cm.data[2];  RXB0D3=cm.data[3];
  557.         RXB0D4=cm.data[4];  RXB0D5=cm.data[5];
  558.         RXB0D6=cm.data[6];  RXB0D7=cm.data[7];
  559.  
  560.         // set extended or not
  561.         RXB0SIDLbits.EXID=1;
  562.  
  563.         // Priority
  564.         RXB0CON = (RXB0CON & 0b11111100) | prio;
  565.  
  566.         // mark as redy to transmit
  567.         _asm
  568.             bsf RXB0CON, 3, 0
  569.         _endasm
  570.  
  571.         return TRUE;
  572.  
  573. }
  574.  
  575.  
  576.  
  577. #endif //USE_CAN
  578.  
  579.  
  580.  
  581.  
  582. void TBLRD_Func()
  583. {
  584. _asm
  585.     TBLRD
  586. _endasm
  587. }
  588.  
  589. void TBLWT_Func()
  590. {
  591. _asm
  592.     TBLWT
  593. _endasm
  594. }
  595.  
  596.  
  597. void TBLWTPOSTINC_Func (void){
  598. _asm
  599.     TBLWTPOSTINC
  600. _endasm
  601. }
  602.  
  603. DWORD tickGet()
  604. {
  605.     return tickCounter;
  606. }
  607.  
  608.  
  609. #pragma code user = RM_RESET_VECTOR
  610.