main.c 36.3 KB
Newer Older
1
#include <stdio.h>
Damien's avatar
Damien committed
2
3
#include <stm32f4xx.h>
#include <stm32f4xx_rcc.h>
4
#include <stm32f4xx_syscfg.h>
5
#include <stm32f4xx_gpio.h>
6
#include <stm32f4xx_exti.h>
7
#include <stm32f4xx_tim.h>
Damien's avatar
Damien committed
8
#include <stm32f4xx_pwr.h>
9
#include <stm32f4xx_rtc.h>
10
#include <stm32f4xx_usart.h>
11
#include <stm32f4xx_rng.h>
12
#include <stm_misc.h>
Damien's avatar
Damien committed
13
14
#include "std.h"

15
#include "misc.h"
16
17
18
19
20
21
22
23
#include "ff.h"
#include "mpconfig.h"
#include "nlr.h"
#include "misc.h"
#include "lexer.h"
#include "lexerstm.h"
#include "parse.h"
#include "obj.h"
24
#include "compile.h"
25
26
27
#include "runtime0.h"
#include "runtime.h"
#include "repl.h"
Damien's avatar
Damien committed
28
#include "gc.h"
29
#include "systick.h"
30
#include "led.h"
31
#include "servo.h"
32
#include "lcd.h"
33
#include "storage.h"
34
#include "mma.h"
35
#include "usart.h"
Damien's avatar
Damien committed
36
#include "usb.h"
37
#include "timer.h"
38
#include "audio.h"
39
#include "pybwlan.h"
40
#include "i2c.h"
41
42

int errno;
Damien's avatar
Damien committed
43

Damien's avatar
Damien committed
44
45
extern uint32_t _heap_start;

46
47
static FATFS fatfs0;

48
49
50
51
52
53
54
55
56
57
58
59
void flash_error(int n) {
    for (int i = 0; i < n; i++) {
        led_state(PYB_LED_R1, 1);
        led_state(PYB_LED_R2, 0);
        sys_tick_delay_ms(250);
        led_state(PYB_LED_R1, 0);
        led_state(PYB_LED_R2, 1);
        sys_tick_delay_ms(250);
    }
    led_state(PYB_LED_R2, 0);
}

60
static void impl02_c_version(void) {
Damien's avatar
Damien committed
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
    int x = 0;
    while (x < 400) {
        int y = 0;
        while (y < 400) {
            volatile int z = 0;
            while (z < 400) {
                z = z + 1;
            }
            y = y + 1;
        }
        x = x + 1;
    }
}

#define PYB_USRSW_PORT (GPIOA)
76
#define PYB_USRSW_PIN (GPIO_Pin_13)
Damien's avatar
Damien committed
77

78
void sw_init(void) {
Damien's avatar
Damien committed
79
    // make it an input with pull-up
80
81
82
83
84
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = PYB_USRSW_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(PYB_USRSW_PORT, &GPIO_InitStructure);
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108

    // the rest does the EXTI interrupt

    /* Enable SYSCFG clock */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

    /* Connect EXTI Line13 to PA13 pin */
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource13);

    /* Configure EXTI Line13, rising edge */
    EXTI_InitTypeDef EXTI_InitStructure;
    EXTI_InitStructure.EXTI_Line = EXTI_Line13;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);

    /* Enable and set EXTI15_10 Interrupt to the lowest priority */
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
Damien's avatar
Damien committed
109
110
}

111
int sw_get(void) {
112
    if (PYB_USRSW_PORT->IDR & PYB_USRSW_PIN) {
Damien's avatar
Damien committed
113
114
115
116
117
118
119
120
121
122
123
124
        // pulled high, so switch is not pressed
        return 0;
    } else {
        // pulled low, so switch is pressed
        return 1;
    }
}

void __fatal_error(const char *msg) {
    lcd_print_strn("\nFATAL ERROR:\n", 14);
    lcd_print_strn(msg, strlen(msg));
    for (;;) {
125
        flash_error(1);
Damien's avatar
Damien committed
126
127
128
    }
}

129
130
131
static qstr pyb_config_source_dir = 0;
static qstr pyb_config_main = 0;

132
133
134
mp_obj_t pyb_source_dir(mp_obj_t source_dir) {
    pyb_config_source_dir = mp_obj_get_qstr(source_dir);
    return mp_const_none;
135
136
}

137
138
139
mp_obj_t pyb_main(mp_obj_t main) {
    pyb_config_main = mp_obj_get_qstr(main);
    return mp_const_none;
140
141
142
}

// sync all file systems
143
mp_obj_t pyb_sync(void) {
144
    storage_flush();
145
    return mp_const_none;
146
}
Damien's avatar
Damien committed
147

148
149
150
mp_obj_t pyb_delay(mp_obj_t count) {
    sys_tick_delay_ms(mp_obj_get_int(count));
    return mp_const_none;
Damien's avatar
Damien committed
151
152
}

153
mp_obj_t pyb_led(mp_obj_t state) {
154
    led_state(PYB_LED_G1, rt_is_true(state));
Damien's avatar
Damien committed
155
156
157
    return state;
}

158
mp_obj_t pyb_sw(void) {
Damien's avatar
Damien committed
159
    if (sw_get()) {
160
        return mp_const_true;
Damien's avatar
Damien committed
161
    } else {
162
        return mp_const_false;
Damien's avatar
Damien committed
163
164
165
    }
}

