summaryrefslogtreecommitdiffstatshomepage
path: root/stmhal
diff options
context:
space:
mode:
Diffstat (limited to 'stmhal')
-rw-r--r--stmhal/accel.c26
-rw-r--r--stmhal/adc.c44
-rw-r--r--stmhal/dac.c4
-rw-r--r--stmhal/extint.c145
-rw-r--r--stmhal/gendoc.py356
-rw-r--r--stmhal/i2c.c159
-rw-r--r--stmhal/led.c19
-rw-r--r--stmhal/modpyb.c41
-rw-r--r--stmhal/pin.c157
-rw-r--r--stmhal/rng.c4
-rw-r--r--stmhal/servo.c24
-rw-r--r--stmhal/spi.c87
-rw-r--r--stmhal/uart.c69
-rw-r--r--stmhal/usrsw.c34
14 files changed, 935 insertions, 234 deletions
diff --git a/stmhal/accel.c b/stmhal/accel.c
index f93833d1c3..d3373f1ebd 100644
--- a/stmhal/accel.c
+++ b/stmhal/accel.c
@@ -14,6 +14,13 @@
#if MICROPY_HW_HAS_MMA7660
+/// \moduleref pyb
+/// \class Accel - accelerometer control
+///
+/// Accel is an object that controls the accelerometer.
+///
+/// Raw values are between -30 and 30.
+
#define MMA_ADDR (0x98)
#define MMA_REG_X (0)
#define MMA_REG_Y (1)
@@ -83,6 +90,8 @@ typedef struct _pyb_accel_obj_t {
STATIC pyb_accel_obj_t pyb_accel_obj;
+/// \classmethod \constructor()
+/// Create and return an accelerometer object.
STATIC mp_obj_t pyb_accel_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
// check arguments
mp_arg_check_num(n_args, n_kw, 0, 0, false);
@@ -100,32 +109,38 @@ STATIC mp_obj_t read_axis(int axis) {
return mp_obj_new_int(MMA_AXIS_SIGNED_VALUE(data[0]));
}
+/// \method x()
+/// Get the x-axis value.
STATIC mp_obj_t pyb_accel_x(mp_obj_t self_in) {
return read_axis(MMA_REG_X);
}
-
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_accel_x_obj, pyb_accel_x);
+/// \method y()
+/// Get the y-axis value.
STATIC mp_obj_t pyb_accel_y(mp_obj_t self_in) {
return read_axis(MMA_REG_Y);
}
-
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_accel_y_obj, pyb_accel_y);
+/// \method z()
+/// Get the z-axis value.
STATIC mp_obj_t pyb_accel_z(mp_obj_t self_in) {
return read_axis(MMA_REG_Z);
}
-
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_accel_z_obj, pyb_accel_z);
+/// \method tilt()
+/// Get the tilt register.
STATIC mp_obj_t pyb_accel_tilt(mp_obj_t self_in) {
uint8_t data[1];
HAL_I2C_Mem_Read(&I2CHandle1, MMA_ADDR, MMA_REG_TILT, I2C_MEMADD_SIZE_8BIT, data, 1, 200);
return mp_obj_new_int(data[0]);
}
-
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_accel_tilt_obj, pyb_accel_tilt);
+/// \method filtered_xyz()
+/// Get a 3-tuple of filtered x, y and z values.
STATIC mp_obj_t pyb_accel_filtered_xyz(mp_obj_t self_in) {
pyb_accel_obj_t *self = self_in;
@@ -146,7 +161,6 @@ STATIC mp_obj_t pyb_accel_filtered_xyz(mp_obj_t self_in) {
return mp_obj_new_tuple(3, tuple);
}
-
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_accel_filtered_xyz_obj, pyb_accel_filtered_xyz);
STATIC mp_obj_t pyb_accel_read(mp_obj_t self_in, mp_obj_t reg) {
@@ -154,7 +168,6 @@ STATIC mp_obj_t pyb_accel_read(mp_obj_t self_in, mp_obj_t reg) {
HAL_I2C_Mem_Read(&I2CHandle1, MMA_ADDR, mp_obj_get_int(reg), I2C_MEMADD_SIZE_8BIT, data, 1, 200);
return mp_obj_new_int(data[0]);
}
-
MP_DEFINE_CONST_FUN_OBJ_2(pyb_accel_read_obj, pyb_accel_read);
STATIC mp_obj_t pyb_accel_write(mp_obj_t self_in, mp_obj_t reg, mp_obj_t val) {
@@ -163,7 +176,6 @@ STATIC mp_obj_t pyb_accel_write(mp_obj_t self_in, mp_obj_t reg, mp_obj_t val) {
HAL_I2C_Mem_Write(&I2CHandle1, MMA_ADDR, mp_obj_get_int(reg), I2C_MEMADD_SIZE_8BIT, data, 1, 200);
return mp_const_none;
}
-
MP_DEFINE_CONST_FUN_OBJ_3(pyb_accel_write_obj, pyb_accel_write);
STATIC const mp_map_elem_t pyb_accel_locals_dict_table[] = {
diff --git a/stmhal/adc.c b/stmhal/adc.c
index d7d2cf0893..0cc2a9a904 100644
--- a/stmhal/adc.c
+++ b/stmhal/adc.c
@@ -14,16 +14,19 @@
#include "genhdr/pins.h"
#include "timer.h"
-// Usage Model:
-//
-// adc = pyb.ADC(pin)
-// val = adc.read()
-//
-// adc = pyb.ADCAll(resolution)
-// val = adc.read_channel(channel)
-// val = adc.read_core_temp()
-// val = adc.read_core_vbat()
-// val = adc.read_core_vref()
+/// \moduleref pyb
+/// \class ADC - analog to digital conversion: read analog values on a pin
+///
+/// Usage:
+///
+/// adc = pyb.ADC(pin) # create an analog object from a pin
+/// val = adc.read() # read an analog value
+///
+/// adc = pyb.ADCAll(resolution) # creale an ADCAll object
+/// val = adc.read_channel(channel) # read the given channel
+/// val = adc.read_core_temp() # read MCU temperature
+/// val = adc.read_core_vbat() # read MCU VBAT
+/// val = adc.read_core_vref() # read MCU VREF
/* ADC defintions */
#define ADCx (ADC1)
@@ -118,6 +121,9 @@ STATIC void adc_print(void (*print)(void *env, const char *fmt, ...), void *env,
print(env, " channel=%lu>", self->channel);
}
+/// \classmethod \constructor(pin)
+/// Create an ADC object associated with the given pin.
+/// This allows you to then read analog values on that pin.
STATIC mp_obj_t adc_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
// check number of arguments
mp_arg_check_num(n_args, n_kw, 1, 1, false);
@@ -155,15 +161,30 @@ STATIC mp_obj_t adc_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_
return o;
}
+/// \method read()
+/// Read the value on the analog pin and return it. The returned value
+/// will be between 0 and 4095.
STATIC mp_obj_t adc_read(mp_obj_t self_in) {
pyb_obj_adc_t *self = self_in;
uint32_t data = adc_read_channel(&self->handle);
return mp_obj_new_int(data);
}
-
STATIC MP_DEFINE_CONST_FUN_OBJ_1(adc_read_obj, adc_read);
+/// \method read_timed(buf, freq)
+/// Read analog values into the given buffer at the given frequency.
+///
+/// Example:
+///
+/// adc = pyb.ADC(pyb.Pin.board.X19) # create an ADC on pin X19
+/// buf = bytearray(100) # create a buffer of 100 bytes
+/// adc.read_timed(buf, 10) # read analog values into buf at 10Hz
+/// # this will take 10 seconds to finish
+/// for val in buf: # loop over all values
+/// print(val) # print the value out
+///
+/// This function does not allocate any memory.
STATIC mp_obj_t adc_read_timed(mp_obj_t self_in, mp_obj_t buf_in, mp_obj_t freq_in) {
pyb_obj_adc_t *self = self_in;
@@ -196,7 +217,6 @@ STATIC mp_obj_t adc_read_timed(mp_obj_t self_in, mp_obj_t buf_in, mp_obj_t freq_
return mp_obj_new_int(bufinfo.len);
}
-
STATIC MP_DEFINE_CONST_FUN_OBJ_3(adc_read_timed_obj, adc_read_timed);
STATIC const mp_map_elem_t adc_locals_dict_table[] = {
diff --git a/stmhal/dac.c b/stmhal/dac.c
index 73f1fb2593..cd62f017fb 100644
--- a/stmhal/dac.c
+++ b/stmhal/dac.c
@@ -13,6 +13,10 @@
#include "timer.h"
#include "dac.h"
+/// \moduleref pyb
+/// \class DAC - digital to analog conversion
+///
+
STATIC DAC_HandleTypeDef DAC_Handle;
void dac_init(void) {
diff --git a/stmhal/extint.c b/stmhal/extint.c
index 289a519762..346e2851ee 100644
--- a/stmhal/extint.c
+++ b/stmhal/extint.c
@@ -15,58 +15,52 @@
#include "pin.h"
#include "extint.h"
-// Usage Model:
-//
-// There are a total of 22 interrupt lines. 16 of these can come from GPIO pins
-// and the remaining 6 are from internal sources.
-//
-// For lines 0 thru 15, a given line can map to the corresponding line from an
-// arbitrary port. So line 0 can map to Px0 where x is A, B, C, ... and
-// line 1 can map to Px1 where x is A, B, C, ...
-//
-// def callback(line):
-// print("line =", line)
-//
-// # Note: ExtInt will automatically configure the gpio line as an input.
-// extint = pyb.ExtInt(pin, pyb.ExtInt.IRQ_FALLING, pyb.GPIO.PULL_UP, callback)
-//
-// Now every time a falling edge is seen on the X1 pin, the callback will be
-// called. Caution: mechanical pushbuttons have "bounce" and pushing or
-// releasing a switch will often generate multiple edges.
-// See: http://www.eng.utah.edu/~cs5780/debouncing.pdf for a detailed
-// explanation, along with various techniques for debouncing.
-//
-// Trying to register 2 callbacks onto the same pin will throw an exception.
-//
-// If pin is passed as an integer, then it is assumed to map to one of the
-// internal interrupt sources, and must be in the range 16 thru 22.
-//
-// All other pin objects go through the pin mapper to come up with one of the
-// gpio pins.
-//
-// extint = pyb.ExtInt(pin, mode, pull, callback)
-//
-// Valid modes are pyb.ExtInt.IRQ_RISING, pyb.ExtInt.IRQ_FALLING,
-// pyb.ExtInt.IRQ_RISING_FALLING, pyb.ExtInt.EVT_RISING,
-// pyb.ExtInt.EVT_FALLING, and pyb.ExtInt.EVT_RISING_FALLING.
-//
-// Only the IRQ_xxx modes have been tested. The EVT_xxx modes have
-// something to do with sleep mode and the WFE instruction.
-//
-// Valid pull values are pyb.GPIO.PULL_UP, pyb.GPIO.PULL_DOWN, pyb.GPIO.PULL_NONE.
-//
-// extint.line() will return the line number that pin was mapped to.
-// extint.disable() can be use to disable the interrupt associated with a given
-// exti object. This could be useful for debouncing.
-// extint.enable() enables a disabled interrupt
-// extint.swint() will allow the callback to be triggered from software.
-//
-// pyb.ExtInt.regs() will dump the values of the EXTI registers.
-//
-// There is also a C API, so that drivers which require EXTI interrupt lines
-// can also use this code. See extint.h for the available functions and
-// usrsw.h for an example of using this.
-//
+/// \moduleref pyb
+/// \class ExtInt - configure I/O pins to interrupt on external events
+///
+/// There are a total of 22 interrupt lines. 16 of these can come from GPIO pins
+/// and the remaining 6 are from internal sources.
+///
+/// For lines 0 thru 15, a given line can map to the corresponding line from an
+/// arbitrary port. So line 0 can map to Px0 where x is A, B, C, ... and
+/// line 1 can map to Px1 where x is A, B, C, ...
+///
+/// def callback(line):
+/// print("line =", line)
+///
+/// Note: ExtInt will automatically configure the gpio line as an input.
+///
+/// extint = pyb.ExtInt(pin, pyb.ExtInt.IRQ_FALLING, pyb.GPIO.PULL_UP, callback)
+///
+/// Now every time a falling edge is seen on the X1 pin, the callback will be
+/// called. Caution: mechanical pushbuttons have "bounce" and pushing or
+/// releasing a switch will often generate multiple edges.
+/// See: http://www.eng.utah.edu/~cs5780/debouncing.pdf for a detailed
+/// explanation, along with various techniques for debouncing.
+///
+/// Trying to register 2 callbacks onto the same pin will throw an exception.
+///
+/// If pin is passed as an integer, then it is assumed to map to one of the
+/// internal interrupt sources, and must be in the range 16 thru 22.
+///
+/// All other pin objects go through the pin mapper to come up with one of the
+/// gpio pins.
+///
+/// extint = pyb.ExtInt(pin, mode, pull, callback)
+///
+/// Valid modes are pyb.ExtInt.IRQ_RISING, pyb.ExtInt.IRQ_FALLING,
+/// pyb.ExtInt.IRQ_RISING_FALLING, pyb.ExtInt.EVT_RISING,
+/// pyb.ExtInt.EVT_FALLING, and pyb.ExtInt.EVT_RISING_FALLING.
+///
+/// Only the IRQ_xxx modes have been tested. The EVT_xxx modes have
+/// something to do with sleep mode and the WFE instruction.
+///
+/// Valid pull values are pyb.GPIO.PULL_UP, pyb.GPIO.PULL_DOWN, pyb.GPIO.PULL_NONE.
+///
+/// There is also a C API, so that drivers which require EXTI interrupt lines
+/// can also use this code. See extint.h for the available functions and
+/// usrsw.h for an example of using this.
+
// TODO Add python method to change callback object.
#define EXTI_OFFSET (EXTI_BASE - PERIPH_BASE)
@@ -204,29 +198,45 @@ void extint_swint(uint line) {
EXTI->SWIER = (1 << line);
}
+/// \method line()
+/// Return the line number that the pin is mapped to.
STATIC mp_obj_t extint_obj_line(mp_obj_t self_in) {
extint_obj_t *self = self_in;
return MP_OBJ_NEW_SMALL_INT(self->line);
}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_line_obj,i extint_obj_line);
+/// \method enable()
+/// Enable a disabled interrupt.
STATIC mp_obj_t extint_obj_enable(mp_obj_t self_in) {
extint_obj_t *self = self_in;
extint_enable(self->line);
return mp_const_none;
}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_enable_obj, extint_obj_enable);
+/// \method disable()
+/// Disable the interrupt associated with the ExtInt object.
+/// This could be useful for debouncing.
STATIC mp_obj_t extint_obj_disable(mp_obj_t self_in) {
extint_obj_t *self = self_in;
extint_disable(self->line);
return mp_const_none;
}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_disable_obj, extint_obj_disable);
+/// \method swint()
+/// Trigger the callback from software.
STATIC mp_obj_t extint_obj_swint(mp_obj_t self_in) {
extint_obj_t *self = self_in;
extint_swint(self->line);
return mp_const_none;
}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_swint_obj, extint_obj_swint);
+// TODO document as a staticmethod
+/// \classmethod regs()
+/// Dump the values of the EXTI registers.
STATIC mp_obj_t extint_regs(void) {
printf("EXTI_IMR %08lx\n", EXTI->IMR);
printf("EXTI_EMR %08lx\n", EXTI->EMR);
@@ -236,9 +246,24 @@ STATIC mp_obj_t extint_regs(void) {
printf("EXTI_PR %08lx\n", EXTI->PR);
return mp_const_none;
}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(extint_regs_fun_obj, extint_regs);
+STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(extint_regs_obj, (mp_obj_t)&extint_regs_fun_obj);
-// line_obj = pyb.ExtInt(pin, mode, pull, callback)
-
+/// \classmethod \constructor(pin, mode, pull, callback)
+/// Create an ExtInt object:
+///
+/// - `pin` is the pin on which to enable the interrupt (can be a pin object or any valid pin name).
+/// - `mode` can be one of:
+/// - `ExtInt.IRQ_RISING` - trigger on a rising edge;
+/// - `ExtInt.IRQ_FALLING` - trigger on a falling edge;
+/// - `ExtInt.IRQ_RISING_FALLING` - trigger on a rising or falling edge.
+/// - `pull` can be one of:
+/// - `pyb.Pin.PULL_NONE` - no pull up or down resistors;
+/// - `pyb.Pin.PULL_UP` - enable the pull-up resistor;
+/// - `pyb.Pin.PULL_DOWN` - enable the pull-down resistor.
+/// - `callback` is the function to call when the interrupt triggers. The
+/// callback function must accept exactly 1 argument, which is the line that
+/// triggered the interrupt.
STATIC const mp_arg_t pyb_extint_make_new_args[] = {
{ MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
@@ -268,19 +293,17 @@ STATIC void extint_obj_print(void (*print)(void *env, const char *fmt, ...), voi
print(env, "<ExtInt line=%u>", self->line);
}
-STATIC MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_line_obj, extint_obj_line);
-STATIC MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_enable_obj, extint_obj_enable);
-STATIC MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_disable_obj, extint_obj_disable);
-STATIC MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_swint_obj, extint_obj_swint);
-STATIC MP_DEFINE_CONST_FUN_OBJ_0(extint_regs_fun_obj, extint_regs);
-STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(extint_regs_obj, (mp_obj_t)&extint_regs_fun_obj);
-
STATIC const mp_map_elem_t extint_locals_dict_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_line), (mp_obj_t)&extint_obj_line_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_enable), (mp_obj_t)&extint_obj_enable_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_disable), (mp_obj_t)&extint_obj_disable_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_swint), (mp_obj_t)&extint_obj_swint_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_regs), (mp_obj_t)&extint_regs_obj },
+
+ // class constants
+ /// \constant IRQ_RISING - interrupt on a rising edge
+ /// \constant IRQ_FALLING - interrupt on a falling edge
+ /// \constant IRQ_RISING_FALLING - interrupt on a rising or falling edge
{ MP_OBJ_NEW_QSTR(MP_QSTR_IRQ_RISING), MP_OBJ_NEW_SMALL_INT(GPIO_MODE_IT_RISING) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_IRQ_FALLING), MP_OBJ_NEW_SMALL_INT(GPIO_MODE_IT_FALLING) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_IRQ_RISING_FALLING), MP_OBJ_NEW_SMALL_INT(GPIO_MODE_IT_RISING_FALLING) },
diff --git a/stmhal/gendoc.py b/stmhal/gendoc.py
new file mode 100644
index 0000000000..634de6f093
--- /dev/null
+++ b/stmhal/gendoc.py
@@ -0,0 +1,356 @@
+"""
+Generate documentation for pyboard API from C files.
+"""
+
+import os
+import argparse
+import re
+import markdown
+
+# given a list of (name,regex) pairs, find the first one that matches the given line
+def re_match_first(regexs, line):
+ for name, regex in regexs:
+ match = re.match(regex, line)
+ if match:
+ return name, match
+ return None, None
+
+def makedirs(d):
+ if not os.path.isdir(d):
+ os.makedirs(d)
+
+class Lexer:
+ class LexerError(Exception):
+ pass
+
+ class EOF(Exception):
+ pass
+
+ class Break(Exception):
+ pass
+
+ def __init__(self, file):
+ self.filename = file
+ with open(file, 'rt') as f:
+ line_num = 0
+ lines = []
+ for line in f:
+ line_num += 1
+ line = line.strip()
+ if line == '///':
+ lines.append((line_num, ''))
+ elif line.startswith('/// '):
+ lines.append((line_num, line[4:]))
+ elif len(lines) > 0 and lines[-1][1] is not None:
+ lines.append((line_num, None))
+ if len(lines) > 0 and lines[-1][1] is not None:
+ lines.append((line_num, None))
+ self.cur_line = 0
+ self.lines = lines
+
+ def opt_break(self):
+ if len(self.lines) > 0 and self.lines[0][1] is None:
+ self.lines.pop(0)
+
+ def next(self):
+ if len(self.lines) == 0:
+ raise Lexer.EOF
+ else:
+ l = self.lines.pop(0)
+ self.cur_line = l[0]
+ if l[1] is None:
+ raise Lexer.Break
+ else:
+ return l[1]
+
+ def error(self, msg):
+ print('({}:{}) {}'.format(self.filename, self.cur_line, msg))
+ raise Lexer.LexerError
+
+class DocItem:
+ def __init__(self):
+ self.doc = []
+
+ def add_doc(self, lex):
+ try:
+ while True:
+ line = lex.next()
+ if len(line) > 0 or len(self.doc) > 0:
+ self.doc.append(line)
+ except Lexer.Break:
+ pass
+
+ def dump(self):
+ return '\n'.join(self.doc)
+
+class DocConstant(DocItem):
+ def __init__(self, name, descr):
+ super().__init__()
+ self.name = name
+ self.descr = descr
+
+ def dump(self, ctx):
+ return '{}.{} - {}'.format(ctx, self.name, self.descr)
+
+class DocFunction(DocItem):
+ def __init__(self, name, args):
+ super().__init__()
+ self.name = name
+ self.args = args
+
+ def dump(self, ctx):
+ if self.name == '\\constructor':
+ s = '### `{}{}`'.format(ctx, self.args)
+ elif self.name == '\\call':
+ s = '### `{}{}`'.format(ctx, self.args)
+ else:
+ s = '### `{}.{}{}`'.format(ctx, self.name, self.args)
+ return s + '\n' + super().dump()
+
+class DocClass(DocItem):
+ def __init__(self, name, descr):
+ super().__init__()
+ self.name = name
+ self.descr = descr
+ self.constructors = {}
+ self.classmethods = {}
+ self.methods = {}
+ self.constants = {}
+
+ def process_classmethod(self, lex, d):
+ name = d['id']
+ if name == '\\constructor':
+ dict_ = self.constructors
+ else:
+ dict_ = self.classmethods
+ if name in dict_:
+ lex.error("multiple definition of method '{}'".format(name))
+ method = dict_[name] = DocFunction(name, d['args'])
+ method.add_doc(lex)
+
+ def process_method(self, lex, d):
+ name = d['id']
+ dict_ = self.methods
+ if name in dict_:
+ lex.error("multiple definition of method '{}'".format(name))
+ method = dict_[name] = DocFunction(name, d['args'])
+ method.add_doc(lex)
+
+ def process_constant(self, lex, d):
+ name = d['id']
+ if name in self.constants:
+ lex.error("multiple definition of constant '{}'".format(name))
+ self.constants[name] = DocConstant(name, d['descr'])
+ lex.opt_break()
+
+ def dump(self):
+ s = []
+ s.append('')
+ s.append('# class {}'.format(self.name))
+ s.append('')
+ s.append(super().dump())
+ if len(self.constructors) > 0:
+ s.append('')
+ s.append("## Constructors")
+ for f in sorted(self.constructors.values(), key=lambda x:x.name):
+ s.append('')
+ s.append(f.dump(self.name))
+ if len(self.classmethods) > 0:
+ s.append('')
+ s.append("## Class methods")
+ for f in sorted(self.classmethods.values(), key=lambda x:x.name):
+ s.append('')
+ s.append(f.dump(self.name))
+ if len(self.methods) > 0:
+ s.append('')
+ s.append("## Methods")
+ for f in sorted(self.methods.values(), key=lambda x:x.name):
+ s.append('')
+ s.append(f.dump(self.name.lower()))
+ if len(self.constants) > 0:
+ s.append('')
+ s.append("## Constants")
+ for c in sorted(self.constants.values(), key=lambda x:x.name):
+ s.append('')
+ s.append('`{}`'.format(c.dump(self.name)))
+ return '\n'.join(s)
+
+class DocModule(DocItem):
+ def __init__(self, name, descr):
+ super().__init__()
+ self.name = name
+ self.descr = descr
+ self.functions = {}
+ self.constants = {}
+ self.classes = {}
+ self.cur_class = None
+
+ def new_file(self):
+ self.cur_class = None
+
+ def process_function(self, lex, d):
+ name = d['id']
+ if name in self.functions:
+ lex.error("multiple definition of function '{}'".format(name))
+ function = self.functions[name] = DocFunction(name, d['args'])
+ function.add_doc(lex)
+
+ #def process_classref(self, lex, d):
+ # name = d['id']
+ # self.classes[name] = name
+ # lex.opt_break()
+
+ def process_class(self, lex, d):
+ name = d['id']
+ if name in self.classes:
+ lex.error("multiple definition of class '{}'".format(name))
+ self.cur_class = self.classes[name] = DocClass(name, d['descr'])
+ self.cur_class.add_doc(lex)
+
+ def process_classmethod(self, lex, d):
+ self.cur_class.process_classmethod(lex, d)
+
+ def process_method(self, lex, d):
+ self.cur_class.process_method(lex, d)
+
+ def process_constant(self, lex, d):
+ self.cur_class.process_constant(lex, d)
+
+ def dump(self):
+ s = []
+ s.append('# module {}'.format(self.name))
+ s.append('')
+ s.append(super().dump())
+ s.append('')
+ s.append('## Functions')
+ for f in sorted(self.functions.values(), key=lambda x:x.name):
+ s.append('')
+ s.append(f.dump(self.name))
+ s.append('')
+ s.append('## Classes')
+ for c in sorted(self.classes.values(), key=lambda x:x.name):
+ s.append('')
+ s.append('[`{}.{}`]({}/index.html) - {}'.format(self.name, c.name, c.name, c.descr))
+ return '\n'.join(s)
+
+ def write(self, dir):
+ index = markdown.markdown(self.dump())
+ with open(os.path.join(dir, 'index.html'), 'wt') as f:
+ f.write(index)
+ for c in self.classes.values():
+ class_dir = os.path.join(dir, c.name)
+ makedirs(class_dir)
+ class_dump = c.dump()
+ class_dump = 'part of the [{} module](../index.html)'.format(self.name) + '\n' + class_dump
+ index = markdown.markdown(class_dump)
+ with open(os.path.join(class_dir, 'index.html'), 'wt') as f:
+ f.write(index)
+
+class Doc:
+ def __init__(self):
+ self.modules = {}
+ self.cur_module = None
+
+ def new_file(self):
+ self.cur_module = None
+ for m in self.modules.values():
+ m.new_file()
+
+ def check_module(self, lex):
+ if self.cur_module is None:
+ lex.error('module not defined')
+
+ def process_module(self, lex, d):
+ name = d['id']
+ if name in self.modules:
+ lex.error("multiple definition of module '{}'".format(name))
+ self.cur_module = self.modules[name] = DocModule(name, d['descr'])
+ self.cur_module.add_doc(lex)
+
+ def process_moduleref(self, lex, d):
+ name = d['id']
+ if name not in self.modules:
+ lex.error('module {} referenced before definition'.format(name))
+ self.cur_module = self.modules[name]
+
+ #def process_classref(self, lex, d):
+ # self.cur_module.process_classref(lex, d)
+
+ def process_class(self, lex, d):
+ self.check_module(lex)
+ self.cur_module.process_class(lex, d)
+
+ def process_function(self, lex, d):
+ self.check_module(lex)
+ self.cur_module.process_function(lex, d)
+
+ def process_classmethod(self, lex, d):
+ self.check_module(lex)
+ self.cur_module.process_classmethod(lex, d)
+
+ def process_method(self, lex, d):
+ self.check_module(lex)
+ self.cur_module.process_method(lex, d)
+
+ def process_constant(self, lex, d):
+ self.check_module(lex)
+ self.cur_module.process_constant(lex, d)
+
+ def write(self, dir):
+ for m in self.modules.values():
+ mod_dir = os.path.join(dir, 'module', m.name)
+ makedirs(mod_dir)
+ m.write(mod_dir)
+
+regex_descr = r'(?P<descr>.*)'
+
+doc_regexs = (
+ (Doc.process_module, re.compile(r'\\module (?P<id>[a-z]+) - ' + regex_descr + r'$')),
+ (Doc.process_moduleref, re.compile(r'\\moduleref (?P<id>[a-z]+)$')),
+ (Doc.process_function, re.compile(r'\\function (?P<id>[a-z0-9_]+)(?P<args>\(.*\))$')),
+ (Doc.process_classmethod, re.compile(r'\\classmethod (?P<id>\\?[a-z0-9_]+)(?P<args>\(.*\))$')),
+ (Doc.process_method, re.compile(r'\\method (?P<id>\\?[a-z0-9_]+)(?P<args>\(.*\))$')),
+ (Doc.process_constant, re.compile(r'\\constant (?P<id>[A-Z0-9_]+) - ' + regex_descr + r'$')),
+ #(Doc.process_classref, re.compile(r'\\classref (?P<id>[A-Za-z0-9_]+)$')),
+ (Doc.process_class, re.compile(r'\\class (?P<id>[A-Za-z0-9_]+) - ' + regex_descr + r'$')),
+)
+
+def process_file(file, doc):
+ lex = Lexer(file)
+ doc.new_file()
+ try:
+ try:
+ while True:
+ line = lex.next()
+ fun, match = re_match_first(doc_regexs, line)
+ if fun == None:
+ lex.error('unknown line format: {}'.format(line))
+ fun(doc, lex, match.groupdict())
+
+ except Lexer.Break:
+ lex.error('unexpected break')
+
+ except Lexer.EOF:
+ pass
+
+ except Lexer.LexerError:
+ return False
+
+ return True
+
+def main():
+ cmd_parser = argparse.ArgumentParser(description='Generate documentation for pyboard API from C files.')
+ cmd_parser.add_argument('--outdir', metavar='<output dir>', default='gendoc-out', help='ouput directory')
+ cmd_parser.add_argument('files', nargs='+', help='input files')
+ args = cmd_parser.parse_args()
+
+ doc = Doc()
+ for file in args.files:
+ print('processing', file)
+ if not process_file(file, doc):
+ return
+ doc.write(args.outdir)
+ print('written to', args.outdir)
+
+if __name__ == "__main__":
+ main()
diff --git a/stmhal/i2c.c b/stmhal/i2c.c
index 2c804a23d0..ef9da496d0 100644
--- a/stmhal/i2c.c
+++ b/stmhal/i2c.c
@@ -14,50 +14,54 @@
#include "bufhelper.h"
#include "i2c.h"
-// Usage model:
-//
-// I2C objects are created attached to a specific bus. They can be initialised
-// when created, or initialised later on:
-//
-// from pyb import I2C
-//
-// i2c = I2C(1) # create on bus 1
-// i2c = I2C(1, I2C.MASTER) # create and init as a master
-// i2c.deinit() # turn off the peripheral
-// i2c.init(I2C.MASTER, baudrate=20000) # init as a master
-// i2c.init(I2C.SLAVE, addr=0x42) # init as a slave with given address
-//
-// Printing the i2c object gives you information about its configuration.
-//
-// Basic methods for slave are send and recv:
-//
-// i2c.send('abc') # send 3 bytes
-// i2c.send(0x42) # send a single byte, given by the number
-// data = i2c.recv(3) # receive 3 bytes
-//
-// To receive inplace, first create a bytearray:
-//
-// data = bytearray(3) # create a buffer
-// i2c.recv(data) # receive 3 bytes, writing them into data
-//
-// You can specify a timeout (in ms):
-//
-// i2c.send(b'123', timeout=2000) # timout after 2 seconds
-//
-// A master must specify the recipient's address:
-//
-// i2c.init(I2C.MASTER)
-// i2c.send('123', 0x42) # send 3 bytes to slave with address 0x42
-// i2c.send(b'456', addr=0x42) # keyword for address
-//
-// Master also has other methods:
-//
-// i2c.is_ready(0x42) # check if slave 0x42 is ready
-// i2c.scan() # scan for slaves on the bus, returning
-// # a list of valid addresses
-// i2c.mem_read(3, 0x42, 2) # read 3 bytes from memory of slave 0x42,
-// # starting at address 2 in the slave
-// i2c.mem_write('abc', 0x42, 2, timeout=1000)
+/// \moduleref pyb
+/// \class I2C - a two-wire serial protocol
+///
+/// I2C is a two-wire protocol for communicating between devices. At the physical
+/// level it consists of 2 wires: SCL and SDA, the clock and data lines respectively.
+///
+/// I2C objects are created attached to a specific bus. They can be initialised
+/// when created, or initialised later on:
+///
+/// from pyb import I2C
+///
+/// i2c = I2C(1) # create on bus 1
+/// i2c = I2C(1, I2C.MASTER) # create and init as a master
+/// i2c.init(I2C.MASTER, baudrate=20000) # init as a master
+/// i2c.init(I2C.SLAVE, addr=0x42) # init as a slave with given address
+/// i2c.deinit() # turn off the peripheral
+///
+/// Printing the i2c object gives you information about its configuration.
+///
+/// Basic methods for slave are send and recv:
+///
+/// i2c.send('abc') # send 3 bytes
+/// i2c.send(0x42) # send a single byte, given by the number
+/// data = i2c.recv(3) # receive 3 bytes
+///
+/// To receive inplace, first create a bytearray:
+///
+/// data = bytearray(3) # create a buffer
+/// i2c.recv(data) # receive 3 bytes, writing them into data
+///
+/// You can specify a timeout (in ms):
+///
+/// i2c.send(b'123', timeout=2000) # timout after 2 seconds
+///
+/// A master must specify the recipient's address:
+///
+/// i2c.init(I2C.MASTER)
+/// i2c.send('123', 0x42) # send 3 bytes to slave with address 0x42
+/// i2c.send(b'456', addr=0x42) # keyword for address
+///
+/// Master also has other methods:
+///
+/// i2c.is_ready(0x42) # check if slave 0x42 is ready
+/// i2c.scan() # scan for slaves on the bus, returning
+/// # a list of valid addresses
+/// i2c.mem_read(3, 0x42, 2) # read 3 bytes from memory of slave 0x42,
+/// # starting at address 2 in the slave
+/// i2c.mem_write('abc', 0x42, 2, timeout=1000)
#define PYB_I2C_MASTER (0)
#define PYB_I2C_SLAVE (1)
@@ -176,6 +180,14 @@ STATIC void pyb_i2c_print(void (*print)(void *env, const char *fmt, ...), void *
}
}
+/// \method init(mode, *, addr=0x12, baudrate=400000, gencall=False)
+///
+/// Initialise the I2C bus with the given parameters:
+///
+/// - `mode` must be either `I2C.MASTER` or `I2C.SLAVE`
+/// - `addr` is the 7-bit address (only sensible for a slave)
+/// - `baudrate` is the SCL clock rate (only sensible for a master)
+/// - `gencall` is whether to support general call mode
STATIC const mp_arg_t pyb_i2c_init_args[] = {
{ MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_addr, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0x12} },
@@ -213,6 +225,13 @@ STATIC mp_obj_t pyb_i2c_init_helper(const pyb_i2c_obj_t *self, uint n_args, cons
return mp_const_none;
}
+/// \classmethod \constructor(bus, ...)
+///
+/// Construct an I2C object on the given bus. `bus` can be 1 or 2.
+/// With no additional parameters, the I2C object is created but not
+/// initialised (it has the settings from the last initialisation of
+/// the bus, if any). If extra arguments are given, the bus is initialised.
+/// See `init` for parameters of initialisation.
STATIC mp_obj_t pyb_i2c_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
// check arguments
mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
@@ -243,6 +262,8 @@ STATIC mp_obj_t pyb_i2c_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_arg
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2c_init_obj, 1, pyb_i2c_init);
+/// \method deinit()
+/// Turn off the I2C bus.
STATIC mp_obj_t pyb_i2c_deinit(mp_obj_t self_in) {
pyb_i2c_obj_t *self = self_in;
i2c_deinit(self->i2c);
@@ -250,7 +271,8 @@ STATIC mp_obj_t pyb_i2c_deinit(mp_obj_t self_in) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_i2c_deinit_obj, pyb_i2c_deinit);
-// Check if an I2C device responds to the given address.
+/// \method is_ready(addr)
+/// Check if an I2C device responds to the given address. Only valid when in master mode.
STATIC mp_obj_t pyb_i2c_is_ready(mp_obj_t self_in, mp_obj_t i2c_addr_o) {
pyb_i2c_obj_t *self = self_in;
@@ -271,7 +293,9 @@ STATIC mp_obj_t pyb_i2c_is_ready(mp_obj_t self_in, mp_obj_t i2c_addr_o) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_i2c_is_ready_obj, pyb_i2c_is_ready);
-// Scan all I2C addresses from 0x01 to 0x7f and return a list of those that respond.
+/// \method scan()
+/// Scan all I2C addresses from 0x01 to 0x7f and return a list of those that respond.
+/// Only valid when in master mode.
STATIC mp_obj_t pyb_i2c_scan(mp_obj_t self_in) {
pyb_i2c_obj_t *self = self_in;
@@ -295,6 +319,14 @@ STATIC mp_obj_t pyb_i2c_scan(mp_obj_t self_in) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_i2c_scan_obj, pyb_i2c_scan);
+/// \method send(send, addr=0x00, timeout=5000)
+/// Send data on the bus:
+///
+/// - `send` is the data to send (an integer to send, or a buffer object)
+/// - `addr` is the address to send to (only required in master mode)
+/// - `timeout` is the timeout in milliseconds to wait for the send
+///
+/// Return value: `None`.
STATIC const mp_arg_t pyb_i2c_send_args[] = {
{ MP_QSTR_send, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_addr, MP_ARG_INT, {.u_int = PYB_I2C_MASTER_ADDRESS} },
@@ -335,6 +367,17 @@ STATIC mp_obj_t pyb_i2c_send(uint n_args, const mp_obj_t *args, mp_map_t *kw_arg
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2c_send_obj, 1, pyb_i2c_send);
+/// \method recv(send, addr=0x00, timeout=5000)
+///
+/// Receive data on the bus:
+///
+/// - `recv` can be an integer, which is the number of bytes to receive,
+/// or a mutable buffer, which will be filled with received bytes
+/// - `addr` is the address to receive from (only required in master mode)
+/// - `timeout` is the timeout in milliseconds to wait for the receive
+///
+/// Return value: if `recv` is an integer then a new buffer of the bytes received,
+/// otherwise the same buffer that was passed in to `recv`.
STATIC const mp_arg_t pyb_i2c_recv_args[] = {
{ MP_QSTR_recv, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_addr, MP_ARG_INT, {.u_int = PYB_I2C_MASTER_ADDRESS} },
@@ -379,6 +422,17 @@ STATIC mp_obj_t pyb_i2c_recv(uint n_args, const mp_obj_t *args, mp_map_t *kw_arg
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2c_recv_obj, 1, pyb_i2c_recv);
+/// \method mem_read(data, addr, memaddr, timeout=5000)
+///
+/// Read from the memory of an I2C device:
+///
+/// - `data` can be an integer or a buffer to read into
+/// - `addr` is the I2C device address
+/// - `memaddr` is the memory location within the I2C device
+/// - `timeout` is the timeout in milliseconds to wait for the read
+///
+/// Returns the read data.
+/// This is only valid in master mode.
STATIC const mp_arg_t pyb_i2c_mem_read_args[] = {
{ MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_addr, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
@@ -422,6 +476,17 @@ STATIC mp_obj_t pyb_i2c_mem_read(uint n_args, const mp_obj_t *args, mp_map_t *kw
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_i2c_mem_read_obj, 1, pyb_i2c_mem_read);
+/// \method mem_write(data, addr, memaddr, timeout=5000)
+///
+/// Write to the memory of an I2C device:
+///
+/// - `data` can be an integer or a buffer to write from
+/// - `addr` is the I2C device address
+/// - `memaddr` is the memory location within the I2C device
+/// - `timeout` is the timeout in milliseconds to wait for the write
+///
+/// Returns `None`.
+/// This is only valid in master mode.
STATIC mp_obj_t pyb_i2c_mem_write(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) {
pyb_i2c_obj_t *self = args[0];
@@ -465,6 +530,8 @@ STATIC const mp_map_elem_t pyb_i2c_locals_dict_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_mem_write), (mp_obj_t)&pyb_i2c_mem_write_obj },
// class constants
+ /// \constant MASTER - for initialising the bus to master mode
+ /// \constant SLAVE - for initialising the bus to slave mode
{ MP_OBJ_NEW_QSTR(MP_QSTR_MASTER), MP_OBJ_NEW_SMALL_INT(PYB_I2C_MASTER) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_SLAVE), MP_OBJ_NEW_SMALL_INT(PYB_I2C_SLAVE) },
};
diff --git a/stmhal/led.c b/stmhal/led.c
index 5a0dd7b9ac..28c918e0e8 100644
--- a/stmhal/led.c
+++ b/stmhal/led.c
@@ -12,6 +12,11 @@
#include "pin.h"
#include "genhdr/pins.h"
+/// \moduleref pyb
+/// \class LED - LED object
+///
+/// The LED object controls an individual LED (Light Emitting Diode).
+
typedef struct _pyb_led_obj_t {
mp_obj_base_t base;
machine_uint_t led_id;
@@ -195,6 +200,10 @@ void led_obj_print(void (*print)(void *env, const char *fmt, ...), void *env, mp
print(env, "<LED %lu>", self->led_id);
}
+/// \classmethod \constructor(id)
+/// Create an LED object associated with the given LED:
+///
+/// - `id` is the LED number, 1-4.
STATIC mp_obj_t led_obj_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
// check arguments
mp_arg_check_num(n_args, n_kw, 1, 1, false);
@@ -211,24 +220,34 @@ STATIC mp_obj_t led_obj_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const
return (mp_obj_t)&pyb_led_obj[led_id];
}
+/// \method on()
+/// Turn the LED on.
mp_obj_t led_obj_on(mp_obj_t self_in) {
pyb_led_obj_t *self = self_in;
led_state(self->led_id, 1);
return mp_const_none;
}
+/// \method off()
+/// Turn the LED off.
mp_obj_t led_obj_off(mp_obj_t self_in) {
pyb_led_obj_t *self = self_in;
led_state(self->led_id, 0);
return mp_const_none;
}
+/// \method toggle()
+/// Toggle the LED between on and off.
mp_obj_t led_obj_toggle(mp_obj_t self_in) {
pyb_led_obj_t *self = self_in;
led_toggle(self->led_id);
return mp_const_none;
}
+/// \method intensity([value])
+/// Get or set the LED intensity. Intensity ranges between 0 (off) and 255 (full on).
+/// If no argument is given, return the LED intensity.
+/// If an argument is given, set the LED intensity and return `None`.
mp_obj_t led_obj_intensity(uint n_args, const mp_obj_t *args) {
pyb_led_obj_t *self = args[0];
if (n_args == 1) {
diff --git a/stmhal/modpyb.c b/stmhal/modpyb.c
index a4e54fb87c..2f53bb5234 100644
--- a/stmhal/modpyb.c
+++ b/stmhal/modpyb.c
@@ -32,7 +32,12 @@
#include "modpyb.h"
#include "ff.h"
-// print lots of info about the board
+/// \module pyb - functions related to the pyboard
+///
+/// The `pyb` module contains specific functions related to the pyboard.
+
+/// \function info([dump_alloc_table])
+/// Print out lots of information about the board.
STATIC mp_obj_t pyb_info(uint n_args, const mp_obj_t *args) {
// get and print unique id; 96 bits
{
@@ -99,14 +104,16 @@ STATIC mp_obj_t pyb_info(uint n_args, const mp_obj_t *args) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_info_obj, 0, 1, pyb_info);
-// get unique MCU id; 96 bits = 12 bytes
+/// \function unique_id()
+/// Returns a string of 12 bytes (96 bits), which is the unique ID for the MCU.
STATIC mp_obj_t pyb_unique_id(void) {
byte *id = (byte*)0x1fff7a10;
return mp_obj_new_bytes(id, 12);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_unique_id_obj, pyb_unique_id);
-// get clock frequencies
+/// \function freq()
+/// Return a tuple of clock frequencies: (SYSCLK, HCLK, PCLK1, PCLK2).
// TODO should also be able to set frequency via this function
STATIC mp_obj_t pyb_freq(void) {
mp_obj_t tuple[4] = {
@@ -119,24 +126,31 @@ STATIC mp_obj_t pyb_freq(void) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_freq_obj, pyb_freq);
-// sync all file systems
+/// \function sync()
+/// Sync all file systems.
STATIC mp_obj_t pyb_sync(void) {
storage_flush();
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_sync_obj, pyb_sync);
+/// \function millis()
+/// Returns the number of milliseconds since the board was last reset.
STATIC mp_obj_t pyb_millis(void) {
return mp_obj_new_int(HAL_GetTick());
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_millis_obj, pyb_millis);
+/// \function delay(ms)
+/// Delay for the given number of milliseconds.
STATIC mp_obj_t pyb_delay(mp_obj_t count) {
HAL_Delay(mp_obj_get_int(count));
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_delay_obj, pyb_delay);
+/// \function udelay(us)
+/// Delay for the given number of microseconds.
STATIC mp_obj_t pyb_udelay(mp_obj_t usec) {
uint32_t count = 0;
const uint32_t utime = (168 * mp_obj_get_int(usec) / 5);
@@ -146,28 +160,32 @@ STATIC mp_obj_t pyb_udelay(mp_obj_t usec) {
}
}
}
-
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_udelay_obj, pyb_udelay);
+/// \function wfi()
+/// Wait for an interrupt.
+/// This executies a `wfi` instruction which reduces power consumption
+/// of the MCU until an interrupt occurs, at which point execution continues.
STATIC mp_obj_t pyb_wfi(void) {
__WFI();
return mp_const_none;
}
-
MP_DEFINE_CONST_FUN_OBJ_0(pyb_wfi_obj, pyb_wfi);
+/// \function disable_irq()
+/// Disable interrupt requests.
STATIC mp_obj_t pyb_disable_irq(void) {
__disable_irq();
return mp_const_none;
}
-
MP_DEFINE_CONST_FUN_OBJ_0(pyb_disable_irq_obj, pyb_disable_irq);
+/// \function enable_irq()
+/// Enable interrupt requests.
STATIC mp_obj_t pyb_enable_irq(void) {
__enable_irq();
return mp_const_none;
}
-
MP_DEFINE_CONST_FUN_OBJ_0(pyb_enable_irq_obj, pyb_enable_irq);
#if 0
@@ -224,12 +242,16 @@ STATIC mp_obj_t pyb_standby(void) {
MP_DEFINE_CONST_FUN_OBJ_0(pyb_standby_obj, pyb_standby);
+/// \function have_cdc()
+/// Return True if USB is connected as a serial device, False otherwise.
STATIC mp_obj_t pyb_have_cdc(void ) {
return MP_BOOL(usb_vcp_is_connected());
}
-
STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_have_cdc_obj, pyb_have_cdc);
+/// \function hid((buttons, x, y, z))
+/// Takes a 4-tuple (or list) and sends it to the USB host (the PC) to
+/// signal a HID mouse-motion event.
STATIC mp_obj_t pyb_hid_send_report(mp_obj_t arg) {
mp_obj_t *items;
mp_obj_get_array_fixed_n(arg, 4, &items);
@@ -241,7 +263,6 @@ STATIC mp_obj_t pyb_hid_send_report(mp_obj_t arg) {
usb_hid_send_report(data);
return mp_const_none;
}
-
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_hid_send_report_obj, pyb_hid_send_report);
MP_DECLARE_CONST_FUN_OBJ(pyb_source_dir_obj); // defined in main.c
diff --git a/stmhal/pin.c b/stmhal/pin.c
index f6c79d5d28..06923a492d 100644
--- a/stmhal/pin.c
+++ b/stmhal/pin.c
@@ -12,54 +12,61 @@
#include "runtime.h"
#include "pin.h"
-// Usage Model:
-//
-// All Board Pins are predefined as pyb.Pin.board.Name
-//
-// x1_pin = pyb.Pin.board.X1
-//
-// g = pyb.Pin(pyb.Pin.board.X1, pyb.Pin.IN)
-//
-// CPU pins which correspond to the board pins are available
-// as pyb.cpu.Name. For the CPU pins, the names are the port letter
-// followed by the pin number. On the PYBV4, pyb.Pin.board.X1 and
-// pyb.Pin.cpu.B6 are the same pin.
-//
-// You can also use strings:
-//
-// g = pyb.Pin('X1', pyb.Pin.OUT_PP)
-//
-// Users can add their own names:
-//
-// pyb.Pin.dict["LeftMotorDir"] = pyb.Pin.cpu.C12
-// g = pyb.Pin("LeftMotorDir", pyb.Pin.OUT_OD)
-//
-// and can query mappings
-//
-// pin = pyb.Pin("LeftMotorDir")
-//
-// Users can also add their own mapping function:
-//
-// def MyMapper(pin_name):
-// if pin_name == "LeftMotorDir":
-// return pyb.Pin.cpu.A0
-//
-// pyb.Pin.mapper(MyMapper)
-//
-// So, if you were to call: pyb.Pin("LeftMotorDir", pyb.Pin.OUT_PP)
-// then "LeftMotorDir" is passed directly to the mapper function.
-//
-// To summarize, the following order determines how things get mapped into
-// an ordinal pin number:
-//
-// 1 - Directly specify a pin object
-// 2 - User supplied mapping function
-// 3 - User supplied mapping (object must be usable as a dictionary key)
-// 4 - Supply a string which matches a board pin
-// 5 - Supply a string which matches a CPU port/pin
-//
-// You can set pyb.Pin.debug(True) to get some debug information about
-// how a particular object gets mapped to a pin.
+/// \moduleref pyb
+/// \class Pin - control I/O pins
+///
+/// A pin is the basic object to control I/O pins. It has methods to set
+/// the mode of the pin (input, output, etc) and methods to get and set the
+/// digital logic level. For analog control of a pin, see the ADC class.
+///
+/// Usage Model:
+///
+/// All Board Pins are predefined as pyb.Pin.board.Name
+///
+/// x1_pin = pyb.Pin.board.X1
+///
+/// g = pyb.Pin(pyb.Pin.board.X1, pyb.Pin.IN)
+///
+/// CPU pins which correspond to the board pins are available
+/// as `pyb.cpu.Name`. For the CPU pins, the names are the port letter
+/// followed by the pin number. On the PYBv1.0, `pyb.Pin.board.X1` and
+/// `pyb.Pin.cpu.B6` are the same pin.
+///
+/// You can also use strings:
+///
+/// g = pyb.Pin('X1', pyb.Pin.OUT_PP)
+///
+/// Users can add their own names:
+///
+/// pyb.Pin.dict["LeftMotorDir"] = pyb.Pin.cpu.C12
+/// g = pyb.Pin("LeftMotorDir", pyb.Pin.OUT_OD)
+///
+/// and can query mappings
+///
+/// pin = pyb.Pin("LeftMotorDir")
+///
+/// Users can also add their own mapping function:
+///
+/// def MyMapper(pin_name):
+/// if pin_name == "LeftMotorDir":
+/// return pyb.Pin.cpu.A0
+///
+/// pyb.Pin.mapper(MyMapper)
+///
+/// So, if you were to call: `pyb.Pin("LeftMotorDir", pyb.Pin.OUT_PP)`
+/// then `"LeftMotorDir"` is passed directly to the mapper function.
+///
+/// To summarise, the following order determines how things get mapped into
+/// an ordinal pin number:
+///
+/// 1. Directly specify a pin object
+/// 2. User supplied mapping function
+/// 3. User supplied mapping (object must be usable as a dictionary key)
+/// 4. Supply a string which matches a board pin
+/// 5. Supply a string which matches a CPU port/pin
+///
+/// You can set `pyb.Pin.debug(True)` to get some debug information about
+/// how a particular object gets mapped to a pin.
// Pin class variables
STATIC mp_obj_t pin_class_mapper;
@@ -152,6 +159,8 @@ const pin_obj_t *pin_find(mp_obj_t user_obj) {
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "pin '%s' not a valid pin identifier", pin_name));
}
+/// \method __str__()
+/// Return a string describing the pin object.
STATIC void pin_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
pin_obj_t *self = self_in;
print(env, "<Pin %s>", self->name);
@@ -159,7 +168,9 @@ STATIC void pin_print(void (*print)(void *env, const char *fmt, ...), void *env,
STATIC mp_obj_t pin_obj_init(uint n_args, mp_obj_t *args);
-// Pin constructor
+/// \classmethod \constructor(id, ...)
+/// Create a new Pin object associated with the id. If additional arguments are given,
+/// they are used to initialise the pin. See `init`.
STATIC mp_obj_t pin_make_new(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 1, 3, false);
@@ -178,7 +189,8 @@ STATIC mp_obj_t pin_make_new(mp_obj_t self_in, uint n_args, uint n_kw, const mp_
return (mp_obj_t)pin;
}
-// class method
+/// \classmethod mapper([fun])
+/// Get or set the pin mapper function.
STATIC mp_obj_t pin_mapper(uint n_args, mp_obj_t *args) {
if (n_args > 1) {
pin_class_mapper = args[1];
@@ -189,7 +201,8 @@ STATIC mp_obj_t pin_mapper(uint n_args, mp_obj_t *args) {
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pin_mapper_fun_obj, 1, 2, pin_mapper);
STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(pin_mapper_obj, (mp_obj_t)&pin_mapper_fun_obj);
-// class method
+/// \classmethod dict([dict])
+/// Get or set the pin mapper dictionary.
STATIC mp_obj_t pin_map_dict(uint n_args, mp_obj_t *args) {
if (n_args > 1) {
pin_class_map_dict = args[1];
@@ -200,7 +213,8 @@ STATIC mp_obj_t pin_map_dict(uint n_args, mp_obj_t *args) {
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pin_map_dict_fun_obj, 1, 2, pin_map_dict);
STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(pin_map_dict_obj, (mp_obj_t)&pin_map_dict_fun_obj);
-// class method
+/// \classmethod debug([state])
+/// Get or set the debugging state (`True` or `False` for on or off).
STATIC mp_obj_t pin_debug(uint n_args, mp_obj_t *args) {
if (n_args > 1) {
pin_class_debug = mp_obj_is_true(args[1]);
@@ -211,6 +225,22 @@ STATIC mp_obj_t pin_debug(uint n_args, mp_obj_t *args) {
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pin_debug_fun_obj, 1, 2, pin_debug);
STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(pin_debug_obj, (mp_obj_t)&pin_debug_fun_obj);
+/// \method init(mode, pull=Pin.PULL_NONE)
+/// Initialise the pin:
+///
+/// - `mode` can be one of:
+/// - `Pin.IN` - configure the pin for input;
+/// - `Pin.OUT_PP` - configure the pin for output, with push-pull control;
+/// - `Pin.OUT_OD` - configure the pin for output, with open-drain control;
+/// - `Pin.AF_PP` - configure the pin for alternate function, pull-pull;
+/// - `Pin.AF_OD` - configure the pin for alternate function, open-drain;
+/// - `Pin.ANALOG` - configure the pin for analog.
+/// - `pull` can be one of:
+/// - `Pin.PULL_NONE` - no pull up or down resistors;
+/// - `Pin.PULL_UP` - enable the pull-up resistor;
+/// - `Pin.PULL_DOWN` - enable the pull-down resistor.
+///
+/// Returns: `None`.
STATIC mp_obj_t pin_obj_init(uint n_args, mp_obj_t *args) {
pin_obj_t *self = args[0];
@@ -242,6 +272,13 @@ STATIC mp_obj_t pin_obj_init(uint n_args, mp_obj_t *args) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pin_init_obj, 2, 3, pin_obj_init);
+/// \method value([value])
+/// Get or set the digital logic level of the pin:
+///
+/// - With no argument, return 0 or 1 depending on the logic level of the pin.
+/// - With `value` given, set the logic level of the pin. `value` can be
+/// anything that converts to a boolean. If it converts to `True`, the pin
+/// is set high, otherwise it is set low.
STATIC mp_obj_t pin_value(uint n_args, mp_obj_t *args) {
pin_obj_t *self = args[0];
if (n_args == 1) {
@@ -259,6 +296,8 @@ STATIC mp_obj_t pin_value(uint n_args, mp_obj_t *args) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pin_value_obj, 1, 2, pin_value);
+/// \method low()
+/// Set the pin to a low logic level.
STATIC mp_obj_t pin_low(mp_obj_t self_in) {
pin_obj_t *self = self_in;
self->gpio->BSRRH = self->pin_mask;
@@ -266,6 +305,8 @@ STATIC mp_obj_t pin_low(mp_obj_t self_in) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_low_obj, pin_low);
+/// \method high()
+/// Set the pin to a high logic level.
STATIC mp_obj_t pin_high(mp_obj_t self_in) {
pin_obj_t *self = self_in;
self->gpio->BSRRL = self->pin_mask;
@@ -273,18 +314,24 @@ STATIC mp_obj_t pin_high(mp_obj_t self_in) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_high_obj, pin_high);
+/// \method name()
+/// Get the pin name.
STATIC mp_obj_t pin_name(mp_obj_t self_in) {
pin_obj_t *self = self_in;
return MP_OBJ_NEW_QSTR(qstr_from_str(self->name));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_name_obj, pin_name);
+/// \method port()
+/// Get the pin port.
STATIC mp_obj_t pin_port(mp_obj_t self_in) {
pin_obj_t *self = self_in;
return MP_OBJ_NEW_SMALL_INT((mp_small_int_t)self->port);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_port_obj, pin_port);
+/// \method pin()
+/// Get the pin number.
STATIC mp_obj_t pin_pin(mp_obj_t self_in) {
pin_obj_t *self = self_in;
return MP_OBJ_NEW_SMALL_INT((mp_small_int_t)self->pin);
@@ -311,6 +358,12 @@ STATIC const mp_map_elem_t pin_locals_dict_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_cpu), (mp_obj_t)&pin_cpu_pins_obj },
// class constants
+ /// \constant IN - initialise the pin to input mode
+ /// \constant OUT_PP - initialise the pin to output mode with a push-pull drive
+ /// \constant OUT_OD - initialise the pin to output mode with an open-drain drive
+ /// \constant PULL_NONE - don't enable any pull up or down resistors on the pin
+ /// \constant PULL_UP - enable the pull-up resistor on the pin
+ /// \constant PULL_DOWN - enable the pull-down resistor on the pin
{ MP_OBJ_NEW_QSTR(MP_QSTR_IN), MP_OBJ_NEW_SMALL_INT(GPIO_MODE_INPUT) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_OUT_PP), MP_OBJ_NEW_SMALL_INT(GPIO_MODE_OUTPUT_PP) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_OUT_OD), MP_OBJ_NEW_SMALL_INT(GPIO_MODE_OUTPUT_OD) },
diff --git a/stmhal/rng.c b/stmhal/rng.c
index 19e624f792..beda9a78b7 100644
--- a/stmhal/rng.c
+++ b/stmhal/rng.c
@@ -10,6 +10,8 @@
#if MICROPY_HW_ENABLE_RNG
+/// \moduleref pyb
+
STATIC RNG_HandleTypeDef RNGHandle = {.Instance = NULL};
void rng_init0(void) {
@@ -30,6 +32,8 @@ uint32_t rng_get(void) {
return HAL_RNG_GetRandomNumber(&RNGHandle);
}
+/// \function rng()
+/// Return a 30-bit hardware generated random number.
STATIC mp_obj_t pyb_rng_get(void) {
if (RNGHandle.State == HAL_RNG_STATE_RESET) {
rng_init();
diff --git a/stmhal/servo.c b/stmhal/servo.c
index 02e2c209b8..2ebe64376f 100644
--- a/stmhal/servo.c
+++ b/stmhal/servo.c
@@ -11,6 +11,11 @@
#include "timer.h"
#include "servo.h"
+/// \moduleref pyb
+/// \class Servo - 3-wire hobby servo driver
+///
+/// Servo controls standard hobby servos with 3-wires (ground, power, signal).
+
// this servo driver uses hardware PWM to drive servos on PA0, PA1, PA2, PA3 = X1, X2, X3, X4
// TIM2 and TIM5 have CH1, CH2, CH3, CH4 on PA0-PA3 respectively
// they are both 32-bit counters with 16-bit prescaler
@@ -156,6 +161,8 @@ STATIC void pyb_servo_print(void (*print)(void *env, const char *fmt, ...), void
print(env, "<Servo %lu at %luus>", self->servo_id, 10 * self->pulse_cur);
}
+/// \classmethod \constructor(id)
+/// Create a servo object. `id` is 1-4.
STATIC mp_obj_t pyb_servo_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
// check arguments
mp_arg_check_num(n_args, n_kw, 1, 1, false);
@@ -177,6 +184,8 @@ STATIC mp_obj_t pyb_servo_make_new(mp_obj_t type_in, uint n_args, uint n_kw, con
return s;
}
+/// \method pulse_width([value])
+/// Get or set the pulse width in milliseconds.
STATIC mp_obj_t pyb_servo_pulse_width(uint n_args, const mp_obj_t *args) {
pyb_servo_obj_t *self = args[0];
if (n_args == 1) {
@@ -190,9 +199,10 @@ STATIC mp_obj_t pyb_servo_pulse_width(uint n_args, const mp_obj_t *args) {
return mp_const_none;
}
}
-
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_servo_pulse_width_obj, 1, 2, pyb_servo_pulse_width);
+/// \method calibration([pulse_min, pulse_max, pulse_centre, [pulse_angle_90, pulse_speed_100]])
+/// Get or set the calibration of the servo timing.
STATIC mp_obj_t pyb_servo_calibration(uint n_args, const mp_obj_t *args) {
pyb_servo_obj_t *self = args[0];
if (n_args == 1) {
@@ -221,9 +231,13 @@ STATIC mp_obj_t pyb_servo_calibration(uint n_args, const mp_obj_t *args) {
// bad number of arguments
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "calibration expecting 1, 4 or 6 arguments, got %d", n_args));
}
-
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_servo_calibration_obj, 1, 6, pyb_servo_calibration);
+/// \method angle([angle, time=0])
+/// Get or set the angle of the servo.
+///
+/// - `angle` is the angle to move to in degrees.
+/// - `time` is the number of milliseconds to take to get to the specified angle.
STATIC mp_obj_t pyb_servo_angle(uint n_args, const mp_obj_t *args) {
pyb_servo_obj_t *self = args[0];
if (n_args == 1) {
@@ -247,9 +261,13 @@ STATIC mp_obj_t pyb_servo_angle(uint n_args, const mp_obj_t *args) {
return mp_const_none;
}
}
-
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_servo_angle_obj, 1, 3, pyb_servo_angle);
+/// \method speed([speed, time=0])
+/// Get or set the speed of a continuous rotation servo.
+///
+/// - `speed` is the speed to move to change to, between -100 and 100.
+/// - `time` is the number of milliseconds to take to get to the specified speed.
STATIC mp_obj_t pyb_servo_speed(uint n_args, const mp_obj_t *args) {
pyb_servo_obj_t *self = args[0];
if (n_args == 1) {
diff --git a/stmhal/spi.c b/stmhal/spi.c
index bad71e87b1..ba550f9799 100644
--- a/stmhal/spi.c
+++ b/stmhal/spi.c
@@ -14,24 +14,28 @@
#include "bufhelper.h"
#include "spi.h"
-// Usage model:
-//
-// See usage model of I2C in i2c.c. SPI is very similar. Main difference is
-// parameters to init the SPI bus:
-//
-// from pyb import SPI
-// spi = SPI(1, SPI.MASTER, baudrate=600000, polarity=1, phase=1, crc=0x7)
-//
-// Only required parameter is mode, SPI.MASTER or SPI.SLAVE. Polarity can be
-// 0 or 1, and is the level the idle clock line sits at. Phase can be 1 or 2
-// for number of edges. Crc can be None for no CRC, or a polynomial specifier.
-//
-// Additional method for SPI:
-//
-// data = spi.send_recv(b'1234') # send 4 bytes and receive 4 bytes
-// buf = bytearray(4)
-// spi.send_recv(b'1234', buf) # send 4 bytes and receive 4 into buf
-// spi.send_recv(buf, buf) # send/recv 4 bytes from/to buf
+/// \moduleref pyb
+/// \class SPI - a master-driven serial protocol
+///
+/// SPI is a serial protocol that is driven by a master. At the physical level
+/// there are 3 lines: SCK, MOSI, MISO.
+///
+/// See usage model of I2C; SPI is very similar. Main difference is
+/// parameters to init the SPI bus:
+///
+/// from pyb import SPI
+/// spi = SPI(1, SPI.MASTER, baudrate=600000, polarity=1, phase=1, crc=0x7)
+///
+/// Only required parameter is mode, SPI.MASTER or SPI.SLAVE. Polarity can be
+/// 0 or 1, and is the level the idle clock line sits at. Phase can be 1 or 2
+/// for number of edges. Crc can be None for no CRC, or a polynomial specifier.
+///
+/// Additional method for SPI:
+///
+/// data = spi.send_recv(b'1234') # send 4 bytes and receive 4 bytes
+/// buf = bytearray(4)
+/// spi.send_recv(b'1234', buf) # send 4 bytes and receive 4 into buf
+/// spi.send_recv(buf, buf) # send/recv 4 bytes from/to buf
#if MICROPY_HW_ENABLE_SPI1
SPI_HandleTypeDef SPIHandle1 = {.Instance = NULL};
@@ -194,6 +198,12 @@ STATIC void pyb_spi_print(void (*print)(void *env, const char *fmt, ...), void *
}
}
+/// \method init(mode, baudrate=328125, *, polarity=1, phase=1, bits=8, firstbit=SPI.MSB, ti=false, crc=None)
+///
+/// Initialise the SPI bus with the given parameters:
+///
+/// - `mode` must be either `SPI.MASTER` or `SPI.SLAVE`.
+/// - `baudrate` is the SCK clock rate (only sensible for a master).
STATIC const mp_arg_t pyb_spi_init_args[] = {
{ MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 328125} },
@@ -258,6 +268,13 @@ STATIC mp_obj_t pyb_spi_init_helper(const pyb_spi_obj_t *self, uint n_args, cons
return mp_const_none;
}
+/// \classmethod \constructor(bus, ...)
+///
+/// Construct an SPI object on the given bus. `bus` can be 1 or 2.
+/// With no additional parameters, the SPI object is created but not
+/// initialised (it has the settings from the last initialisation of
+/// the bus, if any). If extra arguments are given, the bus is initialised.
+/// See `init` for parameters of initialisation.
STATIC mp_obj_t pyb_spi_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
// check arguments
mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
@@ -288,6 +305,8 @@ STATIC mp_obj_t pyb_spi_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_arg
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_init_obj, 1, pyb_spi_init);
+/// \method deinit()
+/// Turn off the SPI bus.
STATIC mp_obj_t pyb_spi_deinit(mp_obj_t self_in) {
pyb_spi_obj_t *self = self_in;
spi_deinit(self->spi);
@@ -295,6 +314,13 @@ STATIC mp_obj_t pyb_spi_deinit(mp_obj_t self_in) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_spi_deinit_obj, pyb_spi_deinit);
+/// \method send(send, *, timeout=5000)
+/// Send data on the bus:
+///
+/// - `send` is the data to send (an integer to send, or a buffer object).
+/// - `timeout` is the timeout in milliseconds to wait for the send.
+///
+/// Return value: `None`.
STATIC const mp_arg_t pyb_spi_send_args[] = {
{ MP_QSTR_send, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} },
@@ -327,6 +353,16 @@ STATIC mp_obj_t pyb_spi_send(uint n_args, const mp_obj_t *args, mp_map_t *kw_arg
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_send_obj, 1, pyb_spi_send);
+/// \method recv(recv, *, timeout=5000)
+///
+/// Receive data on the bus:
+///
+/// - `recv` can be an integer, which is the number of bytes to receive,
+/// or a mutable buffer, which will be filled with received bytes.
+/// - `timeout` is the timeout in milliseconds to wait for the receive.
+///
+/// Return value: if `recv` is an integer then a new buffer of the bytes received,
+/// otherwise the same buffer that was passed in to `recv`.
STATIC const mp_arg_t pyb_spi_recv_args[] = {
{ MP_QSTR_recv, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} },
@@ -363,6 +399,17 @@ STATIC mp_obj_t pyb_spi_recv(uint n_args, const mp_obj_t *args, mp_map_t *kw_arg
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_spi_recv_obj, 1, pyb_spi_recv);
+/// \method send_recv(send, recv=None, *, timeout=5000)
+///
+/// Send and receive data on the bus at the same time:
+///
+/// - `send` is the data to send (an integer to send, or a buffer object).
+/// - `recv` is a mutable buffer which will be filled with received bytes.
+/// It can be the same as `send`, or omitted. If omitted, a new buffer will
+/// be created.
+/// - `timeout` is the timeout in milliseconds to wait for the receive.
+///
+/// Return value: the buffer with the received bytes.
STATIC const mp_arg_t pyb_spi_send_recv_args[] = {
{ MP_QSTR_send, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_recv, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
@@ -436,6 +483,10 @@ STATIC const mp_map_elem_t pyb_spi_locals_dict_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_send_recv), (mp_obj_t)&pyb_spi_send_recv_obj },
// class constants
+ /// \constant MASTER - for initialising the bus to master mode
+ /// \constant SLAVE - for initialising the bus to slave mode
+ /// \constant MSB - set the first bit to MSB
+ /// \constant LSB - set the first bit to LSB
{ MP_OBJ_NEW_QSTR(MP_QSTR_MASTER), MP_OBJ_NEW_SMALL_INT(SPI_MODE_MASTER) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_SLAVE), MP_OBJ_NEW_SMALL_INT(SPI_MODE_SLAVE) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_MSB), MP_OBJ_NEW_SMALL_INT(SPI_FIRSTBIT_MSB) },
diff --git a/stmhal/uart.c b/stmhal/uart.c
index a31de44f8e..6dc60ca084 100644
--- a/stmhal/uart.c
+++ b/stmhal/uart.c
@@ -12,20 +12,25 @@
#include "bufhelper.h"
#include "uart.h"
-// Usage model:
-//
-// See usage model of I2C in i2c.c. UART is very similar. Main difference is
-// parameters to init the UART bus:
-//
-// from pyb import UART
-// uart = UART(1, 9600) # init with given baudrate
-// uart.init(9600, bits=8, stop=1, parity=None) # init with given parameters
-//
-// Bits can be 8 or 9, stop can be 1 or 2, parity can be None, 0 (even), 1 (odd).
-//
-// Extra method:
-//
-// uart.any() # returns True if any characters waiting
+/// \moduleref pyb
+/// \class UART - duplex serial communication bus
+///
+/// UART implements the standard UART/USART duplex serial communications protocol. At
+/// the physical level it consists of 2 lines: RX and TX.
+///
+/// See usage model of I2C. UART is very similar. Main difference is
+/// parameters to init the UART bus:
+///
+/// from pyb import UART
+///
+/// uart = UART(1, 9600) # init with given baudrate
+/// uart.init(9600, bits=8, stop=1, parity=None) # init with given parameters
+///
+/// Bits can be 8 or 9, stop can be 1 or 2, parity can be None, 0 (even), 1 (odd).
+///
+/// Extra method:
+///
+/// uart.any() # returns True if any characters waiting
struct _pyb_uart_obj_t {
mp_obj_base_t base;
@@ -225,6 +230,14 @@ STATIC void pyb_uart_print(void (*print)(void *env, const char *fmt, ...), void
}
}
+/// \method init(baudrate, *, bits=8, stop=1, parity=None)
+///
+/// Initialise the SPI bus with the given parameters:
+///
+/// - `baudrate` is the clock rate.
+/// - `bits` is the number of bits per byte, 8 or 9.
+/// - `stop` is the number of stop bits, 1 or 2.
+/// - `parity` is the parity, `None`, 0 (even) or 1 (odd).
STATIC const mp_arg_t pyb_uart_init_args[] = {
{ MP_QSTR_baudrate, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 9600} },
{ MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} },
@@ -265,6 +278,13 @@ STATIC mp_obj_t pyb_uart_init_helper(pyb_uart_obj_t *self, uint n_args, const mp
return mp_const_none;
}
+/// \classmethod \constructor(bus, ...)
+///
+/// Construct a UART object on the given bus. `bus` can be 1-6, or 'XA', 'XB', 'YA', or 'YB'.
+/// With no additional parameters, the UART object is created but not
+/// initialised (it has the settings from the last initialisation of
+/// the bus, if any). If extra arguments are given, the bus is initialised.
+/// See `init` for parameters of initialisation.
STATIC mp_obj_t pyb_uart_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
// check arguments
mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
@@ -310,6 +330,8 @@ STATIC mp_obj_t pyb_uart_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_ar
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_uart_init_obj, 1, pyb_uart_init);
+/// \method deinit()
+/// Turn off the UART bus.
STATIC mp_obj_t pyb_uart_deinit(mp_obj_t self_in) {
pyb_uart_obj_t *self = self_in;
uart_deinit(self);
@@ -317,6 +339,8 @@ STATIC mp_obj_t pyb_uart_deinit(mp_obj_t self_in) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_uart_deinit_obj, pyb_uart_deinit);
+/// \method any()
+/// Return `True` if any characters waiting, else `False`.
STATIC mp_obj_t pyb_uart_any(mp_obj_t self_in) {
pyb_uart_obj_t *self = self_in;
if (uart_rx_any(self)) {
@@ -327,6 +351,13 @@ STATIC mp_obj_t pyb_uart_any(mp_obj_t self_in) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_uart_any_obj, pyb_uart_any);
+/// \method send(send, *, timeout=5000)
+/// Send data on the bus:
+///
+/// - `send` is the data to send (an integer to send, or a buffer object).
+/// - `timeout` is the timeout in milliseconds to wait for the send.
+///
+/// Return value: `None`.
STATIC const mp_arg_t pyb_uart_send_args[] = {
{ MP_QSTR_send, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} },
@@ -359,6 +390,16 @@ STATIC mp_obj_t pyb_uart_send(uint n_args, const mp_obj_t *args, mp_map_t *kw_ar
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_uart_send_obj, 1, pyb_uart_send);
+/// \method recv(recv, *, timeout=5000)
+///
+/// Receive data on the bus:
+///
+/// - `recv` can be an integer, which is the number of bytes to receive,
+/// or a mutable buffer, which will be filled with received bytes.
+/// - `timeout` is the timeout in milliseconds to wait for the receive.
+///
+/// Return value: if `recv` is an integer then a new buffer of the bytes received,
+/// otherwise the same buffer that was passed in to `recv`.
STATIC const mp_arg_t pyb_uart_recv_args[] = {
{ MP_QSTR_recv, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} },
diff --git a/stmhal/usrsw.c b/stmhal/usrsw.c
index 7a077e1dbb..1e8d35487a 100644
--- a/stmhal/usrsw.c
+++ b/stmhal/usrsw.c
@@ -14,17 +14,22 @@
#if MICROPY_HW_HAS_SWITCH
-// Usage Model:
-//
-// sw = pyb.Switch() # create a switch object
-// sw() # get state (True if pressed, False otherwise)
-// sw.callback(f) # register a callback to be called when the
-// # switch is pressed down
-// sw.callback(None) # remove the callback
-//
-// Example:
-//
-// pyb.Switch().callback(lambda: pyb.LED(1).toggle())
+/// \moduleref pyb
+/// \class Switch - switch object
+///
+/// A Switch object is used to control a push-button switch.
+///
+/// Usage:
+///
+/// sw = pyb.Switch() # create a switch object
+/// sw() # get state (True if pressed, False otherwise)
+/// sw.callback(f) # register a callback to be called when the
+/// # switch is pressed down
+/// sw.callback(None) # remove the callback
+///
+/// Example:
+///
+/// pyb.Switch().callback(lambda: pyb.LED(1).toggle())
// this function inits the switch GPIO so that it can be used
void switch_init0(void) {
@@ -55,6 +60,8 @@ void pyb_switch_print(void (*print)(void *env, const char *fmt, ...), void *env,
print(env, "Switch()");
}
+/// \classmethod \constructor()
+/// Create and return a switch object.
STATIC mp_obj_t pyb_switch_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
// check arguments
mp_arg_check_num(n_args, n_kw, 0, 0, false);
@@ -70,6 +77,8 @@ STATIC mp_obj_t pyb_switch_make_new(mp_obj_t type_in, uint n_args, uint n_kw, co
return (mp_obj_t)&pyb_switch_obj;
}
+/// \method \call()
+/// Return the switch state: `True` if pressed down, `False` otherwise.
mp_obj_t pyb_switch_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
// get switch state
mp_arg_check_num(n_args, n_kw, 0, 0, false);
@@ -84,6 +93,9 @@ STATIC mp_obj_t switch_callback(mp_obj_t line) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(switch_callback_obj, switch_callback);
+/// \method callback(fun)
+/// Register the given function to be called when the switch is pressed down.
+/// If `fun` is `None`, then it disables the callback.
mp_obj_t pyb_switch_callback(mp_obj_t self_in, mp_obj_t callback) {
pyb_switch_obj_t *self = self_in;
self->callback = callback;