Subversion Repositories HomeAutomation

Rev

Details | Last modification | View Log | SVN | RSS feed

Rev Author Line No. Line
59 jimmy 1
/**
142 jimmy 2
 * CAN communication interface. This is a general, high-level interface for CAN
3
 * communication. Underlying CAN controllers are abstracted by this interface,
4
 * so the application needs not work directly with the CAN controllers.
59 jimmy 5
 *
142 jimmy 6
 * The interface is fully queued in both directions. Both queues can configured
7
 * in terms of size, and can also be disabled completely to save both RAM and
8
 * FLASH space (the queue routines are removed by preprocessor when queues
9
 * are disabled). When queues are enabled, the application has to call
10
 * Can_Service as often as possible, so messages can be moved between the CAN
11
 * controller and the message queues. This might be handled by interrupts in
12
 * future. When queues are disabled, on the other hand, the application has
13
 * to call Can_Receive as often as possible in order not to loose any messages.
59 jimmy 14
 *
142 jimmy 15
 * @date    2006-11-21
16
 *
59 jimmy 17
 * @author  Jimmy Myhrman
18
 *  
19
 */
20
 
21
/*-----------------------------------------------------------------------------
22
 * Includes
23
 *---------------------------------------------------------------------------*/
24
#include <string.h>
25
#include <can.h>
142 jimmy 26
#include <mcu.h>
27
#include <stdio.h>
28
#include <timebase.h>
29
#include <assert.h>
30
#if CAN_CONTROLLER == CAN_CONTROLLER_MCP2515
31
    #include "mcp2515.h"
32
#endif
59 jimmy 33
 
34
 
142 jimmy 35
/*-----------------------------------------------------------------------------
36
 * Prerequisites
37
 *---------------------------------------------------------------------------*/
38
#ifndef CAN_CONTROLLER
39
    #error CAN_CONTROLLER not specified! Edit can_cfg.h !
59 jimmy 40
#endif
41
 
142 jimmy 42
#ifndef CAN_BITRATE
43
    #error CAN_BITRATE not defined! Edit can_cfg.h !
44
#endif
59 jimmy 45
 
142 jimmy 46
#ifndef CAN_QUEUE_SIZE_RX
47
    #error CAN_QUEUE_SIZE_RX not defined! Edit can_cfg.h !
48
#endif
49
 
50
#ifndef CAN_QUEUE_SIZE_TX
51
    #error CAN_QUEUE_SIZE_TX not defined! Edit can_cfg.h !
52
#endif
53
 
54
#ifndef CAN_CONTROLLER_NR_TX_BUFFERS
55
    #error CAN_CONTROLLER_NR_TX_BUFFERS not defined! Edit can_cfg.h !
56
#endif
57
 
58
#ifndef CAN_CONTROLLER_NR_RX_BUFFERS
59
    #error CAN_CONTROLLER_NR_RX_BUFFERS not defined! Edit can_cfg.h !
60
#endif
61
 
62
 
59 jimmy 63
/*-----------------------------------------------------------------------------
142 jimmy 64
 * Type Definitions
59 jimmy 65
 *---------------------------------------------------------------------------*/
66
 
142 jimmy 67
#if (CAN_QUEUE_SIZE_TX > 0) || (CAN_QUEUE_SIZE_RX > 0)
59 jimmy 68
/**
142 jimmy 69
 * CAN Message Queue Type. Stores and manages several CAN messages
70
 * in a FIFO structure.
71
 */
72
typedef struct {
73
    Can_Message_t *dataPtr; /* pointer to the data storage */
74
    uint8_t head;       /* current OUTPUT position */
75
    uint8_t tail;       /* current INPUT position */
76
    uint8_t nrElements; /* nr of elements currently queued */
77
    uint8_t size;       /* total size of the queue */
78
} Can_MessageQueue_t;
79
#endif
80
 
81
 
82
/*-----------------------------------------------------------------------------
83
 * Private (static) variables
84
 *---------------------------------------------------------------------------*/
85
 
86
#if CAN_QUEUE_SIZE_TX > 0
87
    /* TX queue object and static memory storage buffer for this queue */