166
/*
167
168
169
170
171
172
void g(uint i) {
    printf("g:%d\n", i);
    if (i & 1) {
        nlr_jump((void*)(42 + i));
    }
}
173
void f(void) {
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
    nlr_buf_t nlr;
    int i;
    for (i = 0; i < 4; i++) {
        printf("f:loop:%d:%p\n", i, &nlr);
        if (nlr_push(&nlr) == 0) {
            // normal
            //printf("a:%p:%p %p %p %u\n", &nlr, nlr.ip, nlr.sp, nlr.prev, nlr.ret_val);
            g(i);
            printf("f:lp:%d:nrm\n", i);
            nlr_pop();
        } else {
            // nlr
            //printf("b:%p:%p %p %p %u\n", &nlr, nlr.ip, nlr.sp, nlr.prev, nlr.ret_val);
            printf("f:lp:%d:nlr:%d\n", i, (int)nlr.ret_val);
        }
    }
}
191
void nlr_test(void) {
192
193
    f(1);
}
194
195
*/

196
void fatality(void) {
197
198
199
200
201
202
    led_state(PYB_LED_R1, 1);
    led_state(PYB_LED_G1, 1);
    led_state(PYB_LED_R2, 1);
    led_state(PYB_LED_G2, 1);
}

203
static const char fresh_boot_py[] =
204
205
206
207
208
209
210
211
212
213
214
"# boot.py -- run on boot-up\n"
"# can run arbitrary Python, but best to keep it minimal\n"
"\n"
"pyb.source_dir('/src')\n"
"pyb.main('main.py')\n"
"#pyb.usb_usr('VCP')\n"
"#pyb.usb_msd(True, 'dual partition')\n"
"#pyb.flush_cache(False)\n"
"#pyb.error_log('error.txt')\n"
;

215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
static const char fresh_main_py[] =
"# main.py -- put your code here!\n"
;

static const char *help_text =
"Welcome to Micro Python!\n\n"
"This is a *very* early version of Micro Python and has minimal functionality.\n\n"
"Specific commands for the board:\n"
"    pyb.info()     -- print some general information\n"
"    pyb.gc()       -- run the garbage collector\n"
"    pyb.delay(<n>) -- wait for n milliseconds\n"
"    pyb.Led(<n>)   -- create Led object for LED n (n=1,2)\n"
"                      Led methods: on(), off()\n"
"    pyb.Servo(<n>) -- create Servo object for servo n (n=1,2,3,4)\n"
"                      Servo methods: angle(<x>)\n"
"    pyb.switch()   -- return True/False if switch pressed or not\n"
"    pyb.accel()    -- get accelerometer values\n"
"    pyb.rand()     -- get a 16-bit random number\n"
;

// get some help about available functions
236
static mp_obj_t pyb_help(void) {
237
    printf("%s", help_text);
238
    return mp_const_none;
239
240
}

241
// get lots of info about the board
242
static mp_obj_t pyb_info(void) {
243
244
245
246
247
248
    // get and print unique id; 96 bits
    {
        byte *id = (byte*)0x1fff7a10;
        printf("ID=%02x%02x%02x%02x:%02x%02x%02x%02x:%02x%02x%02x%02x\n", id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7], id[8], id[9], id[10], id[11]);
    }

249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
    // get and print clock speeds
    // SYSCLK=168MHz, HCLK=168MHz, PCLK1=42MHz, PCLK2=84MHz
    {
        RCC_ClocksTypeDef rcc_clocks;
        RCC_GetClocksFreq(&rcc_clocks);
        printf("S=%lu\nH=%lu\nP1=%lu\nP2=%lu\n", rcc_clocks.SYSCLK_Frequency, rcc_clocks.HCLK_Frequency, rcc_clocks.PCLK1_Frequency, rcc_clocks.PCLK2_Frequency);
    }

    // to print info about memory
    {
        extern void *_sidata;
        extern void *_sdata;
        extern void *_edata;
        extern void *_sbss;
        extern void *_ebss;
        extern void *_estack;
        extern void *_etext;
        printf("_sidata=%p\n", &_sidata);
        printf("_sdata=%p\n", &_sdata);
        printf("_edata=%p\n", &_edata);
        printf("_sbss=%p\n", &_sbss);
        printf("_ebss=%p\n", &_ebss);
        printf("_estack=%p\n", &_estack);
        printf("_etext=%p\n", &_etext);
        printf("_heap_start=%p\n", &_heap_start);
    }

276
277
278
279
280
281
282
283
284
285
    // GC info
    {
        gc_info_t info;
        gc_info(&info);
        printf("GC:\n");
        printf("  %lu total\n", info.total);
        printf("  %lu : %lu\n", info.used, info.free);
        printf("  1=%lu 2=%lu m=%lu\n", info.num_1block, info.num_2block, info.max_block);
    }

286
287
288
289
290
    // free space on flash
    {
        DWORD nclst;
        FATFS *fatfs;
        f_getfree("0:", &nclst, &fatfs);
291
        printf("LFS free: %u bytes\n", (uint)(nclst * fatfs->csize * 512));
292
    }
293

294
    return mp_const_none;
295
296
}

297
// SD card test
298
static mp_obj_t pyb_sd_test(void) {
299
300
    extern void sdio_init(void);
    sdio_init();
301
    return mp_const_none;
302
303
}

304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
static void SYSCLKConfig_STOP(void) {
    /* After wake-up from STOP reconfigure the system clock */
    /* Enable HSE */
    RCC_HSEConfig(RCC_HSE_ON);

    /* Wait till HSE is ready */
    while (RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET) {
    }

    /* Enable PLL */
    RCC_PLLCmd(ENABLE);

    /* Wait till PLL is ready */
    while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) {
    }

    /* Select PLL as system clock source */
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

    /* Wait till PLL is used as system clock source */
    while (RCC_GetSYSCLKSource() != 0x08) {
    }
}

328
static mp_obj_t pyb_stop(void) {
329
330
331
332
333
334
335
336
337
338
339
340
    PWR_EnterSTANDBYMode();
    //PWR_FlashPowerDownCmd(ENABLE); don't know what the logic is with this

    /* Enter Stop Mode */
    PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);

    /* Configures system clock after wake-up from STOP: enable HSE, PLL and select 
     *        PLL as system clock source (HSE and PLL are disabled in STOP mode) */
    SYSCLKConfig_STOP();

    //PWR_FlashPowerDownCmd(DISABLE);

