...
 
Commits (28)
......@@ -4,23 +4,39 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
## [v1.10] - 2019-09-05 21:42 - [JerusalemArtichoke]
[JerusalemArtichoke]: https://card10.badge.events.ccc.de/release/card10-v1.10-JerusalemArtichoke.zip
### Added
- **ws2812**: Connect Neopixels to the wristband GPIOs and make your card10
even more colorful!
- `micropython.mem_use()` function.
- DigiClk is now in the default prelude!
- High-pass filter and pulse detection in default ECG app.
- Actually added `uuid` module - it was not built into the firmware before,
by accident.
- `leds.get_rgb()`: Get the current color of an LED.
- `leds.get_rocket()`: Get the current brightness of one of the rockets.
- `micropython.mem_use()` function.
- The analog-clock can now also set the time using the buttons.
### Changed
- **Pycardium**: Switched from `long-long` to `mpz` integer representation.
This should resolve any issues with large numbers which had popped up so far.
- Refactored BME680 sensor interface.
- Made OpenOCD scripts work with more debuggers out of the box.
- Internal changes in preparation for button-interrupts.
### Fixed
- Backlight and Vibration motor were not reset when switching apps.
- Mismatch in default settings of the *Card10 Nickname* app.
- Fixed the PMIC ADC muxer not being properly reset to neutral after a
measurement.
- Fixed wrong timezone offset calculation in `utime.time_ms()`.
- Fixed bug where `\` characters were not parsed as path separators.
- Fixed the alignment request check in our ELF l0der.
- Fixed a buffer-overflow in the config-parser.
## [v1.9] - 2019-08-28 23:23 - [IcebergLettuce]
......@@ -224,7 +240,8 @@ fbf7c8c0 fix(menu.py) Refactored menu.py based on !138
## [v1.0] - 2019-08-21 00:50
Initial release.
[Unreleased]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.9...master
[Unreleased]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.10...master
[v1.10]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.9...v1.10
[v1.9]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.8...v1.9
[v1.8]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.7...v1.8
[v1.7]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.6...v1.7
......
......@@ -90,6 +90,7 @@ html_context = {
autodoc_mock_imports = [
"buttons",
"interrupt",
"sys_bme680",
"sys_display",
"sys_leds",
"sys_max30001",
......
......@@ -115,6 +115,12 @@ command to soft-reset card10.
...
(gdb)
.. note::
You will also find the following self-describing gdb files in the firmware
root directory, which do not require additional arguments:
``flash-all.gdb, flash-bootloader.gdb,
flash-both.gdb, flash-epicardium.gdb, flash-pycardium.gdb``
.. warning::
If you are used to use ``mon reset halt``, be aware that the card10 prototypes
do not connect the reset line to the debugger. OpenOCD is configured to only do
......
......@@ -2,6 +2,7 @@
``bme680`` - Environmental Sensor
=================================
Allows access to environmental data of card10's surroundings.
**Example**:
......@@ -9,17 +10,28 @@
import bme680, utime
bme680.init()
with bme680.Bme680() as environment:
while True:
d = environment.get_data()
while True:
temperature, humidity, pressure, resistance = bme680.get_data()
print("Temperature: {:10.2f} °C".format(d.temperature))
print("Humidity: {:10.2f} % r.h.".format(d.humidity))
print("Pressure: {:10.2f} hPa".format(d.pressure))
print("Gas Resistance: {:10.2f} Ω".format(d.resistance))
print("Temperature: {:10.2f} °C".format(temperature))
print("Humidity: {:10.2f} % r.h.".format(humidity))
print("Pressure: {:10.2f} hPa".format(pressure))
print("Gas Resistance: {:10.2f} Ω".format(resistance))
utime.sleep(1)
utime.sleep(1)
Sensor Class
------------
.. autoclass:: bme680.Bme680
:members:
Deprecated Interface
--------------------
The following functions should no longer be used directly. The only exist for
compatibility as they were the old BME680 interface in previous firmware
versions.
.. py:function:: init()
......@@ -29,6 +41,8 @@
:py:func:`bme680.init`.
.. versionadded:: 1.4
.. deprecated:: 1.10
Use the :py:class:`bme680.Bme680` class instead.
.. py:function:: get_data()
......@@ -38,9 +52,13 @@
``pressure`` (hPa) and ``gas resistance`` (Ohm).
.. versionadded:: 1.4
.. deprecated:: 1.10
Use the :py:class:`bme680.Bme680` class instead.
.. py:function:: deinit()
Deinitialize the sensor.
.. versionadded:: 1.4
.. deprecated:: 1.10
Use the :py:class:`bme680.Bme680` class instead.
......@@ -201,6 +201,8 @@ Struct module.
UUID(bytes='\x12\x34\x56\x78' * 4)
UUID(int=0x12345678123456781234567812345678)
.. versionadded:: 1.10
.. py:attribute:: bytes
UUID as ``bytes()`` object
......@@ -221,7 +223,4 @@ Struct module.
Generate a new UUID version 4 (random UUID).
.. todo::
This function is not yet usable because we don't have
:py:func:`os.urandom` yet.
.. versionadded:: 1.10
......@@ -4,6 +4,8 @@
==========================
The ``ws2812`` module controls LEDs of the WS2812 type. Just as the ``leds`` module, it exposes a function :py:func:`ws2812.set_all`, which works a similar fashion.
.. versionadded:: 1.10
.. py:function:: set_all(pin, colors)
Set multiple of the LEDs to RGB values.
......@@ -28,3 +30,4 @@ The ``ws2812`` module controls LEDs of the WS2812 type. Just as the ``leds`` mod
i += 1
utime.sleep_ms(10)
.. versionadded:: 1.10
......@@ -604,6 +604,8 @@ API(API_LEDS_SET, void epic_leds_set(int led, uint8_t r, uint8_t g, uint8_t b));
* are the 4 "ambient" LEDs.
* :param uint8_t * rgb: need tree byte array to get the value of red, green and blue.
* :returns: ``0`` on success or ``-EPERM`` if the LED is blocked by personal-state.
*
* .. versionadded:: 1.10
*/
API(API_LEDS_GET, int epic_leds_get_rgb(int led, uint8_t * rgb));
......@@ -755,6 +757,8 @@ API(API_LEDS_SET_ROCKET, void epic_leds_set_rocket(int led, uint8_t value));
* | ``2`` | Green | Right |
* +-------+--------+----------+
* :returns value: Brightness of LED (value between 0 and 31) or ``-EINVAL`` if the LED/rocket does not exists.
*
* .. versionadded:: 1.10
*/
API(API_LEDS_GET_ROCKET, int epic_leds_get_rocket(int led));
......@@ -1876,6 +1880,8 @@ API(API_USB_CDCACM, int epic_usb_cdcacm(void));
* :param uint8_t pin: The gpio pin to be used for data.
* :param uint8_t * pixels: The buffer, in which the pixel data is stored.
* :param uint32_t n_bytes: The size of the buffer.
*
* .. versionadded:: 1.10
*/
API(API_WS2812_WRITE, void epic_ws2812_write(uint8_t pin, uint8_t *pixels, uint32_t n_bytes));
......
......@@ -545,7 +545,8 @@ _load_pie(int fd, int size, Elf32_Ehdr *hdr, struct l0dable_info *info)
if (phdr.p_type == PT_LOAD) {
// Check alignment request.
if ((phdr.p_vaddr % phdr.p_align) != 0) {
if ((phdr.p_offset % phdr.p_align) !=
(phdr.p_vaddr % phdr.p_align)) {
LOG_ERR("l0der",
"_load_pie: phdr %d alignment too strict",
i);
......
......@@ -4,6 +4,10 @@
#include "modules/config.h"
#include "card10-version.h"
#include "gfx.h"
#include "display.h"
#include "version-splash.h"
#include "FreeRTOS.h"
#include "task.h"
#include "mxc_delay.h"
......@@ -27,10 +31,19 @@ int main(void)
* Version Splash
*/
const char *version_buf = CARD10_VERSION;
const int off = (160 - (int)strlen(version_buf) * 14) / 2;
mxc_delay(500000);
epic_disp_clear(0x0000);
epic_disp_print(10, 20, "Epicardium", 0xfe20, 0x0000);
epic_disp_print(off > 0 ? off : 0, 40, version_buf, 0xfe20, 0x0000);
if (strcmp(CARD10_VERSION, "v1.10") == 0) {
gfx_copy_region_raw(
&display_screen, 0, 0, 160, 80, 2, version_splash
);
} else {
const int off = (160 - (int)strlen(version_buf) * 14) / 2;
epic_disp_print(10, 20, "Epicardium", 0xfe20, 0x0000);
epic_disp_print(
off > 0 ? off : 0, 40, version_buf, 0xfe20, 0x0000
);
}
epic_disp_update();
mxc_delay(2000000);
......
......@@ -26,7 +26,7 @@ uint8_t epic_buttons_read(uint8_t mask)
* Not using PB_Get() here as that performs one I2C transcation
* per button.
*/
uint8_t pin_status = ~portexpander_get();
uint8_t pin_status = ~portexpander_in_get(0xFF);
hwlock_release(HWLOCK_I2C);
......
......@@ -270,17 +270,17 @@ void load_config(void)
);
return;
}
char buf[CONFIG_MAX_LINE_LENGTH];
char buf[CONFIG_MAX_LINE_LENGTH + 1];
int lineNumber = 0;
int nread;
do {
//zero-terminate in case file is empty
buf[0] = '\0';
nread = epic_file_read(fd, buf, sizeof(buf));
if (nread < sizeof(buf)) {
nread = epic_file_read(fd, buf, CONFIG_MAX_LINE_LENGTH);
if (nread < CONFIG_MAX_LINE_LENGTH) {
//add fake EOL to ensure termination
buf[nread] = '\n';
buf[nread++] = '\n';
}
//zero-terminate buffer
buf[nread] = '\0';
char *line = buf;
char *eol = NULL;
int last_eol = 0;
......@@ -344,4 +344,5 @@ void load_config(void)
}
}
} while (nread == sizeof(buf));
epic_file_close(fd);
}
This diff is collapsed.
......@@ -215,6 +215,7 @@ void core1_stop(void)
void card10_poll(void)
{
pmic_poll();
portexpander_poll();
}
void card10_reset(void)
......
......@@ -22,7 +22,7 @@ void display_set_reset_pin(uint8_t state)
if (!portexpander_detected()) {
MAX77650_setDO(state ? true : false);
} else {
portexpander_set(4, state);
portexpander_out_put(PIN_4, state);
}
}
......
......@@ -285,10 +285,7 @@ static uint8_t power_pin_conversion(uint8_t group)
static void power_all(void)
{
for (int i = 0; i < 3; i++) {
portexpander_prep(i, 0);
}
portexpander_update();
portexpander_out_clr(PIN_0 | PIN_1 | PIN_2);
}
void leds_update_power(void)
......@@ -301,14 +298,14 @@ void leds_update_power(void)
if (new_groups == active_groups) {
return;
}
for (int i = 0; i < 3; i++) {
if (i < new_groups) {
portexpander_prep(power_pin_conversion(i), 0);
} else {
portexpander_prep(power_pin_conversion(i), 1);
}
uint8_t out_val = 0;
for (int i = new_groups; i < 3; ++i) {
out_val |= (1 << power_pin_conversion(i));
}
portexpander_update();
portexpander_out_put(PIN_0 | PIN_1 | PIN_2, out_val);
if (active_groups < new_groups) {
for (int i = 0; i < powerup_wait_cycles; i++) {
__NOP();
......@@ -343,7 +340,7 @@ void leds_update(void)
void leds_flashlight(bool power)
{
portexpander_set(7, (power) ? 0 : 1);
portexpander_out_put(PIN_7, (power) ? 0 : 1);
}
void leds_set_gamma_table(uint8_t rgb_channel, uint8_t table[256])
......
......@@ -40,6 +40,10 @@
#include "portexpander.h"
#include "MAX77650-Arduino-Library.h"
#include <stddef.h>
static const uint8_t expander_pins[] = { 5, 0x0, 3, 6 };
static pb_callback pb_callbacks[4] = { NULL };
/******************************************************************************/
int PB_Init(void)
{
......@@ -59,24 +63,71 @@ int PB_Init(void)
return retval;
}
static void pe_pb_callback(gpio_int_pol_t edge_type, void *cbdata)
{
unsigned int pb = (unsigned int)cbdata;
if (pb_callbacks[pb - 1]) {
pb_callbacks[pb - 1](pb, edge_type == GPIO_INT_FALLING);
}
}
static void gpio_pb_callback(void *cbdata)
{
unsigned int pb = (unsigned int)cbdata;
if (pb_callbacks[pb - 1]) {
int level = GPIO_InGet(&pb_pin[pb - 1]);
pb_callbacks[pb - 1](pb, !level);
}
}
/******************************************************************************/
int PB_RegisterCallback(unsigned int pb, pb_callback callback)
{
MXC_ASSERT(pb < num_pbs);
MXC_ASSERT((pb > 0) && (pb <= num_pbs));
if (pb == 2) {
return E_INVALID;
}
pb_callbacks[pb - 1] = callback;
uint8_t mask = (1 << expander_pins[pb - 1]);
// TODO: portexpander support
if (callback) {
// Register callback
GPIO_RegisterCallback(&pb_pin[pb], callback, (void *)pb);
if (portexpander_detected()) {
// Register callback
portexpander_register_callback(
mask, pe_pb_callback, (void *)pb
);
// Configure and enable interrupt
portexpander_int_config(mask, GPIO_INT_BOTH);
portexpander_int_enable(mask);
} else {
// Register callback
GPIO_RegisterCallback(
&pb_pin[pb - 1], gpio_pb_callback, (void *)pb
);
// Configure and enable interrupt
GPIO_IntConfig(&pb_pin[pb], GPIO_INT_EDGE, GPIO_INT_FALLING);
GPIO_IntEnable(&pb_pin[pb]);
NVIC_EnableIRQ((IRQn_Type)MXC_GPIO_GET_IRQ(pb_pin[pb].port));
// Configure and enable interrupt
GPIO_IntConfig(
&pb_pin[pb - 1], GPIO_INT_EDGE, GPIO_INT_BOTH
);
GPIO_IntEnable(&pb_pin[pb - 1]);
NVIC_EnableIRQ((IRQn_Type)MXC_GPIO_GET_IRQ(
pb_pin[pb - 1].port)
);
}
} else {
// Disable interrupt and clear callback
GPIO_IntDisable(&pb_pin[pb]);
GPIO_RegisterCallback(&pb_pin[pb], NULL, NULL);
if (portexpander_detected()) {
// Disable interrupt and clear callback
portexpander_int_disable(mask);
portexpander_register_callback(mask, NULL, NULL);
} else {
// Disable interrupt and clear callback
GPIO_IntDisable(&pb_pin[pb - 1]);
GPIO_RegisterCallback(&pb_pin[pb - 1], NULL, NULL);
}
}
return E_NO_ERROR;
......@@ -85,25 +136,46 @@ int PB_RegisterCallback(unsigned int pb, pb_callback callback)
//******************************************************************************
void PB_IntEnable(unsigned int pb)
{
// TODO: portexpander support
MXC_ASSERT(pb < num_pbs);
GPIO_IntEnable(&pb_pin[pb]);
MXC_ASSERT((pb > 0) && (pb <= num_pbs));
if (pb == 2) {
return;
}
if (portexpander_detected()) {
portexpander_int_enable((1 << expander_pins[pb - 1]));
} else {
GPIO_IntEnable(&pb_pin[pb - 1]);
}
}
//******************************************************************************
void PB_IntDisable(unsigned int pb)
{
// TODO: portexpander support
MXC_ASSERT(pb < num_pbs);
GPIO_IntDisable(&pb_pin[pb]);
MXC_ASSERT((pb > 0) && (pb <= num_pbs));
if (pb == 2) {
return;
}
if (portexpander_detected()) {
portexpander_int_disable((1 << expander_pins[pb - 1]));
} else {
GPIO_IntDisable(&pb_pin[pb - 1]);
}
}
//******************************************************************************
void PB_IntClear(unsigned int pb)
{
// TODO: portexpander support
MXC_ASSERT(pb < num_pbs);
GPIO_IntClr(&pb_pin[pb]);
MXC_ASSERT((pb > 0) && (pb <= num_pbs));
if (pb == 2) {
return;
}
if (portexpander_detected()) {
portexpander_int_clr((1 << expander_pins[pb - 1]));
} else {
GPIO_IntClr(&pb_pin[pb - 1]);
}
}
//******************************************************************************
......@@ -116,8 +188,8 @@ int PB_Get(unsigned int pb)
case 3:
case 4:
if (portexpander_detected()) {
uint8_t port = portexpander_get();
return (port & (1 << expander_pins[pb - 1])) == 0;
return portexpander_in_get(
(1 << expander_pins[pb - 1])) == 0;
} else {
return GPIO_InGet(&pb_pin[pb - 1]) == 0;
}
......
/**
* @file pb.h
* @brief Pushbutton driver header file.
*/
/* ****************************************************************************
* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of Maxim Integrated
* Products, Inc. shall not be used except as stated in the Maxim Integrated
* Products, Inc. Branding Policy.
*
* The mere transfer of this software does not imply any licenses
* of trade secrets, proprietary technology, copyrights, patents,
* trademarks, maskwork rights, or any other form of intellectual
* property whatsoever. Maxim Integrated Products, Inc. retains all
* ownership rights.
*
* $Date: 2018-10-31 15:32:51 +0000 (Wed, 31 Oct 2018) $
* $Revision: 38826 $
*
*************************************************************************** */
#ifndef _PB_H_
#define _PB_H_
#include "gpio.h"
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @ingroup bsp
* @defgroup pushbutton_evkit Push button driver board support
* @{
*/
/* **** Global Variables **** */
extern const gpio_cfg_t pb_pin[];
extern const unsigned int num_pbs;
/* **** Function Prototypes **** */
/**
* @brief Initialize all push buttons.
* @return \c #E_NO_ERROR Push buttons initialized successfully.
* @return "Error Code" @ref MXC_Error_Codes "Error Code" if unsuccessful.
*
*/
int PB_Init(void);
/**
* Type alias @c pb_callback for the push button callback.
* @details The function is of type:
* @code
* void pb_callback(unsigned int pb, bool falling)
* @endcode
* To receive notification of a push button event, define a callback
* function and pass it as a pointer to the PB_RegisterCallback(unsigned int pb, pb_callback callback) function.
* @param pb push button index that triggered the callback.
*/
typedef void (*pb_callback)(unsigned int pb, bool falling);
/**
* @brief Register or Unregister a callback handler for events on the @p pb push button.
* @details
* - Calling this function with a pointer to a function @p callback, configures the pushbutton @p pb and enables the
* interrupt to handle the push button events.
* - Calling this function with a <tt>NULL</tt> pointer will disable the interrupt and unregister the
* callback function.
* @p pb must be a value between 0 and \c num_pbs.
*
* @param pb push button index to receive event callbacks.
* @param callback Callback function pointer of type @c pb_callback
* @return #E_NO_ERROR if configured and callback registered successfully.
* @return "Error Code" @ref MXC_Error_Codes "Error Code" if unsuccessful.
*/
int PB_RegisterCallback(unsigned int pb, pb_callback callback);
/**
* @brief Register or Unregister a callback handler for rising and falling events on the @p pb push button.
* @details
* - Calling this function with a pointer to a function @p callback, configures the pushbutton @p pb and enables the
* interrupt to handle the push button events.
* - Calling this function with a <tt>NULL</tt> pointer will disable the interrupt and unregister the
* callback function.
* @p pb must be a value between 0 and \c num_pbs.
*
* @param pb push button index to receive event callbacks.
* @param callback Callback function pointer of type @c pb_callback
* @return #E_NO_ERROR if configured and callback registered successfully.
* @return "Error Code" @ref MXC_Error_Codes "Error Code" if unsuccessful.
*/
int PB_RegisterRiseFallCallback(unsigned int pb, pb_callback callback);
/**
* @brief Enable a callback interrupt.
* @note PB_RegisterCallback must be called prior to enabling the callback interrupt.
* @param pb push button index value between 0 and \c num_pbs.
*/
void PB_IntEnable(unsigned int pb);
/**
* @brief Disable a callback interrupt.
* @param pb push button index
*/
void PB_IntDisable(unsigned int pb);
/**
* @brief Clear a callback interrupt.
* @param pb push button index value between 0 and \c num_pbs.
*/
void PB_IntClear(unsigned int pb);
/**
* @brief Get the current state of the push button.
* @param pb push button index value between 0 and \c num_pbs.
* @return TRUE The button is pressed.
* @return FALSE The button is not pressed.
*/
int PB_Get(unsigned int pb);
/**@}*/
#ifdef __cplusplus
}
#endif
#endif /* _PB_H_ */
/* PCAL6408A I2C port expander */
/* **** Includes **** */
#include "portexpander.h"
#include "mxc_config.h"
#include "mxc_assert.h"
#include "i2c.h"
#include <stdio.h>
......@@ -7,10 +12,7 @@
#include <string.h>
#include <stdbool.h>
// PCAL6408A I2C port expander
static bool detected = false;
static uint8_t output_state;
/* **** Definitions **** */
/* clang-format off */
#define PE_ADDR 0x42
......@@ -40,95 +42,296 @@ static uint8_t output_state;
#define PE_INPUT_MASK ((uint8_t)0b01101000) // 3, 5, 6 = input
/* **** Globals **** */
static bool detected = false;
static volatile bool interrupt_pending;
static uint8_t type_state = 0xFF;
static uint8_t output_state = 0xFF;
static uint8_t pull_enable_state = 0x00;
static uint8_t pull_selection_state = 0xFF;
static uint8_t int_mask_state = 0xFF;
static gpio_int_pol_t int_edge_config[8] = { 0 };
static pe_callback callbacks[8] = { NULL };
static void *cbparam[8] = { NULL };
const gpio_cfg_t pe_int_pin = { PORT_1, PIN_7, GPIO_FUNC_IN, GPIO_PAD_PULL_UP };
static const portexpander_cfg_t pe_pin_config[] = {
{ PE_INPUT_MASK, GPIO_FUNC_IN, GPIO_PAD_PULL_UP },
{ ~PE_INPUT_MASK, GPIO_FUNC_OUT, GPIO_PAD_PULL_UP },
};
/* **** Functions **** */
static int portexpander_write(uint8_t command, uint8_t data)
{
uint8_t i2c_data[2] = { command, data };
return I2C_MasterWrite(MXC_I2C1_BUS0, PE_ADDR, i2c_data, 2, 0);
}
/* ************************************************************************** */
static int portexpander_read(uint8_t command, uint8_t *data)
{
I2C_MasterWrite(MXC_I2C1_BUS0, PE_ADDR, &command, 1, 1);
return I2C_MasterRead(MXC_I2C1_BUS0, PE_ADDR, data, 1, 0);
}
void portexpander_init(void)
/* ************************************************************************** */
int portexpander_init(void)
{
int ret;
// Enable pull-ups for buttons (type defaults to pull-up)
ret = portexpander_write(PE_C_PULL_ENABLE, PE_INPUT_MASK);
// Set _all_ outputs to open-drain to support the high side p-channel transistors.
ret = portexpander_write(PE_C_OUTPUT_PORT_CONFIG, PE_OUT_OPEN_DRAIN);
if (ret != 2) {
printf("portexpander NOT detected\n");
detected = false;
return;
return E_NO_DEVICE;
}
detected = true;
// Set _all_ outputs to open-drain to support the high side p-channel transistors.
portexpander_write(PE_C_OUTPUT_PORT_CONFIG, PE_OUT_OPEN_DRAIN);
// Set outputs to high
portexpander_out_set(~PE_INPUT_MASK);
// Enable pull-ups for buttons
// Enable outputs for the transistors, the LED and the LCD reset
portexpander_write(PE_C_CONFIG, PE_INPUT_MASK);
for (int i = 0; i < sizeof(pe_pin_config) / sizeof(pe_pin_config[0]);
i++) {
MXC_ASSERT(
portexpander_config(&pe_pin_config[i]) == E_NO_ERROR
);
}
// Latch inputs so we can figure out whether an interrupt was caused by a rising or falling edge
portexpander_write(PE_C_INPUT_LATCH, PE_INPUT_MASK);
// Configure interrupt GPIO
MXC_ASSERT(GPIO_Config(&pe_int_pin) == E_NO_ERROR);
// Configure and enable portexpander interrupt
GPIO_RegisterCallback(
&pe_int_pin, &portexpander_interrupt_callback, NULL
);
MXC_ASSERT(
GPIO_IntConfig(&pe_int_pin, GPIO_INT_EDGE, GPIO_INT_FALLING) ==
E_NO_ERROR);
GPIO_IntEnable(&pe_int_pin);
NVIC_EnableIRQ((IRQn_Type)MXC_GPIO_GET_IRQ(pe_int_pin.port));
return E_SUCCESS;
}
// Set outputs to high (i.e. open-drain)
output_state = ~PE_INPUT_MASK;
portexpander_write(PE_C_OUTPUT_PORT, output_state);
/* ************************************************************************** */
int portexpander_config(const portexpander_cfg_t *cfg)
{
// Set the GPIO type
switch (cfg->func) {
case GPIO_FUNC_IN:
type_state |= cfg->mask;
break;
case GPIO_FUNC_OUT:
type_state &= ~cfg->mask;
break;
default:
return E_BAD_PARAM;
}
if (portexpander_write(PE_C_CONFIG, type_state) != 2) {
return E_NO_DEVICE;
}
switch (cfg->pad) {
case GPIO_PAD_NONE:
pull_enable_state &= ~cfg->mask;
break;
case GPIO_PAD_PULL_UP:
pull_selection_state |= cfg->mask;
pull_enable_state |= cfg->mask;
break;
case GPIO_PAD_PULL_DOWN:
pull_selection_state &= ~cfg->mask;
pull_enable_state |= cfg->mask;
break;
default:
return E_BAD_PARAM;
}
portexpander_write(PE_C_PULL_ENABLE, pull_selection_state);
portexpander_write(PE_C_PULL_ENABLE, pull_enable_state);
return E_NO_ERROR;
}
uint8_t portexpander_get(void)
/* ************************************************************************** */
uint8_t portexpander_in_get(uint8_t mask)
{
// Reading the input port clears interrupts, so we need to check them here to avoid losing information
portexpander_poll();
uint8_t buf = 0xFF;
if (detected) {
portexpander_read(PE_C_INPUT_PORT, &buf);
}
return buf;
return buf & mask;
}
/* ************************************************************************** */
bool portexpander_detected(void)
{
return detected;
}
void portexpander_set(uint8_t pin, uint8_t value)
/* ************************************************************************** */
void portexpander_out_set(uint8_t mask)
{
if (detected && pin < 8) {
if (value) {
output_state |= (1 << pin);
} else {
output_state &= ~(1 << pin);
}
if (detected) {
output_state |= mask;
portexpander_write(PE_C_OUTPUT_PORT, output_state);
}
}
void portexpander_prep(uint8_t pin, uint8_t value)
/* ************************************************************************** */
void portexpander_out_clr(uint8_t mask)
{
if (pin < 8) {
if (value) {
output_state |= (1 << pin);
} else {
output_state &= ~(1 << pin);
}
if (detected) {
output_state &= ~mask;
portexpander_write(PE_C_OUTPUT_PORT, output_state);
}
}
void portexpander_update(void)
/* ************************************************************************** */
uint8_t portexpander_out_get(uint8_t mask)
{
return output_state & mask;
}
/* ************************************************************************** */
void portexpander_out_put(uint8_t mask, uint8_t val)
{
if (detected) {
output_state = (output_state & ~mask) | (val & mask);
portexpander_write(PE_C_OUTPUT_PORT, output_state);
}
}
void portexpander_set_mask(uint8_t mask, uint8_t values)
/* ************************************************************************** */
void portexpander_out_toggle(uint8_t mask)
{
if (detected) {
output_state &= ~(mask & ~values);
output_state |= mask & values;
output_state ^= mask;
portexpander_write(PE_C_OUTPUT_PORT, output_state);
}
}
/* ************************************************************************** */
void portexpander_int_config(uint8_t mask, gpio_int_pol_t edge)
{
if (detected) {
for (uint8_t pin = 0; pin < 8; ++pin) {
if (mask & (1 << pin)) {
int_edge_config[pin] = edge;
}
}
}
}
/* ************************************************************************** */
void portexpander_int_enable(uint8_t mask)
{
if (detected) {
int_mask_state &= ~mask;
portexpander_write(PE_C_INT_MASK, int_mask_state);
}
}
/* ************************************************************************** */
void portexpander_int_disable(uint8_t mask)
{
if (detected) {
int_mask_state |= mask;
portexpander_write(PE_C_INT_MASK, int_mask_state);
}
}
/* ************************************************************************** */
uint8_t portexpander_int_status()
{
uint8_t buf = 0;
if (detected) {
portexpander_read(PE_C_INT_STATUS, &buf);
}
return buf;
}
/* ************************************************************************** */
void portexpander_int_clr(uint8_t mask)
{
if (detected) {
uint8_t tmp_mask = int_mask_state | mask;
// Setting an interrupt mask clears the corresponding interrupt
portexpander_write(PE_C_INT_MASK, tmp_mask);
portexpander_write(PE_C_INT_MASK, int_mask_state);
}
}
/* ************************************************************************** */
int portexpander_register_callback(
uint8_t mask, pe_callback callback, void *cbdata
) {
if (!detected) {
return E_NO_DEVICE;
}
for (uint8_t pin = 0; pin < 8; ++pin) {
if (mask & (1 << pin)) {
callbacks[pin] = callback;
cbparam[pin] = cbdata;
}
}
return E_NO_ERROR;
}
/* ************************************************************************** */
__attribute__((weak)) void portexpander_interrupt_callback(void *_)
{
GPIO_IntDisable(&pe_int_pin);
GPIO_IntClr(&pe_int_pin);
interrupt_pending = true;
}
/* ************************************************************************** */
void portexpander_poll()
{
if (detected && interrupt_pending) {
interrupt_pending = false;
uint8_t caused_by = portexpander_int_status();
// Port read resets interrupts
uint8_t port_levels = portexpander_in_get(0xFF);
GPIO_IntEnable(&pe_int_pin);
for (uint8_t pin = 0; pin < 8; ++pin) {
if ((caused_by & (1 << pin)) && callbacks[pin]) {
gpio_int_pol_t edge_type =
(port_levels & (1 << pin) ?
GPIO_INT_RISING :
GPIO_INT_FALLING);
if ((int_edge_config[pin] == GPIO_INT_BOTH) ||
(edge_type == int_edge_config[pin])) {
callbacks[pin](edge_type, cbparam[pin]);
}
}
}
}
}
#ifndef PORTEXPANDER_H
#define PORTEXPANDER_H
#include "mxc_config.h"
#include <stdint.h>
#include <stdbool.h>
void portexpander_init(void);
uint8_t portexpander_get(void);
void portexpander_set(uint8_t pin, uint8_t value);
void portexpander_set_mask(uint8_t mask, uint8_t values);
void portexpander_prep(uint8_t pin, uint8_t value);
void portexpander_update(void);
/**
* Structure type for configuring the portexpander.
*/
typedef struct {
uint8_t mask; /**< Pin mask (multiple pins may be set) */
gpio_func_t func; /**< Function type */
gpio_pad_t pad; /**< Pad type */
} portexpander_cfg_t;
typedef void (*pe_callback)(gpio_int_pol_t edge_type, void *cbdata);
int portexpander_init(void);
bool portexpander_detected(void);
int portexpander_config(const portexpander_cfg_t *cfg);
uint8_t portexpander_in_get(uint8_t mask);
void portexpander_out_set(uint8_t mask);
void portexpander_out_clr(uint8_t mask);
void portexpander_out_put(uint8_t mask, uint8_t val);
void portexpander_out_toggle(uint8_t mask);
uint8_t portexpander_out_get(uint8_t mask);
void portexpander_int_config(uint8_t mask, gpio_int_pol_t edge);
void portexpander_int_enable(uint8_t mask);
void portexpander_int_disable(uint8_t mask);
uint8_t portexpander_int_status();
void portexpander_int_clr(uint8_t mask);
int portexpander_register_callback(uint8_t mask, pe_callback callback, void *cbdata);
void portexpander_poll();
void portexpander_interrupt_callback(void *_);
#endif
# Maxim Integrated MAX32665 OpenOCD target configuration file
# www.maximintegrated.com
# selecting transport method, to prevenet auto-selecting something else
transport select swd
# Set the reset pin configuration
reset_config none
......
......@@ -11,28 +11,6 @@ import os
CONFIG_NAME = "clock.json"
class Time:
def __init__(self, start=0):
self.time = start
self.wait_time = 0.95
def tick(self):
sleep(self.wait_time)
self.time += 1
@property
def second(self):
return self.time % 60
@property
def minute(self):
return (self.time / 60) % 60
@property
def hour(self):
return (self.time / 3600) % 24
class Clock:
def __init__(
self,
......@@ -60,7 +38,6 @@ class Clock:
)
self.run_once = run_once
self.offsetx = offsetx
self.time = Time()
self.theme = 0
self.default_themes = [
{
......@@ -181,46 +158,36 @@ class Clock:
colored = False
try:
with display.open() as disp:
button_pressed = False
while True:
self.updateClock(disp)
localtime = utime.localtime()
self.updateClock(disp, localtime)
if self.run_once:
break
# check for button presses
v = buttons.read(buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT)
if v == 0:
button_pressed = False
v = buttons.read(
buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT | buttons.TOP_RIGHT
)
button_pressed = v != 0
if not button_pressed and v & buttons.BOTTOM_LEFT != 0:
button_pressed = True
if button_pressed and v & buttons.BOTTOM_LEFT != 0:
self.setTheme(self.theme - 1)
self.writeConfig()
elif not button_pressed and v & buttons.BOTTOM_RIGHT != 0:
button_pressed = True
elif button_pressed and v & buttons.BOTTOM_RIGHT != 0:
self.setTheme(self.theme + 1)
self.writeConfig()
elif button_pressed and v & buttons.TOP_RIGHT != 0:
self.setTime(disp, localtime)
utime.sleep_ms(23)
except KeyboardInterrupt:
for i in range(11):
leds.set(i, (0, 0, 0))
return
def drawImage(self, image):
with display.open() as d:
d.clear()
for x in range(len(image)):
for y in range(len(image[x])):
d.pixel(
x + self.offsetx,
y,
col=(255, 255, 255) if image[x][y] else (0, 0, 0),
)
d.update()
def updateClock(self, disp):
def updateClock(self, disp, localtime):
disp.clear(self.background_col)
localtime = utime.localtime()
disp.pixel(self.center[0] + self.offsetx, self.center[1], col=self.center_col)
hour_coords = self.circlePoint(
......@@ -284,6 +251,49 @@ class Clock:
disp.update()
def setTime(self, disp, localtime):
accepted = False
previously_pressed_button = buttons.TOP_RIGHT
button_repeat_counter = 0
set_seconds = utime.mktime(localtime)
while not accepted:
v = buttons.read(
buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT | buttons.TOP_RIGHT
)
button_pressed = v != 0
if button_pressed:
if v & previously_pressed_button != 0:
button_repeat_counter += 1
else:
if v & buttons.BOTTOM_LEFT != 0:
previously_pressed_button = buttons.BOTTOM_LEFT
elif v & buttons.BOTTOM_RIGHT != 0:
previously_pressed_button = buttons.BOTTOM_RIGHT
elif (
v & buttons.TOP_RIGHT != 0
and previously_pressed_button != buttons.TOP_RIGHT
):
accepted = True
else:
previously_pressed_button = 0
else:
previously_pressed_button = 0
button_repeat_counter = 0
seconds_change = int(min(1.1 ** button_repeat_counter, 60 * 23 + 1))
if previously_pressed_button == buttons.BOTTOM_LEFT:
set_seconds -= seconds_change
elif previously_pressed_button == buttons.BOTTOM_RIGHT:
set_seconds += seconds_change
self.updateClock(disp, utime.localtime(set_seconds))
utime.sleep_ms(23)
utime.set_time(int(set_seconds))
utime.sleep_ms(123)
def circlePoint(self, t):
return (
int(round(self.radius * math.cos(t))) + self.center[0],
......
import os
import sys
import leds
import display
import buttons
sys.path.append("/apps/digiclk/")
import monotime as utime
import draw
DIGITS = [
(True, True, True, True, True, True, False),
(False, True, True, False, False, False, False),
(True, True, False, True, True, False, True),
(True, True, True, True, False, False, True),
(False, True, True, False, False, True, True),
(True, False, True, True, False, True, True),
(True, False, True, True, True, True, True),
(True, True, True, False, False, False, False),
(True, True, True, True, True, True, True),
(True, True, True, True, False, True, True),
]
def renderNum(d, num, x):
draw.Grid7Seg(d, x, 0, 7, DIGITS[num // 10], (255, 255, 255))
draw.Grid7Seg(d, x + 5, 0, 7, DIGITS[num % 10], (255, 255, 255))
def renderColon(d):
draw.GridVSeg(d, 11, 2, 7, 2, (255, 255, 255))
draw.GridVSeg(d, 11, 4, 7, 2, (255, 255, 255))
def renderText(d, text, blankidx=None):
bs = bytearray(text)
if blankidx != None:
bs[blankidx : blankidx + 1] = b"_"
d.print(
MODES[MODE] + " " + bs.decode(), fg=(255, 255, 255), bg=None, posx=0, posy=7 * 8
)
def renderBar(d, num):
d.rect(20, 78, 20 + num * 2, 80, col=(255, 255, 255))
def render(d):
ltime = utime.localtime()
years = ltime[0]
months = ltime[1]
days = ltime[2]
hours = ltime[3]
mins = ltime[4]
secs = ltime[5]
d.clear()
if MODE == CHANGE_YEAR:
renderNum(d, years // 100, 1)
renderNum(d, years % 100, 13)
elif MODE == CHANGE_MONTH:
renderNum(d, months, 13)
elif MODE == CHANGE_DAY:
renderNum(d, days, 13)
else:
renderNum(d, hours, 1)
renderNum(d, mins, 13)
if MODE not in (CHANGE_YEAR, CHANGE_MONTH, CHANGE_DAY) and secs % 2 == 0:
renderColon(d)
renderText(d, NAME, None)
renderBar(d, secs)
d.update()
LONG_DELAY = 400
BUTTON_UPDATE_TIME = 100
BUTTON_SEL = 1 << 0
BUTTON_SEL_LONG = 1 << 1
BUTTON_UP = 1 << 2
BUTTON_UP_LONG = 1 << 3
BUTTON_DOWN = 1 << 4
BUTTON_DOWN_LONG = 1 << 5
pressed_prev = 0
button_long_prev = {BUTTON_SEL: False, BUTTON_UP: False, BUTTON_DOWN: False}
button_times = {BUTTON_SEL: 0, BUTTON_UP: 0, BUTTON_DOWN: 0}
def checkButton(button, button_long, osbutton, pressed, t):
cur_buttons = 0
if pressed & osbutton and not pressed_prev & osbutton:
button_times[button] = t
button_long_prev[button] = False
elif pressed_prev & osbutton:
if button_times[button] + LONG_DELAY < t:
cur_buttons |= button_long
button_times[button] = t
button_long_prev[button] = True
elif not pressed & osbutton and not button_long_prev[button]:
cur_buttons |= button
return cur_buttons
def checkButtons():
global pressed_prev
t = utime.time_monotonic_ms()
pressed = buttons.read(
buttons.BOTTOM_LEFT | buttons.TOP_RIGHT | buttons.BOTTOM_RIGHT
)
cur_buttons = 0
cur_buttons |= checkButton(
BUTTON_SEL, BUTTON_SEL_LONG, buttons.BOTTOM_LEFT, pressed, t
)
cur_buttons |= checkButton(BUTTON_UP, BUTTON_UP_LONG, buttons.TOP_RIGHT, pressed, t)
cur_buttons |= checkButton(
BUTTON_DOWN, BUTTON_DOWN_LONG, buttons.BOTTOM_RIGHT, pressed, t
)
pressed_prev = pressed
return cur_buttons
def modTime(yrs, mth, day, hrs, mns, sec):
ltime = utime.localtime()
new = utime.mktime(
(
ltime[0] + yrs,
ltime[1] + mth,
ltime[2] + day,
ltime[3] + hrs,
ltime[4] + mns,
ltime[5] + sec,
None,
None,
)
)
utime.set_time(new)
def ctrl_display(bs):
global MODE, updated
updated = True
if bs & BUTTON_SEL_LONG:
MODE = CHANGE_HOURS
else:
updated = False
def ctrl_chg_hrs(bs):
global MODE, updated
updated = True
if bs & BUTTON_SEL_LONG:
MODE = DISPLAY
elif bs & BUTTON_SEL:
MODE = CHANGE_MINUTES
elif bs & BUTTON_UP_LONG:
modTime(0, 0, 0, 10, 0, 0)
elif bs & BUTTON_DOWN_LONG:
modTime(0, 0, 0, -10, 0, 0)
elif bs & BUTTON_UP:
modTime(0, 0, 0, 1, 0, 0)
elif bs & BUTTON_DOWN:
modTime(0, 0, 0, -1, 0, 0)
else:
updated = False
def ctrl_chg_mns(bs):
global MODE, updated
updated = True
if bs & BUTTON_SEL_LONG:
MODE = DISPLAY
elif bs & BUTTON_SEL:
MODE = CHANGE_SECONDS
elif bs & BUTTON_UP_LONG:
modTime(0, 0, 0, 0, 10, 0)
elif bs & BUTTON_DOWN_LONG:
modTime(0, 0, 0, 0, -10, 0)
elif bs & BUTTON_UP:
modTime(0, 0, 0, 0, 1, 0)
elif bs & BUTTON_DOWN:
modTime(0, 0, 0, 0, -1, 0)
else:
updated = False
def ctrl_chg_sec(bs):
global MODE, updated
updated = True
if bs & BUTTON_SEL_LONG:
MODE = DISPLAY
elif bs & BUTTON_SEL:
MODE = CHANGE_YEAR
elif bs & BUTTON_UP_LONG:
modTime(0, 0, 0, 0, 0, 10)
elif bs & BUTTON_DOWN_LONG:
modTime(0, 0, 0, 0, 0, -10)
elif bs & BUTTON_UP:
modTime(0, 0, 0, 0, 0, 1)
elif bs & BUTTON_DOWN:
modTime(0, 0, 0, 0, 0, -1)
else:
updated = False
def ctrl_chg_yrs(bs):
global MODE, updated
updated = True
if bs & BUTTON_SEL_LONG:
MODE = DISPLAY
elif bs & BUTTON_SEL:
MODE = CHANGE_MONTH
elif bs & BUTTON_UP_LONG:
modTime(10, 0, 0, 0, 0, 0)
elif bs & BUTTON_DOWN_LONG:
modTime(-10, 0, 0, 0, 0, 0)
elif bs & BUTTON_UP:
modTime(1, 0, 0, 0, 0, 0)
elif bs & BUTTON_DOWN:
modTime(-1, 0, 0, 0, 0, 0)
else:
updated = False
def ctrl_chg_mth(bs):
global MODE, updated
updated = True
if bs & BUTTON_SEL_LONG:
MODE = DISPLAY
elif bs & BUTTON_SEL:
MODE = CHANGE_DAY
elif bs & BUTTON_UP_LONG:
modTime(0, 6, 0, 0, 0, 0)
elif bs & BUTTON_DOWN_LONG:
modTime(0, -6, 0, 0, 0, 0)
elif bs & BUTTON_UP:
modTime(0, 1, 0, 0, 0, 0)
elif bs & BUTTON_DOWN:
modTime(0, -1, 0, 0, 0, 0)
else:
updated = False
def ctrl_chg_day(bs):
global MODE, updated
updated = True
if bs & BUTTON_SEL_LONG:
MODE = DISPLAY
elif bs & BUTTON_SEL:
MODE = CHANGE_HOURS
elif bs & BUTTON_UP_LONG:
modTime(0, 0, 10, 0, 0, 0)
elif bs & BUTTON_DOWN_LONG: