Subversion Repositories HomeAutomation

Rev

Rev 249 | Rev 320 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | SVN | RSS feed

Rev 249 Rev 317
Line 3... Line 3...
3
 * Takes incomming packages and adjusts PWM output.
3
 * Takes incomming packages and adjusts PWM output.
4
 * Input: -128 to +127
4
 * Input: -128 to +127
5
 * Position msg: <byte0 BLINDS_CMD> <byte1 position> <byte2 servo number>
5
 * Position msg: <byte0 BLINDS_CMD> <byte1 position> <byte2 servo number>
6
 * Turning msg: <byte0 START/STOP> <byte 1 direction> <byte2 servo number>
6
 * Turning msg: <byte0 START/STOP> <byte 1 direction> <byte2 servo number>
7
 *
7
 *
8
 * @date    2001-01-01
8
 * @date    2007-01-01
9
 * @author  Erik Larsson
9
 * @author  Erik Larsson
10
 *
10
 *
11
 * TODO fixa en ordentlig failsafe som kan bryta matningen till servona
-
 
12
 */
11
 */
13
 
12
 
14
#define TWO_BUTTON_MODE 0 /* If using two (or just one but nothing more) auxiliary buttons */
13
//#define TWO_BUTTON_MODE /* If using two (or just one but nothing more) auxiliary buttons */
15
 
14
 
16
/*-----------------------------------------------------------------------------
15
/*-----------------------------------------------------------------------------
17
 * Includes
16
 * Includes
18
 *---------------------------------------------------------------------------*/
17
 *---------------------------------------------------------------------------*/
19
/* system files */
18
/* system files */
20
#include <avr/io.h>
19
#include <avr/io.h>
21
#include <avr/interrupt.h>
20
#include <avr/interrupt.h>
22
#include <avr/wdt.h>
21
#include <avr/wdt.h>
23
#include <stdio.h>
22
#include <stdio.h>
24
/* lib files */
23
/* lib files */
25
#include <can.h>
24
#include <bios.h>
26
#include <timebase.h>
-
 
27
 
-
 
28
#include <tc1047.h>
25
#include <tc1047.h>
29
#include <rc_servo.h>
26
#include <rc_servo.h>
-
 
27
 
-
 
28
#include <eqlazer_funcdefs.h>
30
 
29
 
31
/* defines */
30
/* defines */
-
 
31
#define NUMBER_OF_RCS 1
-
 
32
#define NODE_ID     0x03L // FIXME ändras för varje nod
-
 
33
#define GROUP_ID    0x01L // ^
-
 
34
 
32
#define FAILSAFE_MODE       0x0F
35
#define FAILSAFE_MODE       0x0F
33
#define FAILSAFE_TRESHOLD   60  /* Degrees celcius when nodeBlinds goes into failsafe mode */
36
#define FAILSAFE_TRESHOLD   60  /* Degrees celcius when nodeBlinds goes into failsafe mode */
34
 
37
 
35
#define BLINDS_CMD_GET      0x1300
-
 
36
#define BLINDS_CMD_SEND     0x1301
-
 
37
 
-
 
38
#define BLINDS_CMD_ABS      0x01 /* Absolute position */
38
#define BLINDS_CMD_ABS      0x01 /* Absolute position */
39
#define BLINDS_CMD_REL      0x02 /* Relative position */
39
#define BLINDS_CMD_REL      0x02 /* Relative position */
40
#define BLINDS_CMD_START    0x03 /* Start turning RC-servo */
40
#define BLINDS_CMD_START    0x03 /* Start turning RC-servo */
41
#define BLINDS_CMD_STOP     0x04 /* Stop turning RC-servo */
41
#define BLINDS_CMD_STOP     0x04 /* Stop turning RC-servo */
42
#define BLINDS_CMD_RESET    0x05
42
#define BLINDS_CMD_RESET    0x05
Line 45... Line 45...
45
#define BLINDS_TURN_STOP    0x00
45
#define BLINDS_TURN_STOP    0x00
46
#define BLINDS_TURN_LEFT    0x01
46
#define BLINDS_TURN_LEFT    0x01
47
#define BLINDS_TURN_RIGHT   0x02
47
#define BLINDS_TURN_RIGHT   0x02
48
 
48
 