341
    return mp_const_none;
342
343
}

344
static mp_obj_t pyb_standby(void) {
345
    PWR_EnterSTANDBYMode();
346
    return mp_const_none;
347
348
}

349
350
351
mp_obj_t pyb_usart_send(mp_obj_t data) {
    usart_tx_char(mp_obj_get_int(data));
    return mp_const_none;
352
353
}

354
355
mp_obj_t pyb_usart_receive(void) {
    return mp_obj_new_int(usart_rx_char());
356
357
}

358
mp_obj_t pyb_usart_status(void) {
359
    if (usart_rx_any()) {
360
        return mp_const_true;
361
    } else {
362
        return mp_const_false;
363
364
365
    }
}

Damien's avatar
Damien committed
366
367
368
369
370
371
372
373
374
375
376
377
char *strdup(const char *str) {
    uint32_t len = strlen(str);
    char *s2 = m_new(char, len + 1);
    memcpy(s2, str, len);
    s2[len] = 0;
    return s2;
}

#define READLINE_HIST_SIZE (8)

static const char *readline_hist[READLINE_HIST_SIZE] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};

378
379
380
381
382
void stdout_tx_str(const char *str) {
    usart_tx_str(str);
    usb_vcp_send_str(str);
}

383
int readline(vstr_t *line, const char *prompt) {
384
    stdout_tx_str(prompt);
385
    int len = vstr_len(line);
Damien's avatar
Damien committed
386
387
    int escape = 0;
    int hist_num = 0;
388
    for (;;) {
389
390
391
392
393
394
395
396
397
        char c;
        for (;;) {
            if (usb_vcp_rx_any() != 0) {
                c = usb_vcp_rx_get();
                break;
            } else if (usart_rx_any()) {
                c = usart_rx_char();
                break;
            }
Damien's avatar
Damien committed
398
            sys_tick_delay_ms(1);
399
400
401
            if (storage_needs_flush()) {
                storage_flush();
            }
402
        }
Damien's avatar
Damien committed
403
404
405
406
        if (escape == 0) {
            if (c == 4 && vstr_len(line) == len) {
                return 0;
            } else if (c == '\r') {
407
                stdout_tx_str("\r\n");
Damien's avatar
Damien committed
408
409
410
411
412
413
414
415
416
417
                for (int i = READLINE_HIST_SIZE - 1; i > 0; i--) {
                    readline_hist[i] = readline_hist[i - 1];
                }
                readline_hist[0] = strdup(vstr_str(line));
                return 1;
            } else if (c == 27) {
                escape = true;
            } else if (c == 127) {
                if (vstr_len(line) > len) {
                    vstr_cut_tail(line, 1);
418
                    stdout_tx_str("\b \b");
Damien's avatar
Damien committed
419
420
421
                }
            } else if (32 <= c && c <= 126) {
                vstr_add_char(line, c);
422
                stdout_tx_str(line->buf + line->len - 1);
423
            }
Damien's avatar
Damien committed
424
425
426
427
428
429
430
431
432
433
434
435
436
        } else if (escape == 1) {
            if (c == '[') {
                escape = 2;
            } else {
                escape = 0;
            }
        } else if (escape == 2) {
            escape = 0;
            if (c == 'A') {
                // up arrow
                if (hist_num < READLINE_HIST_SIZE && readline_hist[hist_num] != NULL) {
                    // erase line
                    for (int i = line->len - len; i > 0; i--) {
437
                        stdout_tx_str("\b \b");
Damien's avatar
Damien committed
438
439
440
441
442
                    }
                    // set line to history
                    line->len = len;
                    vstr_add_str(line, readline_hist[hist_num]);
                    // draw line
443
                    stdout_tx_str(readline_hist[hist_num]);
Damien's avatar
Damien committed
444
445
446
447
448
449
                    // increase hist num
                    hist_num += 1;
                }
            }
        } else {
            escape = 0;
450
        }
Damien's avatar
Damien committed
451
        sys_tick_delay_ms(10);
452
    }
453
454
}

455
void do_repl(void) {
456
    stdout_tx_str("Micro Python build <git hash> on 2/1/2014; PYBv3 with STM32F405RG\r\n");
457
    stdout_tx_str("Type \"help()\" for more information.\r\n");
458
459
460

    vstr_t line;
    vstr_init(&line);
461
462

    for (;;) {
463
464
465
        vstr_reset(&line);
        int ret = readline(&line, ">>> ");
        if (ret == 0) {
466
            // EOF
467
468
469
470
471
            break;
        }

        if (vstr_len(&line) == 0) {
            continue;
472
        }
473

474
        if (mp_repl_is_compound_stmt(vstr_str(&line))) {
475
            for (;;) {
476
477
478
479
480
                vstr_add_char(&line, '\n');
                int len = vstr_len(&line);
                int ret = readline(&line, "... ");
                if (ret == 0 || vstr_len(&line) == len) {
                    // done entering compound statement
481
482
483
484
485
                    break;
                }
            }
        }

486
487
488
489
        mp_lexer_str_buf_t sb;
        mp_lexer_t *lex = mp_lexer_new_from_str_len("<stdin>", vstr_str(&line), vstr_len(&line), false, &sb);
        mp_parse_node_t pn = mp_parse(lex, MP_PARSE_SINGLE_INPUT);
        mp_lexer_free(lex);
490

491
        if (pn != MP_PARSE_NODE_NULL) {
492
493
494
495
496
497
498
499
500
501
502
            mp_obj_t module_fun = mp_compile(pn, true);
            if (module_fun != mp_const_none) {
                nlr_buf_t nlr;
                uint32_t start = sys_tick_counter;
                if (nlr_push(&nlr) == 0) {
                    rt_call_function_0(module_fun);
                    nlr_pop();
                    // optional timing
                    if (0) {
                        uint32_t ticks = sys_tick_counter - start; // TODO implement a function that does this properly
                        printf("(took %lu ms)\n", ticks);
503
                    }
504
505
506
507
                } else {
                    // uncaught exception
                    mp_obj_print((mp_obj_t)nlr.ret_val);
                    printf("\n");
508
509
510
511
                }
            }
        }
    }
512

513
    stdout_tx_str("\r\n");
514
515
516
}