88
    static Can_MessageQueue_t canTxQueue;
89
    static Can_Message_t canTxQueueDataStorage[CAN_QUEUE_SIZE_TX];
90
#endif
91
#if CAN_QUEUE_SIZE_RX > 0
92
    /* RX queue object and static memory storage buffer for this queue */
93
    static Can_MessageQueue_t canRxQueue;
94
    static Can_Message_t canRxQueueDataStorage[CAN_QUEUE_SIZE_RX];
95
#endif
96
 
97
 
98
/*-----------------------------------------------------------------------------
99
 * Private Function Prototypes
100
 *---------------------------------------------------------------------------*/
101
 
102
/* functions that deal with the controller hardware */
103
static Can_Return_t Can_SendImmediately(Can_Message_t *msg);
104
static Can_Return_t Can_ReceiveFromController(Can_Message_t *msg, uint8_t rxBuffer);
105
static uint8_t Can_MessagesAvailableInController(void);
106
static Can_Return_t Can_ControllerCheckError(void);
107
 
108
#if (CAN_QUEUE_SIZE_TX > 0) || (CAN_QUEUE_SIZE_RX > 0)
109
/* functions that deal with the message queues */
110
static void Can_QueueInit(Can_MessageQueue_t *q, uint8_t size, Can_Message_t *dataPtr);
111
static Can_Message_t* Can_QueueInsert(Can_MessageQueue_t *q);
112
static Can_Message_t* Can_QueueReadTailPtr(Can_MessageQueue_t *q);
113
static void Can_QueueRemoveTail(Can_MessageQueue_t *q);
114
#endif
115
 
116
 
117
/*-----------------------------------------------------------------------------
118
 * Public Functions
119
 *---------------------------------------------------------------------------*/
120
 
121
/**
127 jimmy 122
 * Initializes the CAN interface. Edit can_cfg.h to choose bitrate.
59 jimmy 123
 *
124
 * @return
125
 *      CAN_OK if initialization was successful.
142 jimmy 126
 *      CAN_INIT_FAIL_SET_BITRATE if bitrate could not be set correctly.
127
 *      CAN_INIT_FAIL_SET_MODE if the controller could not be set to normal operation mode.
128
 *      CAN_INIT_FAIL in case of general error.
59 jimmy 129
 */
127 jimmy 130
Can_Return_t Can_Init() {
142 jimmy 131
    #if CAN_QUEUE_SIZE_TX > 0
132
        /* initialize TX queue */
133
        Can_QueueInit(&canTxQueue, CAN_QUEUE_SIZE_TX, canTxQueueDataStorage);
134
    #endif
135
    #if CAN_QUEUE_SIZE_RX > 0
136
        /* initialize RX queue */
137
        Can_QueueInit(&canRxQueue, CAN_QUEUE_SIZE_RX, canRxQueueDataStorage);
138
    #endif
139
 
140
    #if CAN_CONTROLLER == CAN_CONTROLLER_NULL
141
        /*
142
         * Initialize null device.
143
         */
144
        return CAN_OK;
145
    #endif
146
 
147
    #if CAN_CONTROLLER == CAN_CONTROLLER_MCP2515
148
        /*
149
         * Initialize MCP2515 device.
150
         */
151
        if (MCP2515_Init(CAN_BITRATE) != MCP2515_OK) {
152
            return CAN_FAIL;
153
        }
154
        if (MCP2515_SetCanCtrlMode(MODE_NORMAL) != MCP2515_OK) {
155
            return CAN_FAIL;
156
        }
157
        return CAN_OK;
158
    #endif
159
 
160
    return CAN_FAIL;
161
}
59 jimmy 162
 
142 jimmy 163
 
164
/**
165
 * Sends a CAN message. The message will be put into the transmission queue,
166
 * and hence, it might not be sent immediately. If the queue is full, the
167
 * message will not be taken care of, and CAN_FAIL is returned.
168
 *
169
 * @param msg
170
 *      Pointer to the CAN message.
171
 *
172
 * @return
173
 *      CAN_OK if the message was successfully enqueued.
174
 *      CAN_FAIL if the transmission queue is full.
175
 */