49
#define STEPS_PER_TURN_PERIOD 10 /* number of steps for each adjustment */
49
#define STEPS_PER_TURN_PERIOD   10 /* number of steps for each adjustment */
50
#define TURN_PERIOD         100 /* milliseconds */
50
#define TURN_PERIOD             100 /* ms */
51
#define STATUS_SEND_PERIOD  10000 /* milliseconds */
51
#define STATUS_SEND_PERIOD      10000 /* ms */
52
#define FAILSAFE_SEND_PERIOD    10000 /* milliseconds */
52
#define FAILSAFE_SEND_PERIOD    10000 /* ms */
53
#define BOUNCE_TIME         10 /* milliseconds */
53
#define BOUNCE_TIME             10 /* ms */
54
#define BUTTON_ID           0x1202
54
#define SERVO_FEED_TIME         2000 /* ms */
55
#define BUTTON1             1
55
#define BUTTON1                 1
56
#define BUTTON2             2
56
#define BUTTON2                 2
-
 
57
 
-
 
58
#define TRUE 1
-
 
59
#define FALSE !TRUE
57
 
60
 
58
/*-------------------------------------------------------------------------
61
/*-------------------------------------------------------------------------
59
 * Global variables
62
 * Global variables
60
 * -----------------------------------------------------------------------*/
63
 * -----------------------------------------------------------------------*/
61
#if TWO_BUTTON_MODE
64
/*#if defined(TWO_BUTTON_MODE)
62
Can_Message_t txMsg_Button;
65
Can_Message_t txMsg_Button;
63
#endif
66
#endif*/
-
 
67
 
-
 
68
uint8_t msg_received = FALSE;
-
 
69
 
-
 
70
Can_Message_t txMsg;
-
 
71
Can_Message_t rxMsg;
64
 
72
 
65
/*----------------------------------------------------------------------------
73
/*----------------------------------------------------------------------------
66
 * Functions
74
 * Functions
67
 * -------------------------------------------------------------------------*/
75
 * -------------------------------------------------------------------------*/
68
void blindsFailsafe();
76
void blindsFailsafe();
-
 
77
 
-
 
78
/* Takes care about incoming messages and parses them if this node is adressed */
-
 
79
void can_receive(Can_Message_t *msg){
-
 
80
    uint32_t act;
-
 
81
    uint8_t m, act_type, group, node;
-
 
82
    if( ((msg->Id & CLASS_MASK) >> CLASS_MASK_BITS) == CLASS_ACT ){
-
 
83
        /* Parse message */
-
 
84
        act = (msg->Id & TYPE_MASK) >> TYPE_MASK_BITS;
-
 
85
        act_type = (act & ACT_TYPE_MASK) >> ACT_TYPE_BITS;
-
 
86
        group = (act & ACT_GROUP_MASK) >> ACT_GROUP_BITS;
-
 
87
        node = (act & ACT_NODE_MASK) >> ACT_NODE_BITS;
-
 
88
 
-
 
89
        /* Check if it is command to control this servo */
-
 
90
        if(act_type == ACT_TYPE_SERVO){
-
 
91
            if(group == GROUP_ID || node == NODE_ID){
-
 
92
                for(m=0;m<msg->DataLength;m++){
-
 
93
                    rxMsg.Data.bytes[m] = msg->Data.bytes[m];
-
 
94
                }
-
 
95
                msg_received = TRUE;
-
 
96
            }
-
 
97
        }
-
 
98
    }
-
 
99
}
69
 
100
 
70
/*----------------------------------------------------------------------------
101
/*----------------------------------------------------------------------------
71
 * Interrupt routines
102
 * Interrupt routines
72
 * -------------------------------------------------------------------------*/
103
 * -------------------------------------------------------------------------*/
73
#if TWO_BUTTON_MODE
104
#if defined(TWO_BUTTON_MODE)
-
 