bool do_file(const char *filename) {
517
518
    mp_lexer_file_buf_t fb;
    mp_lexer_t *lex = mp_lexer_new_from_file(filename, &fb);
519
520
521
522
523
524

    if (lex == NULL) {
        printf("could not open file '%s' for reading\n", filename);
        return false;
    }

525
526
    mp_parse_node_t pn = mp_parse(lex, MP_PARSE_FILE_INPUT);
    mp_lexer_free(lex);
527

528
    if (pn == MP_PARSE_NODE_NULL) {
529
530
531
        return false;
    }

532
    mp_obj_t module_fun = mp_compile(pn, false);
533
    if (module_fun == mp_const_none) {
534
535
536
537
538
539
540
541
542
543
        return false;
    }

    nlr_buf_t nlr;
    if (nlr_push(&nlr) == 0) {
        rt_call_function_0(module_fun);
        nlr_pop();
        return true;
    } else {
        // uncaught exception
544
        mp_obj_print((mp_obj_t)nlr.ret_val);
545
546
547
        printf("\n");
        return false;
    }
548
549
}

550
551
552
553
554
555
#define RAM_START (0x20000000) // fixed for chip
#define HEAP_END  (0x2001c000) // tunable
#define RAM_END   (0x20020000) // fixed for chip

void gc_helper_get_regs_and_clean_stack(machine_uint_t *regs, machine_uint_t heap_end);

556
void gc_collect(void) {
557
    uint32_t start = sys_tick_counter;
Damien's avatar
Damien committed
558
    gc_collect_start();
559
560
561
562
    gc_collect_root((void**)RAM_START, (((uint32_t)&_heap_start) - RAM_START) / 4);
    machine_uint_t regs[10];
    gc_helper_get_regs_and_clean_stack(regs, HEAP_END);
    gc_collect_root((void**)HEAP_END, (RAM_END - HEAP_END) / 4); // will trace regs since they now live in this function on the stack
Damien's avatar
Damien committed
563
    gc_collect_end();
564
    uint32_t ticks = sys_tick_counter - start; // TODO implement a function that does this properly
565
566
567
568
569
570
571
572
573
574

    if (0) {
        // print GC info
        gc_info_t info;
        gc_info(&info);
        printf("GC@%lu %lums\n", start, ticks);
        printf(" %lu total\n", info.total);
        printf(" %lu : %lu\n", info.used, info.free);
        printf(" 1=%lu 2=%lu m=%lu\n", info.num_1block, info.num_2block, info.max_block);
    }
575
576
}

577
mp_obj_t pyb_gc(void) {
578
    gc_collect();
579
    return mp_const_none;
580
581
}

582
583
mp_obj_t pyb_gpio(int n_args, mp_obj_t *args) {
    //assert(1 <= n_args && n_args <= 2);
Damien's avatar
Damien committed
584

585
586
587
588
589
590
591
    const char *pin_name = qstr_str(mp_obj_get_qstr(args[0]));
    GPIO_TypeDef *port;
    switch (pin_name[0]) {
        case 'A': case 'a': port = GPIOA; break;
        case 'B': case 'b': port = GPIOB; break;
        case 'C': case 'c': port = GPIOC; break;
        default: goto pin_error;
592
    }
593
594
595
596
    uint pin_num = 0;
    for (const char *s = pin_name + 1; *s; s++) {
        if (!('0' <= *s && *s <= '9')) {
            goto pin_error;
Damien's avatar
Damien committed
597
        }
598
599
600
601
        pin_num = 10 * pin_num + *s - '0';
    }
    if (!(0 <= pin_num && pin_num <= 15)) {
        goto pin_error;
Damien's avatar
Damien committed
602
    }
603

604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
    if (n_args == 1) {
        // get pin
        if ((port->IDR & (1 << pin_num)) != (uint32_t)Bit_RESET) {
            return MP_OBJ_NEW_SMALL_INT(1);
        } else {
            return MP_OBJ_NEW_SMALL_INT(0);
        }
    } else {
        // set pin
        if (rt_is_true(args[1])) {
            // set pin high
            port->BSRRL = 1 << pin_num;
        } else {
            // set pin low
            port->BSRRH = 1 << pin_num;
        }
        return mp_const_none;
    }
622

623
624
pin_error:
    nlr_jump(mp_obj_new_exception_msg_1_arg(rt_q_ValueError, "pin %s does not exist", pin_name));
Damien's avatar
Damien committed
625
626
}

627
628
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_gpio_obj, 1, 2, pyb_gpio);

629
630
mp_obj_t pyb_hid_send_report(mp_obj_t arg) {
    mp_obj_t *items = mp_obj_get_array_fixed_n(arg, 4);
631
    uint8_t data[4];
632
633
634
635
    data[0] = mp_obj_get_int(items[0]);
    data[1] = mp_obj_get_int(items[1]);
    data[2] = mp_obj_get_int(items[2]);
    data[3] = mp_obj_get_int(items[3]);
636
    usb_hid_send_report(data);
637
    return mp_const_none;
638
639
}