176
Can_Return_t Can_Send(Can_Message_t *msg) {
177
    #if CAN_QUEUE_SIZE_TX == 0
178
        /* TX queue is disabled, so send immediately */
179
        return Can_SendImmediately(msg);
180
    #else
181
        #if 0
182
        printf("Can_Send()\n\r");
183
        printf("    queue size before = %u\n\r", canTxQueue.nrElements);
184
        #endif
185
        /* check if there is room available in the tx queue */
186
        if (canTxQueue.nrElements >= canTxQueue.size) {
187
            #if 0
188
            printf("    TX queue full!\n\r");
189
            #endif
190
            return CAN_FAIL;
191
        }
192
        /* put message in queue */
193
        Can_Message_t *ptr = Can_QueueInsert(&canTxQueue);
194
        memcpy(ptr, msg, sizeof(Can_Message_t));
195
        #if 0
196
        printf("    queue size after = %u\n\r", canTxQueue.nrElements);
197
        #endif
198
        return CAN_OK;
199
    #endif
200
}
201
 
202
 
203
/**
204
 * Services the CAN subsystem. Messages waiting in the transmission queue
205
 * will be transmitted if the controller is not busy, and any messages
206
 * received by the controller will be moved from the controller buffers
207
 * into the internal reception queue.
208
 */
209
void Can_Service() {
210
    #if CAN_QUEUE_SIZE_TX > 0
211
        /* try to transmit messages waiting in the transmission queue */
212
        for (uint8_t i=0; i<CAN_CONTROLLER_NR_TX_BUFFERS; i++) {
213
            if (canTxQueue.nrElements > 0) {
214
                #if 0
215
                printf("msg in TX queue. trying to send...\n");
216
                #endif
217
                /* ensure the queue operations are atomic */
218
                Mcu_DisableIRQ();
219
                Can_Message_t *txMsg = Can_QueueReadTailPtr(&canTxQueue);
220
                if (Can_SendImmediately(txMsg) == CAN_OK) {
221
                    /* transmission OK, remove from queue */
222
                    Can_QueueRemoveTail(&canTxQueue);
223
                }
224
                Mcu_EnableIRQ();
225
            }
226
            else {
227
                /* quit the loop if the queue is empty */
228
                break;
229
            }
230
        }
231
    #endif
59 jimmy 232
 
142 jimmy 233
    #if CAN_QUEUE_SIZE_RX > 0
234
        /* move all received messages from CAN controller to reception queue */
235
        Can_Message_t rxMsg;
236
        /* all RX buffers have to be checked (and in correct order) */
237
        for (uint8_t buf=0; buf<CAN_CONTROLLER_NR_RX_BUFFERS; buf++) {
238
            /* if buffer contains a message, handle it */
239
            if (Can_ReceiveFromController(&rxMsg, buf) == CAN_OK) {
240
                /* ensure the queue operations are atomic */
241
                Mcu_DisableIRQ();
242
                /* try to get access to the queue */
243
                Can_Message_t *destPtr = Can_QueueInsert(&canRxQueue);
244
                /* if we can insert into queue, copy the message */
245
                if (destPtr != 0) {
246
                    memcpy(destPtr, &rxMsg, sizeof(Can_Message_t));
247
                }
248
                else {
249
                    /* otherwise, the received message will be discarded... */
250
                }
251
                Mcu_EnableIRQ();
252
            }
253
        }
254
    #endif
255
 
256
    /* TODO: check error status in controller */
59 jimmy 257
}
258
 
259
 
260
/**
142 jimmy 261
 * Receives a CAN message that is waiting in the reception queue. If no
262
 * messages have been received, CAN_NO_MSG_AVAILABLE is returned.
263
 * Otherwise, a CAN message is copied into the specified message buffer
264
 * and deleted from the internal message reception queue. In this case,
265
 * CAN_OK is returned.
266
 *
267
 * @param msg
268
 *          Pointer to the message storage buffer into which the message
269
 *          should be copied.
270
 *
271
 * @return
272
 *          CAN_OK if a received message was successfully copied into the buffer.
273
 *          CAN_NO_MSG_AVAILABLE if no messages are available.
274
 */