105
#error two button mode is still untested
74
ISR( PCINT2_vect )
106
ISR( PCINT2_vect )
75
{
107
{
76
    /*
108
    /*
77
     * Auxiliary button pressed and released
109
     * Auxiliary button pressed and released
78
     * Check time between press and release to eliminate bounces
110
     * Check time between press and release to eliminate bounces
Line 80... Line 112...
80
    static uint32_t buttonTime[2];
112
    static uint32_t buttonTime[2];
81
 
113
 
82
    txMsg_Button.RemoteFlag = 0;
114
    txMsg_Button.RemoteFlag = 0;
83
    txMsg_Button.ExtendedFlag = 1;
115
    txMsg_Button.ExtendedFlag = 1;
84
 
116
 
85
    if( (PIND & (1<<PD6)) == 0){
117
    if( (PIND & (1<<PD1)) == 0){
86
        /* Button pressed */
118
        /* Button pressed */
87
        buttonTime[0] = Timebase_CurrentTime();
119
        buttonTime[0] = Timebase_CurrentTime();
88
    }else if( (PIND & (1<<PD6)) == 1){
120
    }else if( (PIND & (1<<PD1)) == 1){
89
        if( Timebase_PassedTimeMillis( buttonTime[0] ) >= BOUNCE_TIME ){
121
        if( Timebase_PassedTimeMillis( buttonTime[0] ) >= BOUNCE_TIME ){
90
            /* No bounce, send message */
122
            /* No bounce, send message */
91
            txMsg_Button.Id = BUTTON_ID;
123
            txMsg_Button.Id = BUTTON_ID;
92
            txMsg_Button.DataLength = 1;
124
            txMsg_Button.DataLength = 1;
93
            txMsg_Button.Data.bytes[0] = BUTTON1;
125
            txMsg_Button.Data.bytes[0] = BUTTON1;
Line 95... Line 127...
95
        }else{
127
        }else{
96
            /* Just bounces, ignore it */
128
            /* Just bounces, ignore it */
97
            return;
129
            return;
98
        }
130
        }
99
    }
131
    }
100
    if( (PIND & (1<<PD7)) == 0){
132
    if( (PIND & (1<<PD2)) == 0){
101
        /* Button pressed */
133
        /* Button pressed */
102
        buttonTime[1] = Timebase_CurrentTime();
134
        buttonTime[1] = Timebase_CurrentTime();
103
    }else if( (PIND & (1<<PD7)) == 1){
135
    }else if( (PIND & (1<<PD2)) == 1){
104
        if( Timebase_PassedTimeMillis( buttonTime[1] ) >= BOUNCE_TIME ){
136
        if( Timebase_PassedTimeMillis( buttonTime[1] ) >= BOUNCE_TIME ){
105
            /* No bounce, send message */
137
            /* No bounce, send message */
106
            txMsg_Button.Id = BUTTON_ID;
138
            txMsg_Button.Id = BUTTON_ID;
107
            txMsg_Button.DataLength = 1;
139
            txMsg_Button.DataLength = 1;
108
            txMsg_Button.Data.bytes[0] = BUTTON2;
140
            txMsg_Button.Data.bytes[0] = BUTTON2;
109
            return;
141
            return;
110
        }else{
142
        }else{
111
            /* Just bounces, ignore it */
143
            /* Just bounces, ignore it */
112
            return;
144
            return;
113
        }
145
        }
114
    }
146
    }
115
}
147
}
116
#endif
148
#endif
117
 
149
 
118
/*-----------------------------------------------------------------------------
150
/*-----------------------------------------------------------------------------
119
 * Main Program
151
 * Main Program
120
 *---------------------------------------------------------------------------*/
152
 *---------------------------------------------------------------------------*/