640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
static void rtc_init(void) {
    /* Enable the PWR clock */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);

    /* Allow access to RTC */
    PWR_BackupAccessCmd(ENABLE);

    /* Enable the LSE OSC */
    RCC_LSEConfig(RCC_LSE_ON);

    /* Wait till LSE is ready */
    while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) {
    }

    /* Select the RTC Clock Source */
    RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
    /* ck_spre(1Hz) = RTCCLK(LSE) /(uwAsynchPrediv + 1)*(uwSynchPrediv + 1)*/
    uint32_t uwSynchPrediv = 0xFF;
    uint32_t uwAsynchPrediv = 0x7F;

    /* Enable the RTC Clock */
    RCC_RTCCLKCmd(ENABLE);

    /* Wait for RTC APB registers synchronisation */
    RTC_WaitForSynchro();

    /* Configure the RTC data register and RTC prescaler */
    RTC_InitTypeDef RTC_InitStructure;
    RTC_InitStructure.RTC_AsynchPrediv = uwAsynchPrediv;
    RTC_InitStructure.RTC_SynchPrediv = uwSynchPrediv;
    RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24;
    RTC_Init(&RTC_InitStructure);

    // Set the date (BCD)
    RTC_DateTypeDef RTC_DateStructure;
    RTC_DateStructure.RTC_Year = 0x13;
    RTC_DateStructure.RTC_Month = RTC_Month_October;
    RTC_DateStructure.RTC_Date = 0x26;
    RTC_DateStructure.RTC_WeekDay = RTC_Weekday_Saturday;
    RTC_SetDate(RTC_Format_BCD, &RTC_DateStructure);

    // Set the time (BCD)
    RTC_TimeTypeDef RTC_TimeStructure;
    RTC_TimeStructure.RTC_H12     = RTC_H12_AM;
    RTC_TimeStructure.RTC_Hours   = 0x01;
    RTC_TimeStructure.RTC_Minutes = 0x53;
    RTC_TimeStructure.RTC_Seconds = 0x00;
    RTC_SetTime(RTC_Format_BCD, &RTC_TimeStructure);

    // Indicator for the RTC configuration
    //RTC_WriteBackupRegister(RTC_BKP_DR0, 0x32F2);
}

693
mp_obj_t pyb_rtc_read(void) {
694
695
696
    RTC_TimeTypeDef RTC_TimeStructure;
    RTC_GetTime(RTC_Format_BIN, &RTC_TimeStructure);
    printf("%02d:%02d:%02d\n", RTC_TimeStructure.RTC_Hours, RTC_TimeStructure.RTC_Minutes, RTC_TimeStructure.RTC_Seconds);
697
    return mp_const_none;
698
699
}

700
701
702
703
704
705
706
typedef struct _pyb_file_obj_t {
    mp_obj_base_t base;
    FIL fp;
} pyb_file_obj_t;

void file_obj_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
    printf("<file %p>", self_in);
Damien's avatar
Damien committed
707
708
}

709
710
711
mp_obj_t file_obj_read(mp_obj_t self_in, mp_obj_t arg) {
    pyb_file_obj_t *self = self_in;
    int n = mp_obj_get_int(arg);
Damien's avatar
Damien committed
712
713
    char *buf = m_new(char, n + 1);
    UINT n_out;
714
    f_read(&self->fp, buf, n, &n_out);
Damien's avatar
Damien committed
715
    buf[n_out] = 0;
716
    return mp_obj_new_str(qstr_from_str_take(buf, n + 1));
Damien's avatar
Damien committed
717
718
}

719
720
721
mp_obj_t file_obj_write(mp_obj_t self_in, mp_obj_t arg) {
    pyb_file_obj_t *self = self_in;
    const char *s = qstr_str(mp_obj_get_qstr(arg));
Damien's avatar
Damien committed
722
    UINT n_out;
723
    FRESULT res = f_write(&self->fp, s, strlen(s), &n_out);
Damien's avatar
Damien committed
724
725
726
727
728
    if (res != FR_OK) {
        printf("File error: could not write to file; error code %d\n", res);
    } else if (n_out != strlen(s)) {
        printf("File error: could not write all data to file; wrote %d / %d bytes\n", n_out, strlen(s));
    }
729
    return mp_const_none;
Damien's avatar
Damien committed
730
731
}

732
733
734
735
mp_obj_t file_obj_close(mp_obj_t self_in) {
    pyb_file_obj_t *self = self_in;
    f_close(&self->fp);
    return mp_const_none;
Damien's avatar
Damien committed
736
737
}

738
739
740
741
static MP_DEFINE_CONST_FUN_OBJ_2(file_obj_read_obj, file_obj_read);
static MP_DEFINE_CONST_FUN_OBJ_2(file_obj_write_obj, file_obj_write);
static MP_DEFINE_CONST_FUN_OBJ_1(file_obj_close_obj, file_obj_close);

Damien's avatar
Damien committed
742
// TODO gc hook to close the file if not already closed
743
744
745

static const mp_obj_type_t file_obj_type = {
    { &mp_const_type },
Damien's avatar
Damien committed
746
    "File",
747
748
749
750
751
752
753
754
755
756
757
    file_obj_print, // print
    NULL, // call_n
    NULL, // unary_op
    NULL, // binary_op
    NULL, // getiter
    NULL, // iternext
    { // method list
        { "read", &file_obj_read_obj },
        { "write", &file_obj_write_obj },
        { "close", &file_obj_close_obj },
        {NULL, NULL},
Damien's avatar
Damien committed
758
759
760
    }
};