275
Can_Return_t Can_Receive(Can_Message_t *msg) {
276
 
277
    #if CAN_QUEUE_SIZE_RX > 0
278
        /*
279
         * RX queue is enabled, so try to get message from queue.
280
         */
281
        Can_Message_t *pSrc;    /* Source pointer */
282
        /* Check if there is anything available in the RxQueue */
283
        if (canRxQueue.nrElements == 0) {
284
            return CAN_NO_MSG_AVAILABLE;
285
        }
286
        /* ensure the queue operations are atomic */
287
        Mcu_DisableIRQ();
288
        pSrc = Can_QueueReadTailPtr(&canRxQueue);
289
        /* copy frame from queue, and then remove from queue */
290
        memcpy(msg, pSrc, sizeof(Can_Message_t));
291
        Can_QueueRemoveTail(&canRxQueue);
292
        Mcu_EnableIRQ();
293
        /* a message has successfully been copied and removed from queue */
294
        return CAN_OK;
295
    #else
296
        /*
297
         * RX queue is disabled, so try to get message from controller.
298
         * We don't know which buffer to get the message from, so best
299
         * we can do is to get from buf0 first time, and then increase
300
         * buffer number at each call, finally wrapping around at
301
         * CAN_CONTROLLER_NR_RX_BUFFERS.
302
         */
303
        static uint8_t rxBuffer = 0;
304
        /* worst case is that we need to check all available rx buffers in order to find a message */
305
        for (uint8_t i=0; i<CAN_CONTROLLER_NR_RX_BUFFERS; i++) {
306
            /* is there a message available in this buffer? */
307
            if (Can_ReceiveFromController(msg, rxBuffer) == CAN_OK) {
308
                /* increase buffer number and return the message */
309
                rxBuffer = (rxBuffer + 1) % CAN_CONTROLLER_NR_RX_BUFFERS;
310
                return CAN_OK;
311
            }
312
            /* increase buffer number so we can check next buffer */
313
            rxBuffer = (rxBuffer + 1) % CAN_CONTROLLER_NR_RX_BUFFERS;
314
        }
315
        /* all buffers were checked, but no message found */
316
        return CAN_NO_MSG_AVAILABLE;
317
    #endif
318
}
319
 
320
 
321
 
322
/*-----------------------------------------------------------------------------
323
 * Private Functions
324
 *---------------------------------------------------------------------------*/  
325
 
326
/**
327
 * Sends a CAN message immediately with the controller hardware. If the CAN
328
 * controller is busy, the function will return CAN_SEND_FAIL_TX_BUSY.
59 jimmy 329
 *
330
 * @param msg
331
 *      Pointer to the CAN message storage buffer.
142 jimmy 332
 *
59 jimmy 333
 * @return
142 jimmy 334
 *      CAN_OK if the message was successfully sent to the controller.
335
 *      CAN_FAIL if the controller is busy.
59 jimmy 336
 */
142 jimmy 337
static Can_Return_t Can_SendImmediately(Can_Message_t* msg) {
338
    #if CAN_CONTROLLER == CAN_CONTROLLER_NULL
339
        /*
340
         * Send with null device.
341
         */
342
        return CAN_OK;
343
    #endif
59 jimmy 344
 
142 jimmy 345
    #if CAN_CONTROLLER == CAN_CONTROLLER_MCP2515
346
        /*
347
         * Send with MCP2515 device.
348
         */
349
        uint8_t res, txbuf_n;
350
        res = MCP2515_GetNextFreeTXBuf(&txbuf_n); // info = addr.
351
        if (res == MCP_ALLTXBUSY) {
352
            return CAN_FAIL;
353
        }
354
        MCP2515_WriteCanMsg(txbuf_n, msg);
355
        MCP2515_StartTransmit(txbuf_n);
356
        return CAN_OK;
357
    #endif
59 jimmy 358
}
359
 
360
 
