Branch data Line data Source code
1 : : // Include MicroPython API.
2 : : #include "py/runtime.h"
3 : :
4 : : // Used to get the time in the Timer class example.
5 : : #include "py/mphal.h"
6 : :
7 : : // This is the function which will be called from Python as cexample.add_ints(a, b).
8 : 2 : static mp_obj_t example_add_ints(mp_obj_t a_obj, mp_obj_t b_obj) {
9 : : // Extract the ints from the micropython input objects.
10 : 2 : int a = mp_obj_get_int(a_obj);
11 : 2 : int b = mp_obj_get_int(b_obj);
12 : :
13 : : // Calculate the addition and convert to MicroPython object.
14 : 2 : return mp_obj_new_int(a + b);
15 : : }
16 : : // Define a Python reference to the function above.
17 : : static MP_DEFINE_CONST_FUN_OBJ_2(example_add_ints_obj, example_add_ints);
18 : :
19 : : // This structure represents Timer instance objects.
20 : : typedef struct _example_Timer_obj_t {
21 : : // All objects start with the base.
22 : : mp_obj_base_t base;
23 : : // Everything below can be thought of as instance attributes, but they
24 : : // cannot be accessed by MicroPython code directly. In this example we
25 : : // store the time at which the object was created.
26 : : mp_uint_t start_time;
27 : : } example_Timer_obj_t;
28 : :
29 : : // This is the Timer.time() method. After creating a Timer object, this
30 : : // can be called to get the time elapsed since creating the Timer.
31 : 12 : static mp_obj_t example_Timer_time(mp_obj_t self_in) {
32 : : // The first argument is self. It is cast to the *example_Timer_obj_t
33 : : // type so we can read its attributes.
34 : 12 : example_Timer_obj_t *self = MP_OBJ_TO_PTR(self_in);
35 : :
36 : : // Get the elapsed time and return it as a MicroPython integer.
37 : 12 : mp_uint_t elapsed = mp_hal_ticks_ms() - self->start_time;
38 : 12 : return mp_obj_new_int_from_uint(elapsed);
39 : : }
40 : : static MP_DEFINE_CONST_FUN_OBJ_1(example_Timer_time_obj, example_Timer_time);
41 : :
42 : : // This represents Timer.__new__ and Timer.__init__, which is called when
43 : : // the user instantiates a Timer object.
44 : 6 : static mp_obj_t example_Timer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
45 : : // Allocates the new object and sets the type.
46 : 6 : example_Timer_obj_t *self = mp_obj_malloc(example_Timer_obj_t, type);
47 : :
48 : : // Initializes the time for this Timer instance.
49 : 6 : self->start_time = mp_hal_ticks_ms();
50 : :
51 : : // The make_new function always returns self.
52 : 6 : return MP_OBJ_FROM_PTR(self);
53 : : }
54 : :
55 : : // This collects all methods and other static class attributes of the Timer.
56 : : // The table structure is similar to the module table, as detailed below.
57 : : static const mp_rom_map_elem_t example_Timer_locals_dict_table[] = {
58 : : { MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&example_Timer_time_obj) },
59 : : };
60 : : static MP_DEFINE_CONST_DICT(example_Timer_locals_dict, example_Timer_locals_dict_table);
61 : :
62 : : // This defines the type(Timer) object.
63 : : MP_DEFINE_CONST_OBJ_TYPE(
64 : : example_type_Timer,
65 : : MP_QSTR_Timer,
66 : : MP_TYPE_FLAG_NONE,
67 : : make_new, example_Timer_make_new,
68 : : locals_dict, &example_Timer_locals_dict
69 : : );
70 : :
71 : : // What follows is a *separate* class definition that demonstrates more
72 : : // advanced techniques to implement other Python-like features, such as:
73 : : //
74 : : // - A custom representation for __repr__ and __str__.
75 : : // - Custom attribute handling to create a read/write "property".
76 : : //
77 : : // It re-uses some of the elements of the basic Timer class. This is allowed
78 : : // because they both use example_Timer_obj_t as the instance structure.
79 : :
80 : : // Handles AdvancedTimer.__repr__, AdvancedTimer.__str__.
81 : 4 : static void example_AdvancedTimer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
82 : :
83 : : // Get the elapsed time. In this case, it's also a demonstration of calling
84 : : // the equivalent of self.time() in the C API. This is not usually very
85 : : // efficient, but it can sometimes be useful.
86 : 4 : mp_uint_t elapsed = mp_obj_get_int(example_Timer_time(self_in));
87 : :
88 : : // We'll make all representations print at least the class name.
89 : 4 : mp_printf(print, "%q()", MP_QSTR_AdvancedTimer);
90 : :
91 : : // Decide what else to print based on print kind.
92 [ + + ]: 4 : if (kind == PRINT_STR) {
93 : : // For __str__, let's attempt to make it more readable.
94 : 2 : mp_printf(print, " # created %d seconds ago", elapsed / 1000);
95 : : }
96 : 4 : }
97 : :
98 : : // Handles AdvancedTimer.seconds for reading and writing.
99 : 12 : static void example_AdvancedTimer_attribute_handler(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
100 : :
101 : : // In this example, we only want to handle the .seconds attribute in a
102 : : // special way.
103 [ + + ]: 12 : if (attr != MP_QSTR_seconds) {
104 : : // Attribute not found, continue lookup in locals dict. This way,
105 : : // methods like .time() will be handled normally.
106 : 2 : dest[1] = MP_OBJ_SENTINEL;
107 : 2 : return;
108 : : }
109 : :
110 : : // Get reference to AdvancedTimer instance.
111 : 10 : example_Timer_obj_t *self = MP_OBJ_TO_PTR(self_in);
112 : :
113 : : // Check if this is a read operation.
114 [ + + ]: 10 : if (dest[0] == MP_OBJ_NULL) {
115 : : // It's read, so "return" elapsed seconds by storing it in dest[0].
116 : 6 : mp_uint_t elapsed = mp_hal_ticks_ms() - self->start_time;
117 : 6 : dest[0] = mp_obj_new_int_from_uint(elapsed / 1000);
118 : 6 : return;
119 : : }
120 : : // Check if this is a delete or store operation.
121 [ + - ]: 4 : else if (dest[0] == MP_OBJ_SENTINEL) {
122 : : // It's delete or store. Now check which one.
123 [ + - ]: 4 : if (dest[1] == MP_OBJ_NULL) {
124 : : // It's delete. But in this example we don't want to allow it
125 : : // so we just return.
126 : : return;
127 : : } else {
128 : : // It's write. First, get the value that the user is trying to set.
129 : 4 : mp_uint_t desired_ms = mp_obj_get_int(dest[1]) * 1000;
130 : : // Use it to update the start time. This way, the next read will
131 : : // report the updated time.
132 : 2 : self->start_time = mp_hal_ticks_ms() - desired_ms;
133 : :
134 : : // Indicate successful store.
135 : 2 : dest[0] = MP_OBJ_NULL;
136 : 2 : return;
137 : : }
138 : : }
139 : : }
140 : :
141 : : // This defines the type(AdvancedTimer) object.
142 : : MP_DEFINE_CONST_OBJ_TYPE(
143 : : example_type_AdvancedTimer,
144 : : MP_QSTR_AdvancedTimer,
145 : : MP_TYPE_FLAG_NONE,
146 : : attr, example_AdvancedTimer_attribute_handler,
147 : : print, example_AdvancedTimer_print,
148 : : make_new, example_Timer_make_new,
149 : : locals_dict, &example_Timer_locals_dict
150 : : );
151 : :
152 : : // Define all attributes of the module.
153 : : // Table entries are key/value pairs of the attribute name (a string)
154 : : // and the MicroPython object reference.
155 : : // All identifiers and strings are written as MP_QSTR_xxx and will be
156 : : // optimized to word-sized integers by the build system (interned strings).
157 : : static const mp_rom_map_elem_t example_module_globals_table[] = {
158 : : { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_cexample) },
159 : : { MP_ROM_QSTR(MP_QSTR_add_ints), MP_ROM_PTR(&example_add_ints_obj) },
160 : : { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&example_type_Timer) },
161 : : { MP_ROM_QSTR(MP_QSTR_AdvancedTimer), MP_ROM_PTR(&example_type_AdvancedTimer) },
162 : : };
163 : : static MP_DEFINE_CONST_DICT(example_module_globals, example_module_globals_table);
164 : :
165 : : // Define module object.
166 : : const mp_obj_module_t example_user_cmodule = {
167 : : .base = { &mp_type_module },
168 : : .globals = (mp_obj_dict_t *)&example_module_globals,
169 : : };
170 : :
171 : : // Register the module to make it available in Python.
172 : : MP_REGISTER_MODULE(MP_QSTR_cexample, example_user_cmodule);
|