761
762
763
764
765
mp_obj_t pyb_io_open(mp_obj_t o_filename, mp_obj_t o_mode) {
    const char *filename = qstr_str(mp_obj_get_qstr(o_filename));
    const char *mode = qstr_str(mp_obj_get_qstr(o_mode));
    pyb_file_obj_t *self = m_new_obj(pyb_file_obj_t);
    self->base.type = &file_obj_type;
Damien's avatar
Damien committed
766
767
    if (mode[0] == 'r') {
        // open for reading
768
        FRESULT res = f_open(&self->fp, filename, FA_READ);
Damien's avatar
Damien committed
769
770
        if (res != FR_OK) {
            printf("FileNotFoundError: [Errno 2] No such file or directory: '%s'\n", filename);
771
            return mp_const_none;
Damien's avatar
Damien committed
772
773
774
        }
    } else if (mode[0] == 'w') {
        // open for writing, truncate the file first
775
        FRESULT res = f_open(&self->fp, filename, FA_WRITE | FA_CREATE_ALWAYS);
Damien's avatar
Damien committed
776
777
        if (res != FR_OK) {
            printf("?FileError: could not create file: '%s'\n", filename);
778
            return mp_const_none;
Damien's avatar
Damien committed
779
780
781
        }
    } else {
        printf("ValueError: invalid mode: '%s'\n", mode);
782
        return mp_const_none;
Damien's avatar
Damien committed
783
    }
784
    return self;
Damien's avatar
Damien committed
785
786
}

787
788
mp_obj_t pyb_rng_get(void) {
    return mp_obj_new_int(RNG_GetRandomNumber() >> 16);
789
790
}

791
int main(void) {
792
    // TODO disable JTAG
Damien's avatar
Damien committed
793

794
795
796
    // update the SystemCoreClock variable
    SystemCoreClockUpdate();

797
798
799
800
801
802
    // set interrupt priority config to use all 4 bits for pre-empting
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

    // enable the CCM RAM and the GPIO's
    RCC->AHB1ENR |= RCC_AHB1ENR_CCMDATARAMEN | RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN;

803
    // configure SDIO pins to be high to start with (apparently makes it more robust)
804
805
806
807
808
809
810
811
812
813
814
815
816
817
    {
      GPIO_InitTypeDef GPIO_InitStructure;
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
      GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
      GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
      GPIO_Init(GPIOC, &GPIO_InitStructure);

      // Configure PD.02 CMD line
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
      GPIO_Init(GPIOD, &GPIO_InitStructure);
    }

818
819
    // basic sub-system init
    sys_tick_init();
Damien's avatar
Damien committed
820
    led_init();
821
    rtc_init();
822
823
824
825
826

    // turn on LED to indicate bootup
    led_state(PYB_LED_G1, 1);

    // more sub-system init
Damien's avatar
Damien committed
827
    sw_init();
828
    storage_init();
829
830

    //usart_init(); disabled while wi-fi is enabled
Damien's avatar
Damien committed
831

832
833
    int first_soft_reset = true;

834
835
soft_reset:

Damien's avatar
Damien committed
836
    // GC init
837
    gc_init(&_heap_start, (void*)HEAP_END);
Damien's avatar
Damien committed
838

839
    // Micro Python init
840
841
    qstr_init();
    rt_init();
842

843
    // LCD init
844
    //lcd_init(); disabled while servos on PA0 PA1
845

846
847
848
    // servo
    servo_init();

849
    // audio
850
    //audio_init();
851

852
853
854
    // timer
    timer_init();

855
    // RNG
856
    if (1) {
857
858
859
860
        RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_RNG, ENABLE);
        RNG_Cmd(ENABLE);
    }

861
    // add some functions to the python namespace
862
    {
863
864
        rt_store_name(qstr_from_str_static("help"), rt_make_function_0(pyb_help));

865
        mp_obj_t m = mp_obj_new_module(qstr_from_str_static("pyb"));
866
        rt_store_attr(m, qstr_from_str_static("info"), rt_make_function_0(pyb_info));
867
        rt_store_attr(m, qstr_from_str_static("sd_test"), rt_make_function_0(pyb_sd_test));
868
869
        rt_store_attr(m, qstr_from_str_static("stop"), rt_make_function_0(pyb_stop));
        rt_store_attr(m, qstr_from_str_static("standby"), rt_make_function_0(pyb_standby));
870
871
        rt_store_attr(m, qstr_from_str_static("source_dir"), rt_make_function_1(pyb_source_dir));
        rt_store_attr(m, qstr_from_str_static("main"), rt_make_function_1(pyb_main));
872
        rt_store_attr(m, qstr_from_str_static("sync"), rt_make_function_0(pyb_sync));
873
874
875
        rt_store_attr(m, qstr_from_str_static("gc"), rt_make_function_0(pyb_gc));
        rt_store_attr(m, qstr_from_str_static("delay"), rt_make_function_1(pyb_delay));
        rt_store_attr(m, qstr_from_str_static("led"), rt_make_function_1(pyb_led));
876
        rt_store_attr(m, qstr_from_str_static("switch"), rt_make_function_0(pyb_sw));
877
        rt_store_attr(m, qstr_from_str_static("servo"), rt_make_function_2(pyb_servo_set));
878
        rt_store_attr(m, qstr_from_str_static("pwm"), rt_make_function_2(pyb_pwm_set));
879
880
881
        rt_store_attr(m, qstr_from_str_static("accel"), (mp_obj_t)&pyb_mma_read_obj);
        rt_store_attr(m, qstr_from_str_static("mma_read"), (mp_obj_t)&pyb_mma_read_all_obj);
        rt_store_attr(m, qstr_from_str_static("mma_mode"), (mp_obj_t)&pyb_mma_write_mode_obj);
882
        rt_store_attr(m, qstr_from_str_static("hid"), rt_make_function_1(pyb_hid_send_report));
883
        rt_store_attr(m, qstr_from_str_static("time"), rt_make_function_0(pyb_rtc_read));
884
885
886
        rt_store_attr(m, qstr_from_str_static("uout"), rt_make_function_1(pyb_usart_send));
        rt_store_attr(m, qstr_from_str_static("uin"), rt_make_function_0(pyb_usart_receive));
        rt_store_attr(m, qstr_from_str_static("ustat"), rt_make_function_0(pyb_usart_status));
887
        rt_store_attr(m, qstr_from_str_static("rand"), rt_make_function_0(pyb_rng_get));
888
        rt_store_attr(m, qstr_from_str_static("Led"), rt_make_function_1(pyb_Led));
889
        rt_store_attr(m, qstr_from_str_static("Servo"), rt_make_function_1(pyb_Servo));
890
	rt_store_attr(m, qstr_from_str_static("I2C"), rt_make_function_2(pyb_I2C));
891
        rt_store_attr(m, qstr_from_str_static("gpio"), (mp_obj_t)&pyb_gpio_obj);
892
        rt_store_name(qstr_from_str_static("pyb"), m);
Damien's avatar
Damien committed
893
894

        rt_store_name(qstr_from_str_static("open"), rt_make_function_2(pyb_io_open));
895
    }
