Compare commits
20 Commits
thermocycl
...
master
Author | SHA1 | Date |
---|---|---|
King Kévin | f7b99dd76c | |
King Kévin | 3577f1a0f4 | |
King Kévin | 5e154a57e3 | |
King Kévin | 614d875d9a | |
King Kévin | 17dce0d517 | |
King Kévin | 8bd98693f6 | |
King Kévin | 017c649842 | |
King Kévin | 799584a210 | |
King Kévin | 7b31ace475 | |
King Kévin | d8cd409d23 | |
King Kévin | 61d65977ac | |
King Kévin | af28da0a7d | |
King Kévin | e50cd35728 | |
King Kévin | 952d947c1b | |
King Kévin | 353b11e710 | |
King Kévin | 974ca75027 | |
King Kévin | fb088e6057 | |
King Kévin | c085f2d292 | |
King Kévin | 58ef5f3d1b | |
King Kévin | a4b5f95b07 |
|
@ -279,7 +279,7 @@ static void process_command(char* str)
|
|||
void main(void);
|
||||
void main(void)
|
||||
{
|
||||
rcc_clock_setup_in_hse_8mhz_out_72mhz(); // use 8 MHz high speed external clock to generate 72 MHz internal clock
|
||||
rcc_clock_setup_pll(&rcc_hse_configs[RCC_CLOCK_HSE8_72MHZ]); // use 8 MHz high speed external clock to generate 72 MHz internal clock
|
||||
|
||||
#if DEBUG
|
||||
// enable functionalities for easier debug
|
||||
|
@ -378,7 +378,7 @@ void main(void)
|
|||
if (rtc_internal_tick_flag) { // the internal RTC ticked
|
||||
rtc_internal_tick_flag = false; // reset flag
|
||||
action = true; // action has been performed
|
||||
if (0 == (rtc_get_counter_val() % RTC_TICKS_SECOND)) { // one seond has passed
|
||||
if (0 == (rtc_get_counter_val() % RTC_TICKS_SECOND)) { // one second has passed
|
||||
led_toggle(); // toggle LED (good to indicate if main function is stuck)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ void main(void)
|
|||
(*(void(**)(void))((uint32_t)application + 4))(); // start application (by jumping to the reset function which address is stored as second entry of the vector table)
|
||||
}
|
||||
|
||||
rcc_clock_setup_in_hse_8mhz_out_72mhz(); // start main clock
|
||||
rcc_clock_setup_pll(&rcc_hse_configs[RCC_CLOCK_HSE8_72MHZ]); // start main clock
|
||||
board_setup(); // setup board to control LED
|
||||
led_on(); // indicate bootloader started
|
||||
#if defined(BUSVOODOO)
|
||||
|
|
78
global.c
78
global.c
|
@ -28,6 +28,84 @@ static volatile uint8_t user_input_used = 0; /**< how much data has been receive
|
|||
|
||||
static volatile uint32_t sleep_duration = 0; /**< sleep duration count down (in SysTick interrupts) */
|
||||
|
||||
uint8_t addu8_safe(uint8_t a, uint8_t b)
|
||||
{
|
||||
if (a > UINT8_MAX - b) {
|
||||
return UINT8_MAX;
|
||||
} else {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t addu16_safe(uint16_t a, uint16_t b)
|
||||
{
|
||||
if (a > UINT16_MAX - b) {
|
||||
return UINT16_MAX;
|
||||
} else {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t addu32_safe(uint32_t a, uint32_t b)
|
||||
{
|
||||
if (a > UINT32_MAX - b) {
|
||||
return UINT32_MAX;
|
||||
} else {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
|
||||
int8_t adds8_safe(int8_t a, int8_t b)
|
||||
{
|
||||
if (b > 0) {
|
||||
if (a > INT8_MAX - b) {
|
||||
return INT8_MAX;
|
||||
} else {
|
||||
return a + b;
|
||||
}
|
||||
} else {
|
||||
if (a < INT8_MIN + b) {
|
||||
return INT8_MIN;
|
||||
} else {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int16_t adds16_safe(int16_t a, int16_t b)
|
||||
{
|
||||
if (b > 0) {
|
||||
if (a > INT16_MAX - b) {
|
||||
return INT16_MAX;
|
||||
} else {
|
||||
return a + b;
|
||||
}
|
||||
} else {
|
||||
if (a < INT16_MIN + b) {
|
||||
return INT16_MIN;
|
||||
} else {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t adds32_safe(int32_t a, int32_t b)
|
||||
{
|
||||
if (b > 0) {
|
||||
if (a > INT32_MAX - b) {
|
||||
return INT32_MAX;
|
||||
} else {
|
||||
return a + b;
|
||||
}
|
||||
} else {
|
||||
if (a < INT32_MIN + b) {
|
||||
return INT32_MIN;
|
||||
} else {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char* b2s(uint64_t binary, uint8_t rjust)
|
||||
{
|
||||
static char string[64 + 1] = {0}; // the string representation to return
|
||||
|
|
45
global.h
45
global.h
|
@ -24,11 +24,48 @@
|
|||
/** integer underflow/overflow safe uint32_t addition (result to min/max on underflow/overflow) */
|
||||
#define ADDU32_SAFE(a,b) {__typeof__ (a) _a = (a); __typeof__ (b) _b = (b); a = (_b > 0 ? ((_a > UINT32_MAX - _b) ? UINT32_MAX : (_a + _b)) : ((_a < _b) ? 0 : (_a - _b)));}
|
||||
/** integer underflow/overflow safe int8_t addition (result to min/max on underflow/overflow) */
|
||||
#define ADDS8_SAFE(a,b) {__typeof__ (a) _a = (a); __typeof__ (b) _b = (b); a = (_b > 0 ? ((_a > INT8_MAX - _b) ? INT8_MAX : (_a + _b)) : ((_a < INT8_MAX + _b) ? INT8_MAX : (_a - _b)));}
|
||||
#define ADDS8_SAFE(a,b) {__typeof__ (a) _a = (a); __typeof__ (b) _b = (b); a = (_b > 0 ? ((_a > INT8_MAX - _b) ? INT8_MAX : (_a + _b)) : ((_a < INT8_MAX + _b) ? INT8_MAX : (_a + _b)));}
|
||||
/** integer underflow/overflow safe int16_t addition (result to min/max on underflow/overflow) */
|
||||
#define ADDS16_SAFE(a,b) {__typeof__ (a) _a = (a); __typeof__ (b) _b = (b); a = (_b > 0 ? ((_a > INT16_MAX - _b) ? INT16_MAX : (_a + _b)) : ((_a < INT16_MIN + _b) ? INT16_MIN : (_a - _b)));}
|
||||
#define ADDS16_SAFE(a,b) {__typeof__ (a) _a = (a); __typeof__ (b) _b = (b); a = (_b > 0 ? ((_a > INT16_MAX - _b) ? INT16_MAX : (_a + _b)) : ((_a < INT16_MIN + _b) ? INT16_MIN : (_a + _b)));}
|
||||
/** integer underflow/overflow safe int32_t addition (result to min/max on underflow/overflow) */
|
||||
#define ADDS32_SAFE(a,b) {__typeof__ (a) _a = (a); __typeof__ (b) _b = (b); a = (_b > 0 ? ((_a > UINT32_MAX - _b) ? UINT32_MAX : (_a + _b)) : ((_a < INT32_MIN + _b) ? INT32_MIN : (_a - _b)));}
|
||||
#define ADDS32_SAFE(a,b) {__typeof__ (a) _a = (a); __typeof__ (b) _b = (b); a = (_b > 0 ? ((_a > INT32_MAX - _b) ? INT32_MAX : (_a + _b)) : ((_a < INT32_MIN + _b) ? INT32_MIN : (_a + _b)));}
|
||||
|
||||
/** unsigned 8-bit integer overflow safe addition
|
||||
* @param[in] a first part of addition
|
||||
* @param[in] b second part of addition
|
||||
* return result of addition, or type max on overflow
|
||||
*/
|
||||
uint8_t addu8_safe(uint8_t a, uint8_t b);
|
||||
/** unsigned 16-bit integer overflow safe addition
|
||||
* @param[in] a first part of addition
|
||||
* @param[in] b second part of addition
|
||||
* return result of addition, or type max on overflow
|
||||
*/
|
||||
uint16_t addu16_safe(uint16_t a, uint16_t b);
|
||||
/** unsigned 8-bit integer overflow safe addition
|
||||
* @param[in] a first part of addition
|
||||
* @param[in] b second part of addition
|
||||
* return result of addition, or type max on overflow
|
||||
*/
|
||||
uint32_t addu32_safe(uint32_t a, uint32_t b);
|
||||
/** signed 8-bit integer underflow/overflow safe addition
|
||||
* @param[in] a first part of addition
|
||||
* @param[in] b second part of addition
|
||||
* return result of addition, or type max on overflow
|
||||
*/
|
||||
int8_t adds8_safe(int8_t a, int8_t b);
|
||||
/** signed 16-bit integer underflow/overflow safe addition
|
||||
* @param[in] a first part of addition
|
||||
* @param[in] b second part of addition
|
||||
* return result of addition, or type max on overflow
|
||||
*/
|
||||
int16_t adds16_safe(int16_t a, int16_t b);
|
||||
/** signed 32-bit integer underflow/overflow safe addition
|
||||
* @param[in] a first part of addition
|
||||
* @param[in] b second part of addition
|
||||
* return result of addition, or type max on overflow
|
||||
*/
|
||||
int32_t adds32_safe(int32_t a, int32_t b);
|
||||
|
||||
/** build year as number */
|
||||
#define COMPUTE_BUILD_YEAR \
|
||||
|
@ -674,7 +711,7 @@
|
|||
/** symbol for beginning of the application
|
||||
* @note this symbol will be provided by the linker script
|
||||
*/
|
||||
extern char __application_beginning;
|
||||
extern uint32_t __application_beginning;
|
||||
/** symbol for end of the application
|
||||
* @note this symbol will be provided by the linker script
|
||||
*/
|
||||
|
|
|
@ -37,7 +37,7 @@ static void flash_internal_init(void)
|
|||
if ((uint32_t)&__flash_end >= FLASH_BASE) {
|
||||
flash_internal_end = (uint32_t)&__flash_end;
|
||||
} else {
|
||||
flash_internal_end = FLASH_BASE + DESIG_FLASH_SIZE * 1024;
|
||||
flash_internal_end = FLASH_BASE + desig_get_flash_size() * 1024;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ static bool flash_internal_range(uint32_t address, size_t size)
|
|||
uint16_t flash_internal_page_size(void)
|
||||
{
|
||||
if (0 == flash_internal_page) { // we don't know the page size yet
|
||||
if (DESIG_FLASH_SIZE < 256) {
|
||||
if (desig_get_flash_size() < 256) {
|
||||
if ((*(uint32_t*)0x1FFFF000 & 0xFFFE0000) == 0x20000000) { // non-connectivity system memory start detected (MSP address pointing to SRAM
|
||||
flash_internal_page = 1024;
|
||||
} else { // connectivity system memory start is at 0x1FFFB000
|
||||
|
@ -224,7 +224,7 @@ void flash_internal_eeprom_setup(uint16_t pages)
|
|||
|
||||
flash_internal_eeprom_start = 0; // reset start address
|
||||
flash_internal_eeprom_address = 0; // reset EEPROM address
|
||||
if (pages > DESIG_FLASH_SIZE * 1024 / flash_internal_page) { // not enough pages are available
|
||||
if (pages > desig_get_flash_size() * 1024 / flash_internal_page) { // not enough pages are available
|
||||
return;
|
||||
}
|
||||
flash_internal_eeprom_start = flash_internal_end - flash_internal_page * pages; // set EEPROM start (page aligned)
|
||||
|
@ -313,7 +313,7 @@ uint32_t flash_internal_probe_read_size(void)
|
|||
|
||||
uint32_t flash_internal_probe_write_size(void)
|
||||
{
|
||||
if (0 == DESIG_FLASH_SIZE) { // no flash size advertised
|
||||
if (0 == desig_get_flash_size()) { // no flash size advertised
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -324,7 +324,7 @@ uint32_t flash_internal_probe_write_size(void)
|
|||
// prepare for writing the flash
|
||||
flash_unlock(); // unlock flash to be able to write it
|
||||
// try reading and writing the flash, page per page
|
||||
uint32_t start = FLASH_BASE + DESIG_FLASH_SIZE * 1024; // start with the end of the advertised flash
|
||||
uint32_t start = FLASH_BASE + desig_get_flash_size() * 1024; // start with the end of the advertised flash
|
||||
if ((uint32_t)&__flash_end >= FLASH_BASE) { // use linker flash size if provided
|
||||
start = (uint32_t)&__flash_end;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,324 @@
|
|||
/** monospace pixel fonts collection (code)
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @date 2018
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
#include <stdint.h> // standard integer types
|
||||
#include "font.h" // own definitions
|
||||
|
||||
/** 8x5 px monospace bitmap font */
|
||||
const uint16_t font_king8[FONT_GLYPH_NUMBERS * 5] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, // ' '
|
||||
0x00, 0x00, 0xfa, 0x00, 0x00, // '!'
|
||||
0x00, 0xc0, 0x00, 0xc0, 0x00, // '"'
|
||||
0x28, 0x7c, 0x28, 0x7c, 0x28, // '#'
|
||||
0x24, 0x54, 0xfe, 0x54, 0x48, // '$'
|
||||
0xc6, 0xc8, 0x10, 0x26, 0xc6, // '%'
|
||||
0x6c, 0x92, 0x6a, 0x04, 0x0a, // '&'
|
||||
0x00, 0xc0, 0x00, 0x00, 0x00, // '''
|
||||
0x00, 0x3c, 0x42, 0x81, 0x00, // '('
|
||||
0x00, 0x81, 0x42, 0x3c, 0x00, // ')'
|
||||
0x54, 0x38, 0x7c, 0x38, 0x54, // '*'
|
||||
0x10, 0x10, 0x7c, 0x10, 0x10, // '+'
|
||||
0x00, 0x02, 0x0c, 0x00, 0x00, // ','
|
||||
0x00, 0x10, 0x10, 0x10, 0x00, // '-'
|
||||
0x00, 0x00, 0x0c, 0x00, 0x00, // '.'
|
||||
0x06, 0x08, 0x10, 0x20, 0xc0, // '/'
|
||||
0x7c, 0x8a, 0x92, 0xa2, 0x7c, // '0'
|
||||
0x00, 0x42, 0xfe, 0x02, 0x00, // '1'
|
||||
0x00, 0x86, 0x8a, 0x92, 0x62, // '2'
|
||||
0x00, 0x82, 0x92, 0x92, 0x6c, // '3'
|
||||
0x18, 0x28, 0x48, 0xfe, 0x08, // '4'
|
||||
0xe4, 0xa2, 0xa2, 0xa2, 0x9c, // '5'
|
||||
0x7c, 0x92, 0x92, 0x92, 0x0c, // '6'
|
||||
0x80, 0x8e, 0x90, 0xa0, 0xc0, // '7'
|
||||
0x6c, 0x92, 0x92, 0x92, 0x6c, // '8'
|
||||
0x60, 0x92, 0x92, 0x92, 0x7c, // '9'
|
||||
0x00, 0x00, 0x6c, 0x00, 0x00, // ':'
|
||||
0x00, 0x02, 0x6c, 0x00, 0x00, // ';'
|
||||
0x00, 0x10, 0x28, 0x44, 0x00, // '<'
|
||||
0x00, 0x28, 0x28, 0x28, 0x00, // '='
|
||||
0x00, 0x44, 0x28, 0x10, 0x00, // '>'
|
||||
0x40, 0x80, 0x9a, 0xa0, 0x40, // '?'
|
||||
0x7c, 0x82, 0xba, 0xaa, 0x7a, // '@'
|
||||
0x7e, 0x90, 0x90, 0x90, 0x7e, // 'A'
|
||||
0xfe, 0x92, 0x92, 0x92, 0x6c, // 'B'
|
||||
0x7c, 0x82, 0x82, 0x82, 0x82, // 'C'
|
||||
0xfe, 0x82, 0x82, 0x82, 0x7c, // 'D'
|
||||
0xfe, 0x92, 0x92, 0x92, 0x82, // 'E'
|
||||
0xfe, 0x90, 0x90, 0x90, 0x80, // 'F'
|
||||
0x7c, 0x82, 0x82, 0x92, 0x9c, // 'G'
|
||||
0xfe, 0x10, 0x10, 0x10, 0xfe, // 'H'
|
||||
0x00, 0x82, 0xfe, 0x82, 0x00, // 'I'
|
||||
0x00, 0x82, 0x82, 0xfc, 0x00, // 'J'
|
||||
0xfe, 0x10, 0x28, 0x44, 0x82, // 'K'
|
||||
0xfe, 0x02, 0x02, 0x02, 0x02, // 'L'
|
||||
0xfe, 0x40, 0x20, 0x40, 0xfe, // 'M'
|
||||
0xfe, 0x20, 0x10, 0x08, 0xfe, // 'N'
|
||||
0x7c, 0x82, 0x82, 0x82, 0x7c, // 'O'
|
||||
0xfe, 0x90, 0x90, 0x90, 0x60, // 'P'
|
||||
0x7c, 0x82, 0x8a, 0x84, 0x7a, // 'Q'
|
||||
0xfe, 0x90, 0x98, 0x94, 0x62, // 'R'
|
||||
0x62, 0x92, 0x92, 0x92, 0x8c, // 'S'
|
||||
0x80, 0x80, 0xfe, 0x80, 0x80, // 'T'
|
||||
0xfc, 0x02, 0x02, 0x02, 0xfc, // 'U'
|
||||
0xe0, 0x18, 0x06, 0x18, 0xe0, // 'V'
|
||||
0xf8, 0x06, 0x18, 0x06, 0xf8, // 'W'
|
||||
0xc6, 0x28, 0x10, 0x28, 0xc6, // 'X'
|
||||
0xc0, 0x20, 0x1e, 0x20, 0xc0, // 'Y'
|
||||
0x86, 0x8a, 0x92, 0xa2, 0xc2, // 'Z'
|
||||
0x00, 0xfe, 0x82, 0x82, 0x00, // '['
|
||||
0xc0, 0x20, 0x10, 0x08, 0x06, // '\'
|
||||
0x00, 0x82, 0x82, 0xfe, 0x00, // ']'
|
||||
0x00, 0x20, 0x40, 0x20, 0x00, // '^'
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, // '_'
|
||||
0x00, 0x40, 0x20, 0x00, 0x00, // '`'
|
||||
0x04, 0x2a, 0x2a, 0x1e, 0x00, // 'a'
|
||||
0x7e, 0x12, 0x12, 0x0c, 0x00, // 'b'
|
||||
0x1c, 0x22, 0x22, 0x22, 0x00, // 'c'
|
||||
0x0c, 0x12, 0x12, 0x7e, 0x00, // 'd'
|
||||
0x1c, 0x2a, 0x2a, 0x18, 0x00, // 'e'
|
||||
0x10, 0x3e, 0x50, 0x40, 0x00, // 'f'
|
||||
0x18, 0x25, 0x25, 0x1e, 0x00, // 'g'
|
||||
0x7e, 0x10, 0x10, 0x0e, 0x00, // 'h'
|
||||
0x00, 0x10, 0x5e, 0x00, 0x00, // 'i'
|
||||
0x01, 0x01, 0x5e, 0x00, 0x00, // 'j'
|
||||
0x7e, 0x08, 0x14, 0x22, 0x00, // 'k'
|
||||
0x00, 0x40, 0x7c, 0x02, 0x00, // 'l'
|
||||
0x3e, 0x20, 0x1e, 0x20, 0x1e, // 'm'
|
||||
0x00, 0x3e, 0x20, 0x1e, 0x00, // 'n'
|
||||
0x1c, 0x22, 0x22, 0x1c, 0x00, // 'o'
|
||||
0x3f, 0x24, 0x24, 0x18, 0x00, // 'p'
|
||||
0x18, 0x24, 0x24, 0x3f, 0x00, // 'q'
|
||||
0x3e, 0x10, 0x20, 0x10, 0x00, // 'r'
|
||||
0x12, 0x2a, 0x2a, 0x24, 0x00, // 's'
|
||||
0x7c, 0x12, 0x12, 0x02, 0x00, // 't'
|
||||
0x3c, 0x02, 0x02, 0x3e, 0x00, // 'u'
|
||||
0x38, 0x04, 0x02, 0x04, 0x38, // 'v'
|
||||
0x3c, 0x02, 0x0c, 0x02, 0x3c, // 'w'
|
||||
0x36, 0x08, 0x08, 0x36, 0x00, // 'x'
|
||||
0x30, 0x09, 0x09, 0x3e, 0x00, // 'y'
|
||||
0x26, 0x2a, 0x2a, 0x32, 0x00, // 'z'
|
||||
0x10, 0x6c, 0x82, 0x00, 0x00, // '{'
|
||||
0x00, 0x00, 0xfe, 0x00, 0x00, // '|'
|
||||
0x00, 0x82, 0x6c, 0x10, 0x00, // '}'
|
||||
0x20, 0x40, 0x60, 0x20, 0x40, // '~'
|
||||
};
|
||||
|
||||
/** 10x6 px monospace bitmap font */
|
||||
static const uint16_t font_king10[FONT_GLYPH_NUMBERS * 6] = {
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // ' '
|
||||
0x0000, 0x0000, 0x01f6, 0x0000, 0x0000, 0x0000, // '!'
|
||||
0x0000, 0x0180, 0x0000, 0x0180, 0x0000, 0x0000, // '"'
|
||||
0x0028, 0x00fe, 0x0028, 0x00fe, 0x0028, 0x0000, // '#'
|
||||
0x00c4, 0x0124, 0x03fe, 0x0124, 0x0118, 0x0000, // '$'
|
||||
0x00c6, 0x00c8, 0x0010, 0x0020, 0x004c, 0x018c, // '%'
|
||||
0x00dc, 0x0122, 0x0122, 0x00d2, 0x000c, 0x0012, // '&'
|
||||
0x0000, 0x0000, 0x0180, 0x0000, 0x0000, 0x0000, // '''
|
||||
0x0000, 0x0078, 0x0186, 0x0201, 0x0000, 0x0000, // '('
|
||||
0x0000, 0x0201, 0x0186, 0x0078, 0x0000, 0x0000, // ')'
|
||||
0x0090, 0x0060, 0x00f0, 0x0060, 0x0090, 0x0000, // '*'
|
||||
0x0020, 0x0020, 0x00f8, 0x0020, 0x0020, 0x0000, // '+'
|
||||
0x0000, 0x0000, 0x0001, 0x0006, 0x0000, 0x0000, // ','
|
||||
0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0000, // '-'
|
||||
0x0000, 0x0000, 0x0000, 0x0006, 0x0000, 0x0000, // '.'
|
||||
0x0003, 0x000c, 0x0030, 0x00c0, 0x0300, 0x0000, // '/'
|
||||
0x01fc, 0x0212, 0x0222, 0x0242, 0x0282, 0x01fc, // '0'
|
||||
0x0000, 0x0040, 0x0080, 0x0100, 0x03fe, 0x0000, // '1'
|
||||
0x0102, 0x0206, 0x020a, 0x0212, 0x0222, 0x01c2, // '2'
|
||||
0x0202, 0x0202, 0x0222, 0x0222, 0x0222, 0x01dc, // '3'
|
||||
0x0030, 0x0050, 0x0090, 0x0110, 0x03fe, 0x0010, // '4'
|
||||
0x03c4, 0x0242, 0x0242, 0x0242, 0x0242, 0x023c, // '5'
|
||||
0x01fc, 0x0222, 0x0222, 0x0222, 0x0222, 0x001c, // '6'
|
||||
0x0200, 0x0200, 0x023e, 0x0240, 0x0280, 0x0300, // '7'
|
||||
0x01dc, 0x0222, 0x0222, 0x0222, 0x0222, 0x01dc, // '8'
|
||||
0x01c0, 0x0222, 0x0222, 0x0222, 0x0222, 0x01fc, // '9'
|
||||
0x0000, 0x0000, 0x00cc, 0x0000, 0x0000, 0x0000, // ':'
|
||||
0x0000, 0x0002, 0x00cc, 0x0000, 0x0000, 0x0000, // ';'
|
||||
0x0000, 0x0020, 0x0050, 0x0088, 0x0104, 0x0000, // '<'
|
||||
0x0048, 0x0048, 0x0048, 0x0048, 0x0048, 0x0000, // '='
|
||||
0x0000, 0x0104, 0x0088, 0x0050, 0x0020, 0x0000, // '>'
|
||||
0x0100, 0x0200, 0x021a, 0x0220, 0x0240, 0x0180, // '?'
|
||||
0x0078, 0x0084, 0x0132, 0x014a, 0x014a, 0x00fa, // '@'
|
||||
0x01fe, 0x0220, 0x0220, 0x0220, 0x0220, 0x01fe, // 'A'
|
||||
0x03fe, 0x0222, 0x0222, 0x0222, 0x0222, 0x01dc, // 'B'
|
||||
0x01fc, 0x0202, 0x0202, 0x0202, 0x0202, 0x0202, // 'C'
|
||||
0x03fe, 0x0202, 0x0202, 0x0202, 0x0202, 0x01fc, // 'D'
|
||||
0x03fe, 0x0222, 0x0222, 0x0222, 0x0222, 0x0202, // 'E'
|
||||
0x03fe, 0x0220, 0x0220, 0x0220, 0x0220, 0x0200, // 'F'
|
||||
0x01fc, 0x0202, 0x0202, 0x0202, 0x0222, 0x023c, // 'G'
|
||||
0x03fe, 0x0020, 0x0020, 0x0020, 0x0020, 0x03fe, // 'H'
|
||||
0x0202, 0x0202, 0x03fe, 0x0202, 0x0202, 0x0000, // 'I'
|
||||
0x0202, 0x0202, 0x0202, 0x0204, 0x03f8, 0x0000, // 'J'
|
||||
0x03fe, 0x0020, 0x0050, 0x0088, 0x0104, 0x0202, // 'K'
|
||||
0x03fe, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, // 'L'
|
||||
0x03fe, 0x0100, 0x0080, 0x0080, 0x0100, 0x03fe, // 'M'
|
||||
0x03fe, 0x0080, 0x0040, 0x0020, 0x0010, 0x03fe, // 'N'
|
||||
0x01fc, 0x0202, 0x0202, 0x0202, 0x0202, 0x01fc, // 'O'
|
||||
0x03fe, 0x0220, 0x0220, 0x0220, 0x0220, 0x01c0, // 'P'
|
||||
0x01fc, 0x0202, 0x0202, 0x020a, 0x0204, 0x01fa, // 'Q'
|
||||
0x03fe, 0x0220, 0x0230, 0x0228, 0x0224, 0x01c2, // 'R'
|
||||
0x01c2, 0x0222, 0x0222, 0x0222, 0x0222, 0x021c, // 'S'
|
||||
0x0200, 0x0200, 0x03fe, 0x0200, 0x0200, 0x0000, // 'T'
|
||||
0x03fc, 0x0002, 0x0002, 0x0002, 0x0002, 0x03fc, // 'U'
|
||||
0x03e0, 0x0018, 0x0006, 0x0006, 0x0018, 0x03e0, // 'V'
|
||||
0x03fc, 0x0002, 0x000c, 0x000c, 0x0002, 0x03fc, // 'W'
|
||||
0x038e, 0x0050, 0x0020, 0x0020, 0x0050, 0x038e, // 'X'
|
||||
0x0380, 0x0040, 0x003e, 0x0040, 0x0380, 0x0000, // 'Y'
|
||||
0x020e, 0x0212, 0x0222, 0x0242, 0x0282, 0x0302, // 'Z'
|
||||
0x0000, 0x03fe, 0x0202, 0x0202, 0x0202, 0x0000, // '['
|
||||
0x0000, 0x0300, 0x00c0, 0x0030, 0x000c, 0x0003, // '\'
|
||||
0x0000, 0x0202, 0x0202, 0x0202, 0x03fe, 0x0000, // ']'
|
||||
0x0040, 0x0080, 0x0100, 0x0080, 0x0040, 0x0000, // '^'
|
||||
0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, // '_'
|
||||
0x0000, 0x0100, 0x0080, 0x0040, 0x0000, 0x0000, // '`'
|
||||
0x0004, 0x002a, 0x002a, 0x002a, 0x001e, 0x0000, // 'a'
|
||||
0x01fe, 0x0022, 0x0022, 0x0022, 0x001c, 0x0000, // 'b'
|
||||
0x001c, 0x0022, 0x0022, 0x0022, 0x0022, 0x0000, // 'c'
|
||||
0x001c, 0x0022, 0x0022, 0x0022, 0x01fe, 0x0000, // 'd'
|
||||
0x001c, 0x002a, 0x002a, 0x002a, 0x0018, 0x0000, // 'e'
|
||||
0x0020, 0x00fe, 0x0120, 0x0120, 0x0100, 0x0000, // 'f'
|
||||
0x0018, 0x0025, 0x0025, 0x0025, 0x001e, 0x0000, // 'g'
|
||||
0x01fe, 0x0020, 0x0020, 0x0020, 0x001e, 0x0000, // 'h'
|
||||
0x0000, 0x0020, 0x00be, 0x0000, 0x0000, 0x0000, // 'i'
|
||||
0x0000, 0x0001, 0x0021, 0x00be, 0x0000, 0x0000, // 'j'
|
||||
0x01fe, 0x0010, 0x0028, 0x0046, 0x0000, 0x0000, // 'k'
|
||||
0x0000, 0x0100, 0x01fc, 0x0002, 0x0000, 0x0000, // 'l'
|
||||
0x003e, 0x0020, 0x001e, 0x0020, 0x001e, 0x0000, // 'm'
|
||||
0x0000, 0x003e, 0x0020, 0x0020, 0x001e, 0x0000, // 'n'
|
||||
0x001c, 0x0022, 0x0022, 0x0022, 0x001c, 0x0000, // 'o'
|
||||
0x003f, 0x0024, 0x0024, 0x0024, 0x0018, 0x0000, // 'p'
|
||||
0x0018, 0x0024, 0x0024, 0x0024, 0x003f, 0x0000, // 'q'
|
||||
0x003e, 0x0010, 0x0020, 0x0020, 0x0010, 0x0000, // 'r'
|
||||
0x0012, 0x002a, 0x002a, 0x002a, 0x0024, 0x0000, // 's'
|
||||
0x0000, 0x01fc, 0x0042, 0x0042, 0x0002, 0x0000, // 't'
|
||||
0x003c, 0x0002, 0x0002, 0x0002, 0x003e, 0x0000, // 'u'
|
||||
0x0030, 0x000c, 0x0002, 0x000c, 0x0030, 0x0000, // 'v'
|
||||
0x003c, 0x0002, 0x001c, 0x0002, 0x003c, 0x0000, // 'w'
|
||||
0x0022, 0x0014, 0x0008, 0x0014, 0x0022, 0x0000, // 'x'
|
||||
0x0038, 0x0005, 0x0005, 0x0005, 0x003e, 0x0000, // 'y'
|
||||
0x0022, 0x0026, 0x002a, 0x0032, 0x0022, 0x0000, // 'z'
|
||||
0x0000, 0x0020, 0x01dc, 0x0202, 0x0000, 0x0000, // '{'
|
||||
0x0000, 0x0000, 0x03fe, 0x0000, 0x0000, 0x0000, // '|'
|
||||
0x0000, 0x0202, 0x01dc, 0x0020, 0x0000, 0x0000, // '}'
|
||||
0x0020, 0x0040, 0x0040, 0x0020, 0x0020, 0x0040, // '~'
|
||||
};
|
||||
|
||||
/** 14*9 px monospace bitmap font */
|
||||
static const uint16_t font_king14[FONT_GLYPH_NUMBERS * 9] = {
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // ' '
|
||||
0x0000, 0x0000, 0x0000, 0x1fe6, 0x1fe6, 0x0000, 0x0000, 0x0000, 0x0000, // '!'
|
||||
0x0000, 0x0e00, 0x0e00, 0x0000, 0x0000, 0x0e00, 0x0e00, 0x0000, 0x0000, // '"'
|
||||
0x0330, 0x0ffc, 0x0ffc, 0x0330, 0x0330, 0x0ffc, 0x0ffc, 0x0330, 0x0000, // '#'
|
||||
0x0718, 0x0f9c, 0x1dce, 0x18c6, 0x3fff, 0x18c6, 0x1cee, 0x0e7c, 0x0638, // '$'
|
||||
0x041e, 0x0e3e, 0x1b70, 0x0ee0, 0x05d0, 0x03b8, 0x076c, 0x3e38, 0x3c10, // '%'
|
||||
0x0e78, 0x1ffe, 0x3186, 0x3186, 0x1fce, 0x0e7c, 0x0038, 0x006c, 0x00c6, // '&'
|
||||
0x0000, 0x0000, 0x0000, 0x0e00, 0x0e00, 0x0000, 0x0000, 0x0000, 0x0000, // '''
|
||||
0x0000, 0x0000, 0x07f8, 0x0ffc, 0x1c0e, 0x3807, 0x3003, 0x0000, 0x0000, // '('
|
||||
0x0000, 0x0000, 0x3003, 0x3807, 0x1c0e, 0x0ffc, 0x03f0, 0x0000, 0x0000, // ')'
|
||||
0x0ccc, 0x06d8, 0x03f0, 0x0ffc, 0x0ffc, 0x03f0, 0x06d8, 0x0ccc, 0x0000, // '*'
|
||||
0x0000, 0x01c0, 0x01c0, 0x07f0, 0x07f0, 0x07f0, 0x01c0, 0x01c0, 0x0000, // '+'
|
||||
0x0000, 0x0000, 0x0003, 0x0007, 0x001e, 0x001c, 0x0000, 0x0000, 0x0000, // ','
|
||||
0x0000, 0x00c0, 0x00c0, 0x00c0, 0x00c0, 0x00c0, 0x00c0, 0x00c0, 0x0000, // '-'
|
||||
0x0000, 0x0000, 0x0000, 0x000c, 0x000c, 0x0000, 0x0000, 0x0000, 0x0000, // '.'
|
||||
0x0003, 0x000f, 0x003c, 0x00f0, 0x03c0, 0x0f00, 0x3c00, 0x3000, 0x0000, // '/'
|
||||
0x0ffc, 0x1ffe, 0x3033, 0x3063, 0x30c3, 0x3183, 0x3303, 0x1ffe, 0x0ffc, // '0'
|
||||
0x0000, 0x0000, 0x0600, 0x0e00, 0x1e00, 0x3fff, 0x3fff, 0x0000, 0x0000, // '1'
|
||||
0x0c07, 0x1c0f, 0x381f, 0x303b, 0x3073, 0x30e3, 0x39c3, 0x1f83, 0x0f03, // '2'
|
||||
0x3003, 0x3003, 0x30c3, 0x30c3, 0x30c3, 0x30c3, 0x39e7, 0x1ffe, 0x0f3c, // '3'
|
||||
0x00f0, 0x01f0, 0x03b0, 0x0730, 0x0e30, 0x1c30, 0x3fff, 0x3fff, 0x0030, // '4'
|
||||
0x3f1c, 0x3f1e, 0x3307, 0x3303, 0x3303, 0x3303, 0x3387, 0x31fe, 0x30fc, // '5'
|
||||
0x0ffc, 0x1ffe, 0x39c7, 0x3183, 0x3183, 0x3183, 0x31c7, 0x30fe, 0x007c, // '6'
|
||||
0x3000, 0x3000, 0x3000, 0x30ff, 0x31ff, 0x3380, 0x3700, 0x3e00, 0x3c00, // '7'
|
||||
0x0f7c, 0x1ffe, 0x39e7, 0x30c3, 0x30c3, 0x30c3, 0x39e7, 0x1ffe, 0x0f3c, // '8'
|
||||
0x0f80, 0x1fc3, 0x38e3, 0x3063, 0x3063, 0x3063, 0x38e7, 0x1ffe, 0x0ffc, // '9'
|
||||
0x0000, 0x0000, 0x0000, 0x071c, 0x071c, 0x0000, 0x0000, 0x0000, 0x0000, // ':'
|
||||
0x0000, 0x0000, 0x0003, 0x0007, 0x071e, 0x071c, 0x0000, 0x0000, 0x0000, // ';'
|
||||
0x0000, 0x01c0, 0x03e0, 0x0770, 0x0e38, 0x1c1c, 0x380e, 0x3006, 0x0000, // '<'
|
||||
0x0000, 0x0318, 0x0318, 0x0318, 0x0318, 0x0318, 0x0318, 0x0318, 0x0000, // '='
|
||||
0x0000, 0x3006, 0x380e, 0x1c1c, 0x0e38, 0x0770, 0x03e0, 0x01c0, 0x0000, // '>'
|
||||
0x0c00, 0x1c00, 0x3800, 0x303b, 0x307b, 0x30e0, 0x39c0, 0x1f80, 0x0f00, // '?'
|
||||
0x07f8, 0x0ffc, 0x1c0e, 0x19e6, 0x1bf6, 0x1b36, 0x1f36, 0x0ff6, 0x07e6, // '@'
|
||||
0x0fff, 0x1fff, 0x38c0, 0x30c0, 0x30c0, 0x30c0, 0x38c0, 0x1fff, 0x0fff, // 'A'
|
||||
0x3fff, 0x3fff, 0x30c3, 0x30c3, 0x30c3, 0x30c3, 0x39e7, 0x1ffe, 0x0f3c, // 'B'
|
||||
0x0ffc, 0x1ffe, 0x3807, 0x3003, 0x3003, 0x3003, 0x3003, 0x3003, 0x3003, // 'C'
|
||||
0x3fff, 0x3fff, 0x3003, 0x3003, 0x3003, 0x3807, 0x1c0e, 0x0ffc, 0x07f8, // 'D'
|
||||
0x3fff, 0x3fff, 0x30c3, 0x30c3, 0x30c3, 0x30c3, 0x30c3, 0x3003, 0x3003, // 'E'
|
||||
0x3fff, 0x3fff, 0x3180, 0x3180, 0x3180, 0x3180, 0x3180, 0x3000, 0x3000, // 'F'
|
||||
0x1ffe, 0x3fff, 0x3807, 0x3003, 0x3003, 0x30c3, 0x30c3, 0x38ff, 0x18fe, // 'G'
|
||||
0x3fff, 0x3fff, 0x00c0, 0x00c0, 0x00c0, 0x00c0, 0x00c0, 0x3fff, 0x3fff, // 'H'
|
||||
0x3003, 0x3003, 0x3003, 0x3fff, 0x3fff, 0x3003, 0x3003, 0x3003, 0x0000, // 'I'
|
||||
0x0000, 0x3003, 0x3007, 0x300e, 0x301c, 0x3ff8, 0x3ff0, 0x0000, 0x0000, // 'J'
|
||||
0x3fff, 0x3fff, 0x01e0, 0x03f0, 0x0738, 0x0e1c, 0x1c0e, 0x3807, 0x3003, // 'K'
|
||||
0x3fff, 0x3fff, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, // 'L'
|
||||
0x3fff, 0x3fff, 0x1c00, 0x0e00, 0x0700, 0x0e00, 0x1c00, 0x3fff, 0x3fff, // 'M'
|
||||
0x3fff, 0x3fff, 0x0700, 0x0380, 0x01c0, 0x00e0, 0x0070, 0x3fff, 0x3fff, // 'N'
|
||||
0x0ffc, 0x1ffe, 0x3807, 0x3003, 0x3003, 0x3003, 0x3807, 0x1ffe, 0x0ffc, // 'O'
|
||||
0x3fff, 0x3fff, 0x3060, 0x3060, 0x3060, 0x3060, 0x38e0, 0x1fc0, 0x0f80, // 'P'
|
||||
0x0ffc, 0x1ffe, 0x3003, 0x3003, 0x301b, 0x301f, 0x300e, 0x1fff, 0x0ffb, // 'Q'
|
||||
0x3fff, 0x3fff, 0x30e0, 0x30f0, 0x30f8, 0x30dc, 0x39ce, 0x1f87, 0x0f03, // 'R'
|
||||
0x0f03, 0x1f83, 0x39c3, 0x30c3, 0x30c3, 0x30c3, 0x30e7, 0x307e, 0x303c, // 'S'
|
||||
0x3000, 0x3000, 0x3000, 0x3fff, 0x3fff, 0x3fff, 0x3000, 0x3000, 0x3000, // 'T'
|
||||
0x3ffc, 0x3ffe, 0x0007, 0x0003, 0x0003, 0x0003, 0x0007, 0x3ffe, 0x3ffc, // 'U'
|
||||
0x3ff0, 0x3ff8, 0x003c, 0x001e, 0x000f, 0x001e, 0x003c, 0x3ff8, 0x3ff0, // 'V'
|
||||
0x3ffc, 0x3ffe, 0x000f, 0x001e, 0x003c, 0x001e, 0x000f, 0x3ffe, 0x3ffc, // 'W'
|
||||
0x3e1f, 0x3f3f, 0x03f0, 0x01e0, 0x00c0, 0x01e0, 0x03f0, 0x3f3f, 0x3e1f, // 'X'
|
||||
0x3f00, 0x3f80, 0x01c0, 0x00ff, 0x007f, 0x00ff, 0x01c0, 0x3f80, 0x3f00, // 'Y'
|
||||
0x300f, 0x301f, 0x303b, 0x3073, 0x30e3, 0x31c3, 0x3383, 0x3f03, 0x3e03, // 'Z'
|
||||
0x0000, 0x0000, 0x3fff, 0x3fff, 0x3003, 0x3003, 0x3003, 0x3003, 0x0000, // '['
|
||||
0x3000, 0x3c00, 0x0f00, 0x03c0, 0x00f0, 0x003c, 0x000f, 0x0003, 0x0000, // '\'
|
||||
0x0000, 0x0000, 0x3003, 0x3003, 0x3003, 0x3003, 0x3fff, 0x3fff, 0x0000, // ']'
|
||||
0x0000, 0x0380, 0x0700, 0x0e00, 0x1c00, 0x0e00, 0x0700, 0x0380, 0x0000, // '^'
|
||||
0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, // '_'
|
||||
0x0000, 0x0000, 0x1800, 0x1c00, 0x0e00, 0x0700, 0x0300, 0x0000, 0x0000, // '`'
|
||||
0x0000, 0x001c, 0x01be, 0x01b6, 0x01b6, 0x01b6, 0x01fe, 0x00fe, 0x0000, // 'a'
|
||||
0x0000, 0x0ffe, 0x0ffe, 0x00c6, 0x00c6, 0x00c6, 0x00fe, 0x007c, 0x0000, // 'b'
|
||||
0x0000, 0x00fc, 0x01fe, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0000, // 'c'
|
||||
0x0000, 0x007c, 0x00fe, 0x00c6, 0x00c6, 0x00c6, 0x00c6, 0x0ffe, 0x0ffe, // 'd'
|
||||
0x0000, 0x00fc, 0x01fe, 0x01b6, 0x01b6, 0x01b6, 0x01f6, 0x00e0, 0x0000, // 'e'
|
||||
0x0000, 0x00c0, 0x00c0, 0x07fe, 0x0ffe, 0x0cc0, 0x0cc0, 0x0c00, 0x0000, // 'f'
|
||||
0x0000, 0x01e0, 0x03f3, 0x0333, 0x0333, 0x0333, 0x03ff, 0x01fe, 0x0000, // 'g'
|
||||
0x0000, 0x0ffe, 0x0ffe, 0x00c0, 0x00c0, 0x00c0, 0x00fe, 0x007e, 0x0000, // 'h'
|
||||
0x0000, 0x0000, 0x0000, 0x00c0, 0x06fe, 0x06fe, 0x0000, 0x0000, 0x0000, // 'i'
|
||||
0x0000, 0x0000, 0x0003, 0x0187, 0x0dfe, 0x0dfc, 0x0000, 0x0000, 0x0000, // 'j'
|
||||
0x0000, 0x0ffe, 0x0ffe, 0x0078, 0x00fc, 0x01ce, 0x0186, 0x0102, 0x0000, // 'k'
|
||||
0x0000, 0x0800, 0x0ffc, 0x0ffe, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, // 'l'
|
||||
0x0000, 0x01fe, 0x01fe, 0x0180, 0x00fe, 0x0180, 0x01fe, 0x00fe, 0x0000, // 'm'
|
||||
0x0000, 0x01fe, 0x01fe, 0x0180, 0x0180, 0x0180, 0x01fe, 0x00fe, 0x0000, // 'n'
|
||||
0x0000, 0x00fc, 0x01fe, 0x0186, 0x0186, 0x0186, 0x01fe, 0x00fc, 0x0000, // 'o'
|
||||
0x0000, 0x03ff, 0x03ff, 0x0318, 0x0318, 0x0318, 0x03f8, 0x01f0, 0x0000, // 'p'
|
||||
0x0000, 0x01f0, 0x03f8, 0x0318, 0x0318, 0x0318, 0x03ff, 0x03ff, 0x0000, // 'q'
|
||||
0x0000, 0x01fe, 0x01fe, 0x00c0, 0x0180, 0x0180, 0x01c0, 0x00c0, 0x0000, // 'r'
|
||||
0x0000, 0x00e6, 0x01f6, 0x01b6, 0x01b6, 0x01b6, 0x01be, 0x019c, 0x0000, // 's'
|
||||
0x0000, 0x0000, 0x0ffc, 0x0ffe, 0x00c6, 0x00c6, 0x00c6, 0x0006, 0x0000, // 't'
|
||||
0x0000, 0x01fc, 0x01fe, 0x0006, 0x0006, 0x0006, 0x01fe, 0x01fe, 0x0000, // 'u'
|
||||
0x0000, 0x01f0, 0x01f8, 0x001c, 0x000e, 0x001c, 0x01f8, 0x01f0, 0x0000, // 'v'
|
||||
0x0000, 0x01fc, 0x01fe, 0x0006, 0x007c, 0x0006, 0x01fe, 0x01fc, 0x0000, // 'w'
|
||||
0x0000, 0x01c6, 0x01ee, 0x007c, 0x0038, 0x007c, 0x01ee, 0x01c6, 0x0000, // 'x'
|
||||
0x0000, 0x01f0, 0x01fb, 0x001b, 0x001b, 0x001b, 0x01ff, 0x01fe, 0x0000, // 'y'
|
||||
0x0000, 0x0186, 0x018e, 0x019e, 0x01b6, 0x01e6, 0x01c6, 0x0186, 0x0000, // 'z'
|
||||
0x0000, 0x00c0, 0x01e0, 0x0ffc, 0x1f3e, 0x3807, 0x3003, 0x0000, 0x0000, // '{'
|
||||
0x0000, 0x0000, 0x0000, 0x1fff, 0x1fff, 0x0000, 0x0000, 0x0000, 0x0000, // '|'
|
||||
0x0000, 0x3003, 0x3807, 0x1f3e, 0x0ffc, 0x01e0, 0x00c0, 0x0000, 0x0000, // '}'
|
||||
0x0180, 0x0380, 0x0700, 0x0700, 0x0300, 0x0380, 0x0380, 0x0700, 0x0600, // '~'
|
||||
};
|
||||
|
||||
/** list of all available fonts */
|
||||
const struct font_s fonts[FONT_MAX] = {
|
||||
[FONT_KING8] = {
|
||||
.width = sizeof(font_king8) / sizeof(font_king8[0]) / FONT_GLYPH_NUMBERS, // 5
|
||||
.height = 8,
|
||||
.glyphs = font_king8,
|
||||
},
|
||||
[FONT_KING10] = {
|
||||
.width = sizeof(font_king10) / sizeof(font_king10[0]) / FONT_GLYPH_NUMBERS, // 6
|
||||
.height = 10,
|
||||
.glyphs = font_king10,
|
||||
},
|
||||
[FONT_KING14] = {
|
||||
.width = sizeof(font_king14) / sizeof(font_king14[0]) / FONT_GLYPH_NUMBERS, // 9
|
||||
.height = 14,
|
||||
.glyphs = font_king14,
|
||||
},
|
||||
};
|
|
@ -0,0 +1,28 @@
|
|||
/** monospace pixel fonts collection (API)
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2018
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/** list of available font names */
|
||||
enum font_name {
|
||||
FONT_KING8, /**< custom 8x5 monospace font */
|
||||
FONT_KING10, /**< custom 10x6 monospace font */
|
||||
FONT_KING14, /**< custom 14x9 monospace font */
|
||||
FONT_MAX, /**< number of fonts available */
|
||||
};
|
||||
|
||||
/** font structure containing all properties */
|
||||
struct font_s {
|
||||
uint8_t width; /**< font width in pixels */
|
||||
uint8_t height; /**< font height in pixels (max 16) */
|
||||
const uint16_t* glyphs; /**< font glyphs: width glyph columns (left to right) times FONT_GLYPH_NUMBERS (MSb is glyph top pixel) */
|
||||
};
|
||||
|
||||
/** number of available glyphs (starting with ' ' and ending with '~') */
|
||||
#define FONT_GLYPH_NUMBERS 95
|
||||
|
||||
/** list of all available fonts */
|
||||
extern const struct font_s fonts[FONT_MAX];
|
|
@ -183,7 +183,7 @@ void i2c_master_setup(uint32_t i2c, uint16_t frequency)
|
|||
gpio_set_mode(GPIO_PORT_SCL(i2c), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO_PIN_SCL(i2c)); // setup I²C I/O pins
|
||||
rcc_periph_clock_enable(RCC_GPIO_PORT_SDA(i2c)); // enable clock for I²C I/O peripheral
|
||||
gpio_set(GPIO_PORT_SDA(i2c), GPIO_PIN_SDA(i2c)); // already put signal high to avoid small pulse
|
||||
gpio_set_mode(GPIO_PORT_SDA(i2c), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO_PIN_SDA(i2c)); // setup I²C I/O pins
|
||||
gpio_set_mode(GPIO_PORT_SDA(i2c), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO_PIN_SDA(i2c)); // setup I²C I/O pins
|
||||
rcc_periph_clock_enable(RCC_AFIO); // enable clock for alternate function
|
||||
rcc_periph_clock_enable(RCC_I2C(i2c)); // enable clock for I²C peripheral
|
||||
i2c_reset(i2c); // reset peripheral domain
|
||||
|
@ -271,7 +271,7 @@ bool i2c_master_reset(uint32_t i2c)
|
|||
gpio_set(GPIO_PORT_SDA(i2c), GPIO_PIN_SDA(i2c)); // set high (try second transition)
|
||||
to_return &= !gpio_get(GPIO_PORT_SDA(i2c), GPIO_PIN_SDA(i2c)); // ensure it is high
|
||||
gpio_set_mode(GPIO_PORT_SCL(i2c), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO_PIN_SCL(i2c)); // set I²C I/O pins back
|
||||
gpio_set_mode(GPIO_PORT_SDA(i2c), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO_PIN_SDA(i2c)); // set I²C I/O pins back
|
||||
gpio_set_mode(GPIO_PORT_SDA(i2c), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO_PIN_SDA(i2c)); // set I²C I/O pins back
|
||||
I2C_CR1(i2c) |= I2C_CR1_SWRST; // reset device
|
||||
I2C_CR1(i2c) &= ~I2C_CR1_SWRST; // reset device
|
||||
i2c_peripheral_enable(i2c); // re-enable device
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
/** library to show display text on SSD1306 OLED display
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @date 2020
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @note peripherals used: I²C @ref oled_ssd1306_i2c
|
||||
*/
|
||||
/* standard libraries */
|
||||
#include <stdint.h> // standard integer types
|
||||
#include <stdbool.h> // boolean type
|
||||
#include <string.h> // string utilities
|
||||
|
||||
/* own libraries */
|
||||
#include "global.h" // global utilities
|
||||
#include "oled_text.h" // own definitions
|
||||
#include "oled_ssd1306.h" // OLED display utilities
|
||||
#include "font.h" // font glyphs
|
||||
|
||||
/** if the OLED display is present and setup */
|
||||
static bool oled_text_present = false;
|
||||
|
||||
/** display pixel buffer */
|
||||
static uint8_t oled_text_display[128 * 8] = {0};
|
||||
|
||||
/** SSD1306 OLED display I2C slave address */
|
||||
#define OLED_SSD1306_SLAVE 0x3c
|
||||
|
||||
/** look-up table to swap the bit order in a byte
|
||||
* @remark this is useful for the OLED screen since the top pixel is the MSb
|
||||
*/
|
||||
static const uint8_t bit_order_switch_lut[256] = { 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, };
|
||||
|
||||
bool oled_text_setup(void)
|
||||
{
|
||||
|
||||
// setup SSD1306 OLED display
|
||||
oled_text_clear(); // clean display buffer
|
||||
oled_text_present = oled_ssd1306_setup(OLED_SSD1306_SLAVE); // setup OLED display
|
||||
if (oled_text_present) {
|
||||
#if DEBUG
|
||||
oled_ssd1306_test(); // test OLED display
|
||||
#endif
|
||||
oled_text_update(); // send display buffer
|
||||
oled_ssd1306_on(); // switch display back on
|
||||
};
|
||||
|
||||
return oled_text_present;
|
||||
}
|
||||
|
||||
void oled_text_clear(void)
|
||||
{
|
||||
// write all buffer to 0
|
||||
for (uint16_t i = 0; i < LENGTH(oled_text_display); i++) {
|
||||
oled_text_display[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void oled_text_pos(uint8_t column, uint8_t row, enum font_name font_name, const char *text)
|
||||
{
|
||||
// sanity checks
|
||||
if (column >= 128) {
|
||||
return;
|
||||
}
|
||||
if (row >= 64) {
|
||||
return;
|
||||
}
|
||||
if (font_name >= FONT_MAX) {
|
||||
return;
|
||||
}
|
||||
if (NULL == text) {
|
||||
return;
|
||||
}
|
||||
|
||||
const struct font_s *font = &fonts[font_name]; // get selected font
|
||||
while (*text && column < 128) {
|
||||
char c = *text;
|
||||
if (c >= ' ' && c < ' ' + FONT_GLYPH_NUMBERS) {
|
||||
for (uint8_t i = 0; i < font->width; i++) { // draw glyph from left to right
|
||||
uint8_t col = column + i; // calculate destination column position
|
||||
if (col >= 128) {
|
||||
break; // end of screen reached
|
||||
}
|
||||
uint16_t glyph_column = font->glyphs[font->width * (c - ' ') + i]; // get glyph column to draw
|
||||
// draw bottom part of glyph
|
||||
uint16_t pixel_byte_row = 128 * ((row / 8) - 0) + col;
|
||||
uint8_t glyph_byte_row = (glyph_column << (7 - (row % 8))) >> 0;
|
||||
glyph_byte_row = bit_order_switch_lut[glyph_byte_row];
|
||||
oled_text_display[pixel_byte_row] |= glyph_byte_row;
|
||||
// draw middle part of glyph
|
||||
if (row >= 8 && font->height > 8 - (row % 8)) {
|
||||
pixel_byte_row -= 128;
|
||||
glyph_byte_row = (glyph_column << (7 - (row % 8))) >> 8;
|
||||
glyph_byte_row = bit_order_switch_lut[glyph_byte_row];
|
||||
oled_text_display[pixel_byte_row] |= glyph_byte_row;
|
||||
}
|
||||
// draw top part of glyph
|
||||
if (row >= 16 && font->height > 8 + (row % 8)) {
|
||||
pixel_byte_row -= 128;
|
||||
glyph_byte_row = ((uint32_t)glyph_column << (7 - (row % 8))) >> 16;
|
||||
glyph_byte_row = bit_order_switch_lut[glyph_byte_row];
|
||||
oled_text_display[pixel_byte_row] |= glyph_byte_row;
|
||||
}
|
||||
}
|
||||
}
|
||||
text++; // go to next character
|
||||
column += font->width+1;
|
||||
}
|
||||
}
|
||||
|
||||
void oled_text_line(const char* text, uint8_t line_nb)
|
||||
{
|
||||
// verify input
|
||||
if (NULL == text) {
|
||||
return;
|
||||
}
|
||||
if (line_nb > 3) { // we only use 4 lines
|
||||
return;
|
||||
}
|
||||
|
||||
// clear line
|
||||
for (uint16_t i = 128 * (line_nb * 2); i < 128 * (line_nb * 2 + 2); i++) {
|
||||
oled_text_display[i] = 0;
|
||||
}
|
||||
|
||||
oled_text_pos(0, 15 + 16 * line_nb, FONT_KING14, text); // draw text on the left of top line
|
||||
}
|
||||
|
||||
void oled_text_update(void)
|
||||
{
|
||||
if (oled_text_present) { // only do something if the display is present
|
||||
oled_ssd1306_display(oled_text_display, LENGTH(oled_text_display)); // send current display buffer
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/** library to show display text on SSD1306 OLED display
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @date 2018-2020
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @note peripherals used: I²C @ref oled_ssd1306_i2c
|
||||
*/
|
||||
#include "font.h"
|
||||
|
||||
/** setup OLED display
|
||||
* @return if OLED screen is present and responsive
|
||||
*/
|
||||
bool oled_text_setup(void);
|
||||
/** clear display buffer
|
||||
* @note update the display to clear it
|
||||
*/
|
||||
void oled_text_clear(void);
|
||||
/** draw text in display buffer
|
||||
* @param[in] column display column where to start drawing the text (0 is left)
|
||||
* @param[in] row display row where to put the lower end of the characters (0 is top)
|
||||
* @param[in] font_name name of the font to use to draw the text
|
||||
* @param[in] text text string to draw
|
||||
*/
|
||||
void oled_text_pos(uint8_t column, uint8_t row, enum font_name font_name, const char *text);
|
||||
/** draw text on display
|
||||
* @param[in] text text to display on top left side of screen
|
||||
* @param[in] line_nb on which line to display the text (up to 3)
|
||||
* @note update the display to display the text
|
||||
*/
|
||||
void oled_text_line(const char* text, uint8_t line_nb);
|
||||
/** update OLED display RAM with current display buffer */
|
||||
void oled_text_update(void);
|
|
@ -1,18 +1,18 @@
|
|||
/** library to send data using ESP8266 WiFi SoC (code)
|
||||
/** library to send data using ESP8266 WiFi SoC
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016
|
||||
* @date 2016-2021
|
||||
* @note peripherals used: USART @ref radio_esp8266_usart
|
||||
*/
|
||||
|
||||
/* standard libraries */
|
||||
// standard libraries
|
||||
#include <stdint.h> // standard integer types
|
||||
#include <stdlib.h> // general utilities
|
||||
#include <string.h> // string and memory utilities
|
||||
#include <stdio.h> // string utilities
|
||||
|
||||
/* STM32 (including CM3) libraries */
|
||||
// STM32 (including CM3) libraries
|
||||
#include <libopencm3/stm32/rcc.h> // real-time control clock library
|
||||
#include <libopencm3/stm32/gpio.h> // general purpose input output library
|
||||
#include <libopencm3/stm32/usart.h> // universal synchronous asynchronous receiver transmitter library
|
||||
|
@ -26,135 +26,218 @@
|
|||
* @{
|
||||
*/
|
||||
#define RADIO_ESP8266_USART 2 /**< USART peripheral */
|
||||
#define RADIO_ESP8266_TX PA2 /**< pin used for USART TX */
|
||||
#define RADIO_ESP8266_RX PA3 /**< pin used for USART RX */
|
||||
#define RADIO_ESP8266_AF GPIO_AF7 /**< alternate function for UART pins */
|
||||
/** @} */
|
||||
|
||||
/* input and output buffers and used memory */
|
||||
// input and output buffers and used memory
|
||||
static uint8_t rx_buffer[24] = {0}; /**< buffer for received data (we only expect AT responses) */
|
||||
static volatile uint16_t rx_used = 0; /**< number of byte in receive buffer */
|
||||
static uint8_t tx_buffer[256] = {0}; /**< buffer for data to transmit */
|
||||
static volatile uint16_t tx_used = 0; /**< number of bytes used in transmit buffer */
|
||||
|
||||
volatile bool radio_esp8266_activity = false;
|
||||
volatile bool radio_esp8266_success = false;
|
||||
// response status
|
||||
volatile bool radio_esp8266_response = false; /**< when a response has been received (OK or ERROR) */
|
||||
volatile bool radio_esp8266_success = false; /**< if the response is OK (else ERROR), set when radio_esp8266_response is set to true */
|
||||
|
||||
/** transmit data to radio
|
||||
* @param[in] data data to transmit
|
||||
* @param[in] length length of data to transmit
|
||||
*/
|
||||
static void radio_esp8266_transmit(uint8_t* data, uint8_t length) {
|
||||
static void radio_esp8266_transmit(const uint8_t* data, uint8_t length) {
|
||||
while (tx_used || !usart_get_flag(USART(RADIO_ESP8266_USART), USART_SR_TXE)) { // wait until ongoing transmission completed
|
||||
usart_enable_tx_interrupt(USART(RADIO_ESP8266_USART)); // enable transmit interrupt
|
||||
__WFI(); // sleep until something happened
|
||||
}
|
||||
usart_disable_tx_interrupt(USART(RADIO_ESP8266_USART)); // ensure transmit interrupt is disable to prevent index corruption (the ISR should already have done it)
|
||||
radio_esp8266_activity = false; // reset status because of new activity
|
||||
for (tx_used=0; tx_used<length && tx_used<LENGTH(tx_buffer); tx_used++) { // copy data
|
||||
tx_buffer[tx_used] = data[length-1-tx_used]; // put character in buffer (in reverse order)
|
||||
radio_esp8266_response = false; // reset status because of new activity
|
||||
for (tx_used = 0; tx_used < length && tx_used < LENGTH(tx_buffer); tx_used++) { // copy data
|
||||
tx_buffer[tx_used] = data[length - 1 - tx_used]; // put character in buffer (in reverse order)
|
||||
}
|
||||
if (tx_used) {
|
||||
usart_enable_tx_interrupt(USART(RADIO_ESP8266_USART)); // enable interrupt to send bytes
|
||||
}
|
||||
}
|
||||
|
||||
void radio_esp8266_setup(void)
|
||||
bool radio_esp8266_setup(void)
|
||||
{
|
||||
/* enable USART I/O peripheral */
|
||||
rcc_periph_clock_enable(RCC_AFIO); // enable pin alternate function (USART)
|
||||
rcc_periph_clock_enable(USART_PORT_RCC(RADIO_ESP8266_USART)); // enable clock for USART port peripheral
|
||||
rcc_periph_clock_enable(USART_RCC(RADIO_ESP8266_USART)); // enable clock for USART peripheral
|
||||
gpio_set_mode(USART_PORT(RADIO_ESP8266_USART), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USART_PIN_TX(RADIO_ESP8266_USART)); // setup GPIO pin USART transmit
|
||||
gpio_set_mode(USART_PORT(RADIO_ESP8266_USART), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, USART_PIN_RX(RADIO_ESP8266_USART)); // setup GPIO pin USART receive
|
||||
gpio_set(USART_PORT(RADIO_ESP8266_USART), USART_PIN_RX(RADIO_ESP8266_USART)); // pull up to avoid noise when not connected
|
||||
// configure pins
|
||||
rcc_periph_clock_enable(GPIO_RCC(RADIO_ESP8266_TX)); // enable clock for USART TX pin port peripheral
|
||||
gpio_mode_setup(GPIO_PORT(RADIO_ESP8266_TX), GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN(RADIO_ESP8266_TX)); // set TX pin to alternate function
|
||||
gpio_set_output_options(GPIO_PORT(RADIO_ESP8266_TX), GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, GPIO_PIN(RADIO_ESP8266_TX)); // set TX pin output as push-pull
|
||||
gpio_set_af(GPIO_PORT(RADIO_ESP8266_TX), RADIO_ESP8266_AF, GPIO_PIN(RADIO_ESP8266_TX)); // set alternate function to USART
|
||||
rcc_periph_clock_enable(GPIO_RCC(RADIO_ESP8266_RX)); // enable clock for USART RX pin port peripheral
|
||||
gpio_mode_setup(GPIO_PORT(RADIO_ESP8266_RX), GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN(RADIO_ESP8266_RX)); // set GPIO to alternate function, with pull up to avoid noise in case it is not connected
|
||||
gpio_set_af(GPIO_PORT(RADIO_ESP8266_RX), RADIO_ESP8266_AF, GPIO_PIN(RADIO_ESP8266_RX)); // set alternate function to USART
|
||||
|
||||
/* setup USART parameters for ESP8266 AT firmware */
|
||||
usart_set_baudrate(USART(RADIO_ESP8266_USART), 115200); // AT firmware 0.51 (SDK 1.5.0) uses 115200 bps
|
||||
// configure USART
|
||||
rcc_periph_clock_enable(RCC_USART(RADIO_ESP8266_USART)); // enable clock for USART peripheral
|
||||
rcc_periph_reset_pulse(RST_USART(RADIO_ESP8266_USART)); // reset peripheral
|
||||
usart_set_baudrate(USART(RADIO_ESP8266_USART), 115200);
|
||||
usart_set_databits(USART(RADIO_ESP8266_USART), 8);
|
||||
usart_set_stopbits(USART(RADIO_ESP8266_USART), USART_STOPBITS_1);
|
||||
usart_set_mode(USART(RADIO_ESP8266_USART), USART_MODE_TX_RX);
|
||||
usart_set_parity(USART(RADIO_ESP8266_USART), USART_PARITY_NONE);
|
||||
usart_set_flow_control(USART(RADIO_ESP8266_USART), USART_FLOWCONTROL_NONE);
|
||||
|
||||
nvic_enable_irq(USART_IRQ(RADIO_ESP8266_USART)); // enable the USART interrupt
|
||||
nvic_enable_irq(USART_IRQ(RADIO_ESP8266_USART)); // enable the UART interrupt
|
||||
usart_enable_rx_interrupt(USART(RADIO_ESP8266_USART)); // enable receive interrupt
|
||||
usart_enable(USART(RADIO_ESP8266_USART)); // enable USART
|
||||
usart_enable(USART(RADIO_ESP8266_USART)); // enable UART
|
||||
|
||||
/* reset buffer states */
|
||||
// reset buffer states
|
||||
rx_used = 0;
|
||||
tx_used = 0;
|
||||
radio_esp8266_activity = false;
|
||||
radio_esp8266_success = false;
|
||||
|
||||
radio_esp8266_transmit((uint8_t*)"AT\r\n",4); // verify if module is present
|
||||
while (!radio_esp8266_activity || !radio_esp8266_success) { // wait for response
|
||||
__WFI(); // sleep until something happened
|
||||
// verify if ESP8266 is reachable
|
||||
uint16_t timeout = 0; // reset timeout counter
|
||||
radio_esp8266_transmit((uint8_t*)"AT\r\n", 4); // verify if module is present
|
||||
while (!radio_esp8266_response) { // wait for response
|
||||
if (timeout > 100) { // response takes too long
|
||||
return false;
|
||||
}
|
||||
sleep_ms(10); // wait a tiny bit
|
||||
timeout += 10; // remember we waited
|
||||
}
|
||||
radio_esp8266_transmit((uint8_t*)"AT+RST\r\n",8); // reset module
|
||||
while (!radio_esp8266_activity || !radio_esp8266_success) { // wait for response
|
||||
__WFI(); // sleep until something happened
|
||||
if (!radio_esp8266_success) {
|
||||
return false;
|
||||
}
|
||||
while(rx_used<13 || memcmp((char*)&rx_buffer[rx_used-13], "WIFI GOT IP\r\n", 13)!=0) { // wait to have IP
|
||||
__WFI(); // sleep until something happened
|
||||
|
||||
// reset module so it connects to AP
|
||||
timeout = 0; // reset timeout counter
|
||||
radio_esp8266_transmit((uint8_t*)"AT+RST\r\n", 8); // reset module
|
||||
while (!radio_esp8266_response) { // wait for response
|
||||
if (timeout > 100) { // response takes too long
|
||||
return false;
|
||||
}
|
||||
sleep_ms(10); // wait a tiny bit
|
||||
timeout += 10; // remember we waited
|
||||
}
|
||||
radio_esp8266_transmit((uint8_t*)"ATE0\r\n",6); // disable echoing
|
||||
while (!radio_esp8266_activity || !radio_esp8266_success) { // wait for response
|
||||
__WFI(); // sleep until something happened
|
||||
if (!radio_esp8266_success) {
|
||||
return false;
|
||||
}
|
||||
timeout = 0; // reset timeout counter
|
||||
while(rx_used < 13 || 0 != memcmp((char*)&rx_buffer[rx_used - 13], "WIFI GOT IP\r\n", 13)) { // wait to have IP
|
||||
if (timeout > 10000) { // connection takes too long
|
||||
return false;
|
||||
}
|
||||
sleep_ms(10); // wait a tiny bit
|
||||
timeout += 10; // remember we waited
|
||||
}
|
||||
|
||||
// disable echo for better parsing
|
||||
timeout = 0; // reset timeout counter
|
||||
while (!radio_esp8266_response) { // wait for response
|
||||
if (timeout > 100) { // response takes too long
|
||||
return false;
|
||||
}
|
||||
sleep_ms(10); // wait a tiny bit
|
||||
timeout += 10; // remember we waited
|
||||
}
|
||||
if (!radio_esp8266_success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void radio_esp8266_tcp_open(char* host, uint16_t port)
|
||||
bool radio_esp8266_open(const char* host, uint16_t port, bool tcp)
|
||||
{
|
||||
char command[256] = {0}; // string to create command
|
||||
int length = snprintf(command, LENGTH(command), "AT+CIPSTART=\"TCP\",\"%s\",%u\r\n", host, port); // create AT command to establish a TCP connection
|
||||
if (length>0) {
|
||||
int length = snprintf(command, LENGTH(command), "AT+CIPSTART=\"%s\",\"%s\",%u\r\n", tcp ? "TCP" : "UDP", host, port); // create AT command to establish a TCP connection
|
||||
if (length > 0) {
|
||||
uint16_t timeout = 0; // reset timeout counter
|
||||
radio_esp8266_transmit((uint8_t*)command, length);
|
||||
while (!radio_esp8266_response) { // wait for response
|
||||
if (timeout > 1000) { // response takes too long
|
||||
return false;
|
||||
}
|
||||
sleep_ms(10); // wait a tiny bit
|
||||
timeout += 10; // remember we waited
|
||||
}
|
||||
if (!radio_esp8266_success) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void radio_esp8266_send(uint8_t* data, uint8_t length)
|
||||
bool radio_esp8266_send(const uint8_t* data, uint8_t length)
|
||||
{
|
||||
char command[16+1] = {0}; // string to create command
|
||||
char command[16 + 1] = {0}; // string to create command
|
||||
int command_length = snprintf(command, LENGTH(command), "AT+CIPSEND=%u\r\n", length); // create AT command to send data
|
||||
if (command_length>0) {
|
||||
if (command_length > 0) {
|
||||
// start sending
|
||||
uint16_t timeout = 0; // reset timeout counter
|
||||
radio_esp8266_transmit((uint8_t*)command, command_length); // transmit AT command
|
||||
while (!radio_esp8266_activity || !radio_esp8266_success) { // wait for response
|
||||
__WFI(); // sleep until something happened
|
||||
while (!radio_esp8266_response) { // wait for response
|
||||
if (timeout > 1000) { // response takes too long
|
||||
return false;
|
||||
}
|
||||
sleep_ms(10); // wait a tiny bit
|
||||
timeout += 10; // remember we waited
|
||||
}
|
||||
if (!radio_esp8266_success) { // send AT command did not succeed
|
||||
return; // don't transmit data
|
||||
if (!radio_esp8266_success) {
|
||||
return false;
|
||||
}
|
||||
// send actual data
|
||||
timeout = 0; // reset timeout counter
|
||||
radio_esp8266_transmit(data, length); // transmit data
|
||||
while (!radio_esp8266_response) { // wait for response
|
||||
if (timeout > 1000) { // response takes too long
|
||||
return false;
|
||||
}
|
||||
sleep_ms(10); // wait a tiny bit
|
||||
timeout += 10; // remember we waited
|
||||
}
|
||||
if (!radio_esp8266_success) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void radio_esp8266_close(void)
|
||||
bool radio_esp8266_close(void)
|
||||
{
|
||||
uint16_t timeout = 0; // reset timeout counter
|
||||
radio_esp8266_transmit((uint8_t*)"AT+CIPCLOSE\r\n", 13); // send AT command to close established connection
|
||||
while (!radio_esp8266_response) { // wait for response
|
||||
if (timeout > 1000) { // response takes too long
|
||||
return false;
|
||||
}
|
||||
sleep_ms(10); // wait a tiny bit
|
||||
timeout += 10; // remember we waited
|
||||
}
|
||||
if (!radio_esp8266_success) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** USART interrupt service routine called when data has been transmitted or received */
|
||||
void USART_ISR(RADIO_ESP8266_USART)(void)
|
||||
{
|
||||
if (usart_get_interrupt_source(USART(RADIO_ESP8266_USART), USART_SR_TXE)) { // data has been transmitted
|
||||
if (usart_get_flag(USART(RADIO_ESP8266_USART), USART_SR_TXE)) { // data has been transmitted
|
||||
if (tx_used) { // there is still data in the buffer to transmit
|
||||
usart_send(USART(RADIO_ESP8266_USART),tx_buffer[tx_used-1]); // put data in transmit register
|
||||
usart_send(USART(RADIO_ESP8266_USART), tx_buffer[tx_used - 1]); // put data in transmit register
|
||||
tx_used--; // update used size
|
||||
} else { // no data in the buffer to transmit
|
||||
usart_disable_tx_interrupt(USART(RADIO_ESP8266_USART)); // disable transmit interrupt
|
||||
}
|
||||
}
|
||||
if (usart_get_interrupt_source(USART(RADIO_ESP8266_USART), USART_SR_RXNE)) { // data has been received
|
||||
while (rx_used>=LENGTH(rx_buffer)) { // if buffer is full
|
||||
memmove(rx_buffer,&rx_buffer[1],LENGTH(rx_buffer)-1); // drop old data to make space (ring buffer are more efficient but harder to handle)
|
||||
if (usart_get_flag(USART(RADIO_ESP8266_USART), USART_SR_RXNE)) { // data has been received
|
||||
while (rx_used >= LENGTH(rx_buffer)) { // if buffer is full
|
||||
memmove(rx_buffer, &rx_buffer[1], LENGTH(rx_buffer) - 1); // drop old data to make space (ring buffer are more efficient but harder to handle)
|
||||
rx_used--; // update used buffer information
|
||||
}
|
||||
rx_buffer[rx_used++] = usart_recv(USART(RADIO_ESP8266_USART)); // put character in buffer
|
||||
// if the used send a packet with these strings during the commands detection the AT command response will break (AT commands are hard to handle perfectly)
|
||||
if (rx_used>=4 && memcmp((char*)&rx_buffer[rx_used-4], "OK\r\n", 4)==0) { // OK received
|
||||
radio_esp8266_activity = true; // response received
|
||||
if (rx_used >= 4 && 0 == memcmp((char*)&rx_buffer[rx_used - 4], "OK\r\n", 4)) { // OK received
|
||||
radio_esp8266_response = true; // response received
|
||||
radio_esp8266_success = true; // command succeeded
|
||||
rx_used = 0; // reset buffer
|
||||
} else if (rx_used>=7 && memcmp((char*)&rx_buffer[rx_used-7], "ERROR\r\n", 7)==0) { // ERROR received
|
||||
radio_esp8266_activity = true; // response received
|
||||
} else if (rx_used >= 7 && 0 == memcmp((char*)&rx_buffer[rx_used - 7], "ERROR\r\n", 7)) { // ERROR received
|
||||
radio_esp8266_response = true; // response received
|
||||
radio_esp8266_success = false; // command failed
|
||||
rx_used = 0; // reset buffer
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016
|
||||
* @date 2016-2021
|
||||
* @note peripherals used: USART @ref radio_esp8266_usart
|
||||
*/
|
||||
#pragma once
|
||||
|
@ -13,22 +13,23 @@ extern volatile bool radio_esp8266_activity;
|
|||
extern volatile bool radio_esp8266_success;
|
||||
|
||||
/** setup peripherals to communicate with radio
|
||||
* @note this is blocking to ensure we are connected to the WiFi network
|
||||
* @return if it connected to AP
|
||||
*/
|
||||
void radio_esp8266_setup(void);
|
||||
bool radio_esp8266_setup(void);
|
||||
/** establish TCP connection
|
||||
* @param[in] host host to connect to
|
||||
* @param[in] port TCP port to connect to
|
||||
* @note wait for activity to get success status
|
||||
* @param[in] port port number to connect to
|
||||
* @param[in] tcp if connect to a TCP port (else UDP)
|
||||
* @return if operation succeeded
|
||||
*/
|
||||
void radio_esp8266_tcp_open(char* host, uint16_t port);
|
||||
bool radio_esp8266_open(const char* host, uint16_t port, bool tcp);
|
||||
/** send data (requires established connection)
|
||||
* @param[in] data data to send
|
||||
* @param[in] length size of data to send
|
||||
* @note wait for activity to get success status
|
||||
* @return if operation succeeded
|
||||
*/
|
||||
void radio_esp8266_send(uint8_t* data, uint8_t length);
|
||||
bool radio_esp8266_send(const uint8_t* data, uint8_t length);
|
||||
/** close established connection
|
||||
* @note wait for activity to get success status
|
||||
* @return if operation succeeded
|
||||
*/
|
||||
void radio_esp8266_close(void);
|
||||
bool radio_esp8266_close(void);
|
||||
|
|
|
@ -1,177 +0,0 @@
|
|||
/** library to query measurements from Aosong DHT11 temperature and relative humidity sensor
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2020
|
||||
* @note peripherals used: timer channel @ref sensor_dht11_timer
|
||||
*/
|
||||
|
||||
/* standard libraries */
|
||||
#include <stdint.h> // standard integer types
|
||||
|
||||
/* STM32 (including CM3) libraries */
|
||||
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
|
||||
#include <libopencm3/cm3/nvic.h> // interrupt handler
|
||||
#include <libopencm3/stm32/rcc.h> // real-time control clock library
|
||||
#include <libopencm3/stm32/gpio.h> // general purpose input output library
|
||||
#include <libopencm3/stm32/timer.h> // timer utilities
|
||||
|
||||
/* own libraries */
|
||||
#include "sensor_dht11.h" // PZEM electricity meter header and definitions
|
||||
#include "global.h" // common methods
|
||||
|
||||
/** @defgroup sensor_dht11_timer timer peripheral used to measure signal timing for bit decoding
|
||||
* @{
|
||||
*/
|
||||
#define SENSOR_DHT11_TIMER 3 /**< timer peripheral */
|
||||
#define SENSOR_DHT11_CHANNEL 1 /**< channel used as input capture */
|
||||
#define SENSOR_DHT11_JITTER 0.1 /**< signal timing jitter tolerated in timing */
|
||||
/** @} */
|
||||
|
||||
volatile bool sensor_dht11_measurement_received = false;
|
||||
|
||||
/** communication states */
|
||||
volatile enum sensor_dht11_state_t {
|
||||
SENSOR_DHT11_OFF, // no request has started
|
||||
SENSOR_DHT11_HOST_START, // host starts request (and waits >18ms)
|
||||
SENSOR_DHT11_HOST_STARTED, // host started request and waits for slave answer
|
||||
SENSOR_DHT11_SLAVE_START, // slave responds to request and puts signal low for 80 us and high for 80 us
|
||||
SENSOR_DHT11_SLAVE_BIT, // slave is sending bit by putting signal low for 50 us and high (26-28 us = 0, 70 us = 1)
|
||||
SENSOR_DHT11_MAX
|
||||
} sensor_dht11_state = SENSOR_DHT11_OFF; /**< current communication state */
|
||||
|
||||
/** the bit number being sent (MSb first), up to 40 */
|
||||
volatile uint8_t sensor_dht11_bit = 0;
|
||||
|
||||
/** the 40 bits (5 bytes) being sent by the device */
|
||||
volatile uint8_t sensor_dht11_bits[5] = {0};
|
||||
|
||||
/** reset all states */
|
||||
static void sensor_dht11_reset(void)
|
||||
{
|
||||
// reset states
|
||||
sensor_dht11_state = SENSOR_DHT11_OFF;
|
||||
sensor_dht11_bit = 0;
|
||||
sensor_dht11_measurement_received = false;
|
||||
gpio_set(TIM_CH_PORT(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL), TIM_CH_PIN(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL)); // idle is high (using pull-up resistor), pull-up before setting as output else the signal will be low for short
|
||||
gpio_set_mode(TIM_CH_PORT(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, TIM_CH_PIN(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL)); // setup GPIO pin as output (host starts communication before slave replies)
|
||||
timer_ic_disable(TIM(SENSOR_DHT11_TIMER), TIM_IC(SENSOR_DHT11_CHANNEL)); // enable capture interrupt only when receiving data
|
||||
timer_disable_counter(TIM(SENSOR_DHT11_TIMER)); // disable timer
|
||||
}
|
||||
|
||||
void sensor_dht11_setup(void)
|
||||
{
|
||||
// setup timer to measure signal timing for bit decoding (use timer channel as input capture)
|
||||
rcc_periph_clock_enable(RCC_TIM_CH(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL)); // enable clock for GPIO peripheral
|
||||
rcc_periph_clock_enable(RCC_TIM(SENSOR_DHT11_TIMER)); // enable clock for timer peripheral
|
||||
rcc_periph_reset_pulse(RST_TIM(SENSOR_DHT11_TIMER)); // reset timer state
|
||||
timer_set_mode(TIM(SENSOR_DHT11_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock,edge alignment (simple count), and count up
|
||||
timer_set_prescaler(TIM(SENSOR_DHT11_TIMER), 20 - 1); // set the prescaler so this 16 bits timer allows to wait for 18 ms for the start signal ( 1/(72E6/20/(2**16))=18.20ms )
|
||||
timer_ic_set_input(TIM(SENSOR_DHT11_TIMER), TIM_IC(SENSOR_DHT11_CHANNEL), TIM_IC_IN_TI(SENSOR_DHT11_CHANNEL)); // configure ICx to use TIn
|
||||
timer_ic_set_filter(TIM(SENSOR_DHT11_TIMER), TIM_IC(SENSOR_DHT11_CHANNEL), TIM_IC_OFF); // use no filter input (precise timing needed)
|
||||
timer_ic_set_polarity(TIM(SENSOR_DHT11_TIMER), TIM_IC(SENSOR_DHT11_CHANNEL), TIM_IC_FALLING); // capture on rising edge
|
||||
timer_ic_set_prescaler(TIM(SENSOR_DHT11_TIMER), TIM_IC(SENSOR_DHT11_CHANNEL), TIM_IC_PSC_OFF); // don't use any prescaler since we want to capture every pulse
|
||||
|
||||
timer_clear_flag(TIM(SENSOR_DHT11_TIMER), TIM_SR_UIF); // clear flag
|
||||
timer_update_on_overflow(TIM(SENSOR_DHT11_TIMER)); // only use counter overflow as UEV source (use overflow as start time or timeout)
|
||||
timer_enable_irq(TIM(SENSOR_DHT11_TIMER), TIM_DIER_UIE); // enable update interrupt for timer
|
||||
|
||||
timer_clear_flag(TIM(SENSOR_DHT11_TIMER), TIM_SR_CCIF(SENSOR_DHT11_CHANNEL)); // clear input compare flag
|
||||
timer_enable_irq(TIM(SENSOR_DHT11_TIMER), TIM_DIER_CCIE(SENSOR_DHT11_CHANNEL)); // enable capture interrupt
|
||||
|
||||
nvic_enable_irq(NVIC_TIM_IRQ(SENSOR_DHT11_TIMER)); // catch interrupt in service routine
|
||||
|
||||
sensor_dht11_reset(); // reset state
|
||||
}
|
||||
|
||||
bool sensor_dht11_measurement_request(void)
|
||||
{
|
||||
if (sensor_dht11_state != SENSOR_DHT11_OFF) { // not the right state to start (wait up until timeout to reset state)
|
||||
return false;
|
||||
}
|
||||
if (gpio_get(TIM_CH_PORT(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL), TIM_CH_PIN(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL)) == 0) { // signal should be high per default
|
||||
return false;
|
||||
}
|
||||
if (TIM_CR1(TIM(SENSOR_DHT11_TIMER)) & (TIM_CR1_CEN)) { // timer should be off
|
||||
return false;
|
||||
}
|
||||
sensor_dht11_reset(); // reset states
|
||||
|
||||
// send start signal (pull low for > 18 ms)
|
||||
gpio_clear(TIM_CH_PORT(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL), TIM_CH_PIN(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL)); // set signal to low
|
||||
timer_set_counter(TIM(SENSOR_DHT11_TIMER), 0); // reset timer counter
|
||||
timer_enable_counter(TIM(SENSOR_DHT11_TIMER)); // enable timer to wait for 18 ms until overflow
|
||||
sensor_dht11_state = SENSOR_DHT11_HOST_START; // remember we started sending signal
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct sensor_dht11_measurement_t sensor_dht11_measurement_decode(void)
|
||||
{
|
||||
struct sensor_dht11_measurement_t measurement = { 0xff, 0xff }; // measurement to return
|
||||
if (sensor_dht11_bit < 40) { // not enough bits received
|
||||
return measurement;
|
||||
}
|
||||
if ((uint8_t)(sensor_dht11_bits[0] + sensor_dht11_bits[1] + sensor_dht11_bits[2] + sensor_dht11_bits[3]) != sensor_dht11_bits[4]) { // error in checksum (not really parity bit, as mentioned in the datasheet)
|
||||
return measurement;
|
||||
}
|
||||
// calculate measured values (byte 1 and 3 should be the factional value but they are always 0)
|
||||
measurement.humidity = sensor_dht11_bits[0];
|
||||
measurement.temperature = sensor_dht11_bits[2];
|
||||
|
||||
return measurement;
|
||||
}
|
||||
|
||||
/** interrupt service routine called for timer */
|
||||
void TIM_ISR(SENSOR_DHT11_TIMER)(void)
|
||||
{
|
||||
if (timer_get_flag(TIM(SENSOR_DHT11_TIMER), TIM_SR_UIF)) { // overflow update event happened
|
||||
timer_clear_flag(TIM(SENSOR_DHT11_TIMER), TIM_SR_UIF); // clear flag
|
||||
if (sensor_dht11_state == SENSOR_DHT11_HOST_START) { // start signal sent
|
||||
gpio_set_mode(TIM_CH_PORT(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, TIM_CH_PIN(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL)); // switch pin to input (the external pull up with also set the signal high)
|
||||
sensor_dht11_state = SENSOR_DHT11_HOST_STARTED; // switch to next state
|
||||
timer_ic_enable(TIM(SENSOR_DHT11_TIMER), TIM_IC(SENSOR_DHT11_CHANNEL)); // enable capture interrupt only when receiving data
|
||||
} else { // timeout occurred
|
||||
sensor_dht11_reset(); // reset states
|
||||
}
|
||||
} else if (timer_get_flag(TIM(SENSOR_DHT11_TIMER), TIM_SR_CCIF(SENSOR_DHT11_CHANNEL))) { // edge detected on input capture
|
||||
uint16_t time = TIM_CCR(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL); // save captured bit timing (this clear also the flag)
|
||||
timer_set_counter(TIM(SENSOR_DHT11_TIMER), 0); // reset timer counter
|
||||
time = (time * 1E6) / (rcc_ahb_frequency / (TIM_PSC(TIM(SENSOR_DHT11_TIMER)) + 1)); // calculate time in us
|
||||
switch (sensor_dht11_state) {
|
||||
case (SENSOR_DHT11_HOST_STARTED): // the host query data and the slave is responding
|
||||
sensor_dht11_state = SENSOR_DHT11_SLAVE_START; // set new state
|
||||
break;
|
||||
case (SENSOR_DHT11_SLAVE_START): // the slave sent the start signal
|
||||
if (time >= ((80 + 80) * (1 - SENSOR_DHT11_JITTER)) && time <= ((80 + 80)*(1 + SENSOR_DHT11_JITTER))) { // response time should be 80 us low and 80 us high
|
||||
sensor_dht11_state = SENSOR_DHT11_SLAVE_BIT; // set new state
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case (SENSOR_DHT11_SLAVE_BIT): // the slave sent a bit
|
||||
if (sensor_dht11_bit >= 40) { // no bits should be received after 40 bits
|
||||
goto error;
|
||||
}
|
||||
if (time >= ((50 + 26) * (1 - SENSOR_DHT11_JITTER)) && time <= ((50 + 28) * (1 + SENSOR_DHT11_JITTER))) { // bit 0 time should be 50 us low and 26-28 us high
|
||||
sensor_dht11_bits[sensor_dht11_bit / 8] &= ~(1 << (7 - (sensor_dht11_bit % 8))); // clear bit
|
||||
} else if (time >= ((50 + 70)*(1 - SENSOR_DHT11_JITTER)) && time <= ((50 + 70) * (1 + SENSOR_DHT11_JITTER))) { // bit 1 time should be 50 us low and 70 us high
|
||||
sensor_dht11_bits[sensor_dht11_bit / 8] |= (1 << (7 - (sensor_dht11_bit % 8))); // set bit
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
sensor_dht11_bit++;
|
||||
if (sensor_dht11_bit >= 40) { // all bits received
|
||||
sensor_dht11_reset(); // reset states
|
||||
sensor_dht11_bit = 40; // signal decoder all bits have been received
|
||||
sensor_dht11_measurement_received = true; // signal user all bits have been received
|
||||
}
|
||||
break;
|
||||
default: // unexpected state
|
||||
error:
|
||||
sensor_dht11_reset(); // reset states
|
||||
}
|
||||
} else { // no other interrupt should occur
|
||||
while (true); // unhandled exception: wait for the watchdog to bite
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/** library to query measurements from Aosong DHT11 temperature and relative humidity sensor
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2020
|
||||
* @note peripherals used: timer channel @ref sensor_dht11_timer (add external pull-up resistor)
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/** a measurement response has been received */
|
||||
extern volatile bool sensor_dht11_measurement_received;
|
||||
|
||||
/** measurement returned by sensor */
|
||||
struct sensor_dht11_measurement_t {
|
||||
uint8_t humidity; /**< relative humidity in %RH (20-95) */
|
||||
uint8_t temperature; /**< temperature in °C (0-50) */
|
||||
};
|
||||
|
||||
/** setup peripherals to communicate with sensor */
|
||||
void sensor_dht11_setup(void);
|
||||
/** request measurement from sensor
|
||||
* @return request started successfully
|
||||
*/
|
||||
bool sensor_dht11_measurement_request(void);
|
||||
/** decode received measurement
|
||||
* @return decoded measurement (0xff,0xff if invalid)
|
||||
*/
|
||||
struct sensor_dht11_measurement_t sensor_dht11_measurement_decode(void);
|
|
@ -0,0 +1,206 @@
|
|||
/** library to query measurements from Aosong and DHT22/AM2302 temperature and relative humidity sensor
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2020
|
||||
* @note peripherals used: timer channel @ref sensor_dht1122_timer
|
||||
*/
|
||||
|
||||
/* standard libraries */
|
||||
#include <stdint.h> // standard integer types
|
||||
|
||||
/* STM32 (including CM3) libraries */
|
||||
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
|
||||
#include <libopencm3/cm3/nvic.h> // interrupt handler
|
||||
#include <libopencm3/stm32/rcc.h> // real-time control clock library
|
||||
#include <libopencm3/stm32/gpio.h> // general purpose input output library
|
||||
#include <libopencm3/stm32/timer.h> // timer utilities
|
||||
|
||||
/* own libraries */
|
||||
#include "sensor_dht1122.h" // PZEM electricity meter header and definitions
|
||||
#include "global.h" // common methods
|
||||
|
||||
/** @defgroup sensor_dht1122_timer timer peripheral used to measure signal timing for bit decoding
|
||||
* @{
|
||||
*/
|
||||
#define SENSOR_DHT1122_PIN PB4 /**< MCU pin connected to DHT1122 I/O pin, pulled up externally */
|
||||
#define SENSOR_DHT1122_TIMER 3 /**< timer peripheral for DHT1122 pin */
|
||||
#define SENSOR_DHT1122_CHANNEL 1 /**< channel used as input capture */
|
||||
#define SENSOR_DHT1122_AF GPIO_AF2 /**< pin alternate function to use as timer */
|
||||
#define SENSOR_DHT1122_JITTER 0.2 /**< signal timing jitter tolerated in timing */
|
||||
/** @} */
|
||||
|
||||
volatile bool sensor_dht1122_measurement_received = false;
|
||||
|
||||
/** remember if the sensor is a DHT11 or DHT22 */
|
||||
static bool sensor_dht1122_dht22 = false;
|
||||
|
||||
/** communication states */
|
||||
volatile enum sensor_dht1122_state_t {
|
||||
SENSOR_DHT1122_OFF, // no request has started
|
||||
SENSOR_DHT1122_HOST_START, // host starts request (and waits >18ms)
|
||||
SENSOR_DHT1122_HOST_STARTED, // host started request and waits for slave answer
|
||||
SENSOR_DHT1122_SLAVE_START, // slave responds to request and puts signal low for 80 us and high for 80 us
|
||||
SENSOR_DHT1122_SLAVE_BIT, // slave is sending bit by putting signal low for 50 us and high (26-28 us = 0, 70 us = 1)
|
||||
SENSOR_DHT1122_MAX
|
||||
} sensor_dht1122_state = SENSOR_DHT1122_OFF; /**< current communication state */
|
||||
|
||||
/** the bit number being sent (MSb first), up to 40 */
|
||||
volatile uint8_t sensor_dht1122_bit = 0;
|
||||
|
||||
/** the 40 bits (5 bytes) being sent by the device */
|
||||
volatile uint8_t sensor_dht1122_bits[5] = {0};
|
||||
|
||||
/** reset all states */
|
||||
static void sensor_dht1122_reset(void)
|
||||
{
|
||||
// reset states
|
||||
sensor_dht1122_state = SENSOR_DHT1122_OFF;
|
||||
sensor_dht1122_bit = 0;
|
||||
sensor_dht1122_measurement_received = false;
|
||||
gpio_set(GPIO_PORT(SENSOR_DHT1122_PIN), GPIO_PIN(SENSOR_DHT1122_PIN)); // idle is high (using pull-up resistor), pull-up before setting as output else the signal will be low for short
|
||||
gpio_mode_setup(GPIO_PORT(SENSOR_DHT1122_PIN), GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN(SENSOR_DHT1122_PIN)); // setup GPIO pin as output (host starts communication before slave replies)
|
||||
timer_ic_disable(TIM(SENSOR_DHT1122_TIMER), TIM_IC(SENSOR_DHT1122_CHANNEL)); // disable capture interrupt only when receiving data
|
||||
timer_disable_counter(TIM(SENSOR_DHT1122_TIMER)); // disable timer
|
||||
}
|
||||
|
||||
void sensor_dht1122_setup(bool dht22)
|
||||
{
|
||||
sensor_dht1122_dht22 = dht22; // remember sensor type
|
||||
|
||||
// configure pin to use as timer input
|
||||
rcc_periph_clock_enable(GPIO_RCC(SENSOR_DHT1122_PIN)); // enable clock for I²C I/O peripheral
|
||||
gpio_set(GPIO_PORT(SENSOR_DHT1122_PIN), GPIO_PIN(SENSOR_DHT1122_PIN)); // already put signal high
|
||||
gpio_mode_setup(GPIO_PORT(SENSOR_DHT1122_PIN), GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN(SENSOR_DHT1122_PIN)); // set pin to alternate function
|
||||
gpio_set_output_options(GPIO_PORT(SENSOR_DHT1122_PIN), GPIO_OTYPE_OD, GPIO_OSPEED_25MHZ, GPIO_PIN(SENSOR_DHT1122_PIN)); // set pin output as open-drain
|
||||
gpio_set_af(GPIO_PORT(SENSOR_DHT1122_PIN), SENSOR_DHT1122_AF, GPIO_PIN(SENSOR_DHT1122_PIN)); // set alternate function to pin
|
||||
|
||||
// setup timer to measure signal timing for bit decoding (use timer channel as input capture)
|
||||
rcc_periph_clock_enable(RCC_TIM(SENSOR_DHT1122_TIMER)); // enable clock for timer peripheral
|
||||
rcc_periph_reset_pulse(RST_TIM(SENSOR_DHT1122_TIMER)); // reset timer state
|
||||
timer_set_mode(TIM(SENSOR_DHT1122_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock,edge alignment (simple count), and count up
|
||||
timer_enable_break_main_output(TIM(SENSOR_DHT1122_TIMER)); // required to enable some timer, even when no dead time is used
|
||||
// one difference between DHT11 and DHT22 is the request pulse duration
|
||||
if (!sensor_dht1122_dht22) { // for DHT11
|
||||
timer_set_prescaler(TIM(SENSOR_DHT1122_TIMER), 24 - 1); // set the prescaler so this 16 bits timer allows to wait for 18 ms for the start signal ( 1/(84E6/24/(2**16)) = 18.7 ms )
|
||||
} else { // for DHT22
|
||||
timer_set_prescaler(TIM(SENSOR_DHT1122_TIMER), 2 - 1); // set the prescaler so this 16 bits timer allows to wait for 18 ms for the start signal ( 1/(84E6/2/(2**16)) = 1.56 ms )
|
||||
}
|
||||
timer_ic_set_input(TIM(SENSOR_DHT1122_TIMER), TIM_IC(SENSOR_DHT1122_CHANNEL), TIM_IC_IN_TI(SENSOR_DHT1122_CHANNEL)); // configure ICx to use TIn
|
||||
timer_ic_set_filter(TIM(SENSOR_DHT1122_TIMER), TIM_IC(SENSOR_DHT1122_CHANNEL), TIM_IC_OFF); // use no filter input (precise timing needed)
|
||||
timer_ic_set_polarity(TIM(SENSOR_DHT1122_TIMER), TIM_IC(SENSOR_DHT1122_CHANNEL), TIM_IC_FALLING); // capture on falling edge (start of bit)
|
||||
timer_ic_set_prescaler(TIM(SENSOR_DHT1122_TIMER), TIM_IC(SENSOR_DHT1122_CHANNEL), TIM_IC_PSC_OFF); // don't use any prescaler since we want to capture every pulse
|
||||
|
||||
timer_clear_flag(TIM(SENSOR_DHT1122_TIMER), TIM_SR_UIF); // clear flag
|
||||
timer_update_on_overflow(TIM(SENSOR_DHT1122_TIMER)); // only use counter overflow as UEV source (use overflow as start time or timeout)
|
||||
timer_enable_irq(TIM(SENSOR_DHT1122_TIMER), TIM_DIER_UIE); // enable update interrupt for timer
|
||||
|
||||
timer_clear_flag(TIM(SENSOR_DHT1122_TIMER), TIM_SR_CCIF(SENSOR_DHT1122_CHANNEL)); // clear input compare flag
|
||||
timer_enable_irq(TIM(SENSOR_DHT1122_TIMER), TIM_DIER_CCIE(SENSOR_DHT1122_CHANNEL)); // enable capture interrupt
|
||||
|
||||
nvic_enable_irq(NVIC_TIM_IRQ(SENSOR_DHT1122_TIMER)); // catch interrupt in service routine
|
||||
|
||||
sensor_dht1122_reset(); // reset state
|
||||
}
|
||||
|
||||
bool sensor_dht1122_measurement_request(void)
|
||||
{
|
||||
if (sensor_dht1122_state != SENSOR_DHT1122_OFF) { // not the right state to start (wait up until timeout to reset state)
|
||||
return false;
|
||||
}
|
||||
if (gpio_get(GPIO_PORT(SENSOR_DHT1122_PIN), GPIO_PIN(SENSOR_DHT1122_PIN)) == 0) { // signal should be high per default
|
||||
return false;
|
||||
}
|
||||
if (TIM_CR1(TIM(SENSOR_DHT1122_TIMER)) & (TIM_CR1_CEN)) { // timer should be off
|
||||
return false;
|
||||
}
|
||||
sensor_dht1122_reset(); // reset states
|
||||
|
||||
// send start signal (pull low for > 18 ms)
|
||||
gpio_clear(GPIO_PORT(SENSOR_DHT1122_PIN), GPIO_PIN(SENSOR_DHT1122_PIN)); // set signal to low
|
||||
timer_set_counter(TIM(SENSOR_DHT1122_TIMER), 0); // reset timer counter
|
||||
timer_enable_counter(TIM(SENSOR_DHT1122_TIMER)); // enable timer to wait for 18 ms until overflow
|
||||
sensor_dht1122_state = SENSOR_DHT1122_HOST_START; // remember we started sending signal
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct sensor_dht1122_measurement_t sensor_dht1122_measurement_decode(void)
|
||||
{
|
||||
struct sensor_dht1122_measurement_t measurement = { 0xff, 0xff }; // measurement to return
|
||||
if (sensor_dht1122_bit < 40) { // not enough bits received
|
||||
return measurement;
|
||||
}
|
||||
if ((uint8_t)(sensor_dht1122_bits[0] + sensor_dht1122_bits[1] + sensor_dht1122_bits[2] + sensor_dht1122_bits[3]) != sensor_dht1122_bits[4]) { // error in checksum (not really parity bit, as mentioned in the datasheet)
|
||||
return measurement;
|
||||
}
|
||||
if (0 == sensor_dht1122_bits[0] && 0 == sensor_dht1122_bits[1] && 0 == sensor_dht1122_bits[2] && 0 == sensor_dht1122_bits[3] && 0 == sensor_dht1122_bits[4]) { // this is measurement is very unlikely, there must be an error
|
||||
return measurement;
|
||||
}
|
||||
// the other difference between DHT11 and DHT22 is the encoding of the values
|
||||
if (!sensor_dht1122_dht22) { // for DHT11
|
||||
// calculate measured values (byte 1 and 3 should be the factional value but they are always 0)
|
||||
measurement.humidity = sensor_dht1122_bits[0];
|
||||
measurement.temperature = sensor_dht1122_bits[2];
|
||||
} else { // for DHT22
|
||||
measurement.humidity = (int16_t)((sensor_dht1122_bits[0] << 8) + sensor_dht1122_bits[1]) / 10.0;
|
||||
measurement.temperature = (int16_t)((sensor_dht1122_bits[2] << 8) + sensor_dht1122_bits[3]) / 10.0;
|
||||
}
|
||||
|
||||
return measurement;
|
||||
}
|
||||
|
||||
/** interrupt service routine called for timer */
|
||||
void TIM_ISR(SENSOR_DHT1122_TIMER)(void)
|
||||
{
|
||||
if (timer_get_flag(TIM(SENSOR_DHT1122_TIMER), TIM_SR_UIF)) { // overflow update event happened
|
||||
timer_clear_flag(TIM(SENSOR_DHT1122_TIMER), TIM_SR_UIF); // clear flag
|
||||
if (sensor_dht1122_state == SENSOR_DHT1122_HOST_START) { // start signal sent
|
||||
gpio_set(GPIO_PORT(SENSOR_DHT1122_PIN), GPIO_PIN(SENSOR_DHT1122_PIN)); // let pin go so we can use it as input
|
||||
gpio_mode_setup(GPIO_PORT(SENSOR_DHT1122_PIN), GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN(SENSOR_DHT1122_PIN)); // set pin to alternate function so the timer can read the pin
|
||||
sensor_dht1122_state = SENSOR_DHT1122_HOST_STARTED; // switch to next state
|
||||
timer_ic_enable(TIM(SENSOR_DHT1122_TIMER), TIM_IC(SENSOR_DHT1122_CHANNEL)); // enable capture interrupt only when receiving data
|
||||
} else { // timeout occurred
|
||||
sensor_dht1122_reset(); // reset states
|
||||
}
|
||||
} else if (timer_get_flag(TIM(SENSOR_DHT1122_TIMER), TIM_SR_CCIF(SENSOR_DHT1122_CHANNEL))) { // edge detected on input capture
|
||||
uint16_t time = TIM_CCR(SENSOR_DHT1122_TIMER, SENSOR_DHT1122_CHANNEL); // save captured bit timing (this clear also the flag)
|
||||
timer_set_counter(TIM(SENSOR_DHT1122_TIMER), 0); // reset timer counter
|
||||
time = (time * 1E6) / (rcc_ahb_frequency / (TIM_PSC(TIM(SENSOR_DHT1122_TIMER)) + 1)); // calculate time in us
|
||||
switch (sensor_dht1122_state) {
|
||||
case SENSOR_DHT1122_HOST_STARTED: // the host query data and the slave is responding
|
||||
sensor_dht1122_state = SENSOR_DHT1122_SLAVE_START; // set new state
|
||||
break;
|
||||
case SENSOR_DHT1122_SLAVE_START: // the slave sent the start signal
|
||||
if (time >= ((80 + 80) * (1 - SENSOR_DHT1122_JITTER)) && time <= ((80 + 80) * (1 + SENSOR_DHT1122_JITTER))) { // response time should be 80 us low and 80 us high
|
||||
sensor_dht1122_state = SENSOR_DHT1122_SLAVE_BIT; // set new state
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case SENSOR_DHT1122_SLAVE_BIT: // the slave sent a bit
|
||||
if (sensor_dht1122_bit >= 40) { // no bits should be received after 40 bits
|
||||
goto error;
|
||||
}
|
||||
if (time >= ((50 + 26) * (1 - SENSOR_DHT1122_JITTER)) && time <= ((50 + 28) * (1 + SENSOR_DHT1122_JITTER))) { // bit 0 time should be 50 us low and 26-28 us high
|
||||
sensor_dht1122_bits[sensor_dht1122_bit / 8] &= ~(1 << (7 - (sensor_dht1122_bit % 8))); // clear bit
|
||||
} else if (time >= ((50 + 70)*(1 - SENSOR_DHT1122_JITTER)) && time <= ((50 + 70) * (1 + SENSOR_DHT1122_JITTER))) { // bit 1 time should be 50 us low and 70 us high
|
||||
sensor_dht1122_bits[sensor_dht1122_bit / 8] |= (1 << (7 - (sensor_dht1122_bit % 8))); // set bit
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
sensor_dht1122_bit++;
|
||||
if (sensor_dht1122_bit >= 40) { // all bits received
|
||||
sensor_dht1122_reset(); // reset states
|
||||
sensor_dht1122_bit = 40; // signal decoder all bits have been received
|
||||
sensor_dht1122_measurement_received = true; // signal user all bits have been received
|
||||
}
|
||||
break;
|
||||
default: // unexpected state
|
||||
error:
|
||||
sensor_dht1122_reset(); // reset states
|
||||
}
|
||||
} else { // no other interrupt should occur
|
||||
while (true); // unhandled exception: wait for the watchdog to bite
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/** library to query measurements from Aosong DHT11 and DHT22/AM2302 temperature and relative humidity sensor
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2021
|
||||
* @note peripherals used: timer channel @ref sensor_dht1122_timer (add external pull-up resistor)
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/** a measurement response has been received */
|
||||
extern volatile bool sensor_dht1122_measurement_received;
|
||||
|
||||
/** measurement returned by sensor */
|
||||
struct sensor_dht1122_measurement_t {
|
||||
float humidity; /**< relative humidity in %RH (20-95) */
|
||||
float temperature; /**< temperature in °C (0-50) */
|
||||
};
|
||||
|
||||
/** setup peripherals to communicate with sensor
|
||||
* @param[in] dht22 false for DHT11, true for DHT22/AM2302
|
||||
*/
|
||||
void sensor_dht1122_setup(bool dht22);
|
||||
/** request measurement from sensor
|
||||
* @return request started successfully
|
||||
*/
|
||||
bool sensor_dht1122_measurement_request(void);
|
||||
/** decode received measurement
|
||||
* @return decoded measurement (0xff,0xff if invalid)
|
||||
*/
|
||||
struct sensor_dht1122_measurement_t sensor_dht1122_measurement_decode(void);
|
|
@ -1,181 +0,0 @@
|
|||
/** library to query measurements from Aosong DHT22 temperature and relative humidity sensor
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2020
|
||||
* @note peripherals used: GPIO and timer @ref sensor_dht22_timer
|
||||
* @note the DHT22 protocol is very similar but nit completely compatible with the DHT22 protocol: only 1 ms initial host pull low is required (vs. 18 ms), the data is encoded as int16_t (vs. uint8_t), and the signal has more jitter
|
||||
*/
|
||||
|
||||
/* standard libraries */
|
||||
#include <stdint.h> // standard integer types
|
||||
#include <math.h> // maths utilities
|
||||
|
||||
/* STM32 (including CM3) libraries */
|
||||
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
|
||||
#include <libopencm3/cm3/nvic.h> // interrupt handler
|
||||
#include <libopencm3/stm32/rcc.h> // real-time control clock library
|
||||
#include <libopencm3/stm32/gpio.h> // general purpose input output library
|
||||
#include <libopencm3/stm32/timer.h> // timer utilities
|
||||
|
||||
/* own libraries */
|
||||
#include "sensor_dht22.h" // PZEM electricity meter header and definitions
|
||||
#include "global.h" // common methods
|
||||
|
||||
/** @defgroup sensor_dht22_timer timer peripheral used to measure signal timing for bit decoding
|
||||
* @{
|
||||
*/
|
||||
#define SENSOR_DHT22_TIMER 4 /**< timer peripheral */
|
||||
#define SENSOR_DHT22_CHANNEL 3 /**< channel used as input capture */
|
||||
#define SENSOR_DHT22_JITTER 0.2 /**< signal timing jitter tolerated in timing */
|
||||
/** @} */
|
||||
|
||||
volatile bool sensor_dht22_measurement_received = false;
|
||||
|
||||
/** communication states */
|
||||
volatile enum sensor_dht22_state_t {
|
||||
SENSOR_DHT22_OFF, // no request has started
|
||||
SENSOR_DHT22_HOST_START, // host starts request (and waits >18ms)
|
||||
SENSOR_DHT22_HOST_STARTED, // host started request and waits for slave answer
|
||||
SENSOR_DHT22_SLAVE_START, // slave responds to request and puts signal low for 80 us and high for 80 us
|
||||
SENSOR_DHT22_SLAVE_BIT, // slave is sending bit by putting signal low for 50 us and high (26-28 us = 0, 70 us = 1)
|
||||
SENSOR_DHT22_MAX
|
||||
} sensor_dht22_state = SENSOR_DHT22_OFF; /**< current communication state */
|
||||
|
||||
/** the bit number being sent (MSb first), up to 40 */
|
||||
volatile uint8_t sensor_dht22_bit = 0;
|
||||
|
||||
/** the 40 bits (5 bytes) being sent by the device */
|
||||
volatile uint8_t sensor_dht22_bits[5] = {0};
|
||||
|
||||
/** reset all states */
|
||||
static void sensor_dht22_reset(void)
|
||||
{
|
||||
// reset states
|
||||
sensor_dht22_state = SENSOR_DHT22_OFF;
|
||||
sensor_dht22_bit = 0;
|
||||
sensor_dht22_measurement_received = false;
|
||||
|
||||
gpio_set(TIM_CH_PORT(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL), TIM_CH_PIN(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL)); // idle is high (using pull-up resistor), pull-up before setting as output else the signal will be low for short
|
||||
gpio_set_mode(TIM_CH_PORT(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, TIM_CH_PIN(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL)); // setup GPIO pin as output (host starts communication before slave replies)
|
||||
|
||||
timer_ic_disable(TIM(SENSOR_DHT22_TIMER), TIM_IC(SENSOR_DHT22_CHANNEL)); // enable capture interrupt only when receiving data
|
||||
timer_disable_counter(TIM(SENSOR_DHT22_TIMER)); // disable timer
|
||||
}
|
||||
|
||||
void sensor_dht22_setup(void)
|
||||
{
|
||||
// setup timer to measure signal timing for bit decoding (use timer channel as input capture)
|
||||
rcc_periph_clock_enable(RCC_TIM_CH(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL)); // enable clock for GPIO peripheral
|
||||
rcc_periph_clock_enable(RCC_TIM(SENSOR_DHT22_TIMER)); // enable clock for timer peripheral
|
||||
rcc_periph_reset_pulse(RST_TIM(SENSOR_DHT22_TIMER)); // reset timer state
|
||||
timer_set_mode(TIM(SENSOR_DHT22_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock,edge alignment (simple count), and count up
|
||||
timer_set_prescaler(TIM(SENSOR_DHT22_TIMER), 2 - 1); // set the prescaler so this 16 bits timer allows to wait for 18 ms for the start signal ( 1/(72E6/2/(2**16))=1.820ms )
|
||||
timer_ic_set_input(TIM(SENSOR_DHT22_TIMER), TIM_IC(SENSOR_DHT22_CHANNEL), TIM_IC_IN_TI(SENSOR_DHT22_CHANNEL)); // configure ICx to use TIn
|
||||
timer_ic_set_filter(TIM(SENSOR_DHT22_TIMER), TIM_IC(SENSOR_DHT22_CHANNEL), TIM_IC_OFF); // use no filter input (precise timing needed)
|
||||
timer_ic_set_polarity(TIM(SENSOR_DHT22_TIMER), TIM_IC(SENSOR_DHT22_CHANNEL), TIM_IC_FALLING); // capture on rising edge
|
||||
timer_ic_set_prescaler(TIM(SENSOR_DHT22_TIMER), TIM_IC(SENSOR_DHT22_CHANNEL), TIM_IC_PSC_OFF); // don't use any prescaler since we want to capture every pulse
|
||||
|
||||
timer_clear_flag(TIM(SENSOR_DHT22_TIMER), TIM_SR_UIF); // clear flag
|
||||
timer_update_on_overflow(TIM(SENSOR_DHT22_TIMER)); // only use counter overflow as UEV source (use overflow as start time or timeout)
|
||||
timer_enable_irq(TIM(SENSOR_DHT22_TIMER), TIM_DIER_UIE); // enable update interrupt for timer
|
||||
|
||||
timer_clear_flag(TIM(SENSOR_DHT22_TIMER), TIM_SR_CCIF(SENSOR_DHT22_CHANNEL)); // clear input compare flag
|
||||
timer_enable_irq(TIM(SENSOR_DHT22_TIMER), TIM_DIER_CCIE(SENSOR_DHT22_CHANNEL)); // enable capture interrupt
|
||||
|
||||
nvic_enable_irq(NVIC_TIM_IRQ(SENSOR_DHT22_TIMER)); // catch interrupt in service routine
|
||||
|
||||
sensor_dht22_reset(); // reset state
|
||||
}
|
||||
|
||||
bool sensor_dht22_measurement_request(void)
|
||||
{
|
||||
if (sensor_dht22_state != SENSOR_DHT22_OFF) { // not the right state to start (wait up until timeout to reset state)
|
||||
return false;
|
||||
}
|
||||
if (gpio_get(TIM_CH_PORT(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL), TIM_CH_PIN(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL)) == 0) { // signal should be high per default
|
||||
return false;
|
||||
}
|
||||
if (TIM_CR1(TIM(SENSOR_DHT22_TIMER)) & (TIM_CR1_CEN)) { // timer should be off
|
||||
return false;
|
||||
}
|
||||
sensor_dht22_reset(); // reset states
|
||||
|
||||
// send start signal (pull low for > 1 ms)
|
||||
gpio_clear(TIM_CH_PORT(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL), TIM_CH_PIN(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL)); // set signal to low
|
||||
timer_set_counter(TIM(SENSOR_DHT22_TIMER), 0); // reset timer counter
|
||||
timer_enable_counter(TIM(SENSOR_DHT22_TIMER)); // enable timer to wait for 1.8 ms until overflow
|
||||
sensor_dht22_state = SENSOR_DHT22_HOST_START; // remember we started sending signal
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct sensor_dht22_measurement_t sensor_dht22_measurement_decode(void)
|
||||
{
|
||||
struct sensor_dht22_measurement_t measurement = { NAN, NAN }; // measurement to return
|
||||
if (sensor_dht22_bit < 40) { // not enough bits received
|
||||
return measurement;
|
||||
}
|
||||
if ((uint8_t)(sensor_dht22_bits[0] + sensor_dht22_bits[1] + sensor_dht22_bits[2] + sensor_dht22_bits[3]) != sensor_dht22_bits[4]) { // error in checksum (not really parity bit, as mentioned in the datasheet)
|
||||
return measurement;
|
||||
}
|
||||
// calculate measured values (stored as uint16_t deci-value)
|
||||
measurement.humidity = (int16_t)((sensor_dht22_bits[0] << 8) + sensor_dht22_bits[1]) / 10.0;
|
||||
measurement.temperature = (int16_t)((sensor_dht22_bits[2] << 8) + sensor_dht22_bits[3]) / 10.0;
|
||||
|
||||
return measurement;
|
||||
}
|
||||
|
||||
/** interrupt service routine called for timer */
|
||||
void TIM_ISR(SENSOR_DHT22_TIMER)(void)
|
||||
{
|
||||
if (timer_get_flag(TIM(SENSOR_DHT22_TIMER), TIM_SR_UIF)) { // overflow update event happened
|
||||
timer_clear_flag(TIM(SENSOR_DHT22_TIMER), TIM_SR_UIF); // clear flag
|
||||
if (sensor_dht22_state==SENSOR_DHT22_HOST_START) { // start signal sent
|
||||
gpio_set_mode(TIM_CH_PORT(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, TIM_CH_PIN(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL)); // switch pin to input (the external pull up with also set the signal high)
|
||||
sensor_dht22_state = SENSOR_DHT22_HOST_STARTED; // switch to next state
|
||||
timer_ic_enable(TIM(SENSOR_DHT22_TIMER), TIM_IC(SENSOR_DHT22_CHANNEL)); // enable capture interrupt only when receiving data
|
||||
} else { // timeout occurred
|
||||
sensor_dht22_reset(); // reset states
|
||||
}
|
||||
} else if (timer_get_flag(TIM(SENSOR_DHT22_TIMER), TIM_SR_CCIF(SENSOR_DHT22_CHANNEL))) { // edge detected on input capture
|
||||
uint16_t time = TIM_CCR(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL); // save captured bit timing (this clear also the flag)
|
||||
timer_set_counter(TIM(SENSOR_DHT22_TIMER), 0); // reset timer counter
|
||||
time = (time * 1E6) / (rcc_ahb_frequency / (TIM_PSC(TIM(SENSOR_DHT22_TIMER)) + 1)); // calculate time in us
|
||||
switch (sensor_dht22_state) {
|
||||
case (SENSOR_DHT22_HOST_STARTED): // the host query data and the slave is responding
|
||||
sensor_dht22_state = SENSOR_DHT22_SLAVE_START; // set new state
|
||||
break;
|
||||
case (SENSOR_DHT22_SLAVE_START): // the slave sent the start signal
|
||||
if (time >= ((80 + 80) * (1 - SENSOR_DHT22_JITTER)) && time <= ((80 + 80) * (1 + SENSOR_DHT22_JITTER))) { // response time should be 80 us low and 80 us high
|
||||
sensor_dht22_state = SENSOR_DHT22_SLAVE_BIT; // set new state
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case (SENSOR_DHT22_SLAVE_BIT): // the slave sent a bit
|
||||
if (sensor_dht22_bit >= 40) { // no bits should be received after 40 bits
|
||||
goto error;
|
||||
}
|
||||
if (time >= ((50 + 26) * (1 - SENSOR_DHT22_JITTER)) && time <= ((50 + 28) * (1 + SENSOR_DHT22_JITTER))) { // bit 0 time should be 50 us low and 26-28 us high
|
||||
sensor_dht22_bits[sensor_dht22_bit / 8] &= ~(1 << (7 - (sensor_dht22_bit % 8))); // clear bit
|
||||
} else if (time >= ((50 + 70) * (1 - SENSOR_DHT22_JITTER)) && time <= ((50 + 70) * (1 + SENSOR_DHT22_JITTER))) { // bit 1 time should be 50 us low and 70 us high
|
||||
sensor_dht22_bits[sensor_dht22_bit / 8] |= (1 << (7 - (sensor_dht22_bit % 8))); // set bit
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
sensor_dht22_bit++;
|
||||
if (sensor_dht22_bit >= 40) { // all bits received
|
||||
sensor_dht22_reset(); // reset states
|
||||
sensor_dht22_bit = 40; // signal decoder all bits have been received
|
||||
sensor_dht22_measurement_received = true; // signal user all bits have been received
|
||||
}
|
||||
break;
|
||||
default: // unexpected state
|
||||
error:
|
||||
sensor_dht22_reset(); // reset states
|
||||
}
|
||||
} else { // no other interrupt should occur
|
||||
while (true); // unhandled exception: wait for the watchdog to bite
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/** library to query measurements from Aosong DHT22 (aka. AM2302) temperature and relative humidity sensor
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2020
|
||||
* @note peripherals used: timer channel @ref sensor_dht22_timer (add external pull-up resistor)
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/** a measurement response has been received */
|
||||
extern volatile bool sensor_dht22_measurement_received;
|
||||
|
||||
/** measurement returned by sensor */
|
||||
struct sensor_dht22_measurement_t {
|
||||
float humidity; /**< relative humidity in %RH (0-100) */
|
||||
float temperature; /**< temperature in °C (-40-80) */
|
||||
};
|
||||
|
||||
/** setup peripherals to communicate with sensor */
|
||||
void sensor_dht22_setup(void);
|
||||
/** request measurement from sensor
|
||||
* @return request started successfully
|
||||
*/
|
||||
bool sensor_dht22_measurement_request(void);
|
||||
/** decode received measurement
|
||||
* @return decoded measurement (0xff,0xff if invalid)
|
||||
*/
|
||||
struct sensor_dht22_measurement_t sensor_dht22_measurement_decode(void);
|
|
@ -2,7 +2,7 @@
|
|||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017
|
||||
* @date 2017-2020
|
||||
* @note peripherals used: 1-Wire (timer @ref onewire_master_timer, GPIO @ref onewire_master_gpio)
|
||||
* @warning this library does not support parasite power mode and alarms
|
||||
*/
|
||||
|
@ -42,10 +42,10 @@ uint64_t sensor_ds18b20_number(void)
|
|||
return 0; // no slave presence detected
|
||||
}
|
||||
more = onewire_master_rom_search(&code, false); // get next slave ROM code (without alarm)
|
||||
if (0==code) { // error occurred
|
||||
if (0 == code) { // error occurred
|
||||
return 0;
|
||||
}
|
||||
if (0x28==(code&0xff)) { // family code (8-LSb) for DS18B20 sensors is 0x28
|
||||
if (0x28 == (code & 0xff)) { // family code (8-LSb) for DS18B20 sensors is 0x28
|
||||
last = code; // save last found code
|
||||
sensors++; // we found an additional sensor
|
||||
} else {
|
||||
|
@ -68,12 +68,12 @@ bool sensor_ds18b20_list(uint64_t* code)
|
|||
return false; // no slave presence detected
|
||||
}
|
||||
onewire_master_rom_search(code, false); // get next code
|
||||
return (last!=*code); // verify if the last has been found
|
||||
return (last != *code); // verify if the last has been found
|
||||
}
|
||||
|
||||
bool sensor_ds18b20_convert(uint64_t code)
|
||||
{
|
||||
if (0==code && !only) { // asked for broadcast but there are different slaves on bus
|
||||
if (0 == code && !only) { // asked for broadcast but there are different slaves on bus
|
||||
return false; // broadcast not possible when there are also different slaves on bus
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ bool sensor_ds18b20_convert(uint64_t code)
|
|||
}
|
||||
|
||||
// send ROM command to select slave(s)
|
||||
if (0==code) { // broadcast convert
|
||||
if (0 == code) { // broadcast convert
|
||||
if (!onewire_master_rom_skip()) { // select all slaves
|
||||
return false; // ROM command failed
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ bool sensor_ds18b20_convert(uint64_t code)
|
|||
|
||||
float sensor_ds18b20_temperature(uint64_t code)
|
||||
{
|
||||
if (0==code && (sensors>1 || !only)) { // broadcast read requested
|
||||
if (0 == code && (sensors > 1 || !only)) { // broadcast read requested
|
||||
return NAN; // this function is not possible when several sensors or other devices are present
|
||||
}
|
||||
|
||||
|
@ -109,13 +109,19 @@ float sensor_ds18b20_temperature(uint64_t code)
|
|||
}
|
||||
|
||||
// send ROM command to select slave
|
||||
if (!onewire_master_rom_match(code)) { // select specific slave
|
||||
return NAN; // ROM command failed
|
||||
if (0 == code) { // broadcast convert
|
||||
if (!onewire_master_rom_skip()) { // select all slaves
|
||||
return false; // ROM command failed
|
||||
}
|
||||
} else {
|
||||
if (!onewire_master_rom_match(code)) { // select specific slave
|
||||
return false; // ROM command failed
|
||||
}
|
||||
}
|
||||
|
||||
// read scratchpad to get temperature (on byte 0 and 1)
|
||||
uint8_t scratchpad[9] = {0}; // to store read scratchpad
|
||||
if (!onewire_master_function_read(0xbe, scratchpad, sizeof(scratchpad)*8)) { // read complete scratchpad
|
||||
if (!onewire_master_function_read(0xbe, scratchpad, sizeof(scratchpad) * 8)) { // read complete scratchpad
|
||||
return NAN; // error occurred during read
|
||||
}
|
||||
|
||||
|
@ -125,16 +131,16 @@ float sensor_ds18b20_temperature(uint64_t code)
|
|||
}
|
||||
|
||||
// calculate temperature (stored as int16_t but on 0.125 C steps)
|
||||
return ((int16_t)(scratchpad[1]<<8)+scratchpad[0])/16.0; // get temperature (on < 16 precision the last bits are undefined, but that doesn't matter for the end result since the lower precision is still provided)
|
||||
return ((int16_t)(scratchpad[1] << 8) + scratchpad[0]) / 16.0; // get temperature (on < 16 precision the last bits are undefined, but that doesn't matter for the end result since the lower precision is still provided)
|
||||
}
|
||||
|
||||
bool sensor_ds18b20_precision(uint64_t code, uint8_t precision)
|
||||
{
|
||||
if (precision<9 || precision>12) { // check input
|
||||
if (precision < 9 || precision > 12) { // check input
|
||||
return false; // wrong precision value
|
||||
}
|
||||
|
||||
if (0==code && !only) { // asked for broadcast but there are different slaves on bus
|
||||
if (0 == code && !only) { // asked for broadcast but there are different slaves on bus
|
||||
return false; // broadcast not possible when there are also different slaves on bus
|
||||
}
|
||||
|
||||
|
@ -144,7 +150,7 @@ bool sensor_ds18b20_precision(uint64_t code, uint8_t precision)
|
|||
}
|
||||
|
||||
// send ROM command to select slave(s)
|
||||
if (0==code) { // broadcast convert
|
||||
if (0 == code) { // broadcast convert
|
||||
if (!onewire_master_rom_skip()) { // select all slaves
|
||||
return false; // ROM command failed
|
||||
}
|
||||
|
@ -156,7 +162,7 @@ bool sensor_ds18b20_precision(uint64_t code, uint8_t precision)
|
|||
|
||||
// read scratchpad to get alarm values (on byte 2 and 3)
|
||||
uint8_t scratchpad[9] = {0}; // to store read scratchpad
|
||||
if (!onewire_master_function_read(0xbe, scratchpad, sizeof(scratchpad)*8)) { // read complete scratchpad
|
||||
if (!onewire_master_function_read(0xbe, scratchpad, sizeof(scratchpad) * 8)) { // read complete scratchpad
|
||||
return false; // error occurred during read
|
||||
}
|
||||
|
||||
|
@ -169,8 +175,8 @@ bool sensor_ds18b20_precision(uint64_t code, uint8_t precision)
|
|||
uint8_t configuration[3] = {0}; // to store T_HIGH, T_LOW, and configuration
|
||||
configuration[0] = scratchpad[2]; // keep T_HIGH
|
||||
configuration[1] = scratchpad[3]; // keep T_LOW
|
||||
configuration[2] = 0x1f+((precision-9)<<5); // set precision bit (R1-R0 on bit 6-5)
|
||||
if (!onewire_master_function_write(0x4e, configuration, sizeof(configuration)*8)) { // write scratchpad with new configuration (all three bytes must be written)
|
||||
configuration[2] = 0x1f + ((precision - 9) << 5); // set precision bit (R1-R0 on bit 6-5)
|
||||
if (!onewire_master_function_write(0x4e, configuration, sizeof(configuration) * 8)) { // write scratchpad with new configuration (all three bytes must be written)
|
||||
return false; // error occurred during write
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017
|
||||
* @date 2017-2020
|
||||
* @note peripherals used: 1-Wire (timer @ref onewire_master_timer, GPIO @ref onewire_master_gpio)
|
||||
* @warning this library does not support parasite power mode and alarms
|
||||
*/
|
||||
|
@ -36,7 +36,7 @@ bool sensor_ds18b20_convert(uint64_t code);
|
|||
*/
|
||||
float sensor_ds18b20_temperature(uint64_t code);
|
||||
/** set conversion precision
|
||||
* @param[in] code ROM code of sensor to start conversion on (0 for all, if only DS18B20 sensors are on the bus)
|
||||
* @param[in] code ROM code of sensor to start conversion on (0 for single DS18B20 sensor are on the bus)
|
||||
* @param[in] precision precision in bits (9-12)
|
||||
* @return if operation succeeded
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/** library to communication with Maxim MAX1247 12-bit 4-channel ADC using SPI
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2020
|
||||
* @note peripherals used: SPI @ref sensor_max1247_spi
|
||||
*/
|
||||
/* standard libraries */
|
||||
#include <stdint.h> // standard integer types
|
||||
#include <stdlib.h> // general utilities
|
||||
#include <stdbool.h> // boolean utilities
|
||||
|
||||
/* STM32 (including CM3) libraries */
|
||||
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
|
||||
#include <libopencm3/stm32/rcc.h> // real-time control clock library
|
||||
#include <libopencm3/stm32/gpio.h> // general purpose input output library
|
||||
#include <libopencm3/stm32/spi.h> // SPI library
|
||||
|
||||
/* own libraries */
|
||||
#include "global.h" // common methods
|
||||
#include "sensor_max1247.h" // own definitions
|
||||
|
||||
/** @defgroup sensor_max1247_spi SPI peripheral used to communicate with the AS3935
|
||||
* @{
|
||||
*/
|
||||
#define SENSOR_MAX1247_SPI 2 /**< SPI peripheral */
|
||||
/** @} */
|
||||
|
||||
void sensor_max1247_setup(void)
|
||||
{
|
||||
// setup SPI
|
||||
rcc_periph_clock_enable(RCC_SPI_SCK_PORT(SENSOR_MAX1247_SPI)); // enable clock for GPIO peripheral for clock signal
|
||||
gpio_set_mode(SPI_SCK_PORT(SENSOR_MAX1247_SPI), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, SPI_SCK_PIN(SENSOR_MAX1247_SPI)); // set SCK as output (clock speed will be negotiated later)
|
||||
rcc_periph_clock_enable(RCC_SPI_MOSI_PORT(SENSOR_MAX1247_SPI)); // enable clock for GPIO peripheral for MOSI signal
|
||||
gpio_set_mode(SPI_MOSI_PORT(SENSOR_MAX1247_SPI), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, SPI_MOSI_PIN(SENSOR_MAX1247_SPI)); // set MOSI as output
|
||||
rcc_periph_clock_enable(RCC_SPI_MISO_PORT(SENSOR_MAX1247_SPI)); // enable clock for GPIO peripheral for MISO signal
|
||||
gpio_set_mode(SPI_MISO_PORT(SENSOR_MAX1247_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, SPI_MISO_PIN(SENSOR_MAX1247_SPI)); // set MISO as input
|
||||
rcc_periph_clock_enable(RCC_SPI_NSS_PORT(SENSOR_MAX1247_SPI)); // enable clock for GPIO peripheral for NSS (CS) signal
|
||||
gpio_set_mode(SPI_NSS_PORT(SENSOR_MAX1247_SPI), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, SPI_NSS_PIN(SENSOR_MAX1247_SPI)); // set NSS (CS) as output
|
||||
rcc_periph_clock_enable(RCC_AFIO); // enable clock for SPI alternate function
|
||||
rcc_periph_clock_enable(RCC_SPI(SENSOR_MAX1247_SPI)); // enable clock for SPI peripheral
|
||||
spi_reset(SPI(SENSOR_MAX1247_SPI)); // clear SPI values to default
|
||||
spi_init_master(SPI(SENSOR_MAX1247_SPI), SPI_CR1_BAUDRATE_FPCLK_DIV_64, SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE, SPI_CR1_CPHA_CLK_TRANSITION_1, SPI_CR1_DFF_8BIT, SPI_CR1_MSBFIRST); // initialise SPI as master, divide clock by 64 (72E6/64=1125 kHz, max MAX1247 SCK is 2 MHz, maximum SPI PCLK clock is 72 MHz, depending on which SPI is used), set clock polarity to idle low, set clock phase to do bit change on falling edge (polarity depends on clock phase), use 8 bits frames (the control is 8-bit long, and the conversion response 16-bit), use MSb first
|
||||
spi_set_full_duplex_mode(SPI(SENSOR_MAX1247_SPI)); // ensure we are in full duplex mode
|
||||
spi_enable_software_slave_management(SPI(SENSOR_MAX1247_SPI)); // control NSS (CS) manually
|
||||
spi_set_nss_high(SPI(SENSOR_MAX1247_SPI)); // set NSS high (internally) so we can output
|
||||
spi_disable_ss_output(SPI(SENSOR_MAX1247_SPI)); // disable NSS output since we control CS manually
|
||||
gpio_set(SPI_NSS_PORT(SENSOR_MAX1247_SPI), SPI_NSS_PIN(SENSOR_MAX1247_SPI)); // set CS high to unselect device
|
||||
// sadly we can't use the interrupts as events to sleep (WFE) since sleep disables also communication (e.g. going to sleep until Rx buffer is not empty prevents transmission)
|
||||
spi_enable(SPI(SENSOR_MAX1247_SPI)); // enable SPI
|
||||
}
|
||||
|
||||
void sensor_max1247_release(void)
|
||||
{
|
||||
spi_reset(SPI(SENSOR_MAX1247_SPI));
|
||||
spi_disable(SPI(SENSOR_MAX1247_SPI));
|
||||
rcc_periph_clock_disable(RCC_SPI(SENSOR_MAX1247_SPI));
|
||||
gpio_set_mode(SPI_NSS_PORT(SENSOR_MAX1247_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SPI_NSS_PIN(SENSOR_MAX1247_SPI));
|
||||
gpio_set_mode(SPI_MISO_PORT(SENSOR_MAX1247_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SPI_MISO_PIN(SENSOR_MAX1247_SPI));
|
||||
gpio_set_mode(SPI_MOSI_PORT(SENSOR_MAX1247_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SPI_MOSI_PIN(SENSOR_MAX1247_SPI));
|
||||
gpio_set_mode(SPI_SCK_PORT(SENSOR_MAX1247_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SPI_SCK_PIN(SENSOR_MAX1247_SPI));
|
||||
}
|
||||
|
||||
uint16_t sensor_max1247_read(uint8_t channel)
|
||||
{
|
||||
if (channel > 3) { // ensure we read only from one of the 4 available channels
|
||||
return UINT16_MAX;
|
||||
}
|
||||
|
||||
gpio_clear(SPI_NSS_PORT(SENSOR_MAX1247_SPI), SPI_NSS_PIN(SENSOR_MAX1247_SPI)); // set CS low to select device
|
||||
const uint8_t channels[4] = { 1, 5, 2, 6 }; // SEL bits corresponding to channel (in single ended mode)
|
||||
uint8_t spi_in = spi_xfer(SPI(SENSOR_MAX1247_SPI), 0x8e | (channels[channel] << 4)); // send conversion control (START bit set, unipolar, single ended, internal clock mode)
|
||||
sleep_us(8); // wait for conversion to finish (max. 7.5 µs using internal clock)
|
||||
spi_in = spi_xfer(SPI(SENSOR_MAX1247_SPI), 0); // read first conversion bytes
|
||||
const uint16_t value = (spi_in << 8) + spi_xfer(SPI(SENSOR_MAX1247_SPI), 0); // read second conversion byte
|
||||
gpio_set(SPI_NSS_PORT(SENSOR_MAX1247_SPI), SPI_NSS_PIN(SENSOR_MAX1247_SPI)); // set CS high to select device
|
||||
if ((value & 0x8000) || (value & 0x0007)) { // ensure it has one leading and 3 trailing zeros
|
||||
return UINT16_MAX;
|
||||
}
|
||||
return value >> 3;
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/** library to communication with Maxim MAX1247 12-bit 4-channel ADC using SPI
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2020
|
||||
* @note peripherals used: SPI @ref sensor_max1247_spi
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/** setup peripherals to communicate with sensor
|
||||
* @note the sensor configuration will be set to default and powered down
|
||||
*/
|
||||
void sensor_max1247_setup(void);
|
||||
/** release peripherals used to communicate with sensor */
|
||||
void sensor_max1247_release(void);
|
||||
/** read conversion from channel
|
||||
* @param[in] channel which of the 4 channels to convert
|
||||
* @return 12-bit conversion value (0xffff if error)
|
||||
*/
|
||||
uint16_t sensor_max1247_read(uint8_t channel);
|
|
@ -0,0 +1,75 @@
|
|||
/** library to communication with Maxim MAX6675 K-type thermocouple to digital temperature sensor using SPI
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2020
|
||||
* @note peripherals used: SPI @ref sensor_max6675_spi
|
||||
*/
|
||||
/* standard libraries */
|
||||
#include <stdint.h> // standard integer types
|
||||
#include <stdlib.h> // general utilities
|
||||
#include <stdbool.h> // boolean utilities
|
||||
#include <math.h> // math utilities
|
||||
|
||||
/* STM32 (including CM3) libraries */
|
||||
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
|
||||
#include <libopencm3/stm32/rcc.h> // real-time control clock library
|
||||
#include <libopencm3/stm32/gpio.h> // general purpose input output library
|
||||
#include <libopencm3/stm32/spi.h> // SPI library
|
||||
|
||||
/* own library */
|
||||
#include "global.h" // common definitions
|
||||
#include "sensor_max6675.h" // own definitions
|
||||
|
||||
/** @defgroup sensor_max6675_spi SPI peripheral used to communicate with the AS3935
|
||||
* @note SCK, MISO, and NSS pins are used
|
||||
*/
|
||||
#define SENSOR_MAX6675_SPI 1 /**< SPI peripheral */
|
||||
|
||||
void sensor_max6675_setup(void)
|
||||
{
|
||||
// setup SPI
|
||||
rcc_periph_clock_enable(RCC_SPI_SCK_PORT(SENSOR_MAX6675_SPI)); // enable clock for GPIO peripheral for clock signal
|
||||
gpio_set_mode(SPI_SCK_PORT(SENSOR_MAX6675_SPI), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, SPI_SCK_PIN(SENSOR_MAX6675_SPI)); // set SCK as output (clock speed will be negotiated later)
|
||||
rcc_periph_clock_enable(RCC_SPI_MISO_PORT(SENSOR_MAX6675_SPI)); // enable clock for GPIO peripheral for MISO signal
|
||||
gpio_set_mode(SPI_MISO_PORT(SENSOR_MAX6675_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, SPI_MISO_PIN(SENSOR_MAX6675_SPI)); // set MISO as input
|
||||
rcc_periph_clock_enable(RCC_SPI_NSS_PORT(SENSOR_MAX6675_SPI)); // enable clock for GPIO peripheral for NSS (CS) signal
|
||||
gpio_set_mode(SPI_NSS_PORT(SENSOR_MAX6675_SPI), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, SPI_NSS_PIN(SENSOR_MAX6675_SPI)); // set NSS (CS) as output
|
||||
rcc_periph_clock_enable(RCC_AFIO); // enable clock for SPI alternate function
|
||||
rcc_periph_clock_enable(RCC_SPI(SENSOR_MAX6675_SPI)); // enable clock for SPI peripheral
|
||||
spi_reset(SPI(SENSOR_MAX6675_SPI)); // clear SPI values to default
|
||||
spi_init_master(SPI(SENSOR_MAX6675_SPI), SPI_CR1_BAUDRATE_FPCLK_DIV_32, SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE, SPI_CR1_CPHA_CLK_TRANSITION_2, SPI_CR1_DFF_16BIT, SPI_CR1_MSBFIRST); // initialise SPI as master, divide clock by 64 (72E6/32=2250 kHz, max AS3935 SCK is 4.3, maximum SPI PCLK clock is 72 Mhz, depending on which SPI is used), set clock polarity to idle low (not that important), set clock phase to do bit change on falling edge (polarity depends on clock phase), use 16 bits frames , use MSb first
|
||||
spi_set_unidirectional_mode(SPI(SENSOR_MAX6675_SPI)); // set simplex mode (only two wires used)
|
||||
// do not set as receive only to trigger transfer (read) using write
|
||||
//spi_set_receive_only_mode(SPI(SENSOR_MAX6675_SPI)); // we will only receive data
|
||||
spi_enable_software_slave_management(SPI(SENSOR_MAX6675_SPI)); // control NSS (CS) manually
|
||||
spi_set_nss_high(SPI(SENSOR_MAX6675_SPI)); // set NSS high (internally) so we can get input
|
||||
spi_disable_ss_output(SPI(SENSOR_MAX6675_SPI)); // disable NSS output since we control CS manually
|
||||
gpio_set(SPI_NSS_PORT(SENSOR_MAX6675_SPI), SPI_NSS_PIN(SENSOR_MAX6675_SPI)); // set CS high to unselect device
|
||||
spi_enable(SPI(SENSOR_MAX6675_SPI)); // enable SPI
|
||||
}
|
||||
|
||||
void sensor_max6675_release(void)
|
||||
{
|
||||
spi_reset(SPI(SENSOR_MAX6675_SPI));
|
||||
spi_disable(SPI(SENSOR_MAX6675_SPI));
|
||||
rcc_periph_clock_disable(RCC_SPI(SENSOR_MAX6675_SPI));
|
||||
gpio_set_mode(SPI_NSS_PORT(SENSOR_MAX6675_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SPI_NSS_PIN(SENSOR_MAX6675_SPI));
|
||||
gpio_set_mode(SPI_MISO_PORT(SENSOR_MAX6675_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SPI_MISO_PIN(SENSOR_MAX6675_SPI));
|
||||
gpio_set_mode(SPI_SCK_PORT(SENSOR_MAX6675_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SPI_SCK_PIN(SENSOR_MAX6675_SPI));
|
||||
}
|
||||
|
||||
float sensor_max6675_read(void)
|
||||
{
|
||||
(void)SPI_DR(SPI(SENSOR_MAX6675_SPI)); // clear RXNE flag (by reading previously received data (not done by spi_read or spi_xref)
|
||||
gpio_clear(SPI_NSS_PORT(SENSOR_MAX6675_SPI), SPI_NSS_PIN(SENSOR_MAX6675_SPI)); // set CS low to select device
|
||||
const uint16_t temp = spi_xfer(SPI(SENSOR_MAX6675_SPI), 0); // read data
|
||||
gpio_set(SPI_NSS_PORT(SENSOR_MAX6675_SPI), SPI_NSS_PIN(SENSOR_MAX6675_SPI)); // set CS high to unselect device
|
||||
if (temp & 0x8002) { // sign and device ID bits should not be set
|
||||
return NAN;
|
||||
}
|
||||
if (temp & 0x0004) { // thermocouple is open
|
||||
return INFINITY;
|
||||
}
|
||||
return (temp >> 3) / 4.0; // return temperature value
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/** library to communication with Maxim MAX6675 K-type thermocouple to digital temperature sensor using SPI
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2020
|
||||
* @note peripherals used: SPI @ref sensor_max6675_spi
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/** setup communication to MAX6675 sensor */
|
||||
void sensor_max6675_setup(void);
|
||||
/** release peripherals used to communicate with MAX6675 sensor */
|
||||
void sensor_max6675_release(void);
|
||||
/** read temperature from MAX6675 sensor
|
||||
* @return temperature (in °C) measured by sensor (infinity if K-thermocouple is missing, nan on error)
|
||||
* @note resolution is in 0.25 °C
|
||||
* @note wait 0.22 s between readings (max. time needed for a conversion)
|
||||
*/
|
||||
float sensor_max6675_read(void);
|
|
@ -234,7 +234,7 @@ static enum usbd_request_return_codes usb_dfu_control_request(usbd_device *usbd_
|
|||
// application data is exceeding enforced flash size for application
|
||||
usb_dfu_status = DFU_STATUS_ERR_ADDRESS;
|
||||
usb_dfu_state = STATE_DFU_ERROR;
|
||||
} else if ((uint32_t)&__application_end < FLASH_BASE && (uint32_t)&__application_beginning + download_offset + download_length >= (uint32_t)(FLASH_BASE + DESIG_FLASH_SIZE * 1024)) {
|
||||
} else if ((uint32_t)&__application_end < FLASH_BASE && (uint32_t)&__application_beginning + download_offset + download_length >= (uint32_t)(FLASH_BASE + desig_get_flash_size() * 1024)) {
|
||||
// application data is exceeding advertised flash size
|
||||
usb_dfu_status = DFU_STATUS_ERR_ADDRESS;
|
||||
usb_dfu_state = STATE_DFU_ERROR;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit cb0661f81de5b1cae52ca99c7b5985b678176db7
|
||||
Subproject commit 664701d7a7f169235c457a3dd117415647aac61b
|
Loading…
Reference in New Issue