361
/**
142 jimmy 362
 * Receives a CAN message from the CAN controller hardware.
59 jimmy 363
 *
364
 * @param msg
142 jimmy 365
 *      Pointer to the message storage buffer into which the message should be copied.
366
 *
367
 * @param rxBuffer
368
 *      Identifies the RX buffer in the controller. Range is [0,CAN_CONTROLLER_NR_RX_BUFFERS-1].
369
 *
59 jimmy 370
 * @return
371
 *      CAN_OK if a received message was successfully copied into the buffer.
142 jimmy 372
 *      CAN_NO_MSG_AVAILABLE if there are is no message available in the specified buffer.
373
 *      CAN_FAIL if the rxBuffer parameter i out of range for the specified controller.
59 jimmy 374
 */
142 jimmy 375
static Can_Return_t Can_ReceiveFromController(Can_Message_t *msg, uint8_t rxBuffer) {
376
    #if CAN_CONTROLLER == CAN_CONTROLLER_NULL
377
        /*
378
         * Receive from null device.
379
         */
380
        return CAN_NO_MSG_AVAILABLE;
381
    #endif
59 jimmy 382
 
142 jimmy 383
    #if CAN_CONTROLLER == CAN_CONTROLLER_MCP2515
384
        /*
385
         * Receive from MCP2515 device.
386
         */
387
        uint8_t stat;
388
        stat = MCP2515_ReadStatus();
389
        if (rxBuffer == 0) {
390
            /* check BUF0 */
391
            if (stat & MCP_STAT_RX0IF) {
392
                /* Msg in Buffer 0 */
393
                MCP2515_ReadCanMsg(MCP_RXBUF_0, msg);
394
                MCP2515_ModifyRegister(MCP_CANINTF, MCP_RX0IF, 0);
395
                return CAN_OK;
396
            }
397
        }
398
        else if (rxBuffer == 1) {
399
            /* check BUF1 */
400
            if (stat & MCP_STAT_RX1IF) {
401
                /* Msg in Buffer 1 */
402
                MCP2515_ReadCanMsg(MCP_RXBUF_1, msg);
403
                MCP2515_ModifyRegister(MCP_CANINTF, MCP_RX1IF, 0);
404
                return CAN_OK;
405
            }
406
        }
407
        else {
408
            /* invalid parameters */
409
            return CAN_FAIL;
410
        }
411
        return CAN_NO_MSG_AVAILABLE;
412
    #endif
59 jimmy 413
}
414
 
415
 
416
/**
142 jimmy 417
 * Checks how many messages are available in the CAN controller hardware.
59 jimmy 418
 *
419
 * @return
142 jimmy 420
 *      The number of messages available (0 if none).
59 jimmy 421
 */
142 jimmy 422
static uint8_t Can_MessagesAvailableInController() {
423
    #if CAN_CONTROLLER == CAN_CONTROLLER_NULL
424
        /*
425
         * Check for messages in null device.
426
         */
427
        return 0;
428
    #endif
59 jimmy 429
 
142 jimmy 430
    #if CAN_CONTROLLER == CAN_CONTROLLER_MCP2515
431
        /*
432
         * Check for message in MCP2515 device.
433
         */
434
        uint8_t res;
435
        res = MCP2515_ReadStatus(); /* RXnIF in Bit 1 and 0 */
436
        if (res & MCP_STAT_RXIF_MASK) {
437
            //TODO: check how many messages are available
438
            return 1;   /* at least one message available */
439
        }
440
        else {
441
            return 0;   /* no messages available */
442
        }
443
    #endif
444
 
445
    return 0;
59 jimmy 446
}
447
 
448
 
449
/*
450
 * Checks Controller-Error-State.
451
 *
452
 * @return
453
 *      CAN_OK if the controller is OK.
142 jimmy 454
 *      CAN_FAIL if errors have occured.
455
 *
59 jimmy 456
 * @todo
457
 *      Styr upp denna funktion lite bättre.
458
 */