121
int main(void) {
153
int main(void) {
122
 
154
 
123
    uint8_t blindsStatus, /* Position (-128 to 127) */
155
    uint8_t //blindsStatus, /* Position (-128 to 127) */
124
            servoToTurn,
156
            servoToTurn,
125
            turnDirection = BLINDS_TURN_STOP; /* Specifies direction to turn or stopped */
157
            turnDirection = BLINDS_TURN_STOP; /* Specifies direction to turn or stopped */
126
    uint32_t boardTemperature = 0;
158
    uint32_t boardTemperature = 0;
127
    uint32_t timeStamp = 0, timeStampTurn = 0;
159
    uint32_t timeStamp = 0, timeStampTurn = 0, servoFeed_timeStamp = 0;
128
 
160
 
129
    adcTemperatureInit();
161
    adcTemperatureInit();
130
    Timebase_Init();
-
 
131
    rcServoInit();
162
    rcServoInit();
132
 
163
 
133
#if TWO_BUTTON_MODE
164
#if defined(TWO_BUTTON_MODE)
134
    /*
165
    /*
135
     * Initialize buttons
166
     * Initialize buttons
136
     * It is possible to use ie. sensors instead
167
     * It is possible to use ie. sensors instead
137
     * but then change this
168
     * but then change this
138
     */
169
     */
139
    /* Set as input and enable pull-up */
170
    /* Set as input and enable pull-up */
140
    DDRD &= ~( (1<<DDD6)|(1<<DDD7) );
171
    DDRD &= ~( (1<<DDD1)|(1<<DDD2) );
141
    PORTD |= (1<<PD6)|(1<<PD7);
172
    PORTD |= (1<<PD1)|(1<<PD2);
142
 
173
 
143
    /* Enable IO-pin interrupt */
174
    /* Enable IO-pin interrupt */
144
    PCICR |= (1<<PCIE1);
175
    PCICR |= (1<<PCIE2);
145
    /* Unmask PD6 and PD7 */
176
    /* Unmask PD1 and PD2 */
146
    PCMSK2 |= (1<<PCINT22) | (1<<PCINT23);
177
    PCMSK2 |= (1<<PCINT16) | (1<<PCINT17);
147
#endif
178
#endif
-
 
179
    /* Set output and turn off the servo current feed */
-
 
180
    DDRC |= (1<<DDC0);
-
 
181
    PORTC &= ~(1<<PC0);
-
 
182
 
148
    sei();
183
    sei();
149
    Can_Init();
-
 
150
 
184
 
151
    Can_Message_t txMsg;
-
 
152
    Can_Message_t rxMsg;
-
 
153
    txMsg.Id = BLINDS_CMD_SEND;
-
 
154
    txMsg.RemoteFlag = 0;
185
    txMsg.RemoteFlag = 0;
155
    txMsg.ExtendedFlag = 1;
186
    txMsg.ExtendedFlag = 1;
-
 
187
    txMsg.Id = (CLASS_SNS << CLASS_MASK_BITS)|(SNS_ACT_STATUS_SERVO << TYPE_MASK_BITS)|(NODE_ID << SID_MASK_BITS);
-
 
188
 
-
 
189
    /* Set up callback for CAN messages */
156
    txMsg.DataLength = 3;
190
    bios->can_callback = &can_receive;
157
 
191
 
158
    /* main loop */
192
    /* main loop */
159
    while (1) {
193
    while (1) {
160
        /* service the CAN routines */
-
 
161
        Can_Service();
-
 
162
 
-
 
163
        /* check if any messages have been received */
194
        /* check if any messages have been received */
164
        while (Can_Receive(&rxMsg) == CAN_OK) {
195
        if(msg_received){
165
            /* This node that control a servo is adressed */
196
            /* This node that control a servo is adressed */
166
            if( rxMsg.Id == BLINDS_CMD_GET ){
-
 
167
 
197
 
168
                if( rxMsg.Data.bytes[0] == BLINDS_CMD_ABS ){
198
            if( rxMsg.Data.bytes[0] == BLINDS_CMD_ABS ){
169
                    /* Set absolute position to servo */
199
                /* Set absolute position to servo */
170
                    setPosition( rxMsg.Data.bytes[1], rxMsg.Data.bytes[2] );
200
                setPosition( rxMsg.Data.bytes[1], rxMsg.Data.bytes[2] );
-
 
201
                timeStampTurn = bios->timebase_get();
171
 
202
 
172
                }else if(rxMsg.Data.bytes[0] == BLINDS_CMD_REL){
203
            }else if(rxMsg.Data.bytes[0] == BLINDS_CMD_REL){
173
                    /* Set relative position to servo */
204
                /* Set relative position to servo */
174
                    alterPosition( rxMsg.Data.bytes[1], rxMsg.Data.bytes[2] );
205
                alterPosition( rxMsg.Data.bytes[1], rxMsg.Data.bytes[2] );
-
 
206
                timeStampTurn = bios->timebase_get();
175
 
207
 
176
                }else if(rxMsg.Data.bytes[0] == BLINDS_CMD_START ){
208
            }else if(rxMsg.Data.bytes[0] == BLINDS_CMD_START ){
177
                    /* Start turning the servo */
209
                /* Start turning the servo */
178
                    turnDirection = rxMsg.Data.bytes[1];
210
                turnDirection = rxMsg.Data.bytes[1];
179
                    servoToTurn = rxMsg.Data.bytes[2];
211
                servoToTurn = rxMsg.Data.bytes[2];
-
 
212
                timeStampTurn = bios->timebase_get();
180
 
213
 
181
                }else if(rxMsg.Data.bytes[0] == BLINDS_CMD_STOP ){
214
            }else if(rxMsg.Data.bytes[0] == BLINDS_CMD_STOP ){
182
                    /* Stop turning */
215
                /* Stop turning */
183
                    turnDirection = BLINDS_TURN_STOP;
216
                turnDirection = BLINDS_TURN_STOP;
184
 
217
 
Line 200... Line 233...
200
#endif
233
#endif
201
#if NUMBER_OF_RCS >2
234
#if NUMBER_OF_RCS >2
202
                        txMsg.Data.bytes[5] = getPosition(3);
235
                    txMsg.Data.bytes[5] = getPosition(3);
203
#endif
236
#endif
204
                        txMsg.DataLength = NUMBER_OF_RCS+3;
237
                    txMsg.DataLength = NUMBER_OF_RCS+3;
205
                        Can_Send( &txMsg );
238
                    bios->can_send(&txMsg);
206
                    }
239
                    }
207
                }
240
                }
-
 
241
                msg_received = FALSE;
208
            }
242
        }
209
        }
-
 
210
 
243
 
211
        if( Timebase_PassedTimeMillis(timeStamp) >= STATUS_SEND_PERIOD ){
244
        if( bios->timebase_get() - timeStamp  >= STATUS_SEND_PERIOD ){
212
            timeStamp = Timebase_CurrentTime();
245
            timeStamp = bios->timebase_get();
213
            /* Send blinds status to CAN */
246
            /* Send blinds status to CAN */
214
 
247
 
215
            boardTemperature = getTC1047temperature();
248
            boardTemperature = getTC1047temperature();
216
 
249
 
217
            if( (int32_t)boardTemperature >= FAILSAFE_TRESHOLD ){
250
            if( (int32_t)boardTemperature >= FAILSAFE_TRESHOLD ){
218
                blindsFailsafe();
251
                blindsFailsafe();
219
            }else{
252
            }else{
220
                txMsg.Data.bytes[0] = boardTemperature & 0x00FF;
253
                txMsg.Data.bytes[0] = boardTemperature & 0x00FF;
221
                txMsg.Data.bytes[1] = (boardTemperature & 0xFF00)>>8;
254
                txMsg.Data.bytes[1] = (boardTemperature & 0xFF00)>>8;
222
                txMsg.Data.bytes[2] = 0x00;
255
                txMsg.Data.bytes[2] = 0x00;
223
#if NUMBER_OF_RCS >0
256
#if NUMBER_OF_RCS >0
224
                txMsg.Data.bytes[3] = getPosition(1);
257
                txMsg.Data.bytes[3] = getPosition(1);
225
#endif
258
#endif
226
#if NUMBER_OF_RCS >1
259
#if NUMBER_OF_RCS >1
227
                txMsg.Data.bytes[4] = getPosition(2);
260
                txMsg.Data.bytes[4] = getPosition(2);
228
#endif
261
#endif
229
#if NUMBER_OF_RCS >2
262
#if NUMBER_OF_RCS >2
230
                txMsg.Data.bytes[5] = getPosition(3);
263
                txMsg.Data.bytes[5] = getPosition(3);
231
#endif
264
#endif
232
                txMsg.DataLength = NUMBER_OF_RCS+3;
265
                txMsg.DataLength = NUMBER_OF_RCS+3;
233
 
266
 
234
                Can_Send( &txMsg );
267
                bios->can_send(&txMsg);
235
            }
268
            }
236
        }
269
        }
237
        /* If start turning servo */
270
        /* If start turning servo */
238
        if( turnDirection ){
271
        if( turnDirection ){
239
            if( Timebase_PassedTimeMillis( timeStampTurn ) >= TURN_PERIOD ){
272
            if( bios->timebase_get() - timeStampTurn >= TURN_PERIOD ){
240
                timeStampTurn = Timebase_CurrentTime();
273
                timeStampTurn = bios->timebase_get();
241
                /* Clockwise or counterclockwise? */
274
                /* Clockwise or counterclockwise? */
242
                if( turnDirection == BLINDS_TURN_LEFT ){
275
                if( turnDirection == BLINDS_TURN_LEFT ){
243
                    alterPosition( -STEPS_PER_TURN_PERIOD , servoToTurn);
276
                    alterPosition( -STEPS_PER_TURN_PERIOD , servoToTurn);
244
                }else if( turnDirection == BLINDS_TURN_RIGHT ){
277
                }else if( turnDirection == BLINDS_TURN_RIGHT ){
245
                    alterPosition( STEPS_PER_TURN_PERIOD , servoToTurn);
278
                    alterPosition( STEPS_PER_TURN_PERIOD , servoToTurn);
246
                }
279
                }
247
            }
280
            }
248
        }
281
        }
-
 
282
        /* Turn off the servo current feed */
-
 
283
        if( bios->timebase_get() - servoFeed_timeStamp >= SERVO_FEED_TIME ){
-
 
284
            PORTC &= ~(1<<PC0);
-
 
285
        }else{
-
 
286
            PORTC |= (1<<PC0);
-
 
287
        }
249
    }
288
    }
