Subversion Repositories HomeAutomation

Rev

Rev 319 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | SVN | RSS feed

Rev 319 Rev 324
1
#include "bios_funs.h"
1
#include "bios_funs.h"
2
#include "avr_cfg.h"
2
#include "avr_cfg.h"
3
#include <inttypes.h>
3
#include <inttypes.h>
4
#include <avr/io.h>
4
#include <avr/io.h>
5
#include <avr/interrupt.h>
5
#include <avr/interrupt.h>
6
#include <avr/wdt.h>
6
#include <avr/wdt.h>
7
#include <avr/pgmspace.h>
7
#include <avr/pgmspace.h>
8
#ifdef BIOS_UART
8
#ifdef BIOS_UART
9
#include <stdio.h>
9
#include <stdio.h>
10
#endif
10
#endif
11
#include <string.h>
11
#include <string.h>
12
#include <util/crc16.h>
12
#include <util/crc16.h>
13
#include "flash.h"
13
#include "flash.h"
14
#include "vectors.h" // Must be included after sfr_defs.h but before any ISR()
14
#include "vectors.h" // Must be included after sfr_defs.h but before any ISR()
15
#include "mcp2515/mcp2515.h"
15
#include "mcp2515/mcp2515.h"
16
 
16
 
17
//---------------------------------------------------------------------------
17
//---------------------------------------------------------------------------
18
// Globals
18
// Globals
19
 
19
 
20
volatile uint32_t gMilliSecTick;
20
volatile uint32_t gMilliSecTick;
21
volatile uint8_t bios_msg_full;
21
volatile uint8_t bios_msg_full;
22
Can_Message_t* bios_msg_ptr; // only a pointer to main-local structure to save .bss space
22
Can_Message_t* bios_msg_ptr; // only a pointer to main-local structure to save .bss space
23
 
23
 
24
//---------------------------------------------------------------------------
24
//---------------------------------------------------------------------------
25
// Function definitions for the exported interface
25
// Function definitions for the exported interface
26
 
26
 
27
void reset(void) {
27
void reset(void) {
28
    // Just loop and let the watchdog reset
28
    // Just loop and let the watchdog reset
29
    cli();
29
    cli();
30
    while(1);
30
    while(1);
31
}
31
}
32
 
32
 
33
#ifdef BIOS_UART
33
#ifdef BIOS_UART
34
void bios_putchar(char c) {
34
void bios_putchar(char c) {
35
    UART_PUTCHAR(c);
35
    UART_PUTCHAR(c);
36
}
36
}
37
 
37
 
38
char bios_getchar(void) {
38
char bios_getchar(void) {
39
    char c;
39
    char c;
40
    UART_GETCHAR(c)
40
    UART_GETCHAR(c)
41
    return c;
41
    return c;
42
}
42
}
43
#endif
43
#endif
44
 
44
 
45
unsigned long timebase_get(void) {
45
unsigned long timebase_get(void) {
46
    uint8_t sreg;
46
    uint8_t sreg;
47
    unsigned long res;
47
    unsigned long res;
48
   
48
   
49
    // Disable interrupts while reading tick count
49
    // Disable interrupts while reading tick count
50
    sreg=SREG;
50
    sreg=SREG;
51
    cli();
51
    cli();
52
   
52
   
53
    res = gMilliSecTick;
53
    res = gMilliSecTick;
54
   
54
   
55
    SREG=sreg;
55
    SREG=sreg;
56
   
56
   
57
    return res;
57
    return res;
58
}
58
}
59
 
59
 
60
// TODO: Insert can driver's Can_Send directly in bios interface.
60
// TODO: Insert can driver's Can_Send directly in bios interface.
61
Can_Return_t can_send(Can_Message_t* msg) {
61
Can_Return_t can_send(Can_Message_t* msg) {
62
    return Can_Send(msg);
62
    return Can_Send(msg);
63
}
63
}
64
 
64
 
65
//---------------------------------------------------------------------------
65
//---------------------------------------------------------------------------
66
// Function definitions for the private functions
66
// Function definitions for the private functions
67
 
67
 
68
#ifdef BIOS_UART
68
#ifdef BIOS_UART
69
static int console_getchar(FILE *stream) {
69
static int console_getchar(FILE *stream) {
70
    return bios_getchar();
70
    return bios_getchar();
71
}
71
}
72
 
72
 
73
static int console_putchar(char c, FILE *stream) {
73
static int console_putchar(char c, FILE *stream) {
74
    bios_putchar(c);
74
    bios_putchar(c);
75
    return 0;
75
    return 0;
76
}
76
}
77
 