142 jimmy 459
static Can_Return_t Can_ControllerCheckError() {
460
    #if CAN_CONTROLLER == CAN_CONTROLLER_NULL
461
        /*
462
         * Check for errors in null device.
463
         */
464
        return CAN_OK;
465
    #endif
59 jimmy 466
 
142 jimmy 467
    #if CAN_CONTROLLER == CAN_CONTROLLER_MCP2515
468
        /*
469
         * Check for errors in MCP2515 device.
470
         */
471
        uint8_t eflg = MCP2515_ReadRegister(MCP_EFLG);
472
        if (eflg & MCP_EFLG_ERRORMASK) {
473
            return CAN_FAIL;    /* errors found */
474
        }
475
        else {
476
            return CAN_OK;      /* no errors found */
477
        }
478
    #endif
479
}
480
 
481
 
482
#if (CAN_QUEUE_SIZE_TX > 0) || (CAN_QUEUE_SIZE_RX > 0)
483
/**
484
 * Initializes a CAN queue.
485
 *
486
 * @param q
487
 *      Pointer to the queue object.
488
 *
489
 * @param size
490
 *      Size of the queue (number of messages that can be queued).
491
 *
492
 * @param dataPtr
493
 *      Pointer to the message storage buffer that should be used by the queue.
494
 */
495
static void Can_QueueInit(Can_MessageQueue_t *q, uint8_t size, Can_Message_t *dataPtr) {
496
    q->dataPtr = dataPtr;
497
    q->head = 0;
498
    q->tail = 0;
499
    q->nrElements = 0;
500
    q->size = size;
501
}
502
 
503
 
504
/**
505
 * Inserts a new element in a queue and returns a pointer to the new element.
506
 * If the queue is full, the last element (the tail) will be overwritten by
507
 * the new element.
508
 *
509
 * @param q
510
 *      Pointer to the queue object.
511
 *
512
 * @return
513
 *      Pointer to the new message element. Use this pointer to copy data into the queue.
514
 */
515
static Can_Message_t* Can_QueueInsert(Can_MessageQueue_t *q) {
516
    Can_Message_t *ptr;
517
    #if 0
518
    printf("INSERT: bef=%u, ", q->nrElements);
519
    #endif
520
    if ((q->nrElements+1) > q->size) {
521
        q->tail = (q->tail + 1) % q->size;
522
        q->nrElements--;
523
    #if 0
524
        printf("Can_QueueInsertViaPtr: QUEUE OVERRUN!\n\r");
525
    #endif
526
    }
527
    ptr = &(q->dataPtr[q->head]);
528
    #if 0
529
    if (q->handled[q->head] == 0) {
530
        printf("Can_QueueInsertViaPtr: discarding non-handled msg!\n\r");
531
    }
532
    #endif
533
    q->head = (q->head + 1) % q->size;
534
    q->nrElements++;
535
    #if 0
536
    printf("aft=%u\n\n", q->nrElements);
537
    #endif
538
    return ptr;
539
}
540
 
541
 
542
/**
543
 * Returns a pointer to the last element in the queue (the tail). Use this pointer
544
 * to extract the message, and then call Can_QueueRemoveTail to actually remove
545
 * the element.
546
 *
547
 * @param q
548
 *      Pointer to the queue object.
549
 *
550
 * @return
551
 *      Pointer to the tail message element.
552
 */
553
static Can_Message_t* Can_QueueReadTailPtr(Can_MessageQueue_t *q) {
554
    if (q->nrElements > 0) {
555
        return &(q->dataPtr[q->tail]);
59 jimmy 556
    }
557
    else {
142 jimmy 558
        return 0;
59 jimmy 559
    }
142 jimmy 560
}
59 jimmy 561
 
142 jimmy 562
 
563
/**
564
 * Removes the last element in the queue (the tail). You should have extracted
565
 * the data using Can_QueueReadTailPtr before using this function.
566
 *
567
 * @param q
568
 *      Pointer to the queue object.
569
 */
570
static void Can_QueueRemoveTail(Can_MessageQueue_t *q) {
571
    if (q->nrElements > 0) {
572
        q->tail = (q->tail + 1) % q->size;
573
        q->nrElements--;
574
        #if 0
575
        printf("queue size = %u\n", q->nrElements);
576
        #endif
577
    }
59 jimmy 578
}
142 jimmy 579
 
580
#endif