250
    return 0;
289
    return 0;
251
}
290
}
252
 
291
 
253
void blindsFailsafe() // TODO fixa failsafe, vad ska hända med PWM?
292
void blindsFailsafe()
254
{
293
{
255
    uint8_t relayStatus;
294
    uint8_t relayStatus;
256
    uint32_t boardTemperature = 0, timeStamp = 0;
295
    uint32_t boardTemperature = 0, timeStamp = 0;
257
 
296
 
258
    Can_Message_t txMsg;
297
    /* Turn of servo current feed */
259
    Can_Message_t rxMsg;
298
    PORTC &= ~(1<<PC0);
260
    txMsg.Id = BLINDS_CMD_SEND;
-
 
261
    txMsg.RemoteFlag = 0;
-
 
262
    txMsg.ExtendedFlag = 1;
-
 
263
    txMsg.DataLength = 4;
-
 
-
 
299
 
264
 
300
 
265
    while(1){
301
    while(1){
266
        /* service the CAN routines */
-
 
267
        Can_Service();
-
 
268
 
-
 
269
        /* check if any messages have been received */
302
        /* check if any messages have been received */
270
        while (Can_Receive(&rxMsg) == CAN_OK) {
303
        while(msg_received) {
271
            /* This relay is adressed*/
304
            /* This relay is adressed*/
272
            if( rxMsg.Id == BLINDS_CMD_GET ){
-
 
273
                if( rxMsg.Data.bytes[0] == BLINDS_CMD_RESET ){
305
            if(rxMsg.Data.bytes[0] == BLINDS_CMD_RESET ){
274
                    /* Return from failsafe mode */
306
                /* Return from failsafe mode */
275
                    return;
307
                return;
276
                }
-
 
277
            }
308
            }
278
        }
309
        }
279
 
310
 
280
        if( Timebase_PassedTimeMillis(timeStamp) >= FAILSAFE_SEND_PERIOD ){
311
        if( bios->timebase_get() - timeStamp >= FAILSAFE_SEND_PERIOD ){
281
            timeStamp = Timebase_CurrentTime();
312
            timeStamp = bios->timebase_get();
282
            /* Send relay status to CAN */
313
            /* Send relay status to CAN */
283
            boardTemperature = getTC1047temperature();
314
            boardTemperature = getTC1047temperature();
284
 
315
 
285
            txMsg.Data.bytes[0] = boardTemperature & 0x00FF;
316
            txMsg.Data.bytes[0] = boardTemperature & 0x00FF;
286
            txMsg.Data.bytes[1] = (boardTemperature & 0xFF00)>>8;
317
            txMsg.Data.bytes[1] = (boardTemperature & 0xFF00)>>8;
Line 294... Line 325...
294
#if NUMBER_OF_RCS >2
325
#if NUMBER_OF_RCS >2
295
            txMsg.Data.bytes[5] = getPosition(3);
326
            txMsg.Data.bytes[5] = getPosition(3);
296
#endif
327
#endif
297
            txMsg.DataLength = NUMBER_OF_RCS+3;
328
            txMsg.DataLength = NUMBER_OF_RCS+3;
298
 
329
 
299
            Can_Send( &txMsg );
330
            bios->can_send(&txMsg);
300
        }
331
        }
301
    }
332
    }
302
}
333
}
303
 
334