77
 
78
static FILE bios_stdio = FDEV_SETUP_STREAM(console_putchar, console_getchar,
78
static FILE bios_stdio = FDEV_SETUP_STREAM(console_putchar, console_getchar,
79
                                         _FDEV_SETUP_RW);
79
                                         _FDEV_SETUP_RW);
80
#endif
80
#endif
81
 
81
 
82
//---------------------------------------------------------------------------
82
//---------------------------------------------------------------------------
83
// Interrupt Service Routines
83
// Interrupt Service Routines
84
// Read app_vectors.h!
84
// Read app_vectors.h!
85
 
85
 
86
ISR(TIMEBASE_VECTOR) {
86
ISR(TIMEBASE_VECTOR) {
87
    TIMEBASE_COUNTER -= TIMEBASE_RELOAD;
87
    TIMEBASE_COUNTER -= TIMEBASE_RELOAD;
88
    gMilliSecTick++;
88
    gMilliSecTick++;
89
    wdt_reset();
89
    wdt_reset();
90
}
90
}
91
 
91
 
92
void Can_Process(Can_Message_t* msg) {
92
void Can_Process(Can_Message_t* msg) {
93
    if (!(msg->ExtendedFlag)) return; // We don't care about standard CAN frames.
93
    if (!(msg->ExtendedFlag)) return; // We don't care about standard CAN frames.
94
   
94
   
95
    if ((msg->Id & CAN_MASK_CLASS)>>CAN_SHIFT_CLASS == CAN_NMT) {
95
    if ((msg->Id & CAN_MASK_CLASS)>>CAN_SHIFT_CLASS == CAN_NMT) {
96
       
96
       
97
        if ((msg->Id & CAN_MASK_NMT_TYPE)>>CAN_SHIFT_NMT_TYPE == CAN_NMT_TIME) {
97
        if ((msg->Id & CAN_MASK_NMT_TYPE)>>CAN_SHIFT_NMT_TYPE == CAN_NMT_TIME) {
98
            // Reset watchdog as long as time messages from the master node
98
            // Reset watchdog as long as time messages from the master node
99
            // are received periodically. If anything hangs the CAN communication
99
            // are received periodically. If anything hangs the CAN communication
100
            // or the master node is not up, the node will be reset.
100
            // or the master node is not up, the node will be reset.
101
            wdt_reset();
101
            wdt_reset();
102
            //TODO: Don't care about time right now... Figure out what to do with it. 
102
            //TODO: Don't care about time right now... Figure out what to do with it. 
103
        }
103
        }
104
       
104
       
105
        if ((msg->Id & CAN_MASK_NMT_RID)>>CAN_SHIFT_NMT_RID == NODE_ID) {
105
        if ((msg->Id & CAN_MASK_NMT_RID)>>CAN_SHIFT_NMT_RID == NODE_ID) {
106
 
106
 
107
            if ((msg->Id & CAN_MASK_NMT_TYPE)>>CAN_SHIFT_NMT_TYPE == CAN_NMT_RESET) {
107
            if ((msg->Id & CAN_MASK_NMT_TYPE)>>CAN_SHIFT_NMT_TYPE == CAN_NMT_RESET) {
108
                reset();
108
                reset();
109
            }
109
            }
110
            // Copy message to bios buffer if bios is done with the previous message.
110
            // Copy message to bios buffer if bios is done with the previous message.
111
            // This is also a check to see if bios is still running. When app is
111
            // This is also a check to see if bios is still running. When app is
112
            // started, bios_msg_full is never reset to 0 so the check fails and all
112
            // started, bios_msg_full is never reset to 0 so the check fails and all
113
            // NMT communication except reset is ignored. 
113
            // NMT communication except reset is ignored. 
114
            if (!(bios_msg_full)) {
114
            if (!(bios_msg_full)) {
115
                memcpy(bios_msg_ptr, msg, sizeof(Can_Message_t));
115
                memcpy(bios_msg_ptr, msg, sizeof(Can_Message_t));
116
                bios_msg_full = 1;
116
                bios_msg_full = 1;
117
            }
117
            }
118
        }      
118
        }      
119
    } else if (bios->can_callback) {
119
    } else if (bios->can_callback) {
120
        //printf("Calling app callback.\n");
120
        //printf("Calling app callback.\n");
121
        bios->can_callback(msg);
121
        bios->can_callback(msg);
122
    }
122
    }
123
}
123
}
124
 
124
 