896

897
898
    // print a message to the LCD
    lcd_print_str(" micro py board\n");
Damien's avatar
Damien committed
899

900
901
902
903
904
905
906
907
908
909
910
911
912
    // check if user switch held (initiates reset of filesystem)
    bool reset_filesystem = false;
    if (sw_get()) {
        reset_filesystem = true;
        for (int i = 0; i < 50; i++) {
            if (!sw_get()) {
                reset_filesystem = false;
                break;
            }
            sys_tick_delay_ms(10);
        }
    }

913
914
915
916
    // local filesystem init
    {
        // try to mount the flash
        FRESULT res = f_mount(&fatfs0, "0:", 1);
917
        if (!reset_filesystem && res == FR_OK) {
918
            // mount sucessful
919
        } else if (reset_filesystem || res == FR_NO_FILESYSTEM) {
920
            // no filesystem, so create a fresh one
921
            // TODO doesn't seem to work correctly when reset_filesystem is true...
922
923
924
925
926
927
928
929
930
931
932
933

            // LED on to indicate creation of LFS
            led_state(PYB_LED_R2, 1);
            uint32_t stc = sys_tick_counter;

            res = f_mkfs("0:", 0, 0);
            if (res == FR_OK) {
                // success creating fresh LFS
            } else {
                __fatal_error("could not create LFS");
            }

934
935
936
937
938
939
940
941
942
943
944
945
            // create src directory
            res = f_mkdir("0:/src");
            // ignore result from mkdir

            // create empty main.py
            FIL fp;
            f_open(&fp, "0:/src/main.py", FA_WRITE | FA_CREATE_ALWAYS);
            UINT n;
            f_write(&fp, fresh_main_py, sizeof(fresh_main_py) - 1 /* don't count null terminator */, &n);
            // TODO check we could write n bytes
            f_close(&fp);

946
947
            // keep LED on for at least 200ms
            sys_tick_wait_at_least(stc, 200);
948
949
950
951
            led_state(PYB_LED_R2, 0);
        } else {
            __fatal_error("could not access LFS");
        }
Damien's avatar
Damien committed
952
953
    }

954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
    // make sure we have a /boot.py
    {
        FILINFO fno;
        FRESULT res = f_stat("0:/boot.py", &fno);
        if (res == FR_OK) {
            if (fno.fattrib & AM_DIR) {
                // exists as a directory
                // TODO handle this case
                // see http://elm-chan.org/fsw/ff/img/app2.c for a "rm -rf" implementation
            } else {
                // exists as a file, good!
            }
        } else {
            // doesn't exist, create fresh file

            // LED on to indicate creation of boot.py
            led_state(PYB_LED_R2, 1);
            uint32_t stc = sys_tick_counter;

            FIL fp;
            f_open(&fp, "0:/boot.py", FA_WRITE | FA_CREATE_ALWAYS);
            UINT n;
976
            f_write(&fp, fresh_boot_py, sizeof(fresh_boot_py) - 1 /* don't count null terminator */, &n);
977
978
979
            // TODO check we could write n bytes
            f_close(&fp);

980
981
            // keep LED on for at least 200ms
            sys_tick_wait_at_least(stc, 200);
982
983
984
985
            led_state(PYB_LED_R2, 0);
        }
    }

986
987
988
989
990
    // run /boot.py
    if (!do_file("0:/boot.py")) {
        flash_error(4);
    }

991
992
993
    // USB
    usb_init();

Damien's avatar
Damien committed
994
    // MMA
995
    if (first_soft_reset) {
Damien's avatar
Damien committed
996
997
998
999
        // init and reset address to zero
        mma_init();
    }

1000
1001
    // turn boot-up LED off
    led_state(PYB_LED_G1, 0);
1002

1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
    // run main script
    {
        vstr_t *vstr = vstr_new();
        vstr_add_str(vstr, "0:/");
        if (pyb_config_source_dir == 0) {
            vstr_add_str(vstr, "src");
        } else {
            vstr_add_str(vstr, qstr_str(pyb_config_source_dir));
        }
        vstr_add_char(vstr, '/');
        if (pyb_config_main == 0) {
            vstr_add_str(vstr, "main.py");
        } else {
            vstr_add_str(vstr, qstr_str(pyb_config_main));
        }
        if (!do_file(vstr_str(vstr))) {
            flash_error(3);
1020
        }
1021
        vstr_free(vstr);
1022
1023
    }

Damien's avatar
Damien committed
1024
    //printf("init;al=%u\n", m_get_total_bytes_allocated()); // 1600, due to qstr_init
1025
    //sys_tick_delay_ms(1000);
Damien's avatar
Damien committed
1026
1027

    // Python!
