1 /*
2  * CANopen main program file for PIC32 microcontroller.
3  *
4  * @file        main_PIC32.c
5  * @author      Janez Paternoster
6  * @copyright   2010 - 2020 Janez Paternoster
7  *
8  * This file is part of CANopenNode, an opensource CANopen Stack.
9  * Project home page is <https://github.com/CANopenNode/CANopenNode>.
10  * For more information on CANopen see <http://www.can-cia.org/>.
11  *
12  * Licensed under the Apache License, Version 2.0 (the "License");
13  * you may not use this file except in compliance with the License.
14  * You may obtain a copy of the License at
15  *
16  *     http://www.apache.org/licenses/LICENSE-2.0
17  *
18  * Unless required by applicable law or agreed to in writing, software
19  * distributed under the License is distributed on an "AS IS" BASIS,
20  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21  * See the License for the specific language governing permissions and
22  * limitations under the License.
23  */
24 
25 
26 #define CO_FSYS     64000      /* (8MHz Quartz used) */
27 #define CO_PBCLK    32000      /* peripheral bus clock */
28 
29 
30 #include "CANopen.h"
31 #include "application.h"
32 #ifdef USE_EEPROM
33     #include "eeprom.h"            /* 25LC128 eeprom chip connected to SPI2A port. */
34 #endif
35 #include <xc.h>                 /* for interrupts */
36 #include <sys/attribs.h>        /* for interrupts */
37 
38 
39 /* Configuration bits */
40     #pragma config FVBUSONIO = OFF      /* USB VBUS_ON Selection (OFF = pin is controlled by the port function) */
41     #pragma config FUSBIDIO = OFF       /* USB USBID Selection (OFF = pin is controlled by the port function) */
42     #pragma config UPLLEN = OFF         /* USB PLL Enable */
43     #pragma config UPLLIDIV = DIV_12    /* USB PLL Input Divider */
44     #pragma config FCANIO = ON          /* CAN IO Pin Selection (ON = default CAN IO Pins) */
45     #pragma config FETHIO = ON          /* Ethernet IO Pin Selection (ON = default Ethernet IO Pins) */
46     #pragma config FMIIEN = ON          /* Ethernet MII Enable (ON = MII enabled) */
47     #pragma config FSRSSEL = PRIORITY_7 /* SRS (Shadow registers set) Select */
48     #pragma config POSCMOD = XT         /* Primary Oscillator */
49     #pragma config FSOSCEN = OFF        /* Secondary oscillator Enable */
50     #pragma config FNOSC = PRIPLL       /* Oscillator Selection */
51     #pragma config FPLLIDIV = DIV_2     /* PLL Input Divider */
52     #pragma config FPLLMUL = MUL_16     /* PLL Multiplier */
53     #pragma config FPLLODIV = DIV_1     /* PLL Output Divider Value */
54     #pragma config FPBDIV = DIV_2       /* Bootup PBCLK divider */
55     #pragma config FCKSM = CSDCMD       /* Clock Switching and Monitor Selection */
56     #pragma config OSCIOFNC = OFF       /* CLKO Enable */
57     #pragma config IESO = OFF           /* Internal External Switch Over */
58 #pragma config FWDTEN = OFF          /* Watchdog Timer Enable */
59     #pragma config WDTPS = PS1024       /* Watchdog Timer Postscale Select (in milliseconds) */
60 #pragma config CP = OFF              /* Code Protect Enable */
61     #pragma config BWP = ON             /* Boot Flash Write Protect */
62     #pragma config PWP = PWP256K        /* Program Flash Write Protect */
63 #ifdef CO_ICS_PGx1
64     #pragma config ICESEL = ICS_PGx1    /* ICE/ICD Comm Channel Select */
65 #else
66     #pragma config ICESEL = ICS_PGx2    /* ICE/ICD Comm Channel Select (2 for Explorer16 board) */
67 #endif
68     #pragma config DEBUG = ON           /* Background Debugger Enable */
69 
70 
71 /* macros */
72     #define CO_TMR_TMR          TMR2             /* TMR register */
73     #define CO_TMR_PR           PR2              /* Period register */
74     #define CO_TMR_CON          T2CON            /* Control register */
75     #define CO_TMR_ISR_FLAG     IFS0bits.T2IF    /* Interrupt Flag bit */
76     #define CO_TMR_ISR_PRIORITY IPC2bits.T2IP    /* Interrupt Priority */
77     #define CO_TMR_ISR_ENABLE   IEC0bits.T2IE    /* Interrupt Enable bit */
78 
79     #define CO_CAN_ISR() void __ISR(_CAN_1_VECTOR, IPL5SOFT) CO_CAN1InterruptHandler(void)
80     #define CO_CAN_ISR_FLAG     IFS1bits.CAN1IF  /* Interrupt Flag bit */
81     #define CO_CAN_ISR_PRIORITY IPC11bits.CAN1IP /* Interrupt Priority */
82     #define CO_CAN_ISR_ENABLE   IEC1bits.CAN1IE  /* Interrupt Enable bit */
83 
84     #define CO_CAN_ISR2() void __ISR(_CAN_2_VECTOR, IPL5SOFT) CO_CAN2InterruptHandler(void)
85     #define CO_CAN_ISR2_FLAG     IFS1bits.CAN2IF  /* Interrupt Flag bit */
86     #define CO_CAN_ISR2_PRIORITY IPC11bits.CAN2IP /* Interrupt Priority */
87     #define CO_CAN_ISR2_ENABLE   IEC1bits.CAN2IE  /* Interrupt Enable bit */
88 
89     #define CO_clearWDT() (WDTCONSET = _WDTCON_WDTCLR_MASK)
90 
91 /* Global variables and objects */
92     volatile uint16_t CO_timer1ms = 0U; /* variable increments each millisecond */
93     const CO_CANbitRateData_t   CO_CANbitRateData[8] = {CO_CANbitRateDataInitializers};
94     static uint32_t tmpU32;
95 #ifdef USE_EEPROM
96     CO_EE_t                     CO_EEO;         /* Eeprom object */
97 #endif
98 
99 
100 /* main ***********************************************************************/
main(void)101 int main (void){
102     CO_NMT_reset_cmd_t reset = CO_RESET_NOT;
103 
104     /* Configure system for maximum performance. plib is necessary for that.*/
105     /* SYSTEMConfig(CO_FSYS*1000, SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE); */
106 
107     /* Enable system multi vectored interrupts */
108     INTCONbits.MVEC = 1;
109     __builtin_enable_interrupts();
110 
111     /* Disable JTAG and trace port */
112     DDPCONbits.JTAGEN = 0;
113     DDPCONbits.TROEN = 0;
114 
115 
116     /* Verify, if OD structures have proper alignment of initial values */
117     if(CO_OD_RAM.FirstWord != CO_OD_RAM.LastWord) while(1) CO_clearWDT();
118     if(CO_OD_EEPROM.FirstWord != CO_OD_EEPROM.LastWord) while(1) CO_clearWDT();
119     if(CO_OD_ROM.FirstWord != CO_OD_ROM.LastWord) while(1) CO_clearWDT();
120 
121 
122     /* initialize EEPROM - part 1 */
123 #ifdef USE_EEPROM
124     CO_ReturnError_t eeStatus = CO_EE_init_1(&CO_EEO, (uint8_t*) &CO_OD_EEPROM, sizeof(CO_OD_EEPROM),
125                             (uint8_t*) &CO_OD_ROM, sizeof(CO_OD_ROM));
126 #endif
127 
128 
129     programStart();
130 
131 
132     /* increase variable each startup. Variable is stored in eeprom. */
133     OD_powerOnCounter++;
134 
135 
136     while(reset != CO_RESET_APP){
137 /* CANopen communication reset - initialize CANopen objects *******************/
138         CO_ReturnError_t err;
139         uint16_t timer1msPrevious;
140         uint16_t TMR_TMR_PREV = 0;
141         uint8_t nodeId;
142         uint16_t CANBitRate;
143 
144         /* disable CAN and CAN interrupts */
145         CO_CAN_ISR_ENABLE = 0;
146         CO_CAN_ISR2_ENABLE = 0;
147 
148         /* Read CANopen Node-ID and CAN bit-rate from object dictionary */
149         nodeId = OD_CANNodeID;
150         if(nodeId<1 || nodeId>127) nodeId = 0x10;
151         CANBitRate = OD_CANBitRate;/* in kbps */
152 
153         /* initialize CANopen */
154         err = CO_init(ADDR_CAN1, nodeId, CANBitRate);
155         if(err != CO_ERROR_NO){
156             while(1) CO_clearWDT();
157             /* CO_errorReport(CO->em, CO_EM_MEMORY_ALLOCATION_ERROR, CO_EMC_SOFTWARE_INTERNAL, err); */
158         }
159 
160 
161         /* initialize eeprom - part 2 */
162 #ifdef USE_EEPROM
163         CO_EE_init_2(&CO_EEO, eeStatus, CO->SDO[0], CO->em);
164 #endif
165 
166 
167         /* initialize variables */
168         timer1msPrevious = CO_timer1ms;
169         OD_performance[ODA_performance_mainCycleMaxTime] = 0;
170         OD_performance[ODA_performance_timerCycleMaxTime] = 0;
171         reset = CO_RESET_NOT;
172 
173 
174 
175         /* Configure Timer interrupt function for execution every 1 millisecond */
176         CO_TMR_CON = 0;
177         CO_TMR_TMR = 0;
178         #if CO_PBCLK > 65000
179             #error wrong timer configuration
180         #endif
181         CO_TMR_PR = CO_PBCLK - 1;  /* Period register */
182         CO_TMR_CON = 0x8000;       /* start timer (TON=1) */
183         CO_TMR_ISR_FLAG = 0;       /* clear interrupt flag */
184         CO_TMR_ISR_PRIORITY = 3;   /* interrupt - set lower priority than CAN (set the same value in interrupt) */
185 
186         /* Configure CAN1 Interrupt (Combined) */
187         CO_CAN_ISR_FLAG = 0;       /* CAN1 Interrupt - Clear flag */
188         CO_CAN_ISR_PRIORITY = 5;   /* CAN1 Interrupt - Set higher priority than timer (set the same value in '#define CO_CAN_ISR_PRIORITY') */
189         CO_CAN_ISR2_FLAG = 0;      /* CAN2 Interrupt - Clear flag */
190         CO_CAN_ISR2_PRIORITY = 5;  /* CAN Interrupt - Set higher priority than timer (set the same value in '#define CO_CAN_ISR_PRIORITY') */
191 
192 
193         communicationReset();
194 
195 
196         /* start CAN and enable interrupts */
197         CO_CANsetNormalMode(CO->CANmodule[0]);
198         CO_TMR_ISR_ENABLE = 1;
199         CO_CAN_ISR_ENABLE = 1;
200 
201 #if CO_NO_CAN_MODULES >= 2
202         CO_CANsetNormalMode(CO->CANmodule[1]);
203         CO_CAN_ISR2_ENABLE = 1;
204 #endif
205 
206 
207         while(reset == CO_RESET_NOT){
208 /* loop for normal program execution ******************************************/
209             uint16_t timer1msCopy, timer1msDiff;
210 
211             CO_clearWDT();
212 
213 
214             /* calculate cycle time for performance measurement */
215             timer1msCopy = CO_timer1ms;
216             timer1msDiff = timer1msCopy - timer1msPrevious;
217             timer1msPrevious = timer1msCopy;
218             uint16_t t0 = CO_TMR_TMR;
219             uint16_t t = t0;
220             if(t >= TMR_TMR_PREV){
221                 t = t - TMR_TMR_PREV;
222                 t = (timer1msDiff * 100) + (t / (CO_PBCLK / 100));
223             }
224             else if(timer1msDiff){
225                 t = TMR_TMR_PREV - t;
226                 t = (timer1msDiff * 100) - (t / (CO_PBCLK / 100));
227             }
228             else t = 0;
229             OD_performance[ODA_performance_mainCycleTime] = t;
230             if(t > OD_performance[ODA_performance_mainCycleMaxTime])
231                 OD_performance[ODA_performance_mainCycleMaxTime] = t;
232             TMR_TMR_PREV = t0;
233 
234 
235             /* Application asynchronous program */
236             programAsync(timer1msDiff);
237 
238             CO_clearWDT();
239 
240 
241             /* CANopen process */
242             reset = CO_process(CO, timer1msDiff, NULL);
243 
244             CO_clearWDT();
245 
246 
247 #ifdef USE_EEPROM
248             CO_EE_process(&CO_EEO);
249 #endif
250         }
251     }
252 
253 
254 /* program exit ***************************************************************/
255 //    CO_DISABLE_INTERRUPTS();
256 
257     /* delete objects from memory */
258     programEnd();
259     CO_delete(ADDR_CAN1);
260 
261     /* reset */
262     SYSKEY = 0x00000000;
263     SYSKEY = 0xAA996655;
264     SYSKEY = 0x556699AA;
265     RSWRSTSET = 1;
266     tmpU32 = RSWRST;
267     while(1);
268 }
269 
270 
271 /* timer interrupt function executes every millisecond ************************/
272 #ifndef USE_EXTERNAL_TIMER_1MS_INTERRUPT
__ISR(_TIMER_2_VECTOR,IPL3SOFT)273 void __ISR(_TIMER_2_VECTOR, IPL3SOFT) CO_TimerInterruptHandler(void){
274 
275     CO_TMR_ISR_FLAG = 0;
276 
277     CO_timer1ms++;
278 
279     if(CO->CANmodule[0]->CANnormal) {
280         bool_t syncWas;
281         int i;
282 
283         /* Process Sync */
284         syncWas = CO_process_SYNC(CO, 1000);
285 
286         /* Read inputs */
287         CO_process_RPDO(CO, syncWas);
288 
289         /* Further I/O or nonblocking application code may go here. */
290 #if CO_NO_TRACE > 0
291         OD_time.epochTimeOffsetMs++;
292         for(i=0; i<OD_traceEnable && i<CO_NO_TRACE; i++) {
293             CO_trace_process(CO->trace[i], OD_time.epochTimeOffsetMs);
294         }
295 #endif
296         program1ms();
297 
298         /* Write outputs */
299         CO_process_TPDO(CO, syncWas, 1000);
300 
301         /* verify timer overflow */
302         if(CO_TMR_ISR_FLAG == 1){
303             CO_errorReport(CO->em, CO_EM_ISR_TIMER_OVERFLOW, CO_EMC_SOFTWARE_INTERNAL, 0);
304             CO_TMR_ISR_FLAG = 0;
305         }
306    }
307 
308     /* calculate cycle time for performance measurement */
309     uint16_t t = CO_TMR_TMR / (CO_PBCLK / 100);
310     OD_performance[ODA_performance_timerCycleTime] = t;
311     if(t > OD_performance[ODA_performance_timerCycleMaxTime])
312         OD_performance[ODA_performance_timerCycleMaxTime] = t;
313 }
314 #endif
315 
316 
317 /* CAN interrupt function *****************************************************/
CO_CAN_ISR()318 CO_CAN_ISR(){
319     CO_CANinterrupt(CO->CANmodule[0]);
320     /* Clear combined Interrupt flag */
321     CO_CAN_ISR_FLAG = 0;
322 }
323 
324 #if CO_NO_CAN_MODULES >= 2
CO_CAN_ISR2()325 CO_CAN_ISR2(){
326     CO_CANinterrupt(CO->CANmodule[1]);
327     /* Clear combined Interrupt flag */
328     CO_CAN_ISR2_FLAG = 0;
329 }
330 #endif
331