125
#define BIOS_APP    1
125
#define BIOS_APP    1
126
#define BIOS_NOAPP  2
126
#define BIOS_NOAPP  2
127
#define BIOS_PGM    3
127
#define BIOS_PGM    3
128
 
128
 
129
int main() {
129
int main() {
130
    void (*app_reset)(void) = 0; // Function pointer to jump to application reset vector.
130
    void (*app_reset)(void) = 0; // Function pointer to jump to application reset vector.
131
    uint8_t bios_state;
131
    uint8_t bios_state;
132
    uint8_t nmt_type;
132
    uint8_t nmt_type;
133
    uint8_t pagebuf[SPM_PAGESIZE];
133
    uint8_t pagebuf[SPM_PAGESIZE];
134
    // These unneccessary initializations uses 12 bytes flash, just to make the compiler happy!
134
    // These unneccessary initializations uses 12 bytes flash, just to make the compiler happy!
135
    uint16_t base_addr = 0;
135
    uint16_t base_addr = 0;
136
    uint16_t offset = 0;
136
    uint16_t offset = 0;
137
    uint16_t addr = 0;
137
    uint16_t addr = 0;
138
    uint8_t len;
138
    uint8_t len;
139
    uint16_t data;
139
    uint16_t data;
140
    uint8_t send_msg = 0;
140
    uint8_t send_msg = 0;
141
    Can_Message_t tx_msg;
141
    Can_Message_t tx_msg;
142
    Can_Message_t bios_msg;
142
    Can_Message_t bios_msg;
143
    bios_msg_ptr = &bios_msg;
143
    bios_msg_ptr = &bios_msg;
144
   
144
   
145
#ifdef BIOS_UART
145
#ifdef BIOS_UART
146
    // Setup USART for debug channel
146
    // Setup USART for debug channel
147
    UART_INIT();
147
    UART_INIT();
148
    stdout = &bios_stdio;
148
    stdout = &bios_stdio;
149
    stdin = &bios_stdio;
149
    stdin = &bios_stdio;
150
#endif
150
#endif
151
   
151
   
152
    // Initialize a suitable hardware timer to create a 1KHz interrupt.
152
    // Initialize a suitable hardware timer to create a 1KHz interrupt.
153
    //TODO: Any point in having timebase in bios after wdt_reset() is moved to Can_Process?
153
    //TODO: Any point in having timebase in bios after wdt_reset() is moved to Can_Process?
154
    TIMEBASE_INIT();
154
    TIMEBASE_INIT();
155
   
155
   
156
    // Enable watchdog timer to protect against an application locking the
156
    // Enable watchdog timer to protect against an application locking the
157
    // node by leaving interrupts disabled. 
157
    // node by leaving interrupts disabled. 
158
    wdt_enable(WDTO_2S);
158
    wdt_enable(WDTO_2S);
159
   
159
   
160
    // Move interrupt vectors to start of bootloader section and enable interrupts
160
    // Move interrupt vectors to start of bootloader section and enable interrupts
161
    IVSEL_REG = _BV(IVCE);
161
    IVSEL_REG = _BV(IVCE);
162
    IVSEL_REG = _BV(IVSEL);
162
    IVSEL_REG = _BV(IVSEL);
163
    sei();
163
    sei();
164
   
164
   
165
    if (Can_Init() != CAN_OK) reset();
165
    if (Can_Init() != CAN_OK) reset();
166
   
166
   
167
    tx_msg.RemoteFlag = 0;
167
    tx_msg.RemoteFlag = 0;
168
    tx_msg.ExtendedFlag = 1;
168
    tx_msg.ExtendedFlag = 1;
169
    tx_msg.Id = CAN_ID_NMT_BIOS_START;
169
    tx_msg.Id = CAN_ID_NMT_BIOS_START;
170
    tx_msg.DataLength = 3;
170
    tx_msg.DataLength = 3;
171
    tx_msg.Data.words[0] = BIOS_VERSION;
171
    tx_msg.Data.words[0] = BIOS_VERSION;
172
#ifdef BIOS_UART
172
#ifdef BIOS_UART
173
    printf("BIOS start\n");
173
    printf("BIOS start\n");
174
#endif
174
#endif
175
    if (pgm_read_word(0) == 0xffff) {
175
    if (pgm_read_word(0) == 0xffff) {
176
        // No application in flash
176
        // No application in flash
177
        // Send CAN_NMT_BIOS_START(BIOS_VERSION, 0)
177
        // Send CAN_NMT_BIOS_START(BIOS_VERSION, 0)
178
        tx_msg.Data.bytes[2] = 0;
178
        tx_msg.Data.bytes[2] = 0;
179
        bios_state = BIOS_NOAPP;
179
        bios_state = BIOS_NOAPP;
180
    } else {
180
    } else {
181
        // Application exists
181
        // Application exists
182
        // Send CAN_NMT_BIOS_START(BIOS_VERSION, 1)
182
        // Send CAN_NMT_BIOS_START(BIOS_VERSION, 1)
183
        tx_msg.Data.bytes[2] = 1;
183
        tx_msg.Data.bytes[2] = 1;
184
        bios_state = BIOS_APP;
184
        bios_state = BIOS_APP;
185
    }
185
    }
186
   
186
   
187
    while (Can_Send(&tx_msg) != CAN_OK);
187
    while (Can_Send(&tx_msg) != CAN_OK);
188
 
188
 
189
    tx_msg.DataLength = 2; // All msg sent after BIOS_START are length 2 so set it once.
189
    tx_msg.DataLength = 2; // All msg sent after BIOS_START are length 2 so set it once.
190
   
190
   
191
    // Main loop
191
    // Main loop
192
    while (1) {
192
    while (1) {
193
        // Wait for message
193
        // Wait for message
194
        while (!bios_msg_full);
194
        while (!bios_msg_full);
195
       
195
       
196
        nmt_type = (bios_msg.Id & CAN_MASK_NMT_TYPE)>>CAN_SHIFT_NMT_TYPE;
196
        nmt_type = (bios_msg.Id & CAN_MASK_NMT_TYPE)>>CAN_SHIFT_NMT_TYPE;
197
       
197
       
198
        switch (bios_state) {
198
        switch (bios_state) {
199
        case BIOS_APP:
199
        case BIOS_APP:
200
            if (nmt_type == CAN_NMT_START_APP) {
200
            if (nmt_type == CAN_NMT_START_APP) {
201
                app_reset(); // Will never return
201
                app_reset(); // Will never return
202
            }
202
            }
203
            // Fall through
203
            // Fall through
204
        case BIOS_NOAPP:
204
        case BIOS_NOAPP:
205
            if (nmt_type == CAN_NMT_PGM_START) {
205
            if (nmt_type == CAN_NMT_PGM_START) {
206
                // Set base address
206
                // Set base address
207
                base_addr = bios_msg.Data.words[0];
207
                base_addr = bios_msg.Data.words[0];
208
                flash_init(pagebuf);
208
                flash_init(pagebuf);
209
                //send CAN_NMT_PGM_ACK(offset)
209
                //send CAN_NMT_PGM_ACK(offset)
210
                tx_msg.Id = CAN_ID_NMT_PGM_ACK;
210
                tx_msg.Id = CAN_ID_NMT_PGM_ACK;
211
                tx_msg.Data.words[0] = base_addr;
211
                tx_msg.Data.words[0] = base_addr;
212
                send_msg = 1;
212
                send_msg = 1;
213
                bios_state = BIOS_PGM;
213
                bios_state = BIOS_PGM;
214
            }
214
            }
215
            if (nmt_type == CAN_NMT_PGM_COPY) {
215
            if (nmt_type == CAN_NMT_PGM_COPY) {
216
                // For BIOS updating over CAN. Upload bios to application area,
216
                // For BIOS updating over CAN. Upload bios to application area,
217
                // send this message with correct parameters to copy data from
217
                // send this message with correct parameters to copy data from
218
                // application to bios area and pray it will come alive again.
218
                // application to bios area and pray it will come alive again.
219
                flash_copy_data(bios_msg.Data.words[0],
219
                flash_copy_data(bios_msg.Data.words[0],
220
                                bios_msg.Data.words[1],
220
                                bios_msg.Data.words[1],
221
                                bios_msg.Data.words[2]);
221
                                bios_msg.Data.words[2]);
222
                // flash_copy_data will never return.
222
                // flash_copy_data will never return.
223
            }
223
            }
224
            break;
224
            break;
225
        case BIOS_PGM:
225
        case BIOS_PGM:
226
            // Default to send CAN_NMT_PGM_ACK(offset)
226
            // Default to send CAN_NMT_PGM_ACK(offset)
227
            tx_msg.Id = CAN_ID_NMT_PGM_ACK;
227
            tx_msg.Id = CAN_ID_NMT_PGM_ACK;
228
           
228
           
229
            if (nmt_type == CAN_NMT_PGM_DATA) {
229
            if (nmt_type == CAN_NMT_PGM_DATA) {
230
                // Set address = base address + offset.
230
                // Set address = base address + offset.
231
                offset = bios_msg.Data.words[0];
231
                offset = bios_msg.Data.words[0];
232
                addr = base_addr + offset;
232
                addr = base_addr + offset;
233
               
233
               
234
                // One of ACK and NACK will be sent, both have offset as data.
234
                // One of ACK and NACK will be sent, both have offset as data.
235
                tx_msg.Data.words[0] = offset;
235
                tx_msg.Data.words[0] = offset;
236
                send_msg = 1;
236
                send_msg = 1;
237
 
237
 
238
                // Flash all data sent, beginning at addr.
238
                // Flash all data sent, beginning at addr.
239
                len = (bios_msg.DataLength+1)/2; // Number of words in message, rounded up.
239
                len = (bios_msg.DataLength+1)/2; // Number of words in message, rounded up.
240
                uint8_t i;
240
                uint8_t i;
241
                for (i=1; i<len; i++) { // Skip first word (offset).
241
                for (i=1; i<len; i++) { // Skip first word (offset).
242
                   
242
                   
243
                    // Abort if trying to write in BIOS area.
243
                    // Abort if trying to write in BIOS area.
244
                    if (addr >= (uint16_t)&__bios_start) {
244
                    if (addr >= (uint16_t)&__bios_start) {
245
                        // Send CAN_NMT_PGM_NACK(offset).
245
                        // Send CAN_NMT_PGM_NACK(offset).
246
                        tx_msg.Id = CAN_ID_NMT_PGM_NACK;
246
                        tx_msg.Id = CAN_ID_NMT_PGM_NACK;
247
                        break; //for loop
247
                        break; //for loop
248
                    }
248
                    }
249
 
249
 
250
                    data = bios_msg.Data.words[i];
250
                    data = bios_msg.Data.words[i];
251
                    flash_write_word(addr, data);
251
                    flash_write_word(addr, data);
252
                    addr += 2;
252
                    addr += 2;
253
                }
253
                }
254
                // Adjust addr if an odd number of bytes were sent, for use in crc calc.
254
                // Adjust addr if an odd number of bytes were sent, for use in crc calc.
255
                addr -= bios_msg.DataLength & 1;
255
                addr -= bios_msg.DataLength & 1;
256
            }
256
            }
257
            if (nmt_type == CAN_NMT_PGM_END) {
257
            if (nmt_type == CAN_NMT_PGM_END) {
258
                // Make sure a partly filled page will be flashed.
258
                // Make sure a partly filled page will be flashed.
259
                flash_flush_buffer();
259
                flash_flush_buffer();
260
               
260
               
261
                // Calculate crc on written data.
261
                // Calculate crc on written data.
262
                //TODO: This will not work if data is not sent sequentially without
262
                //TODO: This will not work if data is not sent sequentially without
263
                // holes, starting with offset=0. We might want to send a NACK if the
263
                // holes, starting with offset=0. We might want to send a NACK if the
264
                // offset in a CAN_NMT_PGM_DATA msg differs from the expected offset.
264
                // offset in a CAN_NMT_PGM_DATA msg differs from the expected offset.
265
                uint16_t loc, crc = 0;
265
                uint16_t loc, crc = 0;
266
                for (loc = base_addr; loc < addr - 2; loc++) { // addr will be last location written + 2.
266
                for (loc = base_addr; loc < addr; loc++) { // addr will be last location written + 2.
267
                    crc = _crc16_update(crc, pgm_read_byte(loc));
267
                    crc = _crc16_update(crc, pgm_read_byte(loc));
268
                }
268
                }
269
               
269
               
270
                tx_msg.Data.words[0] = crc;
270
                tx_msg.Data.words[0] = crc;
271
                send_msg = 1;
271
                send_msg = 1;
272
                bios_state = BIOS_NOAPP;
272
                bios_state = BIOS_NOAPP;
273
            }
273
            }
274
        }
274
        }
275
       
275
       
276
        if (send_msg) {
276
        if (send_msg) {
277
            while (Can_Send(&tx_msg) != CAN_OK);
277
            while (Can_Send(&tx_msg) != CAN_OK);
278
            send_msg = 0;
278
            send_msg = 0;
279
        }
279
        }
280
 
280
 
281
        bios_msg_full = 0; // We're done with this message, ready for the next.
281
        bios_msg_full = 0; // We're done with this message, ready for the next.
282
    }
282
    }
283
 
283
 
284
    return 0;
284
    return 0;
285
}
285
}
286
 
286