1028
    if (0) {
Damien's avatar
Damien committed
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
        //const char *pysrc = "def f():\n  x=x+1\nprint(42)\n";
        const char *pysrc =
            // impl01.py
            /*
            "x = 0\n"
            "while x < 400:\n"
            "    y = 0\n"
            "    while y < 400:\n"
            "        z = 0\n"
            "        while z < 400:\n"
            "            z = z + 1\n"
            "        y = y + 1\n"
            "    x = x + 1\n";
            */
            // impl02.py
1044
            /*
Damien's avatar
Damien committed
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
            "#@micropython.native\n"
            "def f():\n"
            "    x = 0\n"
            "    while x < 400:\n"
            "        y = 0\n"
            "        while y < 400:\n"
            "            z = 0\n"
            "            while z < 400:\n"
            "                z = z + 1\n"
            "            y = y + 1\n"
            "        x = x + 1\n"
            "f()\n";
1057
            */
1058
            /*
Damien's avatar
Damien committed
1059
1060
1061
1062
1063
1064
1065
            "print('in python!')\n"
            "x = 0\n"
            "while x < 4:\n"
            "    pyb_led(True)\n"
            "    pyb_delay(201)\n"
            "    pyb_led(False)\n"
            "    pyb_delay(201)\n"
1066
            "    x += 1\n"
Damien's avatar
Damien committed
1067
1068
1069
            "print('press me!')\n"
            "while True:\n"
            "    pyb_led(pyb_sw())\n";
1070
            */
Damien's avatar
Damien committed
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
            /*
            // impl16.py
            "@micropython.asm_thumb\n"
            "def delay(r0):\n"
            "    b(loop_entry)\n"
            "    label(loop1)\n"
            "    movw(r1, 55999)\n"
            "    label(loop2)\n"
            "    subs(r1, r1, 1)\n"
            "    cmp(r1, 0)\n"
            "    bgt(loop2)\n"
            "    subs(r0, r0, 1)\n"
            "    label(loop_entry)\n"
            "    cmp(r0, 0)\n"
            "    bgt(loop1)\n"
            "print('in python!')\n"
            "@micropython.native\n"
            "def flash(n):\n"
            "    x = 0\n"
            "    while x < n:\n"
            "        pyb_led(True)\n"
            "        delay(249)\n"
            "        pyb_led(False)\n"
            "        delay(249)\n"
            "        x = x + 1\n"
            "flash(20)\n";
            */
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
            // impl18.py
            /*
            "# basic exceptions\n"
            "x = 1\n"
            "try:\n"
            "    x.a()\n"
            "except:\n"
            "    print(x)\n";
            */
            // impl19.py
            "# for loop\n"
            "def f():\n"
            "    for x in range(400):\n"
            "        for y in range(400):\n"
            "            for z in range(400):\n"
            "                pass\n"
            "f()\n";
Damien's avatar
Damien committed
1115

1116
1117
        mp_lexer_str_buf_t mp_lexer_str_buf;
        mp_lexer_t *lex = mp_lexer_new_from_str_len("<stdin>", pysrc, strlen(pysrc), false, &mp_lexer_str_buf);
Damien's avatar
Damien committed
1118

1119
1120
1121
        // nalloc=1740;6340;6836 -> 140;4600;496 bytes for lexer, parser, compiler
        printf("lex; al=%u\n", m_get_total_bytes_allocated());
        sys_tick_delay_ms(1000);
1122
1123
1124
        mp_parse_node_t pn = mp_parse(lex, MP_PARSE_FILE_INPUT);
        mp_lexer_free(lex);
        if (pn != MP_PARSE_NODE_NULL) {
Damien's avatar
Damien committed
1125
            printf("pars;al=%u\n", m_get_total_bytes_allocated());
1126
            sys_tick_delay_ms(1000);
Damien's avatar
Damien committed
1127
            //parse_node_show(pn, 0);
1128
            mp_obj_t module_fun = mp_compile(pn, false);
Damien's avatar
Damien committed
1129
            printf("comp;al=%u\n", m_get_total_bytes_allocated());
1130
            sys_tick_delay_ms(1000);
Damien's avatar
Damien committed
1131

1132
            if (module_fun == mp_const_none) {
1133
1134
                printf("compile error\n");
            } else {
Damien's avatar
Damien committed
1135
1136
                // execute it!

1137
                // flash once
1138
                led_state(PYB_LED_G1, 1);
1139
                sys_tick_delay_ms(100);
1140
                led_state(PYB_LED_G1, 0);
1141
1142
1143

                nlr_buf_t nlr;
                if (nlr_push(&nlr) == 0) {
1144
                    mp_obj_t ret = rt_call_function_0(module_fun);
1145
                    printf("done! got: ");
1146
                    mp_obj_print(ret);
1147
1148
1149
1150
1151
                    printf("\n");
                    nlr_pop();
                } else {
                    // uncaught exception
                    printf("exception: ");
1152
                    mp_obj_print((mp_obj_t)nlr.ret_val);
1153
1154
1155
1156
                    printf("\n");
                }

                // flash once
1157
                led_state(PYB_LED_G1, 1);
1158
                sys_tick_delay_ms(100);
1159
                led_state(PYB_LED_G1, 0);
Damien's avatar
Damien committed
1160

1161
                sys_tick_delay_ms(1000);
Damien's avatar
Damien committed
1162
                printf("nalloc=%u\n", m_get_total_bytes_allocated());
1163
                sys_tick_delay_ms(1000);
Damien's avatar
Damien committed
1164
1165
1166
1167
            }
        }
    }

1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
    // HID example
    if (0) {
        uint8_t data[4];
        data[0] = 0;
        data[1] = 1;
        data[2] = -2;
        data[3] = 0;
        for (;;) {
            if (sw_get()) {
                data[0] = 0x01; // 0x04 is middle, 0x02 is right
            } else {
                data[0] = 0x00;
            }
1181
            mma_start(0x4c /* MMA_ADDR */, 1);
1182
            mma_send_byte(0);
1183