Compare commits
50 Commits
Author | SHA1 | Date | |
---|---|---|---|
f7b99dd76c | |||
3577f1a0f4 | |||
5e154a57e3 | |||
614d875d9a | |||
17dce0d517 | |||
8bd98693f6 | |||
017c649842 | |||
799584a210 | |||
7b31ace475 | |||
d8cd409d23 | |||
61d65977ac | |||
af28da0a7d | |||
e50cd35728 | |||
952d947c1b | |||
353b11e710 | |||
974ca75027 | |||
fb088e6057 | |||
c085f2d292 | |||
58ef5f3d1b | |||
a4b5f95b07 | |||
dd52d0bf98 | |||
6574a65ea6 | |||
620e46938e | |||
9b522b4228 | |||
2f251e0ad7 | |||
d80587c21d | |||
629500e4bd | |||
5a028c23c4 | |||
60279d0a52 | |||
8566a4043b | |||
97d197237c | |||
f261cac536 | |||
905b7d153b | |||
e054774544 | |||
8142854522 | |||
e85af656d3 | |||
a310fb402a | |||
55c6319928 | |||
3b7179d49a | |||
60a1859008 | |||
86ad8ea7ef | |||
b8d4be47e4 | |||
99d4be6a44 | |||
e71515bcce | |||
28604a43fd | |||
5ab9402644 | |||
6fa46ee86c | |||
54456e0cb4 | |||
b48c65b5e0 | |||
9f419e1da0 |
@ -81,10 +81,11 @@ The `bootloader` is started first and immediately jumps to the `application` if
|
||||
The `application` image is the main application and is implemented in `application.c`.
|
||||
It is up to the application to advertise USB DFU support (i.e. as does the provided USB CDC ACM example).
|
||||
|
||||
The `bootlaoder` image will be flashed using SWD (Serial Wire Debug).
|
||||
The `bootloader` image will be flashed using SWD (Serial Wire Debug).
|
||||
For that you need an SWD adapter.
|
||||
The `Makefile` uses a Black Magic Probe (per default), or a ST-Link V2 along OpenOCD software.
|
||||
The `Makefile` uses a ST-Link V2 programmer along OpenOCD software (default), or Black Magic Probe.
|
||||
To flash the `booltoader` using SWD run `rake flash_booloader`.
|
||||
If the development board uses the CKS32 chip STM32 alternative, use `CPUTAPID=0x2ba01477 rake flash_booloader`.
|
||||
|
||||
Once the `bootloader` is flashed it is possible to flash the `application` over USB using the DFU protocol by running `rake flash`.
|
||||
To force the bootloader to start the DFU mode press the user button or short a pin, depending on the board.
|
||||
|
12
Rakefile
12
Rakefile
@ -193,6 +193,8 @@ OOCD_INTERFACE = ENV["OOCD_INTERFACE"] || (SWD_ADAPTER=="STLINKV2" ? "stlink" :
|
||||
OOCD_TARGET = "stm32f1x"
|
||||
# Black Magic Probe port
|
||||
BMP_PORT = ENV["BMP_PORT"] || "/dev/ttyACM0"
|
||||
# set CPUTAPID (0x1ba01477 for STM32, 0x2ba01477 for CKS32/APM32)
|
||||
CPUTAPID = ENV["CPUTAPID"] || "0x1ba01477"
|
||||
|
||||
desc "flash application using USB DFU"
|
||||
task :flash => APPLICATION+".bin" do |t|
|
||||
@ -203,7 +205,7 @@ desc "remove STM32F1 protection using SWD"
|
||||
task :remove_protection do
|
||||
case SWD_ADAPTER
|
||||
when "STLINKV2"
|
||||
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --file target/#{OOCD_TARGET}.cfg --command 'init' --command 'reset init' --command 'stm32f1x unlock 0' --command 'reset init' --command 'flash protect 0 0 last off' --command 'reset init' --command 'stm32f1x options_write 0 SWWDG NORSTSTNDBY NORSTSTOP' --command 'reset init' --command 'stm32f1x mass_erase 0' --command 'shutdown'"
|
||||
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command 'transport select hla_swd' --command 'set CPUTAPID #{CPUTAPID}' --file target/#{OOCD_TARGET}.cfg --command 'init' --command 'halt' --command 'reset init' --command 'stm32f1x unlock 0' --command 'reset init' --command 'flash protect 0 0 last off' --command 'reset init' --command 'stm32f1x options_write 0 SWWDG NORSTSTNDBY NORSTSTOP' --command 'reset init' --command 'stm32f1x mass_erase 0' --command 'shutdown'"
|
||||
when "BMP"
|
||||
sh "#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='set confirm off' --eval-command='monitor swdp_scan' --eval-command='attach 1' --eval-command='monitor option erase' --eval-command='monitor erase_mass' --eval-command='kill' --eval-command='quit'"
|
||||
end
|
||||
@ -213,7 +215,7 @@ desc "flash bootloader using SWD"
|
||||
task :flash_bootloader => BOOTLOADER+".hex" do |t|
|
||||
case SWD_ADAPTER
|
||||
when "STLINKV2"
|
||||
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --file target/#{OOCD_TARGET}.cfg --command 'init' --command 'reset init' --command 'flash erase_sector 0 0 last' --command 'flash write_image erase #{t.source}' --command 'reset' --command 'shutdown'"
|
||||
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command 'transport select hla_swd' --command 'set CPUTAPID #{CPUTAPID}' --file target/#{OOCD_TARGET}.cfg --command 'init' --command 'halt' --command 'reset init' --command 'flash erase_sector 0 0 last' --command 'flash write_image erase #{t.source}' --command 'reset' --command 'shutdown'"
|
||||
when "BMP"
|
||||
sh "#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='set confirm off' --eval-command='monitor swdp_scan' --eval-command='attach 1' --eval-command='monitor erase_mass' --eval-command='load' --eval-command='kill' --eval-command='quit' #{t.source}"
|
||||
end
|
||||
@ -223,7 +225,7 @@ desc "flash application using SWD"
|
||||
task :flash_application => APPLICATION+".hex" do |t|
|
||||
case SWD_ADAPTER
|
||||
when "STLINKV2"
|
||||
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --file target/#{OOCD_TARGET}.cfg --command 'init' --command 'reset init' --command 'flash write_image erase #{t.source}' --command 'reset' --command 'shutdown'"
|
||||
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command 'transport select hla_swd' --command 'set CPUTAPID #{CPUTAPID}' --file target/#{OOCD_TARGET}.cfg --command 'adapter speed 100' --command 'init' --command 'halt' --command 'reset init' --command 'flash write_image erase #{t.source}' --command 'reset' --command 'shutdown'"
|
||||
when "BMP"
|
||||
sh "#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='set confirm off' --eval-command='monitor swdp_scan' --eval-command='attach 1' --eval-command='load' --eval-command='kill' --eval-command='quit' #{t.source}"
|
||||
end
|
||||
@ -235,7 +237,7 @@ task :debug => APPLICATION+".elf" do |t|
|
||||
case SWD_ADAPTER
|
||||
when "STLINKV2"
|
||||
# for GDB to work with openOCD the firmware needs to be reloaded
|
||||
exec("#{GDB} --eval-command='target remote | #{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --file target/#{OOCD_TARGET}.cfg --command \"gdb_port pipe; log_output /dev/null; init\"' --eval-command='monitor reset halt' --eval-command='load' --eval-command='monitor reset init' #{t.source}")
|
||||
exec("#{GDB} --eval-command='target remote | #{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command \"transport select hla_swd\" --command \"set CPUTAPID #{CPUTAPID}\" --file target/#{OOCD_TARGET}.cfg --command \"gdb_port pipe; log_output /dev/null; init\"' #{t.source}")
|
||||
when "BMP"
|
||||
exec("#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='monitor version' --eval-command='monitor swdp_scan' --eval-command='attach 1' #{t.source}")
|
||||
end
|
||||
@ -247,7 +249,7 @@ task :debug_bootloader => BOOTLOADER+".elf" do |t|
|
||||
case SWD_ADAPTER
|
||||
when "STLINKV2"
|
||||
# for GDB to work with openOCD the firmware needs to be reloaded
|
||||
exec("#{GDB} --eval-command='target remote | #{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --file target/#{OOCD_TARGET}.cfg --command \"gdb_port pipe; log_output /dev/null; init\"' --eval-command='monitor reset halt' --eval-command='load' --eval-command='monitor reset init' #{t.source}")
|
||||
exec("#{GDB} --eval-command='target remote | #{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command \"transport select hla_swd\" --command \"set CPUTAPID #{CPUTAPID}\" --file target/#{OOCD_TARGET}.cfg --command \"gdb_port pipe; log_output /dev/null; init\"' --eval-command='monitor reset init' #{t.source}")
|
||||
when "BMP"
|
||||
exec("#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='monitor version' --eval-command='monitor swdp_scan' --eval-command='attach 1' #{t.source}")
|
||||
end
|
||||
|
271
application.c
271
application.c
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** STM32F1 application example
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
*/
|
||||
|
||||
@ -65,13 +52,20 @@
|
||||
*/
|
||||
#define RTC_TICKS_SECOND 4
|
||||
|
||||
#if defined(RTC_DATE_TIME) && RTC_DATE_TIME
|
||||
/** the start time from which to RTC ticks count
|
||||
* @note this allows the 32-bit value to reach further in time, particularly when there are several ticks per second
|
||||
*/
|
||||
const time_t rtc_offset = 1577833200; // We 1. Jan 00:00:00 CET 2020
|
||||
#endif
|
||||
|
||||
/** RTC time when device is started */
|
||||
static time_t time_start = 0;
|
||||
|
||||
/** @defgroup main_flags flag set in interrupts to be processed in main task
|
||||
* @{
|
||||
*/
|
||||
volatile bool rtc_internal_tick_flag = false; /**< flag set when internal RTC ticked */
|
||||
static volatile bool rtc_internal_tick_flag = false; /**< flag set when internal RTC ticked */
|
||||
/** @} */
|
||||
|
||||
size_t putc(char c)
|
||||
@ -192,241 +186,14 @@ static void command_help(void* argument)
|
||||
static void command_version(void* argument)
|
||||
{
|
||||
(void)argument; // we won't use the argument
|
||||
bool fake = false; // if details indicate it's not an STM32
|
||||
printf("firmware date: %04u-%02u-%02u\n", BUILD_YEAR, BUILD_MONTH, BUILD_DAY); // show firmware build date
|
||||
puts("chip family: ");
|
||||
const uint16_t dev_id = DBGMCU_IDCODE & DBGMCU_IDCODE_DEV_ID_MASK;
|
||||
switch (dev_id) {
|
||||
case 0: // DBGMCU_IDCODE is only accessible in debug mode (this is a known issue documented in STM32F10xxC/D/E Errata sheet, without workaround)
|
||||
puts("not readable, retry with debug attached");
|
||||
break;
|
||||
// from RM0008 STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx
|
||||
case 0x412:
|
||||
puts("STM32F10x low-density");
|
||||
break;
|
||||
case 0x410:
|
||||
puts("STM32F10x medium-density");
|
||||
break;
|
||||
case 0x414:
|
||||
puts("STM32F10x high-density");
|
||||
break;
|
||||
case 0x430:
|
||||
puts("STM32F10x XL-density");
|
||||
break;
|
||||
case 0x418:
|
||||
puts("STM32F10xconnectivity");
|
||||
break;
|
||||
// from RM0091 STM32F0x8
|
||||
case 0x444:
|
||||
puts("STM32F03x");
|
||||
break;
|
||||
case 0x445:
|
||||
puts("STM32F04x");
|
||||
break;
|
||||
case 0x440:
|
||||
puts("STM32F05x");
|
||||
break;
|
||||
case 0x448:
|
||||
puts("STM32F07x");
|
||||
break;
|
||||
case 0x442:
|
||||
puts("STM32F09x");
|
||||
break;
|
||||
// from RM0444 STM32G0x1
|
||||
case 0x460:
|
||||
puts("STM32G071xx/STM32G081xx");
|
||||
break;
|
||||
case 0x466:
|
||||
puts("STM32G031xx/STM32G041xx");
|
||||
break;
|
||||
// from RM0090 STM32F4x5/STM32F4x7
|
||||
case 0x413:
|
||||
puts("STM32F405/STM32F407/STM32F415/STM32F417");
|
||||
break;
|
||||
case 0x419:
|
||||
puts("STM32F42x/STM32F43x");
|
||||
break;
|
||||
// from RM0368
|
||||
case 0x423:
|
||||
puts("STM32F401xB/C");
|
||||
break;
|
||||
case 0x433:
|
||||
puts("STM32F401xD/E");
|
||||
break;
|
||||
// from RM0383
|
||||
case 0x431:
|
||||
puts("STM32F411xC/E");
|
||||
break;
|
||||
default:
|
||||
puts("unknown");
|
||||
fake = true;
|
||||
break;
|
||||
}
|
||||
printf(" (DEV_ID=0x%03x)\n", dev_id);
|
||||
puts("chip revision: ");
|
||||
const uint16_t rev_id = DBGMCU_IDCODE >> 16;
|
||||
switch (DBGMCU_IDCODE & DBGMCU_IDCODE_DEV_ID_MASK) {
|
||||
case 0x412:
|
||||
if (0x1000 == rev_id) {
|
||||
putc('A');
|
||||
} else {
|
||||
puts("unknown");
|
||||
}
|
||||
break;
|
||||
case 0x410:
|
||||
if (0x0000 == rev_id) {
|
||||
putc('A');
|
||||
} else if (0x2000 == rev_id) {
|
||||
putc('B');
|
||||
} else if (0x2001 == rev_id) {
|
||||
putc('Z');
|
||||
} else if (0x2003 == rev_id) {
|
||||
puts("1/2/3/X/Y");
|
||||
} else {
|
||||
puts("unknown");
|
||||
}
|
||||
break;
|
||||
case 0x414:
|
||||
if (0x1000 == rev_id) {
|
||||
puts("A/1");
|
||||
} else if (0x1001 == rev_id) {
|
||||
putc('Z');
|
||||
} else if (0x1003 == rev_id) {
|
||||
puts("1/2/3/X/Y");
|
||||
} else {
|
||||
puts("unknown");
|
||||
}
|
||||
break;
|
||||
case 0x430:
|
||||
if (0x1003 == rev_id) {
|
||||
puts("A/1");
|
||||
} else {
|
||||
puts("unknown");
|
||||
}
|
||||
break;
|
||||
case 0x418:
|
||||
if (0x1000 == rev_id) {
|
||||
putc('A');
|
||||
} else if (0x1001 == rev_id) {
|
||||
putc('Z');
|
||||
} else {
|
||||
puts("unknown");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("unknown");
|
||||
break;
|
||||
}
|
||||
printf(" (REV_ID=0x%04x)\n", rev_id);
|
||||
// show flash size
|
||||
puts("flash size: ");
|
||||
if (0xffff == DESIG_FLASH_SIZE) {
|
||||
puts("unknown (probably a defective micro-controller\n");
|
||||
} else {
|
||||
printf("%u KB\n", DESIG_FLASH_SIZE);
|
||||
}
|
||||
// display device identity
|
||||
printf("device id: %08x%08x%04x%04x\n", DESIG_UNIQUE_ID2, DESIG_UNIQUE_ID1, DESIG_UNIQUE_ID0 & 0xffff, DESIG_UNIQUE_ID0 >> 16);
|
||||
// from RM0091 STM32F0x8 reference manual (not sure if it applies to F1)
|
||||
printf("- X,Y wafer coordinate: %08x\n", DESIG_UNIQUE_ID0);
|
||||
printf("- lot number: %c%c%c%c%c%c%c\n", DESIG_UNIQUE_ID2 >> 24, DESIG_UNIQUE_ID2 >> 16, DESIG_UNIQUE_ID2 >> 8, DESIG_UNIQUE_ID2 >> 0, DESIG_UNIQUE_ID1 >> 24, DESIG_UNIQUE_ID1 >> 16, DESIG_UNIQUE_ID1 >> 8);
|
||||
printf("- wafer number: %u\n", DESIG_UNIQUE_ID1 & 0xff);
|
||||
// from ARMv7-M and Cortex-M3 TRM
|
||||
// ARMv7-M B3.2.3
|
||||
printf("CPUID: 0x%08x\n", SCB_CPUID);
|
||||
const uint8_t cpuid_implementer = (SCB_CPUID & SCB_CPUID_IMPLEMENTER) >> SCB_CPUID_IMPLEMENTER_LSB;
|
||||
printf("- implementer: %s (0x%02x)\n", 0x41 == cpuid_implementer ? "ARM" : "unknown", cpuid_implementer);
|
||||
const uint8_t cpuid_architecture = (SCB_CPUID & SCB_CPUID_CONSTANT) >> SCB_CPUID_CONSTANT_LSB;
|
||||
puts("- architecture: ");
|
||||
switch (cpuid_architecture) {
|
||||
case 0xc:
|
||||
puts("ARMv6-M");
|
||||
break;
|
||||
case 0xf:
|
||||
puts("ARMv7-M");
|
||||
break;
|
||||
default:
|
||||
fake = true;
|
||||
puts("unknown");
|
||||
}
|
||||
printf(" (0x%x)\n", cpuid_architecture);
|
||||
const uint16_t cpuid_partno = (SCB_CPUID & SCB_CPUID_PARTNO) >> SCB_CPUID_PARTNO_LSB;
|
||||
puts("- part number: ");
|
||||
switch (cpuid_partno) {
|
||||
case 0xC60:
|
||||
puts("Cortex-M0+");
|
||||
break;
|
||||
case 0xC20:
|
||||
puts("Cortex‑M0");
|
||||
break;
|
||||
case 0xC23: // the ARM spec actually mentions 0xC24
|
||||
puts("Cortex‑M3");
|
||||
break;
|
||||
case 0xC24:
|
||||
puts("Cortex‑M4");
|
||||
break;
|
||||
case 0xC27:
|
||||
puts("Cortex‑M7");
|
||||
break;
|
||||
default:
|
||||
fake = true;
|
||||
puts("unknown");
|
||||
}
|
||||
printf(" (0x%03x)\n", cpuid_partno);
|
||||
const uint8_t cpuid_variant = (SCB_CPUID & SCB_CPUID_VARIANT) >> SCB_CPUID_VARIANT_LSB;
|
||||
printf("- variant: %u\n", cpuid_variant);
|
||||
const uint8_t cpuid_revision = (SCB_CPUID & SCB_CPUID_REVISION) >> SCB_CPUID_REVISION_LSB;
|
||||
printf("- revision: %u\n", cpuid_revision);
|
||||
// ARM CoreSight B2.2.2
|
||||
const uint8_t jep106_continuation = *(uint32_t*)0xE00FFFD0 & 0x0f; // DES_2, PIDR4 bits[3:0]
|
||||
const uint8_t jep106_identification = ((*(uint32_t*)0xE00FFFE8 & 0x7) << 4) + ((*(uint32_t*)0xE00FFFE4 >> 4) & 0xf); // DES_0, PIDR1 bits[7:4] JEP106 identification code bits[3:0], DES_1, PIDR2 bits[2:0] JEP106 identification code bits[6:4]
|
||||
const uint16_t pidr_partno = ((*(uint32_t*)0xE00FFFE4 & 0xf) << 8) + (*(uint32_t*)0xE00FFFE0 & 0xff); // PART_0, PIDR0 bits[7:0] Part number bits[7:0], PART_1, PIDR1 bits[3:0] Part number bits[11:8]
|
||||
puts("JEP106 ID: ");
|
||||
if (0 == jep106_continuation && 0x20 == jep106_identification) {
|
||||
puts("STM");
|
||||
} else if (7 == jep106_continuation && 0x51 == jep106_identification) {
|
||||
puts("GigaDevice");
|
||||
} else if (4 == jep106_continuation && 0x3b == jep106_identification) {
|
||||
puts("ARM");
|
||||
} else {
|
||||
puts("unknown");
|
||||
}
|
||||
printf(" (cont.=%u, ID=0x%02x), part=0x%03x\n", jep106_continuation, jep106_identification, pidr_partno);
|
||||
// guess the micro-controller
|
||||
puts("MCU: ");
|
||||
if (1 == cpuid_variant && 1 == cpuid_revision && 0 == jep106_continuation && 0x20 == jep106_identification) { // STM32 uses Cortex-M3 r1p1 and the right JEP106 ID
|
||||
puts("STM32");
|
||||
} else if (2 == cpuid_variant && 1 == cpuid_revision && 7 == jep106_continuation && 0x51 == jep106_identification) { // GD32 uses Cortex-M3 r2p1 and the right JEP106 ID
|
||||
puts("GD32");
|
||||
fake = true;
|
||||
} else if (2 == cpuid_variant && 1 == cpuid_revision && 4 == jep106_continuation && 0x3b == jep106_identification) { // GD32 uses Cortex-M3 r2p1 and ARM JEP106 ID
|
||||
puts("CS32");
|
||||
fake = true;
|
||||
} else {
|
||||
puts("unknown");
|
||||
fake = true;
|
||||
}
|
||||
putc('\n');
|
||||
// detect fake STM32
|
||||
if (0x412 == dev_id || 0x410 == dev_id || 0x414 == dev_id || 0x430 == dev_id || 0x418 == dev_id) { // STM32F10x
|
||||
// the original STM32F10x uses a Cortex-M3 r1p1
|
||||
if (0xC23 != cpuid_partno) { // Cortex-M3
|
||||
fake = true;
|
||||
}
|
||||
if (1 != cpuid_variant) { // r1
|
||||
fake = true;
|
||||
}
|
||||
if (1 != cpuid_revision) { // p1
|
||||
fake = true;
|
||||
}
|
||||
}
|
||||
printf("this %s to be a genuine STM32\n", fake ? "does not seem" : "seems");
|
||||
printf("device serial: %08x%08x%04x%04x\n", DESIG_UNIQUE_ID2, DESIG_UNIQUE_ID1, DESIG_UNIQUE_ID0 & 0xffff, DESIG_UNIQUE_ID0 >> 16); // not that the half-works are reversed in the first word
|
||||
}
|
||||
|
||||
static void command_uptime(void* argument)
|
||||
{
|
||||
(void)argument; // we won't use the argument
|
||||
uint32_t uptime = (rtc_get_counter_val() - time_start) / RTC_TICKS_SECOND; // get time from internal RTC
|
||||
const uint32_t uptime = (rtc_get_counter_val() - time_start) / RTC_TICKS_SECOND; // get time from internal RTC
|
||||
printf("uptime: %u.%02u:%02u:%02u\n", uptime / (24 * 60 * 60), (uptime / (60 * 60)) % 24, (uptime / 60) % 60, uptime % 60);
|
||||
}
|
||||
|
||||
@ -435,9 +202,10 @@ static void command_datetime(void* argument)
|
||||
{
|
||||
char* datetime = (char*)argument; // argument is optional date time
|
||||
if (NULL == argument) { // no date and time provided, just show the current day and time
|
||||
time_t time_rtc = rtc_get_counter_val() / RTC_TICKS_SECOND; // get time from internal RTC
|
||||
struct tm* time_tm = localtime(&time_rtc); // convert time
|
||||
printf("date: %d-%02d-%02d %02d:%02d:%02d\n", 1900 + time_tm->tm_year, time_tm->tm_mon, time_tm->tm_mday, time_tm->tm_hour, time_tm->tm_min, time_tm->tm_sec);
|
||||
const time_t time_rtc = rtc_get_counter_val() / RTC_TICKS_SECOND + rtc_offset; // get time from internal RTC
|
||||
const struct tm* time_tm = localtime(&time_rtc); // convert time
|
||||
const char* days[] = { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"}; // the days of the week
|
||||
printf("date: %s %d-%02d-%02d %02d:%02d:%02d\n", days[time_tm->tm_wday], 1900 + time_tm->tm_year, 1 + time_tm->tm_mon, time_tm->tm_mday, time_tm->tm_hour, time_tm->tm_min, time_tm->tm_sec);
|
||||
} else { // date and time provided, set it
|
||||
const char* malformed = "date and time malformed, expecting YYYY-MM-DD HH:MM:SS\n";
|
||||
struct tm time_tm; // to store the parsed date time
|
||||
@ -450,15 +218,16 @@ static void command_datetime(void* argument)
|
||||
return;
|
||||
}
|
||||
time_tm.tm_year = strtol(&datetime[0], NULL, 10) - 1900; // parse year
|
||||
time_tm.tm_mon = strtol(&datetime[5], NULL, 10); // parse month
|
||||
time_tm.tm_mon = strtol(&datetime[5], NULL, 10) - 1; // parse month
|
||||
time_tm.tm_mday = strtol(&datetime[8], NULL, 10); // parse day
|
||||
time_tm.tm_hour = strtol(&datetime[11], NULL, 10); // parse hour
|
||||
time_tm.tm_min = strtol(&datetime[14], NULL, 10); // parse minutes
|
||||
time_tm.tm_sec = strtol(&datetime[17], NULL, 10); // parse seconds
|
||||
time_t time_rtc = mktime(&time_tm); // get back seconds
|
||||
time_rtc -= rtc_offset; // remove start offset
|
||||
time_start = time_rtc * RTC_TICKS_SECOND + (rtc_get_counter_val() - time_start); // update uptime with current date
|
||||
rtc_set_counter_val(time_rtc * RTC_TICKS_SECOND); // save date/time to internal RTC
|
||||
printf("date and time saved: %d-%02d-%02d %02d:%02d:%02d\n", 1900 + time_tm.tm_year, time_tm.tm_mon, time_tm.tm_mday, time_tm.tm_hour, time_tm.tm_min, time_tm.tm_sec);
|
||||
printf("date and time saved: %d-%02d-%02d %02d:%02d:%02d\n", 1900 + time_tm.tm_year, 1 + time_tm.tm_mon, time_tm.tm_mday, time_tm.tm_hour, time_tm.tm_min, time_tm.tm_sec);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -510,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
|
||||
@ -609,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)
|
||||
}
|
||||
}
|
||||
|
22
bootloader.c
22
bootloader.c
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** USB DFU bootloader
|
||||
* @file bootloader.c
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2019
|
||||
*/
|
||||
/* standard libraries */
|
||||
@ -75,6 +62,9 @@ void main(void)
|
||||
dfu_force = true; // DFU mode forced
|
||||
}
|
||||
#endif // defined(DFU_FORCE_PIN)
|
||||
rcc_periph_clock_disable(RCC_AFIO); // disable alternate function domain to put it back to default
|
||||
rcc_periph_reset_pulse(GPIO_RST(DFU_FORCE_PIN)); // reset pin GPIO domain
|
||||
rcc_periph_clock_disable(GPIO_RCC(DFU_FORCE_PIN)); // disable pin GPIO domain
|
||||
}
|
||||
|
||||
// start application if valid
|
||||
@ -93,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)
|
||||
|
95
global.c
95
global.c
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** global definitions and methods (code)
|
||||
/** global definitions and methods
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
*/
|
||||
/* standard libraries */
|
||||
@ -41,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
|
||||
|
66
global.h
66
global.h
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** global definitions and methods (API)
|
||||
/** global definitions and methods
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
*/
|
||||
#pragma once
|
||||
@ -37,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 \
|
||||
@ -349,6 +373,10 @@
|
||||
#define RCC_GPIO(x) CAT2(RCC_GPIO,x)
|
||||
/** get RCC for GPIO based on GPIO identifier */
|
||||
#define GPIO_RCC(x) RCC_GPIO(GPIO_PORT_ID(x))
|
||||
/** get RST for GPIO based on GPIO identifier */
|
||||
#define RST_GPIO(x) CAT2(RST_GPIO,x)
|
||||
/** get RST for GPIO based on GPIO identifier */
|
||||
#define GPIO_RST(x) RST_GPIO(GPIO_PORT_ID(x))
|
||||
/** get TIM based on TIM identifier */
|
||||
#define TIM(x) CAT2(TIM,x)
|
||||
/** get RCC for timer based on TIM identifier */
|
||||
@ -683,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
|
||||
*/
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to read/write internal flash
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
* @note peripherals used: none
|
||||
*/
|
||||
@ -23,9 +10,9 @@
|
||||
#include <stdlib.h> // general utilities
|
||||
|
||||
/* STM32 (including CM3) libraries */
|
||||
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
|
||||
#include <libopencm3/stm32/flash.h> // flash utilities
|
||||
#include <libopencm3/stm32/desig.h> // device signature definitions
|
||||
#include <libopencm3/stm32/dbgmcu.h> // debug definitions
|
||||
|
||||
/* own libraries */
|
||||
#include "flash_internal.h" // flash storage library API
|
||||
@ -50,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -83,28 +70,14 @@ 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
|
||||
switch (DBGMCU_IDCODE & DBGMCU_IDCODE_DEV_ID_MASK) { // get page size based on family code
|
||||
case 0x412: // low-density, 16-32 kB flash
|
||||
case 0x410: // medium-density, 64-128 kB flash
|
||||
flash_internal_page = 1024;
|
||||
break;
|
||||
case 0x414: // high-density, 256-512 kB flash
|
||||
case 0x430: // XL-density, 768-1024 kB flash
|
||||
case 0x418: // connectivity, 64-256 kB flash
|
||||
flash_internal_page = 2048;
|
||||
break;
|
||||
case 0: // DBGMCU_IDCODE is only accessible in debug mode (this is a known issue documented in STM32F10xxC/D/E Errata sheet, without workaround)
|
||||
default: // unknown
|
||||
if (DESIG_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
|
||||
flash_internal_page = 2048;
|
||||
}
|
||||
} else {
|
||||
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
|
||||
flash_internal_page = 2048;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
flash_internal_page = 2048;
|
||||
}
|
||||
}
|
||||
|
||||
@ -251,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)
|
||||
@ -320,69 +293,50 @@ int32_t flash_internal_eeprom_write(const uint8_t *eeprom, uint16_t size)
|
||||
return rc;
|
||||
}
|
||||
|
||||
uint16_t flash_internal_probe_size(void)
|
||||
uint32_t flash_internal_probe_read_size(void)
|
||||
{
|
||||
if (0 == DESIG_FLASH_SIZE) { // no flash size advertised
|
||||
// we will check is a flash address is readable until a bus fault occurs
|
||||
cm_disable_faults(); // disable all faults, particularly BusFault
|
||||
SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag
|
||||
SCB_CCR |= SCB_CCR_BFHFNMIGN; // ignore bus faults (but still flag them)
|
||||
uint32_t address = FLASH_BASE; // start with the start of flash
|
||||
while (0 == (SCB_CFSR & SCB_CFSR_BFARVALID)) { // until a bus fault occurs
|
||||
(void)*(volatile uint8_t*)address; // access address
|
||||
address++; // got to next address
|
||||
}
|
||||
SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag
|
||||
SCB_CCR &= ~SCB_CCR_BFHFNMIGN; // re-enable bus fault
|
||||
cm_enable_faults(); // re-enable faults
|
||||
|
||||
return address - 1 - FLASH_BASE;
|
||||
}
|
||||
|
||||
uint32_t flash_internal_probe_write_size(void)
|
||||
{
|
||||
if (0 == desig_get_flash_size()) { // no flash size advertised
|
||||
return 0;
|
||||
}
|
||||
|
||||
// get max flash size based on device identifier (DEV_ID)
|
||||
uint32_t flash_size_max = 0; // max flash size (in bytes)
|
||||
switch (DBGMCU_IDCODE & DBGMCU_IDCODE_DEV_ID_MASK) { // get page size based on family code
|
||||
case 0x412: // low-density, 16-32 kB flash
|
||||
flash_size_max = 32;
|
||||
break;
|
||||
case 0x410: // medium-density, 64-128 kB flash
|
||||
flash_size_max = 128;
|
||||
break;
|
||||
case 0x414: // high-density, 256-512 kB flash
|
||||
flash_size_max = 512;
|
||||
break;
|
||||
case 0x430: // XL-density, 768-1024 kB flash
|
||||
flash_size_max = 1024;
|
||||
break;
|
||||
case 0x418: // connectivity, 64-256 kB flash
|
||||
flash_size_max = 256;
|
||||
break;
|
||||
case 0: // DBGMCU_IDCODE is only accessible in debug mode (this is a known issue documented in STM32F10xxC/D/E Errata sheet, without workaround)
|
||||
default: // unknown
|
||||
if ((*(uint32_t*)0x1FFFF000 & 0xFFFE0000) == 0x20000000) { // non-connectivity system memory start detected (MSP address pointing to SRAM
|
||||
switch (DESIG_FLASH_SIZE) {
|
||||
case 16:
|
||||
case 32:
|
||||
flash_size_max = 32; // low-density, 16-32 kB flash
|
||||
break;
|
||||
case 64:
|
||||
case 128:
|
||||
flash_size_max = 128; // medium-density, 64-128 kB flash
|
||||
break;
|
||||
case 256:
|
||||
case 512:
|
||||
flash_size_max = 512; // high-density, 256-512 kB flash
|
||||
break;
|
||||
case 768:
|
||||
case 1024:
|
||||
flash_size_max = 1024; // XL-density, 768-1024 kB flash
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else { // connectivity system memory start is at 0x1FFFB000
|
||||
flash_size_max = 256; // connectivity, 64-256 kB flash
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (0 == flash_size_max) { // could not determine max flash size
|
||||
return 0;
|
||||
}
|
||||
flash_size_max *= 1024; // get in bytes
|
||||
|
||||
// test if page is writable, starting with last one
|
||||
uint32_t flash_size; // tested flash size (in bytes)
|
||||
const uint16_t test_data = 0x2342; // the data we will write and read to test page
|
||||
// prepare for reading the flash
|
||||
cm_disable_faults(); // disable all faults, particularly BusFault
|
||||
SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag
|
||||
SCB_CCR |= SCB_CCR_BFHFNMIGN; // ignore bus faults (but still flag them)
|
||||
// prepare for writing the flash
|
||||
flash_unlock(); // unlock flash to be able to write it
|
||||
for (flash_size = DESIG_FLASH_SIZE * 1024 - flash_internal_page_size(); flash_size < flash_size_max; flash_size += flash_internal_page_size()) { // don't exceed max size else it will erase the first page (weird behaviour)
|
||||
uint32_t address = FLASH_BASE + flash_size;
|
||||
// try reading and writing the flash, page per page
|
||||
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;
|
||||
}
|
||||
uint32_t address = start; // address to test
|
||||
const uint16_t test_data = 0x2342; // the data we will write and read to test page
|
||||
while (address < 0x1FFFEFFF) { // this is where the system memory starts
|
||||
// try reading the flash
|
||||
(void)*(volatile uint32_t*)address; // access address
|
||||
if (0 != (SCB_CFSR & SCB_CFSR_BFARVALID)) { // until a bus fault occurs
|
||||
break; // page not readable
|
||||
}
|
||||
// try writing the flash
|
||||
flash_erase_page(address); // erase current page
|
||||
if (flash_get_status_flags() != FLASH_SR_EOP) { // operation went wrong
|
||||
break;
|
||||
@ -398,12 +352,13 @@ uint16_t flash_internal_probe_size(void)
|
||||
if (flash_get_status_flags() != FLASH_SR_EOP) { // operation went wrong
|
||||
break;
|
||||
}
|
||||
address += flash_internal_page_size(); // go to next page
|
||||
}
|
||||
flash_lock();
|
||||
flash_clear_status_flags(); // clear all flag
|
||||
flash_lock(); // protect again from writing
|
||||
SCB_CFSR |= SCB_CFSR_BFARVALID; // clear bus fault flag
|
||||
SCB_CCR &= ~SCB_CCR_BFHFNMIGN; // re-enable bus fault
|
||||
cm_enable_faults(); // re-enable faults
|
||||
|
||||
if (flash_size < DESIG_FLASH_SIZE * 1024) { // less than advertised size
|
||||
return 0;
|
||||
} else {
|
||||
return flash_size / 1024;
|
||||
}
|
||||
return address - start;
|
||||
}
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to read/write internal flash
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
* @note peripherals used: none
|
||||
*/
|
||||
@ -57,8 +44,11 @@ bool flash_internal_eeprom_read(uint8_t *eeprom, uint16_t size);
|
||||
* @return number of bytes written (including preserved data), or negative in case of error
|
||||
*/
|
||||
int32_t flash_internal_eeprom_write(const uint8_t *eeprom, uint16_t size);
|
||||
/** probe the real size of the internal flash
|
||||
* @return tested size (in KiB), or 0 if less than advertised
|
||||
* @warning it will write the last page of the advertised size
|
||||
/** probe the readable size of the internal flash
|
||||
* @return tested size (in bytes)
|
||||
*/
|
||||
uint16_t flash_internal_probe_size(void);
|
||||
uint32_t flash_internal_probe_read_size(void);
|
||||
/** probe the additional writable size of the internal flash, after the advertised size (and linker provided)
|
||||
* @return tested size (in bytes)
|
||||
*/
|
||||
uint32_t flash_internal_probe_write_size(void);
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to communicate with an SD card flash memory using the SPI mode (code)
|
||||
* @file flash_sdcard.c
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017
|
||||
* @note peripherals used: SPI @ref flash_sdcard_spi
|
||||
* @warning all calls are blocking
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to communicate with an SD card flash memory using the SPI mode (API)
|
||||
* @file flash_sdcard.h
|
||||
/** library to communicate with an SD card flash memory using the SPI mode
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017
|
||||
* @note peripherals used: SPI @ref flash_sdcard_spi
|
||||
* @warning all calls are blocking
|
||||
|
324
lib/font.c
Normal file
324
lib/font.c
Normal file
@ -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,
|
||||
},
|
||||
};
|
28
lib/font.h
Normal file
28
lib/font.h
Normal file
@ -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];
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to communicate using I²C as master (code)
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2020
|
||||
* @note peripherals used: I2C
|
||||
*/
|
||||
@ -196,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
|
||||
@ -284,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
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to communicate using I²C as master (API)
|
||||
/** library to communicate using I²C as master
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2020
|
||||
* @note peripherals used: I2C
|
||||
*/
|
||||
|
@ -1,19 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** BusVoodoo runtime interrupt table
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2018
|
||||
*/
|
||||
#include "interrupt.h" // own definitions
|
||||
|
@ -1,19 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** BusVoodoo runtime interrupt table
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2018
|
||||
*/
|
||||
#pragma once
|
||||
|
15
lib/ir_nec.c
15
lib/ir_nec.c
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to decode InfraRed NEC code
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2018-2020
|
||||
* @note peripherals used: timer channel @ref ir_nec_timer
|
||||
*/
|
||||
|
15
lib/ir_nec.h
15
lib/ir_nec.h
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to decode InfraRed NEC code
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2018-2020
|
||||
* @note peripherals used: timer channel @ref ir_nec_timer
|
||||
*/
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to communication with Hitacho HD44780 LCD controller
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2019-2020
|
||||
* @note peripherals used: GPIO @ref lcd_hd44780_gpio
|
||||
*/
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to communication with Hitacho HD44780 LCD controller
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2019-2020
|
||||
* @note peripherals used: GPIO @ref lcd_hd44780_gpio, I²C @ref lcd_hd44780_i2c
|
||||
*/
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to communicate with a Titan Micro MAX7219 IC attached to a 4-digit 7-segment (code)
|
||||
* @file led_max7219.c
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017
|
||||
* @note peripherals used: GPIO @ref led_max7219_gpio, timer @ref led_tm1637_timer
|
||||
* @warning all calls are blocking
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to communicate with a Maxim MAX7219 IC attached to a 8-digit 7-segment (API)
|
||||
* @file led_max7219.h
|
||||
/** library to communicate with a Maxim MAX7219 IC attached to a 8-digit 7-segment
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017
|
||||
* @note peripherals used: GPIO @ref led_max7219_gpio, SPI @ref led_max7219_spi
|
||||
* @warning all calls are blocking
|
||||
|
115
lib/led_sk6812rgbw.c
Normal file
115
lib/led_sk6812rgbw.c
Normal file
@ -0,0 +1,115 @@
|
||||
/** library to drive a SK6812RGBW LED chain
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
* @note peripherals used: SPI @ref led_sk6812rgbw_spi, DMA
|
||||
*/
|
||||
|
||||
/* standard libraries */
|
||||
#include <stdint.h> // standard integer types
|
||||
#include <stdlib.h> // general 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
|
||||
#include <libopencm3/stm32/dma.h> // DMA library
|
||||
#include <libopencm3/cm3/nvic.h> // interrupt handler
|
||||
|
||||
#include "led_sk6812rgbw.h" // LED SK6812RGBW library API
|
||||
#include "global.h" // common methods
|
||||
|
||||
/** peripheral configuration */
|
||||
/** @defgroup led_sk6812rgbw_spi SPI peripheral used to control the SK6812RGBW LEDs
|
||||
* @{
|
||||
*/
|
||||
/** SPI peripheral
|
||||
* @note SPI2 is 5V tolerant and can be operated in open-drain mode with 1-10 kO pull-up resistor to 5V to get 5V signal level
|
||||
*/
|
||||
#define LED_SK6812RGBW_SPI 2 /**< SPI peripheral */
|
||||
/** @} */
|
||||
|
||||
/** bit template to encode one byte to be shifted out by SPI to the SK6812RGBW LEDs
|
||||
* @details For each SK6812RGBW bit which needs to be transferred we require to transfer 3 SPI bits.
|
||||
* The first SPI bit is the high start of the SK6812RGBW bit frame.
|
||||
* The second SPI bit determines if the SK6812RGBW bit is a 0 or 1.
|
||||
* The third SPI bit is the last part of the SK6812RGBW bit frame, which is always low.
|
||||
* The binary pattern is 0b100100100100100100100100
|
||||
* Since we encode it LSb first (for easy bit banding across bytes): 01001001 10010010 00100100 = 0x499224
|
||||
*/
|
||||
#define LED_SK6812RGBW_SPI_TEMPLATE 0x499224
|
||||
|
||||
/** data encoded to be shifted out by SPI for the SK6812RGBW, plus the 50us reset (~40 data bits) */
|
||||
static uint8_t led_sk6812rgbw_data[LED_SK6812RGBW_LEDS * 3 * 4 + 40 * 3 / 8 + 1] = {0};
|
||||
|
||||
void led_sk6812rgbw_set_rgb(uint16_t led, uint8_t red, uint8_t green, uint8_t blue, uint8_t white)
|
||||
{
|
||||
// verify the led exists
|
||||
if (led >= LED_SK6812RGBW_LEDS) {
|
||||
return;
|
||||
}
|
||||
|
||||
// we use bit-banding to set the bit. I did not test if it is more efficient than setting the bit directly, but I wanted to use this cool feature
|
||||
uint32_t data_bitband = 0x22000000 + ((uint32_t)&led_sk6812rgbw_data[0] - 0x20000000) * 32; // get base address of data to bit band
|
||||
data_bitband += led * 4 * 24 * 4; // get address for current LED (there are 4 colors per LED, each color uses 24 bits, each bit is encoded in one 32-bit word)
|
||||
for (uint8_t bit = 0; bit < 8; bit++) { // bit from the color to set/clear
|
||||
const uint8_t byte_bit = (1 << bit); // which bit to check
|
||||
const uint8_t data_bit = (7 - bit) * 3 + 1; // the bit in the data stream
|
||||
// set the bitband bit to the corresponding color bit
|
||||
*(uint32_t*)(data_bitband + (0 * 24 + data_bit) * 4) = ((green & byte_bit) ? 1 : 0);
|
||||
*(uint32_t*)(data_bitband + (1 * 24 + data_bit) * 4) = ((red & byte_bit) ? 1 : 0);
|
||||
*(uint32_t*)(data_bitband + (2 * 24 + data_bit) * 4) = ((blue & byte_bit) ? 1 : 0);
|
||||
*(uint32_t*)(data_bitband + (3 * 24 + data_bit) * 4) = ((white & byte_bit) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
void led_sk6812rgbw_setup(void)
|
||||
{
|
||||
// fill buffer with bit pattern
|
||||
for (uint16_t led = 0; led < LED_SK6812RGBW_LEDS; led++) {
|
||||
for (uint8_t color = 0; color < 4; color++) {
|
||||
led_sk6812rgbw_data[led * 3 * 4 + color * 3 + 0] = (uint8_t)(LED_SK6812RGBW_SPI_TEMPLATE >> 16);
|
||||
led_sk6812rgbw_data[led * 3 * 4 + color * 3 + 1] = (uint8_t)(LED_SK6812RGBW_SPI_TEMPLATE >> 8);
|
||||
led_sk6812rgbw_data[led * 3 * 4 + color * 3 + 2] = (uint8_t)(LED_SK6812RGBW_SPI_TEMPLATE >> 0);
|
||||
}
|
||||
}
|
||||
// fill remaining with with 0 to encode the reset code
|
||||
for (uint16_t i = LED_SK6812RGBW_LEDS * 3 * 4; i < LENGTH(led_sk6812rgbw_data); i++) {
|
||||
led_sk6812rgbw_data[i] = 0;
|
||||
}
|
||||
|
||||
// setup SPI to transmit data
|
||||
rcc_periph_clock_enable(RCC_SPI_MOSI_PORT(LED_SK6812RGBW_SPI)); // enable clock for SPI output peripheral
|
||||
gpio_set_mode(SPI_MOSI_PORT(LED_SK6812RGBW_SPI), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, SPI_MOSI_PIN(LED_SK6812RGBW_SPI)); // set MOSI as output (push-pull needs to be shifted to 5V, open-drain consumes up to 5 mA with pull-up resistor of 1 kO)
|
||||
rcc_periph_clock_enable(RCC_AFIO); // enable clock for SPI alternate function
|
||||
rcc_periph_clock_enable(RCC_SPI(LED_SK6812RGBW_SPI)); // enable clock for SPI peripheral
|
||||
spi_reset(SPI(LED_SK6812RGBW_SPI)); // clear SPI values to default
|
||||
spi_set_master_mode(SPI(LED_SK6812RGBW_SPI)); // set SPI as master since we generate the clock
|
||||
spi_set_baudrate_prescaler(SPI(LED_SK6812RGBW_SPI), SPI_CR1_BR_FPCLK_DIV_16); // set clock to 0.44 us per bit
|
||||
spi_set_clock_polarity_1(SPI(LED_SK6812RGBW_SPI)); // clock is high when idle
|
||||
spi_set_clock_phase_1(SPI(LED_SK6812RGBW_SPI)); // output data on second edge (rising)
|
||||
spi_set_dff_8bit(SPI(LED_SK6812RGBW_SPI)); // use 8 bits for simpler encoding (but there will be more interrupts)
|
||||
spi_send_lsb_first(SPI(LED_SK6812RGBW_SPI)); // send least significant bit first (helps during bit banding)
|
||||
spi_set_bidirectional_transmit_only_mode(SPI(LED_SK6812RGBW_SPI)); // we won't receive data
|
||||
spi_set_unidirectional_mode(SPI(LED_SK6812RGBW_SPI)); // we only need to transmit data
|
||||
spi_enable_software_slave_management(SPI(LED_SK6812RGBW_SPI)); // control the slave select in software (so we can start transmission)
|
||||
spi_set_nss_high(SPI(LED_SK6812RGBW_SPI)); // set NSS high so we can output
|
||||
spi_enable(SPI(LED_SK6812RGBW_SPI)); // enable SPI
|
||||
|
||||
// configure DMA to provide the pattern to be shifted out from SPI to the SK6812RGBW LEDs
|
||||
rcc_periph_clock_enable(RCC_DMA_SPI(LED_SK6812RGBW_SPI)); // enable clock for DMA peripheral
|
||||
dma_channel_reset(DMA_SPI(LED_SK6812RGBW_SPI), DMA_CHANNEL_SPI_TX(LED_SK6812RGBW_SPI)); // start with fresh channel configuration
|
||||
dma_set_read_from_memory(DMA_SPI(LED_SK6812RGBW_SPI), DMA_CHANNEL_SPI_TX(LED_SK6812RGBW_SPI)); // set direction from memory to peripheral
|
||||
dma_set_memory_size(DMA_SPI(LED_SK6812RGBW_SPI), DMA_CHANNEL_SPI_TX(LED_SK6812RGBW_SPI), DMA_CCR_MSIZE_8BIT); // read 8 bits from memory
|
||||
dma_set_memory_address(DMA_SPI(LED_SK6812RGBW_SPI), DMA_CHANNEL_SPI_TX(LED_SK6812RGBW_SPI), (uint32_t)led_sk6812rgbw_data); // set bit pattern as source address
|
||||
dma_enable_memory_increment_mode(DMA_SPI(LED_SK6812RGBW_SPI), DMA_CHANNEL_SPI_TX(LED_SK6812RGBW_SPI)); // go through bit pattern
|
||||
dma_set_number_of_data(DMA_SPI(LED_SK6812RGBW_SPI), DMA_CHANNEL_SPI_TX(LED_SK6812RGBW_SPI), LENGTH(led_sk6812rgbw_data)); // set the size of the data to transmit
|
||||
dma_set_peripheral_address(DMA_SPI(LED_SK6812RGBW_SPI), DMA_CHANNEL_SPI_TX(LED_SK6812RGBW_SPI), (uint32_t)&SPI_DR(SPI(LED_SK6812RGBW_SPI))); // set SPI as peripheral destination address
|
||||
dma_set_peripheral_size(DMA_SPI(LED_SK6812RGBW_SPI), DMA_CHANNEL_SPI_TX(LED_SK6812RGBW_SPI), DMA_CCR_PSIZE_8BIT); // write 8 bits to peripheral
|
||||
dma_set_priority(DMA_SPI(LED_SK6812RGBW_SPI), DMA_CHANNEL_SPI_TX(LED_SK6812RGBW_SPI), DMA_CCR_PL_HIGH); // set priority to high since time is crucial for the peripheral
|
||||
dma_enable_circular_mode(DMA_SPI(LED_SK6812RGBW_SPI), DMA_CHANNEL_SPI_TX(LED_SK6812RGBW_SPI)); // always send the data
|
||||
dma_enable_channel(DMA_SPI(LED_SK6812RGBW_SPI), DMA_CHANNEL_SPI_TX(LED_SK6812RGBW_SPI)); // enable DMA channel
|
||||
spi_enable_tx_dma(SPI(LED_SK6812RGBW_SPI)); // use DMA to provide data stream to be transferred
|
||||
}
|
24
lib/led_sk6812rgbw.h
Normal file
24
lib/led_sk6812rgbw.h
Normal file
@ -0,0 +1,24 @@
|
||||
/** library to drive a SK6812RGBW LED chain
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
* @note peripherals used: SPI @ref led_sk6812rgbw_spi, DMA
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/** number of LEDs on the SK6812RGBW strip */
|
||||
#define LED_SK6812RGBW_LEDS (16 * 2)
|
||||
|
||||
/** setup SK6812RGBW LED driver
|
||||
* @note this starts the continuous transmission
|
||||
*/
|
||||
void led_sk6812rgbw_setup(void);
|
||||
/** set color of a single LED
|
||||
* @param[in] led the LED number to set the color
|
||||
* @param[in] red the red color value to set on the LED
|
||||
* @param[in] green the green color value to set on the LED
|
||||
* @param[in] blue the blue color value to set on the LED
|
||||
* @param[in] white the white color value to set on the LED
|
||||
*/
|
||||
void led_sk6812rgbw_set_rgb(uint16_t led, uint8_t red, uint8_t green, uint8_t blue, uint8_t white);
|
124
lib/led_tm1637.c
124
lib/led_tm1637.c
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to communicate with a Titan Micro TM1637 IC attached to a 4-digit 7-segment
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2020
|
||||
* @note peripherals used: GPIO @ref led_tm1637_gpio, timer @ref led_tm1637_timer
|
||||
* @note the protocol is very similar to I2C but incompatible for the following reasons: the capacitance is too large for open-drain type output with weak pull-up resistors (push-pull needs to be used, preventing to get ACKs since no indication of the ACK timing is provided); the devices doesn't use addresses; the STM32 I2C will switch to receiver mode when the first sent byte (the I2C address) has last bit set to 1 (such as for address commands with B7=1 where B7 is transmitted last), preventing to send further bytes (the data byte after the address)
|
||||
@ -59,6 +46,10 @@
|
||||
static enum led_tm1637_brightness_t display_brightness = LED_TM1637_14DIV16;
|
||||
/** if display is on */
|
||||
static bool display_on = false;
|
||||
/** display buffer */
|
||||
static uint8_t led_tm1637_digits[4] = {0};
|
||||
/** if the display is upside down */
|
||||
bool led_tm1637_updown = false;
|
||||
|
||||
/** ASCII characters encoded for the 7 segments digit block
|
||||
* @note starts with space
|
||||
@ -161,8 +152,24 @@ static const uint8_t ascii_7segments[] = {
|
||||
0x40, // 0b01000000 ~
|
||||
};
|
||||
|
||||
void led_tm1637_setup(void)
|
||||
|
||||
/** convert the data for upside down displays
|
||||
* @param[in] data the data to be converted
|
||||
* @return the upside down data
|
||||
*/
|
||||
static uint8_t led_tm1637_rotate(uint8_t data)
|
||||
{
|
||||
uint8_t converted = 0;
|
||||
converted |= (data & 0xc0); // keep g and dot
|
||||
converted |= (data << 3) & 0x38; // rotate abc
|
||||
converted |= (data >> 3) & 0x07; // rotate def
|
||||
return converted;
|
||||
}
|
||||
|
||||
void led_tm1637_setup(bool updown)
|
||||
{
|
||||
led_tm1637_updown = updown; // remember if the display is upside down
|
||||
|
||||
// configure GPIO for CLK and DIO signals
|
||||
rcc_periph_clock_enable(GPIO_RCC(LED_TM1637_CLK_PIN)); // enable clock for GPIO peripheral
|
||||
gpio_set(GPIO_PORT(LED_TM1637_CLK_PIN), GPIO_PIN(LED_TM1637_CLK_PIN)); // idle high
|
||||
@ -177,7 +184,7 @@ void led_tm1637_setup(void)
|
||||
rcc_periph_reset_pulse(RST_TIM(LED_TM1637_TIMER)); // reset timer state
|
||||
timer_set_mode(TIM(LED_TM1637_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(LED_TM1637_TIMER), 0); // don't prescale to get most precise timing ( 1/(72E6/1/(2**16))=0.91 ms > 0.5 us )
|
||||
timer_set_period(TIM(LED_TM1637_TIMER), 500); // set the clock frequency (emprical value until the signal starts to look bad)
|
||||
timer_set_period(TIM(LED_TM1637_TIMER), 500); // set the clock frequency (empirical value until the signal starts to look bad)
|
||||
timer_clear_flag(TIM(LED_TM1637_TIMER), TIM_SR_UIF); // clear flag
|
||||
timer_update_on_overflow(TIM(LED_TM1637_TIMER)); // only use counter overflow as UEV source (use overflow as start time or timeout)
|
||||
}
|
||||
@ -251,79 +258,79 @@ static bool led_tm1637_write(const uint8_t* data, size_t length)
|
||||
return to_return;
|
||||
}
|
||||
|
||||
/** write all data to SRAM (automatic address adding, normal) and control display
|
||||
* @return if write succeeded
|
||||
*/
|
||||
static bool led_tm1637_update(void)
|
||||
{
|
||||
const uint8_t write[] = { 0x40 }; // command: write data, automatic address adding, normal
|
||||
uint8_t data[] = { 0xc0, led_tm1637_digits[0], led_tm1637_digits[1], led_tm1637_digits[2], led_tm1637_digits[3] }; // set digits (start at address 0)
|
||||
if (led_tm1637_updown) { // rotate data
|
||||
data[1] = led_tm1637_rotate(led_tm1637_digits[3]);
|
||||
data[2] = led_tm1637_rotate(led_tm1637_digits[2]) | (led_tm1637_digits[1] & 0x80); // keep the : for the time
|
||||
data[3] = led_tm1637_rotate(led_tm1637_digits[1]) & 0x7f; // remove the : for the time
|
||||
data[4] = led_tm1637_rotate(led_tm1637_digits[0]);
|
||||
}
|
||||
const uint8_t control[] = { (display_on ? 0x88 : 0x80) + (display_brightness & 0x7) }; // command to turn display on/off and set brightness
|
||||
return led_tm1637_write(write, LENGTH(write)) && led_tm1637_write(data, LENGTH(data)) && led_tm1637_write(control, LENGTH(control)); // send commands
|
||||
}
|
||||
|
||||
bool led_tm1637_on(void)
|
||||
{
|
||||
uint8_t data[] = { 0x88 + display_brightness }; // command to turn display on (use set brightness)
|
||||
bool to_return = false; // result to return
|
||||
if (led_tm1637_write(data, LENGTH(data))) { // send command
|
||||
display_on = true; // remember display is on
|
||||
to_return = true; // command succeeded
|
||||
}
|
||||
return to_return; // return result
|
||||
display_on = true; // remember display is on
|
||||
return led_tm1637_update();
|
||||
}
|
||||
|
||||
bool led_tm1637_off(void)
|
||||
{
|
||||
uint8_t data[] = { 0x80 + display_brightness }; // command to turn display off (use set brightness)
|
||||
if (led_tm1637_write(data, LENGTH(data))) { // send command
|
||||
display_on = false; // remember display is off
|
||||
return true; // command succeeded
|
||||
}
|
||||
return false; // return result
|
||||
display_on = false; // remember display is off
|
||||
return led_tm1637_update();
|
||||
}
|
||||
|
||||
bool led_tm1637_brightness(enum led_tm1637_brightness_t brightness)
|
||||
{
|
||||
display_brightness = brightness; // save brightness
|
||||
if (display_on) { // adjust brightness if display is on
|
||||
return led_tm1637_on(); // adjust brightness
|
||||
} else {
|
||||
return true; // command succeeded
|
||||
}
|
||||
return false;
|
||||
return led_tm1637_update();
|
||||
}
|
||||
|
||||
bool led_tm1637_number(uint16_t number, bool zero)
|
||||
{
|
||||
const uint8_t write_data[] = { 0x40 }; // command: write data, automatic address adding, normal
|
||||
const uint8_t digits[] = { // digits to display
|
||||
(number / 1000) % 10,
|
||||
(number / 100) % 10,
|
||||
(number / 10) % 10,
|
||||
(number / 1) % 10,
|
||||
};
|
||||
uint8_t data[] = { 0xc0, 0, 0, 0, 0}; // number to be displayed
|
||||
// convert digits to text to be displayed
|
||||
if (0 == digits[0] && !zero) {
|
||||
data[1] = ascii_7segments[' ' - ' '];
|
||||
led_tm1637_digits[0] = ascii_7segments[' ' - ' '];
|
||||
} else {
|
||||
data[1] = ascii_7segments[digits[0] + '0' - ' '];
|
||||
led_tm1637_digits[0] = ascii_7segments[digits[0] + '0' - ' '];
|
||||
}
|
||||
if (0 == digits[0] && 0 == digits[1] && !zero) {
|
||||
data[2] = ascii_7segments[' ' - ' '];
|
||||
led_tm1637_digits[1] = ascii_7segments[' ' - ' '];
|
||||
} else {
|
||||
data[2] = ascii_7segments[digits[1] + '0' - ' '];
|
||||
led_tm1637_digits[1] = ascii_7segments[digits[1] + '0' - ' '];
|
||||
}
|
||||
if (0 == digits[0] && 0 == digits[1] && 0 == digits[2] && !zero) {
|
||||
data[3] = ascii_7segments[' ' - ' '];
|
||||
led_tm1637_digits[2] = ascii_7segments[' ' - ' '];
|
||||
} else {
|
||||
data[3] = ascii_7segments[digits[2] + '0' - ' '];
|
||||
led_tm1637_digits[2] = ascii_7segments[digits[2] + '0' - ' '];
|
||||
}
|
||||
data[4] = ascii_7segments[digits[3] + '0' - ' '];
|
||||
led_tm1637_digits[3] = ascii_7segments[digits[3] + '0' - ' '];
|
||||
|
||||
// display number
|
||||
return led_tm1637_write(write_data, LENGTH(write_data)) && led_tm1637_write(data, LENGTH(data)); // send commands
|
||||
return led_tm1637_update(); // display number
|
||||
}
|
||||
|
||||
bool led_tm1637_time(uint8_t hours, uint8_t minutes)
|
||||
{
|
||||
uint8_t write_data[] = { 0x40 }; // command: write data, automatic address adding, normal
|
||||
uint8_t data[] = { 0xc0, ascii_7segments[((hours / 10) % 10) + '0' - ' '], ascii_7segments[((hours / 1) % 10) + '0' - ' '] | 0x80, ascii_7segments[((minutes / 10) % 10) + '0' - ' '], ascii_7segments[((minutes / 1) % 10) + '0' - ' '] }; // set address C0H and add data
|
||||
// set digits
|
||||
led_tm1637_digits[0] = ascii_7segments[((hours / 10) % 10) + '0' - ' '];
|
||||
led_tm1637_digits[1] = ascii_7segments[((hours / 1) % 10) + '0' - ' '] | 0x80;
|
||||
led_tm1637_digits[2] = ascii_7segments[((minutes / 10) % 10) + '0' - ' '];
|
||||
led_tm1637_digits[3] = ascii_7segments[((minutes / 1) % 10) + '0' - ' '];
|
||||
|
||||
if (led_tm1637_write(write_data, LENGTH(write_data)) && led_tm1637_write(data, LENGTH(data))) { // send commands
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return led_tm1637_update(); // display time
|
||||
}
|
||||
|
||||
bool led_tm1637_text(char* text)
|
||||
@ -336,11 +343,12 @@ bool led_tm1637_text(char* text)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
uint8_t write_data[] = { 0x40 }; // command: write data, automatic address adding, normal
|
||||
uint8_t data[] = { 0xc0, ascii_7segments[(text[0] & 0x7f) - ' '] | (text[0] & 0x80), ascii_7segments[(text[1] & 0x7f) - ' ']|(text[1] & 0x80), ascii_7segments[(text[2] & 0x7f) - ' ']|(text[2] & 0x80), ascii_7segments[(text[3] & 0x7f) - ' ']|(text[3] & 0x80) }; // set address C0H and add data
|
||||
|
||||
if (led_tm1637_write(write_data, LENGTH(write_data)) && led_tm1637_write(data, LENGTH(data))) { // send commands
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
// set digits
|
||||
led_tm1637_digits[0] = ascii_7segments[(text[0] & 0x7f) - ' '] | (text[0] & 0x80);
|
||||
led_tm1637_digits[1] = ascii_7segments[(text[1] & 0x7f) - ' '] | (text[1] & 0x80);
|
||||
led_tm1637_digits[2] = ascii_7segments[(text[2] & 0x7f) - ' '] | (text[2] & 0x80);
|
||||
led_tm1637_digits[3] = ascii_7segments[(text[3] & 0x7f) - ' '] | (text[3] & 0x80);
|
||||
|
||||
return led_tm1637_update(); // display test
|
||||
}
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to communicate with a Titan Micro TM1637 IC attached to a 4-digit 7-segment
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2020
|
||||
* @note peripherals used: GPIO @ref led_tm1637_gpio, timer @ref led_tm1637_timer
|
||||
* @warning all calls are blocking
|
||||
@ -35,8 +22,9 @@ enum led_tm1637_brightness_t {
|
||||
};
|
||||
|
||||
/** setup communication with TM1637 IC
|
||||
* @param[in] updown if the display is upside down
|
||||
*/
|
||||
void led_tm1637_setup(void);
|
||||
void led_tm1637_setup(bool updown);
|
||||
/** switch display on
|
||||
* @return if transmission succeeded
|
||||
*/
|
||||
@ -69,4 +57,3 @@ bool led_tm1637_time(uint8_t hours, uint8_t minutes);
|
||||
* @return if transmission succeeded
|
||||
*/
|
||||
bool led_tm1637_text(char* text);
|
||||
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to drive a WS2812B LED chain
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
* @note peripherals used: SPI @ref led_ws2812b_spi, DMA
|
||||
*/
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to drive a WS2812B LED chain
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
* @note peripherals used: SPI @ref led_ws2812b_spi, DMA
|
||||
*/
|
||||
@ -23,13 +10,14 @@
|
||||
/** number of LEDs on the WS2812B strip */
|
||||
#define LED_WS2812B_LEDS 48
|
||||
|
||||
/** setup WS2812B LED driver */
|
||||
/** setup WS2812B LED driver
|
||||
* @note this starts the continuous transmission
|
||||
*/
|
||||
void led_ws2812b_setup(void);
|
||||
/** set color of a single LED
|
||||
* @param[in] led the LED number to set the color
|
||||
* @param[in] red the red color value to set on the LED
|
||||
* @param[in] green the green color value to set on the LED
|
||||
* @param[in] blue the blue color value to set on the LED
|
||||
* @note transmission needs to be done separately
|
||||
*/
|
||||
void led_ws2812b_set_rgb(uint16_t led, uint8_t red, uint8_t green, uint8_t blue);
|
||||
|
17
lib/menu.c
17
lib/menu.c
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** definitions to build menus (code)
|
||||
* @file menu.c
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2018
|
||||
*/
|
||||
/* standard libraries */
|
||||
|
19
lib/menu.h
19
lib/menu.h
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** definitions to build menus (API)
|
||||
* @file menu.h
|
||||
/** definitions to build menus
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2018
|
||||
*/
|
||||
#pragma once
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to communicate using microwore as master
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2020
|
||||
* @note peripherals used: GPIO @ref microwire_master_gpio, timer @ref microwire_master_timer
|
||||
* microwire is a 3-Wire half-duplex synchronous bus. It is very similar to SPI without fixed length messages (bit-wised).
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to communicate using microwore as master
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2020
|
||||
* @note peripherals used: GPIO @ref microwire_master_gpio, timer @ref microwire_master_timer
|
||||
* microwire is a 3-Wire half-duplex synchronous bus. It is very similar to SPI without fixed length messages (bit-wised).
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** SSD1306 OLED library (code)
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2018-2019
|
||||
* @note peripherals used: I2C @ref oled_ssd1306_i2c
|
||||
*/
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** SSD1306 OLED library (API)
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2018-2019
|
||||
* @note peripherals used: I2C @ref oled_ssd1306_i2c
|
||||
*/
|
||||
|
133
lib/oled_text.c
Normal file
133
lib/oled_text.c
Normal file
@ -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
|
||||
}
|
||||
}
|
32
lib/oled_text.h
Normal file
32
lib/oled_text.h
Normal file
@ -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,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library for 1-wire protocol as master
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2020
|
||||
* @note peripherals used: timer @ref onewire_master_timer, GPIO @ref onewire_master_gpio
|
||||
* @note overdrive mode is not provided
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library for 1-wire protocol as master
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2020
|
||||
* @note peripherals used: timer @ref onewire_master_timer, GPIO @ref onewire_master_gpio
|
||||
* @note overdrive mode is not provided
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library for 1-wire protocol as master
|
||||
* @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 onewire_slave_timer, GPIO @ref onewire_slave_gpio
|
||||
* @note overdrive mode is not supported
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library for 1-wire protocol as slave
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2020
|
||||
* @note peripherals used: timer @ref onewire_slave_timer, GPIO @ref onewire_slave_gpio
|
||||
* @note overdrive mode is not supported
|
||||
|
61
lib/print.c
61
lib/print.c
@ -1,21 +1,8 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** printing utilities to replace the large printf from the standard library (code)
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @date 2017-2019
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2020
|
||||
*/
|
||||
/* standard libraries */
|
||||
#include <stdint.h> // standard integer types
|
||||
@ -107,7 +94,7 @@ static size_t print_unsigned(char** str, size_t* size, uint64_t u, uint32_t padd
|
||||
uint8_t digits = 0; // to count the number of digits
|
||||
size_t length = 0; // number of characters printed
|
||||
do {
|
||||
number[digits++] = '0'+(u % 10); // store digit
|
||||
number[digits++] = '0' + (u % 10); // store digit
|
||||
u /= 10; // go to next digit
|
||||
} while (u > 0);
|
||||
if (sign) { // print sign
|
||||
@ -132,9 +119,9 @@ static size_t print_unsigned(char** str, size_t* size, uint64_t u, uint32_t padd
|
||||
**/
|
||||
static size_t print_signed(char** str, size_t* size, int64_t d, uint32_t padding, bool sign) {
|
||||
size_t length = 0; // number of characters printed
|
||||
if (d<0) {
|
||||
if (d < 0) {
|
||||
print_printed(&length, print_char(str, size, '-')); // print sign
|
||||
print_printed(&length, print_unsigned(str, size, (uint64_t)-d, padding, false)); // print number (casting because there is one more negative value then positive value)
|
||||
print_printed(&length, print_unsigned(str, size, (uint64_t) - d, padding, false)); // print number (casting because there is one more negative value then positive value)
|
||||
} else {
|
||||
print_printed(&length, print_unsigned(str, size, d, padding, sign)); // print number
|
||||
}
|
||||
@ -155,14 +142,14 @@ static size_t print_float(char** str, size_t* size, double f, uint32_t padding,
|
||||
if (isnan(f)) { // not a number
|
||||
print_printed(&length, print_string(str, size, "NaN")); // print NaN
|
||||
} else if (isinf(f)) { // infinite
|
||||
if (-1==isinf(f)) {
|
||||
if (-1 == isinf(f)) {
|
||||
print_printed(&length, print_char(str, size, '-')); // print sign
|
||||
} else if (sign) {
|
||||
print_printed(&length, print_char(str, size, '+')); // print sign
|
||||
}
|
||||
print_printed(&length, print_string(str, size, "inf")); // print inf
|
||||
} else if (isnormal(f)) { // it should be not 0
|
||||
if (f<0) {
|
||||
if (f < 0) {
|
||||
print_printed(&length, print_char(str, size, '-')); // print sign
|
||||
} else if (sign) {
|
||||
print_printed(&length, print_char(str, size, '+')); // print sign
|
||||
@ -205,7 +192,7 @@ static size_t print_float(char** str, size_t* size, double f, uint32_t padding,
|
||||
print_printed(&length, print_char(str, size, 'E')); // print exponent mark
|
||||
print_printed(&length, print_signed(str, size, exponent, 0, false));
|
||||
}
|
||||
} else { // f=0
|
||||
} else { // f = 0
|
||||
// print sign
|
||||
if (f < 0) {
|
||||
print_printed(&length, print_char(str, size, '-')); // print sign
|
||||
@ -256,30 +243,22 @@ static size_t print_hex(char** str, size_t* size, uint64_t hex, uint32_t padding
|
||||
print_printed(&length, print_char(str, size, '0'));
|
||||
print_printed(&length, print_char(str, size, 'x'));
|
||||
}
|
||||
uint8_t digits = 0; // number of digits to print
|
||||
// figure out number of digits to print
|
||||
if (hex > 0xffffffffffffffUL) {
|
||||
digits = 16;
|
||||
} else if (hex > 0xffffffffffffUL) {
|
||||
digits = 14;
|
||||
} else if (hex > 0xffffffffffUL) {
|
||||
digits = 12;
|
||||
} else if (hex > 0xffffffffUL) {
|
||||
digits = 10;
|
||||
} else if (hex > 0xffffffUL) {
|
||||
digits = 8;
|
||||
} else if (hex > 0xffffUL) {
|
||||
digits = 6;
|
||||
} else if (hex > 0xffUL) {
|
||||
digits = 4;
|
||||
} else {
|
||||
digits = 2;
|
||||
uint8_t nibbles; // number of nibbles to print
|
||||
for (nibbles = 0; nibbles < 16; nibbles++) {
|
||||
if (0 == (hex >> (nibbles * 4))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (uint32_t zeros = digits; zeros < padding; zeros++) { // print padding 0's
|
||||
|
||||
if (0 == padding) {
|
||||
padding = 1; // show at least one hex
|
||||
}
|
||||
for (uint32_t zeros = nibbles; zeros < padding; zeros++) { // print padding 0's
|
||||
print_printed(&length, print_char(str, size, '0')); // print 0
|
||||
}
|
||||
for (uint8_t digit = 0; digit < digits; digit++) { // go through all digits
|
||||
print_printed(&length, print_nibble(str, size, hex >> ((digits - digit - 1) * 4), upcase)); // print nibble (in reverse order)
|
||||
for (uint8_t nibble = 0; nibble < nibbles; nibble++) { // go through all nibbles
|
||||
print_printed(&length, print_nibble(str, size, hex >> ((nibbles - nibble - 1) * 4), upcase)); // print nibble (in reverse order)
|
||||
}
|
||||
return length; // return number of characters printed
|
||||
}
|
||||
|
15
lib/print.h
15
lib/print.h
@ -1,17 +1,3 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** printing utilities to replace the large printf from the standard library (API)
|
||||
* @note use % as format specifier prefix, followed by + to enforce sign or 0x prefix, 0 followed by n for padding or forcing integer part of floating point number, . followed by n for number for fractional precision of floating point numbers, and format specifier
|
||||
* format specifier supported are:
|
||||
@ -30,6 +16,7 @@
|
||||
* - B for uint64_t bits
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2019
|
||||
*/
|
||||
#pragma once
|
||||
|
@ -1,31 +1,18 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to send data using ESP8266 WiFi SoC (code)
|
||||
* @file radio_esp8266.c
|
||||
/** library to send data using ESP8266 WiFi SoC
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @date 2016
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @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
|
||||
@ -39,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
|
||||
}
|
||||
|
@ -1,21 +1,8 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to send data using ESP8266 WiFi SoC (API)
|
||||
* @file radio_esp8266.h
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @date 2016
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2021
|
||||
* @note peripherals used: USART @ref radio_esp8266_usart
|
||||
*/
|
||||
#pragma once
|
||||
@ -26,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);
|
||||
|
145
lib/radio_sx172x.c
Normal file
145
lib/radio_sx172x.c
Normal file
@ -0,0 +1,145 @@
|
||||
/** library to communication with Semtech SX172x LoRa radio module 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 radio_sx172x_spi, GPIO @ref radio_sx172x_gpio
|
||||
* @note the interrupts and corresponding DIO should be handled directly by the user
|
||||
*/
|
||||
/* 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 "radio_sx172x.h" // own definitions
|
||||
|
||||
/** @defgroup radio_sx172x_spi SPI peripheral used to communicate with the SX172x
|
||||
* @{
|
||||
*/
|
||||
#define RADIO_SX172X_SPI 2 /**< SPI peripheral */
|
||||
/** @} */
|
||||
|
||||
/** @defgroup radio_sx172x_gpio GPIO used to control the SX172x
|
||||
* @{
|
||||
*/
|
||||
#define RADIO_SX172X_GPIO_NRESET PB7 /**< reset input (active low) */
|
||||
/** @} */
|
||||
|
||||
bool radio_sx172x_setup(void)
|
||||
{
|
||||
// setup SPI
|
||||
rcc_periph_clock_enable(RCC_SPI_SCK_PORT(RADIO_SX172X_SPI)); // enable clock for GPIO peripheral for clock signal
|
||||
gpio_set_mode(SPI_SCK_PORT(RADIO_SX172X_SPI), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, SPI_SCK_PIN(RADIO_SX172X_SPI)); // set SCK as output (clock speed will be negotiated later)
|
||||
rcc_periph_clock_enable(RCC_SPI_MOSI_PORT(RADIO_SX172X_SPI)); // enable clock for GPIO peripheral for MOSI signal
|
||||
gpio_set_mode(SPI_MOSI_PORT(RADIO_SX172X_SPI), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, SPI_MOSI_PIN(RADIO_SX172X_SPI)); // set MOSI as output
|
||||
rcc_periph_clock_enable(RCC_SPI_MISO_PORT(RADIO_SX172X_SPI)); // enable clock for GPIO peripheral for MISO signal
|
||||
gpio_set_mode(SPI_MISO_PORT(RADIO_SX172X_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, SPI_MISO_PIN(RADIO_SX172X_SPI)); // set MISO as input
|
||||
rcc_periph_clock_enable(RCC_SPI_NSS_PORT(RADIO_SX172X_SPI)); // enable clock for GPIO peripheral for NSS (CS) signal
|
||||
gpio_set_mode(SPI_NSS_PORT(RADIO_SX172X_SPI), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, SPI_NSS_PIN(RADIO_SX172X_SPI)); // set NSS (CS) as output
|
||||
rcc_periph_clock_enable(RCC_AFIO); // enable clock for SPI alternate function
|
||||
rcc_periph_clock_enable(RCC_SPI(RADIO_SX172X_SPI)); // enable clock for SPI peripheral
|
||||
spi_reset(SPI(RADIO_SX172X_SPI)); // clear SPI values to default
|
||||
spi_init_master(SPI(RADIO_SX172X_SPI), SPI_CR1_BAUDRATE_FPCLK_DIV_8, SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE, SPI_CR1_CPHA_CLK_TRANSITION_2, SPI_CR1_DFF_8BIT, SPI_CR1_MSBFIRST); // initialise SPI as master, divide clock by 8 (72E6/8 < 10 MHz, max SX172X SCK is 10 MHz, 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 8-bit frames , use MSb first
|
||||
spi_set_full_duplex_mode(SPI(RADIO_SX172X_SPI)); // ensure we are in full duplex mode
|
||||
spi_enable_software_slave_management(SPI(RADIO_SX172X_SPI)); // control NSS (CS) manually
|
||||
spi_set_nss_high(SPI(RADIO_SX172X_SPI)); // set NSS high (internally) so we can output
|
||||
spi_disable_ss_output(SPI(RADIO_SX172X_SPI)); // disable NSS output since we control CS manually
|
||||
gpio_set(SPI_NSS_PORT(RADIO_SX172X_SPI), SPI_NSS_PIN(RADIO_SX172X_SPI)); // set CS high to unselect device
|
||||
spi_enable(SPI(RADIO_SX172X_SPI)); // enable SPI
|
||||
// use NRESET pin to read ensure it started
|
||||
rcc_periph_clock_enable(GPIO_RCC(RADIO_SX172X_GPIO_NRESET)); // enable clock for GPIO port
|
||||
gpio_set_mode(GPIO_PORT(RADIO_SX172X_GPIO_NRESET), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(RADIO_SX172X_GPIO_NRESET)); // set GPIO as input
|
||||
sleep_ms(10); // wait for POR sequence to complete
|
||||
return (0 != gpio_get(GPIO_PORT(RADIO_SX172X_GPIO_NRESET), GPIO_PIN(RADIO_SX172X_GPIO_NRESET))); // ensure the module has started
|
||||
}
|
||||
|
||||
void radio_sx172x_release(void)
|
||||
{
|
||||
spi_disable(SPI(RADIO_SX172X_SPI));
|
||||
rcc_periph_clock_disable(RCC_SPI(RADIO_SX172X_SPI));
|
||||
gpio_set_mode(SPI_NSS_PORT(RADIO_SX172X_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SPI_NSS_PIN(RADIO_SX172X_SPI));
|
||||
gpio_set_mode(SPI_MISO_PORT(RADIO_SX172X_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SPI_MISO_PIN(RADIO_SX172X_SPI));
|
||||
gpio_set_mode(SPI_MOSI_PORT(RADIO_SX172X_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SPI_MOSI_PIN(RADIO_SX172X_SPI));
|
||||
gpio_set_mode(SPI_SCK_PORT(RADIO_SX172X_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, SPI_SCK_PIN(RADIO_SX172X_SPI));
|
||||
}
|
||||
|
||||
bool radio_sx172x_reset(void)
|
||||
{
|
||||
gpio_clear(GPIO_PORT(RADIO_SX172X_GPIO_NRESET), GPIO_PIN(RADIO_SX172X_GPIO_NRESET)); // set low to reset device
|
||||
gpio_set_mode(GPIO_PORT(RADIO_SX172X_GPIO_NRESET), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(RADIO_SX172X_GPIO_NRESET)); // set GPIO as output
|
||||
sleep_us(200); // set low for at least 100 us (see datasheet section 7.2.2)
|
||||
gpio_set_mode(GPIO_PORT(RADIO_SX172X_GPIO_NRESET), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(RADIO_SX172X_GPIO_NRESET)); // set GPIO as input
|
||||
sleep_ms(6); // wait for at least 5 ms for reset to complete (see datasheet section 7.2.2)
|
||||
return (0 != gpio_get(GPIO_PORT(RADIO_SX172X_GPIO_NRESET), GPIO_PIN(RADIO_SX172X_GPIO_NRESET))); // ensure the module has started
|
||||
}
|
||||
|
||||
uint8_t radio_sx172x_read_register(enum radio_sx172x_register_t name)
|
||||
{
|
||||
gpio_clear(SPI_NSS_PORT(RADIO_SX172X_SPI), SPI_NSS_PIN(RADIO_SX172X_SPI)); // set CS low to select device
|
||||
(void)SPI_DR(SPI(RADIO_SX172X_SPI)); // clear RXNE flag (by reading previously received data)
|
||||
spi_send(SPI(RADIO_SX172X_SPI), name & 0x7f); // send register address (with 7th bit at 0 to read) and any data
|
||||
spi_read(SPI(RADIO_SX172X_SPI)); // wait for data to be transferred, but discard the received data (clears RXNE go get the next data correctly)
|
||||
spi_send(SPI(RADIO_SX172X_SPI), 0); // write any data so we can read back data
|
||||
const uint8_t value = spi_read(SPI(RADIO_SX172X_SPI)); // read data
|
||||
while ((SPI_SR(SPI(RADIO_SX172X_SPI)) & SPI_SR_BSY)); // wait for SPI to not be busy (communication completed)
|
||||
gpio_set(SPI_NSS_PORT(RADIO_SX172X_SPI), SPI_NSS_PIN(RADIO_SX172X_SPI)); // set CS high to unselect device
|
||||
return value;
|
||||
}
|
||||
|
||||
void radio_sx172x_write_register(enum radio_sx172x_register_t name, uint8_t value)
|
||||
{
|
||||
gpio_clear(SPI_NSS_PORT(RADIO_SX172X_SPI), SPI_NSS_PIN(RADIO_SX172X_SPI)); // set CS low to select device
|
||||
spi_send(SPI(RADIO_SX172X_SPI), name | 0x80); // send register address (with 7th bit at 1 to write) and value
|
||||
spi_send(SPI(RADIO_SX172X_SPI), value); // write value
|
||||
while ((SPI_SR(SPI(RADIO_SX172X_SPI)) & SPI_SR_BSY)); // wait for SPI to not be busy (communication completed)
|
||||
gpio_set(SPI_NSS_PORT(RADIO_SX172X_SPI), SPI_NSS_PIN(RADIO_SX172X_SPI)); // set CS high to unselect device
|
||||
}
|
||||
|
||||
void radio_sx172x_read_fifo(uint8_t addr, uint8_t* data, uint8_t length)
|
||||
{
|
||||
if (NULL == data || 0 == length || addr + length > 256) { // sanity check
|
||||
return;
|
||||
}
|
||||
const uint8_t mode = radio_sx172x_read_register(RADIO_SX172X_REG_OP_MODE); // read in which mode we are
|
||||
if (0 == (mode & 0x7)) { // we are in sleep mode
|
||||
radio_sx172x_write_register(RADIO_SX172X_REG_OP_MODE, (mode & 0xf8) | 1); // go to standby mode so we can access the FIFO registers
|
||||
}
|
||||
radio_sx172x_write_register(RADIO_SX172X_REG_LORA_FIFO_ADDR_PTR, addr); // set address to be read
|
||||
gpio_clear(SPI_NSS_PORT(RADIO_SX172X_SPI), SPI_NSS_PIN(RADIO_SX172X_SPI)); // set CS low to select device
|
||||
(void)SPI_DR(SPI(RADIO_SX172X_SPI)); // clear RXNE flag (by reading previously received data)
|
||||
spi_send(SPI(RADIO_SX172X_SPI), RADIO_SX172X_REG_FIFO & 0x7f); // select FIFO register (with 7th bit at 0 to read)
|
||||
spi_read(SPI(RADIO_SX172X_SPI)); // wait for data to be transferred, but discard the received data (clears RXNE go get the next data correctly)
|
||||
for (uint8_t i = 0; i < length; i++) {
|
||||
spi_send(SPI(RADIO_SX172X_SPI), 0); // write any data so we can read back data
|
||||
data[i] = spi_read(SPI(RADIO_SX172X_SPI)); // read data
|
||||
}
|
||||
while ((SPI_SR(SPI(RADIO_SX172X_SPI)) & SPI_SR_BSY)); // wait for SPI to not be busy (communication completed)
|
||||
gpio_set(SPI_NSS_PORT(RADIO_SX172X_SPI), SPI_NSS_PIN(RADIO_SX172X_SPI)); // set CS high to unselect device
|
||||
}
|
||||
|
||||
void radio_sx172x_write_fifo(uint8_t addr, const uint8_t* data, uint8_t length)
|
||||
{
|
||||
if (NULL == data || 0 == length || addr + length > 256) { // sanity check
|
||||
return;
|
||||
}
|
||||
const uint8_t mode = radio_sx172x_read_register(RADIO_SX172X_REG_OP_MODE); // read in which mode we are
|
||||
if (0 == (mode & 0x7)) { // we are in sleep mode
|
||||
radio_sx172x_write_register(RADIO_SX172X_REG_OP_MODE, (mode & 0xf8) | 1); // go to standby mode so we can access the FIFO registers
|
||||
}
|
||||
radio_sx172x_write_register(RADIO_SX172X_REG_LORA_FIFO_ADDR_PTR, addr); // set address to be read
|
||||
gpio_clear(SPI_NSS_PORT(RADIO_SX172X_SPI), SPI_NSS_PIN(RADIO_SX172X_SPI)); // set CS low to select device
|
||||
spi_send(SPI(RADIO_SX172X_SPI), RADIO_SX172X_REG_FIFO | 0x80); // select FIFO register (with 7th bit at 1 to write)
|
||||
for (uint8_t i = 0; i < length; i++) {
|
||||
spi_send(SPI(RADIO_SX172X_SPI), data[i]); // write data
|
||||
}
|
||||
while ((SPI_SR(SPI(RADIO_SX172X_SPI)) & SPI_SR_BSY)); // wait for SPI to not be busy (communication completed)
|
||||
gpio_set(SPI_NSS_PORT(RADIO_SX172X_SPI), SPI_NSS_PIN(RADIO_SX172X_SPI)); // set CS high to unselect device
|
||||
}
|
161
lib/radio_sx172x.h
Normal file
161
lib/radio_sx172x.h
Normal file
@ -0,0 +1,161 @@
|
||||
/** library to communication with Semtech SX172x LoRa radio module 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 radio_sx172x_spi, GPIO @ref radio_sx172x_gpio
|
||||
* @note the interrupts and corresponding DIO should be handled directly by the user
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/** register addresses */
|
||||
enum radio_sx172x_register_t {
|
||||
RADIO_SX172X_REG_FIFO = 0x00,
|
||||
RADIO_SX172X_REG_OP_MODE = 0x01,
|
||||
RADIO_SX172X_REG_FSKOOK_BITRATE_MSB = 0x02,
|
||||
RADIO_SX172X_REG_FSKOOK_BITRATE_LSB = 0x03,
|
||||
RADIO_SX172X_REG_FSKOOK_FDEV_MSB = 0x04,
|
||||
RADIO_SX172X_REG_FSKOOK_FDEV_LSB = 0x05,
|
||||
RADIO_SX172X_REG_FRF_MSB = 0x06,
|
||||
RADIO_SX172X_REG_FRF_MID = 0x07,
|
||||
RADIO_SX172X_REG_FRF_LSB = 0x08,
|
||||
RADIO_SX172X_REG_PA_CONFIG = 0x09,
|
||||
RADIO_SX172X_REG_PA_RAMP = 0x0A,
|
||||
RADIO_SX172X_REG_OCP = 0x0B,
|
||||
RADIO_SX172X_REG_LNA = 0x0C,
|
||||
RADIO_SX172X_REG_FSKOOB_RX_CONFIG = 0x0D,
|
||||
RADIO_SX172X_REG_LORA_FIFO_ADDR_PTR = 0x0D,
|
||||
RADIO_SX172X_REG_FSKOOB_RSSI_CONFIG = 0x0E,
|
||||
RADIO_SX172X_REG_LORA_FIFO_TX_BASE_ADDR = 0x0E,
|
||||
RADIO_SX172X_REG_FSKOOB_RSSI_COLLISION = 0x0F,
|
||||
RADIO_SX172X_REG_LORA_FIFO_RX_BASE_ADDR = 0x0F,
|
||||
RADIO_SX172X_REG_FSKOOB_RSSI_THRESH = 0x10,
|
||||
RADIO_SX172X_REG_LORA_FIFO_RX_CURRENT_ADDR = 0x10,
|
||||
RADIO_SX172X_REG_FSKOOK_RSSI_VALUE = 0x11,
|
||||
RADIO_SX172X_REG_LORA_IRQ_FLAGS_MASK = 0x11,
|
||||
RADIO_SX172X_REG_FSKOOK_RX_BW = 0x12,
|
||||
RADIO_SX172X_REG_LORA_IRQ_FLAGS = 0x12,
|
||||
RADIO_SX172X_REG_FSKOOK_AFC_BW = 0x13,
|
||||
RADIO_SX172X_REG_LORA_RX_NB_BYTES = 0x13,
|
||||
RADIO_SX172X_REG_FSKOOK_OOK_PEAK = 0x14,
|
||||
RADIO_SX172X_REG_LORA_RX_HEADER_CNT_VALUE_MSB = 0x14,
|
||||
RADIO_SX172X_REG_FSKOOK_OOK_FIX = 0x15,
|
||||
RADIO_SX172X_REG_LORA_RX_HEADER_CNT_VALUE_LSB = 0x15,
|
||||
RADIO_SX172X_REG_FSKOOK_OOK_AVG = 0x16,
|
||||
RADIO_SX172X_REG_LORA_RX_PACKET_CNT_VALUE_MSB = 0x16,
|
||||
RADIO_SX172X_REG_LORA_RX_PACKET_CNT_VALUE_LSB = 0x17,
|
||||
RADIO_SX172X_REG_LORA_MODEM_STAT = 0x18,
|
||||
RADIO_SX172X_REG_LORA_PKT_SNR_VALUE = 0x19,
|
||||
RADIO_SX172X_REG_FSKOOK_AFC_FEI = 0x1A,
|
||||
RADIO_SX172X_REG_LORA_PKT_RSSI_VALUE = 0x1A,
|
||||
RADIO_SX172X_REG_FSKOOK_AFC_MSB = 0x1B,
|
||||
RADIO_SX172X_REG_LORA_RSSI_VALUE = 0x1B,
|
||||
RADIO_SX172X_REG_FSKOOK_AFC_LSB = 0x1C,
|
||||
RADIO_SX172X_REG_LORA_HOP_CHANNEL = 0x1C,
|
||||
RADIO_SX172X_REG_FSKOOK_FEI_MSB = 0x1D,
|
||||
RADIO_SX172X_REG_LORA_MODEM_CONFIG_1 = 0x1D,
|
||||
RADIO_SX172X_REG_FSKOOK_FEI_LSB = 0x1E,
|
||||
RADIO_SX172X_REG_LORA_MODEM_CONFIG_2 = 0x1E,
|
||||
RADIO_SX172X_REG_FSKOOK_PREAMBLE_DETECT = 0x1F,
|
||||
RADIO_SX172X_REG_LORA_SYMB_TIMEOUT_LSB = 0x1F,
|
||||
RADIO_SX172X_REG_FSKOOK_RX_TIMEOUT_1 = 0x20,
|
||||
RADIO_SX172X_REG_LORA_PREAMBLE_MSB = 0x20,
|
||||
RADIO_SX172X_REG_FSKOOK_RX_TIMEOUT_2 = 0x21,
|
||||
RADIO_SX172X_REG_LORA_PREAMBLE_LSB = 0x21,
|
||||
RADIO_SX172X_REG_FSKOOK_RX_TIMEOUT_3 = 0x22,
|
||||
RADIO_SX172X_REG_LORA_PAYLOAD_LENGTH = 0x22,
|
||||
RADIO_SX172X_REG_FSKOOK_RX_DELAY = 0x23,
|
||||
RADIO_SX172X_REG_LORA_MAX_PAYLOD_LENGTH = 0x23,
|
||||
RADIO_SX172X_REG_FSKOOK_OSC = 0x24,
|
||||
RADIO_SX172X_REG_LORA_HOP_PERIOD = 0x24,
|
||||
RADIO_SX172X_REG_FSKOOK_PREAMBLE_MSB = 0x25,
|
||||
RADIO_SX172X_REG_LORA_FIFO_RX_BYTE_ADDR = 0x25,
|
||||
RADIO_SX172X_REG_FSKOOK_PREAMBLE_LSB = 0x26,
|
||||
RADIO_SX172X_REG_LORA_MODEM_CONFIG_3 = 0x26,
|
||||
RADIO_SX172X_REG_FSKOOK_SYNC_CONFIG = 0x27,
|
||||
RADIO_SX172X_REG_FSKOOK_SYNC_VALUE_1 = 0x28,
|
||||
RADIO_SX172X_REG_LORA_FEI_MSB = 0x28,
|
||||
RADIO_SX172X_REG_FSKOOK_SYNC_VALUE_2 = 0x29,
|
||||
RADIO_SX172X_REG_LORA_FEI_MID = 0x29,
|
||||
RADIO_SX172X_REG_FSKOOK_SYNC_VALUE_3 = 0x2A,
|
||||
RADIO_SX172X_REG_LORA_FEI_LSB = 0x2A,
|
||||
RADIO_SX172X_REG_FSKOOK_SYNC_VALUE_4 = 0x2B,
|
||||
RADIO_SX172X_REG_FSKOOK_SYNC_VALUE_5 = 0x2C,
|
||||
RADIO_SX172X_REG_LORA_RSSI_WIDEBAND = 0x2C,
|
||||
RADIO_SX172X_REG_FSKOOK_SYNC_VALUE_6 = 0x2D,
|
||||
RADIO_SX172X_REG_FSKOOK_SYNC_VALUE_7 = 0x2E,
|
||||
RADIO_SX172X_REG_FSKOOK_SYNC_VALUE_8 = 0x2F,
|
||||
RADIO_SX172X_REG_LORA_IF_FREQ_1 = 0x2F,
|
||||
RADIO_SX172X_REG_FSKOOK_PACKET_CONFIG_1 = 0x30,
|
||||
RADIO_SX172X_REG_LORA_IF_FREQ_2 = 0x30,
|
||||
RADIO_SX172X_REG_FSKOOK_PACKET_CONFIG_2 = 0x31,
|
||||
RADIO_SX172X_REG_LORA_DETECT_OPTIMIZE = 0x31,
|
||||
RADIO_SX172X_REG_FSKOOK_PAYLOAD_LENGTH = 0x32,
|
||||
RADIO_SX172X_REG_FSKOOK_NODE_ADRS = 0x33,
|
||||
RADIO_SX172X_REG_LORA_INVERT_IQ = 0x33,
|
||||
RADIO_SX172X_REG_FSKOOK_BROADCAST_ADRS = 0x34,
|
||||
RADIO_SX172X_REG_FSKOOK_FIFO_THRESH = 0x35,
|
||||
RADIO_SX172X_REG_FSKOOK_SEQ_CONFIG_1 = 0x36,
|
||||
RADIO_SX172X_REG_LORA_HIGH_BW_OPTIMIZE_1 = 0x36,
|
||||
RADIO_SX172X_REG_FSKOOK_SEQ_CONFIG_2 = 0x37,
|
||||
RADIO_SX172X_REG_LORA_DETECTION_THRESHOLD = 0x37,
|
||||
RADIO_SX172X_REG_FSKOOK_TIMER_RESOLV = 0x38,
|
||||
RADIO_SX172X_REG_FSKOOK_TIMER_1_COEF = 0x39,
|
||||
RADIO_SX172X_REG_LORA_SYNC_WORD = 0x39,
|
||||
RADIO_SX172X_REG_FSKOOK_TIMER_2_COEF = 0x3A,
|
||||
RADIO_SX172X_REG_LORA_HIGH_BW_OPTIMIZE_2 = 0x3A,
|
||||
RADIO_SX172X_REG_FSKOOK_IMAGE_CAL = 0x3B,
|
||||
RADIO_SX172X_REG_LORA_INVERT_IQ_2 = 0x3B,
|
||||
RADIO_SX172X_REG_FSKOOK_TEMP = 0x3C,
|
||||
RADIO_SX172X_REG_FSKOOK_LOW_BAT = 0x3D,
|
||||
RADIO_SX172X_REG_FSKOOK_IRQ_FLAGS_1 = 0x3E,
|
||||
RADIO_SX172X_REG_FSKOOK_IRQ_FLAGS_2 = 0x3F,
|
||||
RADIO_SX172X_REG_DIO_MAPPING_1 = 0x40,
|
||||
RADIO_SX172X_REG_DIO_MAPPING_2 = 0x41,
|
||||
RADIO_SX172X_REG_VERSION = 0x42,
|
||||
RADIO_SX172X_REG_FSKOOK_PLL_HOP = 0x44,
|
||||
RADIO_SX172X_REG_TCX0 = 0x4B,
|
||||
RADIO_SX172X_REG_PA_DAC = 0x4D,
|
||||
RADIO_SX172X_REG_FORMER_TEMP = 0x5B,
|
||||
RADIO_SX172X_REG_FSKOOK_BIT_RATE_FRAC = 0x5D,
|
||||
RADIO_SX172X_REG_AGC_REF = 0x61,
|
||||
RADIO_SX172X_REG_AGC_THRESH_1 = 0x62,
|
||||
RADIO_SX172X_REG_AGC_THRESH_2 = 0x63,
|
||||
RADIO_SX172X_REG_AGC_THRESH_3 = 0x64,
|
||||
RADIO_SX172X_REG_PLL = 0x70,
|
||||
};
|
||||
|
||||
/** setup communication to SX172x
|
||||
* @return if SX172x is ready
|
||||
*/
|
||||
bool radio_sx172x_setup(void);
|
||||
/** release resources and peripherals used by communication */
|
||||
void radio_sx172x_release(void);
|
||||
/** reset SX172x chip
|
||||
* @return if SX172x is ready
|
||||
*/
|
||||
bool radio_sx172x_reset(void);
|
||||
/** read register from SX172x
|
||||
* @param[in] name register name to read
|
||||
* @return register value read
|
||||
*/
|
||||
uint8_t radio_sx172x_read_register(enum radio_sx172x_register_t name);
|
||||
/** write register to SX172x
|
||||
* @param[in] name register name to write
|
||||
* @param[in} value register value to write
|
||||
*/
|
||||
void radio_sx172x_write_register(enum radio_sx172x_register_t name, uint8_t value);
|
||||
/** read FIFO data
|
||||
* @param[in] addr address of the data to be read in the FIFO buffer
|
||||
* @param[out] data buffer to store read data
|
||||
* @param[in] length number of byte to read
|
||||
* @warning puts device in standby mode when in sleep mode
|
||||
*/
|
||||
void radio_sx172x_read_fifo(uint8_t addr, uint8_t* data, uint8_t length);
|
||||
/** write FIFO data
|
||||
* @param[in] addr address of the data to be written in the FIFO buffer
|
||||
* @param[out] data data to be written in FIFO buffer
|
||||
* @param[in] length number of byte to written
|
||||
* @warning puts device in standby mode when in sleep mode
|
||||
*/
|
||||
void radio_sx172x_write_fifo(uint8_t addr, const uint8_t* data, uint8_t length);
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to get time from a DCF77 module
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
* @note peripherals used: GPIO @ref rtc_dcf77_gpio, timer @ref rtc_dcf77_timer
|
||||
*/
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to get time from a DCF77 module
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
* @note peripherals used: GPIO @ref rtc_dcf77_gpio, timer @ref rtc_dcf77_timer
|
||||
*/
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to communicate with the Maxim DS1307 I2C RTC IC (code)
|
||||
* @file rtc_ds1307.c
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2017
|
||||
* @note peripherals used: I²C
|
||||
*/
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to communicate with the Maxim DS1307 I2C RTC IC (API)
|
||||
* @file rtc_ds1307.h
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2017
|
||||
* @note peripherals used: I²C
|
||||
*/
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to communication with ams AS3935 Franklin lightning sensor IC using SPI
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2019
|
||||
* @note peripherals used: SPI @ref sensor_as3935_spi, GPIO @ref sensor_as3935_gpio
|
||||
*/
|
||||
@ -431,7 +418,7 @@ bool sensor_as3935_setup(void)
|
||||
sensor_as3935_power_down(); // power down to save power
|
||||
rcc_periph_clock_enable(RCC_AFIO); // enable alternate function clock for external interrupt
|
||||
// configure IRQ interrupt
|
||||
exti_select_source(GPIO_EXTI(SENSOR_AS3935_GPIO_IRQ), GPIO_PIN(SENSOR_AS3935_GPIO_IRQ)); // mask external interrupt of the IRQ pin only for this port
|
||||
exti_select_source(GPIO_EXTI(SENSOR_AS3935_GPIO_IRQ), GPIO_PORT(SENSOR_AS3935_GPIO_IRQ)); // mask external interrupt of the IRQ pin only for this port
|
||||
exti_set_trigger(GPIO_EXTI(SENSOR_AS3935_GPIO_IRQ), EXTI_TRIGGER_RISING); // IRQ goes high in interrupt
|
||||
exti_enable_request(GPIO_EXTI(SENSOR_AS3935_GPIO_IRQ)); // enable external interrupt
|
||||
nvic_enable_irq(GPIO_NVIC_EXTI_IRQ(SENSOR_AS3935_GPIO_IRQ)); // enable interrupt
|
||||
@ -562,11 +549,10 @@ bool sensor_as3935_tune(void)
|
||||
/** interrupt service routine called when IRQ does high to indicate interrupt (or clock outputs) */
|
||||
void GPIO_EXTI_ISR(SENSOR_AS3935_GPIO_IRQ)(void)
|
||||
{
|
||||
exti_reset_request(GPIO_EXTI(SENSOR_AS3935_GPIO_IRQ)); // reset interrupt
|
||||
if (sensor_as3935_calibrating) {
|
||||
sensor_as3935_interrupt_count++; // increment pulse count to calculate to frequency
|
||||
} else {
|
||||
sensor_as3935_interrupt = true; // notify user
|
||||
}
|
||||
|
||||
exti_reset_request(GPIO_EXTI(SENSOR_AS3935_GPIO_IRQ)); // reset interrupt
|
||||
}
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to communication with ams AS3935 Franklin lightning sensor IC using SPI
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2019
|
||||
* @note peripherals used: SPI @ref sensor_as3935_spi, GPIO @ref sensor_as3935_gpio
|
||||
*/
|
||||
|
@ -1,190 +0,0 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to query measurements from Aosong DHT11 temperature and relative humidity sensor
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @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,41 +0,0 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to query measurements from Aosong DHT11 temperature and relative humidity sensor
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @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);
|
206
lib/sensor_dht1122.c
Normal file
206
lib/sensor_dht1122.c
Normal file
@ -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
|
||||
}
|
||||
}
|
30
lib/sensor_dht1122.h
Normal file
30
lib/sensor_dht1122.h
Normal file
@ -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,194 +0,0 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to query measurements from Aosong DHT22 temperature and relative humidity sensor
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @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,41 +0,0 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to query measurements from Aosong DHT22 (aka. AM2302) temperature and relative humidity sensor
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @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);
|
@ -1,21 +1,8 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library for Maxim DS18B20 digital temperature sensor (using 1-Wire protocol) (code)
|
||||
* @file sensor_ds18b20.c
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @date 2017
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @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
|
||||
*/
|
||||
@ -55,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 {
|
||||
@ -81,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
|
||||
}
|
||||
|
||||
@ -96,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
|
||||
}
|
||||
@ -112,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
|
||||
}
|
||||
|
||||
@ -122,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
|
||||
}
|
||||
|
||||
@ -138,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
|
||||
}
|
||||
|
||||
@ -157,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
|
||||
}
|
||||
@ -169,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
|
||||
}
|
||||
|
||||
@ -182,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
|
||||
}
|
||||
|
||||
|
@ -1,21 +1,8 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library for Maxim DS18B20 digital temperature sensor (using 1-Wire protocol) (API)
|
||||
* @file sensor_ds18b20.h
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @date 2017
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @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
|
||||
*/
|
||||
@ -49,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
|
||||
*/
|
||||
|
82
lib/sensor_max1247.c
Normal file
82
lib/sensor_max1247.c
Normal file
@ -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;
|
||||
}
|
||||
|
20
lib/sensor_max1247.h
Normal file
20
lib/sensor_max1247.h
Normal file
@ -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);
|
75
lib/sensor_max6675.c
Normal file
75
lib/sensor_max6675.c
Normal file
@ -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
|
||||
}
|
19
lib/sensor_max6675.h
Normal file
19
lib/sensor_max6675.h
Normal file
@ -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);
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to query measurements from peacefair PZEM-004 and PZEM-004T electricity meter
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
* @note peripherals used: USART @ref sensor_pzem_usart, timer @ref sensor_pzem_timer
|
||||
*/
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to query measurements from peacefair PZEM-004 and PZEM-004T electricity meter
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
* @note peripherals used: USART @ref sensor_pzem_usart, timer @ref sensor_pzem_timer
|
||||
*/
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to query measurements from eastron SDM120-ModBus electricity meter
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
* @note peripherals used: USART @ref sensor_sdm120_usart , GPIO @ref sensor_sdm120_gpio , timer @ref sensor_sdm120_timer
|
||||
*/
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to query measurements from eastron SDM120-ModBus electricity meter
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
* @note peripherals used: USART @ref sensor_sdm120_usart , GPIO @ref sensor_sdm120_gpio , timer @ref sensor_sdm120_timer
|
||||
*/
|
||||
|
@ -1,21 +1,8 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** terminal prompt interface (code)
|
||||
* @note allows line editing and supports some ANSI escape codes
|
||||
* @file terminal.c
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2018-2019
|
||||
*/
|
||||
/* standard libraries */
|
||||
|
@ -1,21 +1,8 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** terminal prompt interface (API)
|
||||
* @note allows line editing and supports some ANSI escape codes
|
||||
* @file terminal.h
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2018
|
||||
*/
|
||||
|
||||
|
26
lib/uart.c
26
lib/uart.c
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library for UART communication
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
* @note peripherals used: USART @ref uart
|
||||
*/
|
||||
@ -43,7 +30,7 @@
|
||||
#define UART_BAUDRATE 921600 /**< serial baud rate, in bits per second (with 8N1 8 bits, no parity bit, 1 stop bit settings) */
|
||||
|
||||
/* output ring buffer, indexes, and available memory */
|
||||
static volatile uint8_t tx_buffer[64] = {0}; /**< ring buffer for data to transmit */
|
||||
static volatile uint8_t tx_buffer[128] = {0}; /**< ring buffer for data to transmit (size must be a power of 2) */
|
||||
static volatile uint16_t tx_i = 0; /**< current position of transmitted data */
|
||||
static volatile uint16_t tx_used = 0; /**< how much data needs to be transmitted */
|
||||
|
||||
@ -52,6 +39,7 @@ void uart_setup(void)
|
||||
/* enable UART I/O peripheral */
|
||||
rcc_periph_clock_enable(RCC_USART_PORT(UART_ID)); // enable clock for UART port peripheral
|
||||
rcc_periph_clock_enable(RCC_USART(UART_ID)); // enable clock for UART peripheral
|
||||
rcc_periph_reset_pulse(RST_USART(UART_ID)); // reset peripheral
|
||||
rcc_periph_clock_enable(RCC_AFIO); // enable pin alternate function (UART)
|
||||
gpio_set_mode(USART_TX_PORT(UART_ID), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USART_TX_PIN(UART_ID)); // setup GPIO pin UART transmit
|
||||
gpio_set_mode(USART_RX_PORT(UART_ID), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, USART_RX_PIN(UART_ID)); // setup GPIO pin UART receive
|
||||
@ -76,8 +64,7 @@ void uart_setup(void)
|
||||
|
||||
void uart_putchar_blocking(char c)
|
||||
{
|
||||
uart_flush(); // empty buffer first
|
||||
usart_send_blocking(USART(UART_ID), c); // send character
|
||||
usart_send_blocking(USART(UART_ID), c); // send character (this already wait for the buffer to be empty)
|
||||
}
|
||||
|
||||
void uart_flush(void)
|
||||
@ -86,6 +73,7 @@ void uart_flush(void)
|
||||
__WFI(); // sleep until interrupt
|
||||
}
|
||||
usart_wait_send_ready(USART(UART_ID)); // wait until transmit register is empty (transmission might not be complete)
|
||||
while(!usart_get_flag(USART(UART_ID), USART_SR_TC)); // wait for transmission to be complete
|
||||
}
|
||||
|
||||
void uart_putchar_nonblocking(char c)
|
||||
@ -95,7 +83,7 @@ void uart_putchar_nonblocking(char c)
|
||||
// don't go to sleep since this might prevent an interrupt
|
||||
}
|
||||
usart_disable_tx_interrupt(USART(UART_ID)); // disable transmit interrupt to prevent index corruption
|
||||
tx_buffer[(tx_i + tx_used) % LENGTH(tx_buffer)] = c; // put character in buffer
|
||||
tx_buffer[(tx_i + tx_used) & (LENGTH(tx_buffer) - 1)] = c; // put character in buffer
|
||||
tx_used++; // update used buffer
|
||||
usart_enable_tx_interrupt(USART(UART_ID)); // enable transmit interrupt
|
||||
}
|
||||
@ -108,7 +96,7 @@ void USART_ISR(UART_ID)(void)
|
||||
usart_disable_tx_interrupt(USART(UART_ID)); // disable transmit interrupt
|
||||
} else {
|
||||
usart_send(USART(UART_ID), tx_buffer[tx_i]); // put data in transmit register
|
||||
tx_i = (tx_i + 1) % LENGTH(tx_buffer); // update location on buffer
|
||||
tx_i = (tx_i + 1) & (LENGTH(tx_buffer) - 1); // update location on buffer
|
||||
tx_used--; // update used size
|
||||
}
|
||||
}
|
||||
|
15
lib/uart.h
15
lib/uart.h
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library for UART communication
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
* @note peripherals used: USART @ref uart
|
||||
*/
|
||||
|
347
lib/uart_soft.c
347
lib/uart_soft.c
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to control up to 4 independent receive and transmit software UART ports
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
* @note peripherals used: GPIO @ref uart_soft_gpio, timer @ref uart_soft_timer
|
||||
*/
|
||||
@ -22,6 +9,7 @@
|
||||
/* standard libraries */
|
||||
#include <stdint.h> // standard integer types
|
||||
#include <stdlib.h> // general utilities
|
||||
#include <string.h> // memory utilisites
|
||||
|
||||
/* STM32 (including CM3) libraries */
|
||||
#include <libopencm3/stm32/rcc.h> // real-time control clock library
|
||||
@ -39,26 +27,31 @@
|
||||
* @warning only one port must be used per line (pin number)
|
||||
* @{
|
||||
*/
|
||||
#define UART_SOFT_RX_PORT0 B /**< port for receive signal for UART port 0 */
|
||||
#define UART_SOFT_RX_PIN0 9 /**< pin for receive signal for UART port 0 */
|
||||
//#define UART_SOFT_RX_PORT1 A /**< port for receive signal for UART port 0 */
|
||||
//#define UART_SOFT_RX_PIN1 0 /**< pin for receive signal for UART port 0 */
|
||||
//#define UART_SOFT_RX_PORT2 A /**< port for receive signal for UART port 0 */
|
||||
//#define UART_SOFT_RX_PIN2 0 /**< pin for receive signal for UART port 0 */
|
||||
//#define UART_SOFT_RX_PORT3 A /**< port for receive signal for UART port 0 */
|
||||
//#define UART_SOFT_RX_PIN3 0 /**< pin for receive signal for UART port 0 */
|
||||
#define UART_SOFT_TX_PORT0 B /**< port for transmit signal for UART port 0 */
|
||||
#define UART_SOFT_TX_PIN0 8 /**< pin for transmit signal for UART port 0 */
|
||||
//#define UART_SOFT_TX_PORT1 A /**< port for transmit signal for UART port 0 */
|
||||
//#define UART_SOFT_TX_PIN1 0 /**< pin for transmit signal for UART port 0 */
|
||||
//#define UART_SOFT_TX_PORT2 A /**< port for transmit signal for UART port 0 */
|
||||
//#define UART_SOFT_TX_PIN2 0 /**< pin for transmit signal for UART port 0 */
|
||||
//#define UART_SOFT_TX_PORT3 A /**< port for transmit signal for UART port 0 */
|
||||
//#define UART_SOFT_TX_PIN3 0 /**< pin for transmit signal for UART port 0 */
|
||||
#define UART_SOFT_RX0_GPIO PB14 /**< pin for receive signal for UART port 0 */
|
||||
//#define UART_SOFT_RX1_GPIO PA0 /**< pin for receive signal for UART port 1 */
|
||||
//#define UART_SOFT_RX2_GPIO PA0 /**< pin for receive signal for UART port 2 */
|
||||
//#define UART_SOFT_RX3_GPIO PA0 /**< pin for receive signal for UART port 3 */
|
||||
#define UART_SOFT_TX0_GPIO PB13 /**< pin for transmit signal for UART port 0 */
|
||||
//#define UART_SOFT_TX1_GPIO PA0 /**< pin for transmit signal for UART port 1 */
|
||||
//#define UART_SOFT_TX2_GPIO PA0 /**< pin for transmit signal for UART port 2 */
|
||||
//#define UART_SOFT_TX3_GPIO PA0 /**< pin for transmit signal for UART port 3 */
|
||||
/** @} */
|
||||
|
||||
/** buffer size for receive and transmit buffers */
|
||||
#if defined(UART_SOFT_RX3_GPIO) || defined(UART_SOFT_TX3_GPIO)
|
||||
#define UART_NB 4 /*< number of UART ports */
|
||||
#elif defined(UART_SOFT_RX2_GPIO) || defined(UART_SOFT_TX2_GPIO)
|
||||
#define UART_NB 3 /*< number of UART ports */
|
||||
#elif defined(UART_SOFT_RX1_GPIO) || defined(UART_SOFT_TX1_GPIO)
|
||||
#define UART_NB 2 /*< number of UART ports */
|
||||
#elif defined(UART_SOFT_RX0_GPIO) || defined(UART_SOFT_TX0_GPIO)
|
||||
#define UART_NB 1 /*< number of UART ports */
|
||||
#else
|
||||
#define UART_NB 0 /*< number of UART ports */
|
||||
#endif
|
||||
|
||||
/** buffer size for receive and transmit buffers (must be a power of 2) */
|
||||
#define UART_SOFT_BUFFER 128
|
||||
|
||||
/** UART receive state definition */
|
||||
struct soft_uart_rx_state {
|
||||
uint32_t port; /**< UART receive port */
|
||||
@ -66,16 +59,13 @@ struct soft_uart_rx_state {
|
||||
uint32_t rcc; /**< UART receive port peripheral clock */
|
||||
uint32_t exti; /**< UART receive external interrupt */
|
||||
uint32_t irq; /**< UART receive interrupt request */
|
||||
uint32_t baudrate; /**< UART receive baud rate */
|
||||
uint32_t baudrate; /**< UART receive baud rate (in timer ticks, not bps) */
|
||||
volatile uint16_t state; /**< GPIO state for receive pin */
|
||||
volatile uint8_t bit; /**< next UART frame bit to receive */
|
||||
volatile uint8_t byte; /**< byte being received */
|
||||
volatile uint8_t buffer[UART_SOFT_BUFFER]; /**< receive buffer */
|
||||
volatile uint8_t buffer_i; /**< index of current data to be read out */
|
||||
volatile uint8_t buffer_used; /**< how much data is available */
|
||||
volatile bool lock; /**< put lock when changing buffer_i or buffer_used */
|
||||
volatile uint8_t buffer_byte; /**< to temporary store byte while locked */
|
||||
volatile bool buffer_byte_used; /**< signal a byte has been stored in temporary buffer */
|
||||
|
||||
};
|
||||
/** UART transmit state definition */
|
||||
@ -83,7 +73,7 @@ struct soft_uart_tx_state {
|
||||
uint32_t port; /**< UART receive port */
|
||||
uint16_t pin; /**< UART receive pin */
|
||||
uint32_t rcc; /**< UART receive port peripheral clock */
|
||||
uint32_t baudrate; /**< UART receive baud rate */
|
||||
uint32_t baudrate; /**< UART receive baud rate (in timer ticks, not bps) */
|
||||
volatile uint8_t bit; /**< next UART frame bit to transmit */
|
||||
volatile uint8_t byte; /**< byte being transmitted */
|
||||
volatile uint8_t buffer[UART_SOFT_BUFFER]; /**< receive buffer */
|
||||
@ -92,18 +82,18 @@ struct soft_uart_tx_state {
|
||||
volatile bool transmit; /**< flag to know it transmission is ongoing */
|
||||
};
|
||||
|
||||
static struct soft_uart_rx_state* uart_soft_rx_states[4] = {NULL}; /**< states of UART receive ports (up to 4) */
|
||||
static struct soft_uart_tx_state* uart_soft_tx_states[4] = {NULL}; /**< states of UART transmit ports (up to 4) */
|
||||
static struct soft_uart_rx_state uart_soft_rx_states[UART_NB]; /**< states of UART receive ports */
|
||||
static struct soft_uart_tx_state uart_soft_tx_states[UART_NB]; /**< states of UART transmit ports */
|
||||
|
||||
volatile bool uart_soft_received[4] = {false, false, false, false};
|
||||
|
||||
/** @defgroup uart_soft_timer timer used to sample UART signals
|
||||
* @{
|
||||
*/
|
||||
#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0)) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1)) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2)) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN0))
|
||||
#if defined(UART_SOFT_RX0_GPIO) || defined(UART_SOFT_RX1_GPIO) || defined(UART_SOFT_RX2_GPIO) || defined(UART_SOFT_RX3_GPIO)
|
||||
#define UART_SOFT_RX_TIMER 3 /**< timer peripheral for receive signals */
|
||||
#endif
|
||||
#if (defined(UART_SOFT_TX_PORT0) && defined(UART_SOFT_TX_PIN0)) || (defined(UART_SOFT_TX_PORT1) && defined(UART_SOFT_TX_PIN1)) || (defined(UART_SOFT_TX_PORT2) && defined(UART_SOFT_TX_PIN2)) || (defined(UART_SOFT_TX_PORT3) && defined(UART_SOFT_TX_PIN0))
|
||||
#if defined(UART_SOFT_TX0_GPIO) || defined(UART_SOFT_TX1_GPIO) || defined(UART_SOFT_TX2_GPIO) || defined(UART_SOFT_TX3_GPIO)
|
||||
#define UART_SOFT_TX_TIMER 4 /**< timer peripheral for transmit signals */
|
||||
#endif
|
||||
/** @} */
|
||||
@ -112,64 +102,100 @@ static const uint32_t timer_flags[4] = {TIM_SR_CC1IF, TIM_SR_CC2IF, TIM_SR_CC3IF
|
||||
static const uint32_t timer_interrupt[4] = {TIM_DIER_CC1IE, TIM_DIER_CC2IE, TIM_DIER_CC3IE, TIM_DIER_CC4IE}; /**< the interrupt enable for the compare units */
|
||||
static const enum tim_oc_id timer_oc[4] = {TIM_OC1, TIM_OC2, TIM_OC3, TIM_OC4}; /**< the output compares for the compare units */
|
||||
|
||||
bool uart_soft_setup(uint32_t *rx_baudrates, uint32_t *tx_baudrates)
|
||||
bool uart_soft_setup(const uint32_t* rx_baudrates, const uint32_t* tx_baudrates)
|
||||
{
|
||||
(void)rx_baudrates; // ensure compile does no complain even if no receive port is used
|
||||
(void)tx_baudrates; // ensure compile does no complain even if no transmit port is used
|
||||
|
||||
// save UART receive definition
|
||||
#if defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0)
|
||||
uart_soft_rx_states[0] = calloc(1, sizeof(struct soft_uart_rx_state)); // create state definition
|
||||
uart_soft_rx_states[0]->port = GPIO(UART_SOFT_RX_PORT0); // save receive port
|
||||
uart_soft_rx_states[0]->pin = GPIO(UART_SOFT_RX_PIN0); // save receive pin
|
||||
uart_soft_rx_states[0]->rcc = RCC_GPIO(UART_SOFT_RX_PORT0); // save receive port peripheral clock
|
||||
uart_soft_rx_states[0]->exti = EXTI(UART_SOFT_RX_PIN0); // save receive external interrupt
|
||||
uart_soft_rx_states[0]->irq = NVIC_EXTI_IRQ(UART_SOFT_RX_PIN0); // save receive interrupt request
|
||||
memset(&uart_soft_rx_states, 0, sizeof(uart_soft_rx_states)); // initialize receiver configuration array
|
||||
#if defined(UART_SOFT_RX0_GPIO)
|
||||
uart_soft_rx_states[0].port = GPIO_PORT(UART_SOFT_RX0_GPIO); // save receive port
|
||||
uart_soft_rx_states[0].pin = GPIO_PIN(UART_SOFT_RX0_GPIO); // save receive pin
|
||||
uart_soft_rx_states[0].rcc = GPIO_RCC(UART_SOFT_RX0_GPIO); // save receive port peripheral clock
|
||||
uart_soft_rx_states[0].exti = GPIO_EXTI(UART_SOFT_RX0_GPIO); // save receive external interrupt
|
||||
uart_soft_rx_states[0].irq = GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX0_GPIO); // save receive interrupt request
|
||||
#endif
|
||||
#if defined(UART_SOFT_RX1_GPIO)
|
||||
uart_soft_rx_states[1].port = GPIO_PORT(UART_SOFT_RX1_GPIO); // save receive port
|
||||
uart_soft_rx_states[1].pin = GPIO_PIN(UART_SOFT_RX1_GPIO); // save receive pin
|
||||
uart_soft_rx_states[1].rcc = GPIO_RCC(UART_SOFT_RX1_GPIO); // save receive port peripheral clock
|
||||
uart_soft_rx_states[1].exti = GPIO_EXTI(UART_SOFT_RX1_GPIO); // save receive external interrupt
|
||||
uart_soft_rx_states[1].irq = GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX1_GPIO); // save receive interrupt request
|
||||
#endif
|
||||
#if defined(UART_SOFT_RX2_GPIO)
|
||||
uart_soft_rx_states[2].port = GPIO_PORT(UART_SOFT_RX2_GPIO); // save receive port
|
||||
uart_soft_rx_states[2].pin = GPIO_PIN(UART_SOFT_RX2_GPIO); // save receive pin
|
||||
uart_soft_rx_states[2].rcc = GPIO_RCC(UART_SOFT_RX2_GPIO); // save receive port peripheral clock
|
||||
uart_soft_rx_states[2].exti = GPIO_EXTI(UART_SOFT_RX2_GPIO); // save receive external interrupt
|
||||
uart_soft_rx_states[2].irq = GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX2_GPIO); // save receive interrupt request
|
||||
#endif
|
||||
#if defined(UART_SOFT_RX3_GPIO)
|
||||
uart_soft_rx_states[3].port = GPIO_PORT(UART_SOFT_RX3_GPIO); // save receive port
|
||||
uart_soft_rx_states[3].pin = GPIO_PIN(UART_SOFT_RX3_GPIO); // save receive pin
|
||||
uart_soft_rx_states[3].rcc = GPIO_RCC(UART_SOFT_RX3_GPIO); // save receive port peripheral clock
|
||||
uart_soft_rx_states[3].exti = GPIO_EXTI(UART_SOFT_RX3_GPIO); // save receive external interrupt
|
||||
uart_soft_rx_states[3].irq = GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX3_GPIO); // save receive interrupt request
|
||||
#endif
|
||||
|
||||
// setup UART receive GPIO
|
||||
for (uint8_t rx = 0; rx < 4; rx++) {
|
||||
if (!uart_soft_rx_states[rx]) { // verify is receive UART is defined
|
||||
for (uint8_t rx = 0; rx < UART_NB; rx++) {
|
||||
if (0 == uart_soft_rx_states[rx].port) { // verify is receive UART is defined
|
||||
continue; // skip configuration if not defined
|
||||
}
|
||||
if (!rx_baudrates || 0 == rx_baudrates[rx]) { // verify if receive baud rate has been defined
|
||||
return false;
|
||||
}
|
||||
uart_soft_rx_states[rx]->baudrate = rx_baudrates[rx]; // save baud rate
|
||||
rcc_periph_clock_enable(uart_soft_rx_states[rx]->rcc); // enable clock for GPIO peripheral
|
||||
gpio_set_mode(uart_soft_rx_states[rx]->port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, uart_soft_rx_states[rx]->pin); // setup GPIO pin UART receive
|
||||
gpio_set(uart_soft_rx_states[rx]->port, uart_soft_rx_states[rx]->pin); // pull up to avoid noise when not connected
|
||||
uart_soft_rx_states[rx].baudrate = rcc_ahb_frequency / rx_baudrates[rx] - 1; // save baud rate
|
||||
rcc_periph_clock_enable(uart_soft_rx_states[rx].rcc); // enable clock for GPIO peripheral
|
||||
gpio_set(uart_soft_rx_states[rx].port, uart_soft_rx_states[rx].pin); // pull up to avoid noise when not connected
|
||||
gpio_set_mode(uart_soft_rx_states[rx].port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, uart_soft_rx_states[rx].pin); // setup GPIO pin UART receive
|
||||
rcc_periph_clock_enable(RCC_AFIO); // enable alternate function clock for external interrupt
|
||||
exti_select_source(uart_soft_rx_states[rx]->exti, uart_soft_rx_states[rx]->port); // mask external interrupt of this pin only for this port
|
||||
exti_enable_request(uart_soft_rx_states[rx]->exti); // enable external interrupt
|
||||
exti_set_trigger(uart_soft_rx_states[rx]->exti, EXTI_TRIGGER_BOTH); // trigger when button is pressed
|
||||
nvic_enable_irq(uart_soft_rx_states[rx]->irq); // enable interrupt
|
||||
uart_soft_rx_states[rx]->state = gpio_get(uart_soft_rx_states[rx]->port, uart_soft_rx_states[rx]->pin); // save state of GPIO
|
||||
uart_soft_rx_states[rx]->bit = 0; // reset bits received
|
||||
exti_select_source(uart_soft_rx_states[rx].exti, uart_soft_rx_states[rx].port); // mask external interrupt of this pin only for this port
|
||||
exti_enable_request(uart_soft_rx_states[rx].exti); // enable external interrupt
|
||||
exti_set_trigger(uart_soft_rx_states[rx].exti, EXTI_TRIGGER_BOTH); // trigger when button is pressed
|
||||
nvic_enable_irq(uart_soft_rx_states[rx].irq); // enable interrupt
|
||||
uart_soft_rx_states[rx].state = gpio_get(uart_soft_rx_states[rx].port, uart_soft_rx_states[rx].pin); // save state of GPIO
|
||||
uart_soft_rx_states[rx].bit = 0; // reset bits received
|
||||
}
|
||||
|
||||
// save UART transmit definition
|
||||
#if defined(UART_SOFT_TX_PORT0) && defined(UART_SOFT_TX_PIN0)
|
||||
uart_soft_tx_states[0] = calloc(1, sizeof(struct soft_uart_tx_state)); // create state definition
|
||||
uart_soft_tx_states[0]->port = GPIO(UART_SOFT_TX_PORT0); // save receive port
|
||||
uart_soft_tx_states[0]->pin = GPIO(UART_SOFT_TX_PIN0); // save receive pin
|
||||
uart_soft_tx_states[0]->rcc = RCC_GPIO(UART_SOFT_TX_PORT0); // save receive port peripheral clock
|
||||
memset(&uart_soft_tx_states, 0, sizeof(uart_soft_tx_states)); // initialize transmitter configuration array
|
||||
#if defined(UART_SOFT_TX0_GPIO)
|
||||
uart_soft_tx_states[0].port = GPIO_PORT(UART_SOFT_TX0_GPIO); // save receive port
|
||||
uart_soft_tx_states[0].pin = GPIO_PIN(UART_SOFT_TX0_GPIO); // save receive pin
|
||||
uart_soft_tx_states[0].rcc = GPIO_RCC(UART_SOFT_TX0_GPIO); // save receive port peripheral clock
|
||||
#endif
|
||||
#if defined(UART_SOFT_TX1_GPIO)
|
||||
uart_soft_tx_states[1].port = GPIO_PORT(UART_SOFT_TX1_GPIO); // save receive port
|
||||
uart_soft_tx_states[1].pin = GPIO_PIN(UART_SOFT_TX1_GPIO); // save receive pin
|
||||
uart_soft_tx_states[1].rcc = GPIO_RCC(UART_SOFT_TX1_GPIO); // save receive port peripheral clock
|
||||
#endif
|
||||
#if defined(UART_SOFT_TX2_GPIO)
|
||||
uart_soft_tx_states[2].port = GPIO_PORT(UART_SOFT_TX2_GPIO); // save receive port
|
||||
uart_soft_tx_states[2].pin = GPIO_PIN(UART_SOFT_TX2_GPIO); // save receive pin
|
||||
uart_soft_tx_states[2].rcc = GPIO_RCC(UART_SOFT_TX2_GPIO); // save receive port peripheral clock
|
||||
#endif
|
||||
#if defined(UART_SOFT_TX3_GPIO)
|
||||
uart_soft_tx_states[3].port = GPIO_PORT(UART_SOFT_TX3_GPIO); // save receive port
|
||||
uart_soft_tx_states[3].pin = GPIO_PIN(UART_SOFT_TX3_GPIO); // save receive pin
|
||||
uart_soft_tx_states[3].rcc = GPIO_RCC(UART_SOFT_TX3_GPIO); // save receive port peripheral clock
|
||||
#endif
|
||||
|
||||
// setup UART transmit GPIO
|
||||
for (uint8_t tx = 0; tx < 4; tx++) {
|
||||
if (!uart_soft_tx_states[tx]) { // verify is transmit UART is defined
|
||||
for (uint8_t tx = 0; tx < UART_NB; tx++) {
|
||||
if (0 == uart_soft_tx_states[tx].port) { // verify is transmit UART is defined
|
||||
continue; // skip configuration if not defined
|
||||
}
|
||||
if (!tx_baudrates || 0 == tx_baudrates[tx]) { // verify if transmit baud rate has been defined
|
||||
return false;
|
||||
}
|
||||
uart_soft_tx_states[tx]->baudrate = tx_baudrates[tx]; // save baud rate
|
||||
rcc_periph_clock_enable(uart_soft_tx_states[tx]->rcc); // enable clock for GPIO peripheral
|
||||
gpio_set_mode(uart_soft_tx_states[tx]->port, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, uart_soft_tx_states[tx]->pin); // setup GPIO UART transmit pin
|
||||
gpio_set(uart_soft_tx_states[tx]->port, uart_soft_tx_states[tx]->pin); // idle high
|
||||
uart_soft_tx_states[tx].baudrate = rcc_ahb_frequency / tx_baudrates[tx] - 1; // save baud rate
|
||||
rcc_periph_clock_enable(uart_soft_tx_states[tx].rcc); // enable clock for GPIO peripheral
|
||||
gpio_set_mode(uart_soft_tx_states[tx].port, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, uart_soft_tx_states[tx].pin); // setup GPIO UART transmit pin
|
||||
gpio_set(uart_soft_tx_states[tx].port, uart_soft_tx_states[tx].pin); // idle high
|
||||
}
|
||||
|
||||
rcc_periph_clock_enable(GPIO_RCC(PB12));
|
||||
gpio_set_mode(GPIO_PORT(PB12), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(PB12));
|
||||
|
||||
// setup timer
|
||||
#if defined(UART_SOFT_RX_TIMER)
|
||||
rcc_periph_clock_enable(RCC_TIM(UART_SOFT_RX_TIMER)); // enable clock for timer peripheral
|
||||
@ -194,55 +220,45 @@ bool uart_soft_setup(uint32_t *rx_baudrates, uint32_t *tx_baudrates)
|
||||
#if defined(UART_SOFT_RX_TIMER)
|
||||
uint8_t uart_soft_getbyte(uint8_t uart)
|
||||
{
|
||||
if (uart >= 4 || !uart_soft_rx_states[uart]) { // ensure receive UART port is defined
|
||||
return 0; // return
|
||||
if (uart >= UART_NB || 0 == uart_soft_rx_states[uart].port) { // ensure receive UART port is defined
|
||||
return 0;
|
||||
}
|
||||
while (!uart_soft_rx_states[uart]->buffer_used) { // idle until data is available
|
||||
__WFI(); // sleep until interrupt
|
||||
if (0 == uart_soft_rx_states[uart].buffer_used) { // there is no data in the buffer
|
||||
return 0;
|
||||
}
|
||||
uart_soft_rx_states[uart]->lock = true; // set lock
|
||||
uint8_t to_return = uart_soft_rx_states[uart]->buffer[uart_soft_rx_states[uart]->buffer_i]; // get the next available character
|
||||
uart_soft_rx_states[uart]->buffer_i = (uart_soft_rx_states[uart]->buffer_i + 1) % LENGTH(uart_soft_rx_states[uart]->buffer); // update used buffer
|
||||
uart_soft_rx_states[uart]->buffer_used--; // update used buffer
|
||||
uart_soft_rx_states[uart]->lock = false; // free lock
|
||||
if (uart_soft_rx_states[uart]->buffer_byte_used) { // temporary byte has been stored
|
||||
uart_soft_rx_states[uart]->buffer[(uart_soft_rx_states[uart]->buffer_i + uart_soft_rx_states[uart]->buffer_used) % LENGTH(uart_soft_rx_states[uart]->buffer)] = uart_soft_rx_states[uart]->buffer_byte; // put byte in buffer
|
||||
uart_soft_rx_states[uart]->buffer_used++; // update used buffer
|
||||
uart_soft_rx_states[uart]->buffer_byte_used = false; // buffer byte is now in buffer
|
||||
}
|
||||
uart_soft_received[uart] = (0 != uart_soft_rx_states[uart]->buffer_used); // notify user if data is available
|
||||
uart_soft_rx_states[uart]->lock = false; // free lock
|
||||
nvic_disable_irq(uart_soft_rx_states[uart].irq); // disable interrupt to protect the state of the buffer (interrupt can still get pending)
|
||||
uint8_t to_return = uart_soft_rx_states[uart].buffer[uart_soft_rx_states[uart].buffer_i]; // get the next available character
|
||||
uart_soft_rx_states[uart].buffer_i = (uart_soft_rx_states[uart].buffer_i + 1) & (LENGTH(uart_soft_rx_states[uart].buffer) - 1); // update used buffer
|
||||
uart_soft_rx_states[uart].buffer_used--; // update used buffer
|
||||
nvic_enable_irq(uart_soft_rx_states[uart].irq); // re-enable interrupt since critical part is finished
|
||||
uart_soft_received[uart] = (0 != uart_soft_rx_states[uart].buffer_used); // notify user if data is available
|
||||
return to_return;
|
||||
}
|
||||
|
||||
/** timer interrupt service routine to generate UART transmit signals */
|
||||
/** timer interrupt service routine to generate parse receive signal */
|
||||
void TIM_ISR(UART_SOFT_RX_TIMER)(void)
|
||||
{
|
||||
for (uint8_t rx = 0; rx < 4; rx++) {
|
||||
if (timer_interrupt_source(TIM(UART_SOFT_RX_TIMER),timer_flags[rx])) { // got a match on compare for receive pin
|
||||
timer_clear_flag(TIM(UART_SOFT_RX_TIMER),timer_flags[rx]); // clear flag
|
||||
if (!uart_soft_rx_states[rx]) { // verify if RX exists
|
||||
for (uint8_t rx = 0; rx < UART_NB; rx++) {
|
||||
if (timer_interrupt_source(TIM(UART_SOFT_RX_TIMER), timer_flags[rx])) { // got a match on compare for receive pin
|
||||
timer_clear_flag(TIM(UART_SOFT_RX_TIMER), timer_flags[rx]); // clear flag
|
||||
if (0 == uart_soft_rx_states[rx].port) { // verify if RX exists
|
||||
continue; // skip if receive port is not defined it
|
||||
}
|
||||
uart_soft_rx_states[rx]->byte += (0 == (gpio_get(uart_soft_rx_states[rx]->port, uart_soft_rx_states[rx]->pin) ? 0 : 1)<<(uart_soft_rx_states[rx]->bit - 1)); // save bit value
|
||||
if (uart_soft_rx_states[rx]->bit < 8) { // not the last bit received
|
||||
timer_set_oc_value(TIM(UART_SOFT_RX_TIMER), timer_oc[rx], timer_get_counter(TIM(UART_SOFT_RX_TIMER)) + rcc_ahb_frequency / uart_soft_rx_states[rx]->baudrate); // set timer to next bit
|
||||
uart_soft_rx_states[rx]->bit++; // wait for next bit
|
||||
gpio_toggle(GPIO_PORT(PB12), GPIO_PIN(PB12));
|
||||
uart_soft_rx_states[rx].byte += ((0 == gpio_get(uart_soft_rx_states[rx].port, uart_soft_rx_states[rx].pin) ? 0 : 1) << (uart_soft_rx_states[rx].bit - 1)); // save bit value
|
||||
if (uart_soft_rx_states[rx].bit < 8) { // not the last bit received
|
||||
timer_set_oc_value(TIM(UART_SOFT_RX_TIMER), timer_oc[rx], timer_get_counter(TIM(UART_SOFT_RX_TIMER)) + uart_soft_rx_states[rx].baudrate); // set timer to next bit
|
||||
uart_soft_rx_states[rx].bit++; // wait for next bit
|
||||
} else { // last bit received
|
||||
if (uart_soft_rx_states[rx]->lock) { // someone is already reading data
|
||||
uart_soft_rx_states[rx]->buffer_byte = uart_soft_rx_states[rx]->byte; // save byte
|
||||
uart_soft_rx_states[rx]->buffer_byte_used = true; // notify reader there is a temporary byte
|
||||
} else { // buffer can be updated
|
||||
if (uart_soft_rx_states[rx]->buffer_used >= LENGTH(uart_soft_rx_states[rx]->buffer)) { // buffer is full
|
||||
uart_soft_rx_states[rx]->buffer_i = (uart_soft_rx_states[rx]->buffer_i + 1) % LENGTH(uart_soft_rx_states[rx]->buffer); // drop oldest byte
|
||||
uart_soft_rx_states[rx]->buffer_used--; // update buffer usage
|
||||
}
|
||||
uart_soft_rx_states[rx]->buffer[(uart_soft_rx_states[rx]->buffer_i + uart_soft_rx_states[rx]->buffer_used) % LENGTH(uart_soft_rx_states[rx]->buffer)] = uart_soft_rx_states[rx]->byte; // put byte in buffer
|
||||
uart_soft_rx_states[rx]->buffer_used++; // update used buffer
|
||||
uart_soft_received[rx] = true; // notify user data is available
|
||||
if (uart_soft_rx_states[rx].buffer_used >= LENGTH(uart_soft_rx_states[rx].buffer)) { // buffer is full
|
||||
uart_soft_rx_states[rx].buffer_i = (uart_soft_rx_states[rx].buffer_i + 1) & (LENGTH(uart_soft_rx_states[rx].buffer) - 1); // drop oldest byte
|
||||
uart_soft_rx_states[rx].buffer_used--; // update buffer usage
|
||||
}
|
||||
timer_disable_irq(TIM(UART_SOFT_RX_TIMER), timer_interrupt[rx]); // stop_interrupting
|
||||
uart_soft_rx_states[rx]->bit = 0; // next bit should be first bit of next byte
|
||||
uart_soft_rx_states[rx].buffer[(uart_soft_rx_states[rx].buffer_i + uart_soft_rx_states[rx].buffer_used) & (LENGTH(uart_soft_rx_states[rx].buffer) - 1)] = uart_soft_rx_states[rx].byte; // put byte in buffer
|
||||
uart_soft_rx_states[rx].buffer_used++; // update used buffer
|
||||
uart_soft_received[rx] = true; // notify user data is available
|
||||
timer_disable_irq(TIM(UART_SOFT_RX_TIMER), timer_interrupt[rx]); // stop interrupting
|
||||
uart_soft_rx_states[rx].bit = 0; // next bit should be first bit of next byte
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -252,13 +268,13 @@ void TIM_ISR(UART_SOFT_RX_TIMER)(void)
|
||||
#if defined(UART_SOFT_TX_TIMER)
|
||||
void uart_soft_flush(uint8_t uart)
|
||||
{
|
||||
if (uart >= 4 || !uart_soft_tx_states[uart]) { // ensure transmit UART port is defined
|
||||
if (uart >= UART_NB || 0 == uart_soft_tx_states[uart].port) { // ensure transmit UART port is defined
|
||||
return; // return
|
||||
}
|
||||
while (uart_soft_tx_states[uart]->buffer_used) { // idle until buffer is empty
|
||||
while (uart_soft_tx_states[uart].buffer_used) { // idle until buffer is empty
|
||||
__WFI(); // sleep until interrupt
|
||||
}
|
||||
while (uart_soft_tx_states[uart]->transmit) { // idle until transmission is complete
|
||||
while (uart_soft_tx_states[uart].transmit) { // idle until transmission is complete
|
||||
__WFI(); // sleep until interrupt
|
||||
}
|
||||
}
|
||||
@ -267,69 +283,73 @@ void uart_soft_flush(uint8_t uart)
|
||||
* @param[in] uart UART port used for transmission
|
||||
*/
|
||||
static void uart_soft_transmit(uint8_t uart) {
|
||||
if (uart>=4 || !uart_soft_tx_states[uart]) { // ensure transmit UART port is defined
|
||||
if (uart >= UART_NB || 0 == uart_soft_tx_states[uart].port) { // ensure transmit UART port is defined
|
||||
return; // UART transmit port not defined
|
||||
}
|
||||
if (uart_soft_tx_states[uart]->transmit) { // already transmitting
|
||||
if (uart_soft_tx_states[uart].transmit) { // already transmitting
|
||||
return; // transmission is already ongoing
|
||||
}
|
||||
if (!uart_soft_tx_states[uart]->buffer_used) { // no buffered data to transmit
|
||||
if (!uart_soft_tx_states[uart].buffer_used) { // no buffered data to transmit
|
||||
return; // nothing to transmit
|
||||
}
|
||||
uart_soft_tx_states[uart]->byte = uart_soft_tx_states[uart]->buffer[uart_soft_tx_states[uart]->buffer_i]; // get byte
|
||||
uart_soft_tx_states[uart]->buffer_i = (uart_soft_tx_states[uart]->buffer_i + 1) % LENGTH(uart_soft_tx_states[uart]->buffer); // update index
|
||||
uart_soft_tx_states[uart]->buffer_used--; // update used buffer
|
||||
uart_soft_tx_states[uart]->bit = 0; // LSb is transmitted first
|
||||
uart_soft_tx_states[uart]->transmit = true; // start transmission
|
||||
gpio_clear(uart_soft_tx_states[uart]->port, uart_soft_tx_states[uart]->pin); // output start bit
|
||||
timer_set_oc_value(TIM(UART_SOFT_TX_TIMER), timer_oc[uart], timer_get_counter(TIM(UART_SOFT_TX_TIMER)) + (rcc_ahb_frequency / uart_soft_tx_states[uart]->baudrate)); // set timer to output UART frame 1 (data bit 0) in 1 bit
|
||||
timer_disable_irq(TIM(UART_SOFT_TX_TIMER), timer_interrupt[uart]); // disable interrupt to protect the state of the buffer (interrupt can still get pending)
|
||||
uart_soft_tx_states[uart].byte = uart_soft_tx_states[uart].buffer[uart_soft_tx_states[uart].buffer_i]; // get byte
|
||||
uart_soft_tx_states[uart].buffer_i = (uart_soft_tx_states[uart].buffer_i + 1) & (LENGTH(uart_soft_tx_states[uart].buffer) - 1); // update index
|
||||
uart_soft_tx_states[uart].buffer_used--; // update used buffer
|
||||
uart_soft_tx_states[uart].bit = 0; // LSb is transmitted first
|
||||
uart_soft_tx_states[uart].transmit = true; // start transmission
|
||||
gpio_clear(uart_soft_tx_states[uart].port, uart_soft_tx_states[uart].pin); // output start bit
|
||||
timer_set_oc_value(TIM(UART_SOFT_TX_TIMER), timer_oc[uart], timer_get_counter(TIM(UART_SOFT_TX_TIMER)) + uart_soft_tx_states[uart].baudrate); // set timer to output UART frame 1 (data bit 0) in 1 bit
|
||||
timer_clear_flag(TIM(UART_SOFT_TX_TIMER), timer_flags[uart]); // clear flag before enabling interrupt
|
||||
timer_enable_irq(TIM(UART_SOFT_TX_TIMER), timer_interrupt[uart]);// enable timer IRQ for TX for this UART
|
||||
timer_enable_irq(TIM(UART_SOFT_TX_TIMER), timer_interrupt[uart]); // enable timer IRQ for TX for this UART
|
||||
}
|
||||
|
||||
void uart_soft_putbyte_nonblocking(uint8_t uart, uint8_t byte)
|
||||
{
|
||||
if (uart >= 4 || !uart_soft_tx_states[uart]) { // ensure transmit UART port is defined
|
||||
if (uart >= UART_NB || 0 == uart_soft_tx_states[uart].port) { // ensure transmit UART port is defined
|
||||
return; // return
|
||||
}
|
||||
while (uart_soft_tx_states[uart]->buffer_used >= LENGTH(uart_soft_tx_states[uart]->buffer)) { // idle until there is place in the buffer
|
||||
__WFI(); // sleep until something happened
|
||||
}
|
||||
uart_soft_tx_states[uart]->buffer[(uart_soft_tx_states[uart]->buffer_i + uart_soft_tx_states[uart]->buffer_used) % LENGTH(uart_soft_tx_states[uart]->buffer)] = byte; // save byte to be transmitted
|
||||
uart_soft_tx_states[uart]->buffer_used++; // update used buffer
|
||||
while (uart_soft_tx_states[uart].buffer_used >= LENGTH(uart_soft_tx_states[uart].buffer)); // wait until there is place in the buffer
|
||||
timer_disable_irq(TIM(UART_SOFT_TX_TIMER), timer_interrupt[uart]); // disable interrupt to protect the state of the buffer (interrupt can still get pending)
|
||||
uart_soft_tx_states[uart].buffer[(uart_soft_tx_states[uart].buffer_i + uart_soft_tx_states[uart].buffer_used) & (LENGTH(uart_soft_tx_states[uart].buffer) - 1)] = byte; // save byte to be transmitted
|
||||
uart_soft_tx_states[uart].buffer_used++; // update used buffer
|
||||
timer_enable_irq(TIM(UART_SOFT_TX_TIMER), timer_interrupt[uart]); // re-enable interrupts to resume transmission
|
||||
uart_soft_transmit(uart); // start transmission
|
||||
}
|
||||
|
||||
void uart_soft_putbyte_blocking(uint8_t uart, uint8_t byte)
|
||||
{
|
||||
if (uart >= UART_NB || 0 == uart_soft_tx_states[uart].port) { // ensure transmit UART port is defined
|
||||
return; // return
|
||||
}
|
||||
uart_soft_putbyte_nonblocking(uart, byte); // put byte in queue
|
||||
uart_soft_flush(uart); // wait for all byte to be transmitted
|
||||
}
|
||||
|
||||
/** timer interrupt service routine to sample UART receive signals */
|
||||
/** timer interrupt service routine to transmit data */
|
||||
void TIM_ISR(UART_SOFT_TX_TIMER)(void)
|
||||
{
|
||||
for (uint8_t tx = 0; tx < 4; tx++) {
|
||||
for (uint8_t tx = 0; tx < UART_NB; tx++) {
|
||||
if (timer_interrupt_source(TIM(UART_SOFT_TX_TIMER), timer_flags[tx])) { // got a match on compare for transmit pin
|
||||
timer_clear_flag(TIM(UART_SOFT_TX_TIMER), timer_flags[tx]); // clear flag
|
||||
if (!uart_soft_tx_states[tx]) { // verify if transmit is defined
|
||||
if (0 == uart_soft_tx_states[tx].port) { // verify if transmit is defined
|
||||
continue; // skip if transmit port is not defined it
|
||||
}
|
||||
if (uart_soft_tx_states[tx]->bit < 8) { // there is a data bit to transmit
|
||||
if ((uart_soft_tx_states[tx]->byte >> uart_soft_tx_states[tx]->bit) & 0x01) { // bit to transmit is a 1
|
||||
gpio_set(uart_soft_tx_states[tx]->port, uart_soft_tx_states[tx]->pin); // set output to high
|
||||
if (uart_soft_tx_states[tx].bit < 8) { // there is a data bit to transmit
|
||||
if ((uart_soft_tx_states[tx].byte >> uart_soft_tx_states[tx].bit) & 0x01) { // bit to transmit is a 1
|
||||
gpio_set(uart_soft_tx_states[tx].port, uart_soft_tx_states[tx].pin); // set output to high
|
||||
} else { // bit to transmit is a 0
|
||||
gpio_clear(uart_soft_tx_states[tx]->port, uart_soft_tx_states[tx]->pin); // set output to low
|
||||
gpio_clear(uart_soft_tx_states[tx].port, uart_soft_tx_states[tx].pin); // set output to low
|
||||
}
|
||||
timer_set_oc_value(TIM(UART_SOFT_TX_TIMER), timer_oc[tx], timer_get_counter(TIM(UART_SOFT_TX_TIMER)) + (rcc_ahb_frequency / uart_soft_tx_states[tx]->baudrate)); // wait for the next frame bit
|
||||
uart_soft_tx_states[tx]->bit++; // go to next bit
|
||||
} else if (8 == uart_soft_tx_states[tx]->bit) { // transmit stop bit
|
||||
gpio_set(uart_soft_tx_states[tx]->port, uart_soft_tx_states[tx]->pin); // go idle high
|
||||
timer_set_oc_value(TIM(UART_SOFT_TX_TIMER), timer_oc[tx], timer_get_counter(TIM(UART_SOFT_TX_TIMER)) + (rcc_ahb_frequency / uart_soft_tx_states[tx]->baudrate)); // wait for 1 stop bit
|
||||
uart_soft_tx_states[tx]->bit++; // go to next bit
|
||||
timer_set_oc_value(TIM(UART_SOFT_TX_TIMER), timer_oc[tx], timer_get_counter(TIM(UART_SOFT_TX_TIMER)) + uart_soft_tx_states[tx].baudrate); // wait for the next frame bit
|
||||
uart_soft_tx_states[tx].bit++; // go to next bit
|
||||
} else if (8 == uart_soft_tx_states[tx].bit) { // transmit stop bit
|
||||
gpio_set(uart_soft_tx_states[tx].port, uart_soft_tx_states[tx].pin); // go idle high
|
||||
timer_set_oc_value(TIM(UART_SOFT_TX_TIMER), timer_oc[tx], timer_get_counter(TIM(UART_SOFT_TX_TIMER)) + uart_soft_tx_states[tx].baudrate); // wait for 1 stop bit
|
||||
uart_soft_tx_states[tx].bit++; // go to next bit
|
||||
} else { // UART frame is complete
|
||||
timer_disable_irq(TIM(UART_SOFT_TX_TIMER), timer_interrupt[tx]);// enable timer IRQ for TX for this UART
|
||||
uart_soft_tx_states[tx]->transmit = false; // transmission finished
|
||||
uart_soft_tx_states[tx].transmit = false; // transmission finished
|
||||
uart_soft_transmit(tx); // start next transmission (if there is)
|
||||
}
|
||||
} // compare match
|
||||
@ -340,71 +360,72 @@ void TIM_ISR(UART_SOFT_TX_TIMER)(void)
|
||||
/** central function handling receive signal activity */
|
||||
static void uart_soft_receive_activity(void)
|
||||
{
|
||||
for (uint8_t rx = 0; rx < 4; rx++) {
|
||||
if (!uart_soft_rx_states[rx]) { // verify if receive port is not configured
|
||||
for (uint8_t rx = 0; rx < UART_NB; rx++) {
|
||||
if (0 == uart_soft_rx_states[rx].port) { // verify if receive port is not configured
|
||||
continue; // skip if receive port is not defined it
|
||||
}
|
||||
if (uart_soft_rx_states[rx]->state!=gpio_get(uart_soft_rx_states[rx]->port, uart_soft_rx_states[rx]->pin)) { // only do something if state changed
|
||||
uart_soft_rx_states[rx]->state = gpio_get(uart_soft_rx_states[rx]->port, uart_soft_rx_states[rx]->pin); // save new state
|
||||
if (0 == uart_soft_rx_states[rx]->bit) { // start bit edge detected
|
||||
if (0 == uart_soft_rx_states[rx]->state) { // start bit has to be low
|
||||
timer_set_oc_value(TIM(UART_SOFT_RX_TIMER), timer_oc[rx], timer_get_counter(TIM(UART_SOFT_RX_TIMER)) + (rcc_ahb_frequency / uart_soft_rx_states[rx]->baudrate) * 1.5); // set timer to sample data bit 0 in 1.5 bits
|
||||
const uint16_t state_new = gpio_get(uart_soft_rx_states[rx].port, uart_soft_rx_states[rx].pin); // get new state
|
||||
if (uart_soft_rx_states[rx].state != state_new) { // only do something if state changed
|
||||
uart_soft_rx_states[rx].state = state_new; // save new state
|
||||
if (0 == uart_soft_rx_states[rx].bit) { // start bit edge detected
|
||||
if (0 == uart_soft_rx_states[rx].state) { // start bit has to be low
|
||||
timer_set_oc_value(TIM(UART_SOFT_RX_TIMER), timer_oc[rx], timer_get_counter(TIM(UART_SOFT_RX_TIMER)) + (uart_soft_rx_states[rx].baudrate) * 3 / 2); // set timer to sample data bit 0 in 1.5 bits
|
||||
timer_clear_flag(TIM(UART_SOFT_RX_TIMER), timer_flags[rx]); // clear flag before enabling interrupt
|
||||
timer_enable_irq(TIM(UART_SOFT_RX_TIMER), timer_interrupt[rx]);// enable timer IRQ for RX for this UART
|
||||
uart_soft_rx_states[rx]->byte = 0; // reset byte value
|
||||
uart_soft_rx_states[rx]->bit++; // wait for first bit
|
||||
uart_soft_rx_states[rx].byte = 0; // reset byte value
|
||||
uart_soft_rx_states[rx].bit = 1; // wait for first bit
|
||||
}
|
||||
} else { // data bit detected
|
||||
timer_set_oc_value(TIM(UART_SOFT_RX_TIMER), timer_oc[rx], timer_get_counter(TIM(UART_SOFT_RX_TIMER)) + (rcc_ahb_frequency / uart_soft_rx_states[rx]->baudrate) / 2); // resync timer to half a bit (good for drifting transmission, bad if the line is noisy)
|
||||
timer_set_oc_value(TIM(UART_SOFT_RX_TIMER), timer_oc[rx], timer_get_counter(TIM(UART_SOFT_RX_TIMER)) + (uart_soft_rx_states[rx].baudrate) / 2); // resync timer to half a bit (good for drifting transmission, bad if the line is noisy)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** GPIO interrupt service routine to detect UART receive activity */
|
||||
#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && UART_SOFT_RX_PIN0==0) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && UART_SOFT_RX_PIN1==0) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && UART_SOFT_RX_PIN2==0) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && UART_SOFT_RX_PIN3==0)
|
||||
#if (defined(UART_SOFT_RX0_GPIO) && NVIC_EXTI0_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX0_GPIO)) || (defined(UART_SOFT_RX1_GPIO) && NVIC_EXTI0_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX1_GPIO)) || (defined(UART_SOFT_RX2_GPIO) && NVIC_EXTI0_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX2_GPIO)) || (defined(UART_SOFT_RX3_GPIO) && NVIC_EXTI0_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX3_GPIO))
|
||||
void exti0_isr(void)
|
||||
{
|
||||
exti_reset_request(EXTI0); // clear interrupt flag for pin triggers this ISR (pin state will be checked independently)
|
||||
uart_soft_receive_activity(); // check which GPIO changed
|
||||
}
|
||||
#endif
|
||||
#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && UART_SOFT_RX_PIN0==1) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && UART_SOFT_RX_PIN1==1) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && UART_SOFT_RX_PIN2==1) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && UART_SOFT_RX_PIN3==1)
|
||||
#if (defined(UART_SOFT_RX0_GPIO) && NVIC_EXTI1_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX0_GPIO)) || (defined(UART_SOFT_RX1_GPIO) && NVIC_EXTI1_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX1_GPIO)) || (defined(UART_SOFT_RX2_GPIO) && NVIC_EXTI1_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX2_GPIO)) || (defined(UART_SOFT_RX3_GPIO) && NVIC_EXTI1_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX3_GPIO))
|
||||
void exti1_isr(void)
|
||||
{
|
||||
exti_reset_request(EXTI1); // clear interrupt flag for pin triggers this ISR (pin state will be checked independently)
|
||||
uart_soft_receive_activity(); // check which GPIO changed
|
||||
}
|
||||
#endif
|
||||
#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && UART_SOFT_RX_PIN0==2) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && UART_SOFT_RX_PIN1==2) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && UART_SOFT_RX_PIN2==2) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && UART_SOFT_RX_PIN3==2)
|
||||
#if (defined(UART_SOFT_RX0_GPIO) && NVIC_EXTI2_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX0_GPIO)) || (defined(UART_SOFT_RX1_GPIO) && NVIC_EXTI2_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX1_GPIO)) || (defined(UART_SOFT_RX2_GPIO) && NVIC_EXTI2_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX2_GPIO)) || (defined(UART_SOFT_RX3_GPIO) && NVIC_EXTI2_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX3_GPIO))
|
||||
void exti2_isr(void)
|
||||
{
|
||||
exti_reset_request(EXTI2); // clear interrupt flag for pin triggers this ISR (pin state will be checked independently)
|
||||
uart_soft_receive_activity(); // check which GPIO changed
|
||||
}
|
||||
#endif
|
||||
#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && UART_SOFT_RX_PIN0==3) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && UART_SOFT_RX_PIN1==3) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && UART_SOFT_RX_PIN2==3) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && UART_SOFT_RX_PIN3==3)
|
||||
#if (defined(UART_SOFT_RX0_GPIO) && NVIC_EXTI3_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX0_GPIO)) || (defined(UART_SOFT_RX1_GPIO) && NVIC_EXTI3_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX1_GPIO)) || (defined(UART_SOFT_RX2_GPIO) && NVIC_EXTI3_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX2_GPIO)) || (defined(UART_SOFT_RX3_GPIO) && NVIC_EXTI3_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX3_GPIO))
|
||||
void exti3_isr(void)
|
||||
{
|
||||
exti_reset_request(EXTI3); // clear interrupt flag for pin triggers this ISR (pin state will be checked independently)
|
||||
uart_soft_receive_activity(); // check which GPIO changed
|
||||
}
|
||||
#endif
|
||||
#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && UART_SOFT_RX_PIN0==4) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && UART_SOFT_RX_PIN1==4) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && UART_SOFT_RX_PIN2==4) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && UART_SOFT_RX_PIN3==4)
|
||||
#if (defined(UART_SOFT_RX0_GPIO) && NVIC_EXTI4_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX0_GPIO)) || (defined(UART_SOFT_RX1_GPIO) && NVIC_EXTI4_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX1_GPIO)) || (defined(UART_SOFT_RX2_GPIO) && NVIC_EXTI4_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX2_GPIO)) || (defined(UART_SOFT_RX3_GPIO) && NVIC_EXTI4_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX3_GPIO))
|
||||
void exti4_isr(void)
|
||||
{
|
||||
exti_reset_request(EXTI4); // clear interrupt flag for pin triggers this ISR (pin state will be checked independently)
|
||||
uart_soft_receive_activity(); // check which GPIO changed
|
||||
}
|
||||
#endif
|
||||
#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && (UART_SOFT_RX_PIN0==5 || UART_SOFT_RX_PIN0==6 || UART_SOFT_RX_PIN0==7 || UART_SOFT_RX_PIN0==8 || UART_SOFT_RX_PIN0==9)) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && (UART_SOFT_RX_PIN1==5 || UART_SOFT_RX_PIN1==6 || UART_SOFT_RX_PIN1==7 || UART_SOFT_RX_PIN1==8 || UART_SOFT_RX_PIN1==9)) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && (UART_SOFT_RX_PIN2==5 || UART_SOFT_RX_PIN2==6 || UART_SOFT_RX_PIN2==7 || UART_SOFT_RX_PIN2==8 || UART_SOFT_RX_PIN2==9)) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && (UART_SOFT_RX_PIN3==5 || UART_SOFT_RX_PIN3==6 || UART_SOFT_RX_PIN3==7 || UART_SOFT_RX_PIN3==8 || UART_SOFT_RX_PIN3==9))
|
||||
#if (defined(UART_SOFT_RX0_GPIO) && NVIC_EXTI9_5_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX0_GPIO)) || (defined(UART_SOFT_RX1_GPIO) && NVIC_EXTI9_5_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX1_GPIO)) || (defined(UART_SOFT_RX2_GPIO) && NVIC_EXTI9_5_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX2_GPIO)) || (defined(UART_SOFT_RX3_GPIO) && NVIC_EXTI9_5_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX3_GPIO))
|
||||
void exti9_5_isr(void)
|
||||
{
|
||||
exti_reset_request(EXTI5 | EXTI6 | EXTI7 | EXTI8 | EXTI9); // clear interrupt flag for pin triggers this ISR (pin state will be checked independently)
|
||||
uart_soft_receive_activity(); // check which GPIO changed
|
||||
}
|
||||
#endif
|
||||
#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && (UART_SOFT_RX_PIN0==10 || UART_SOFT_RX_PIN0==11 || UART_SOFT_RX_PIN0==12 || UART_SOFT_RX_PIN0==13 || UART_SOFT_RX_PIN0==14 || UART_SOFT_RX_PIN0==15)) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && (UART_SOFT_RX_PIN1==10 || UART_SOFT_RX_PIN1==11 || UART_SOFT_RX_PIN1==12 || UART_SOFT_RX_PIN1==13 || UART_SOFT_RX_PIN1==14 || UART_SOFT_RX_PIN1==15)) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && (UART_SOFT_RX_PIN2==10 || UART_SOFT_RX_PIN2==11 || UART_SOFT_RX_PIN2==12 || UART_SOFT_RX_PIN2==13 || UART_SOFT_RX_PIN2==14 || UART_SOFT_RX_PIN2==15)) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && (UART_SOFT_RX_PIN3==10 || UART_SOFT_RX_PIN3==11 || UART_SOFT_RX_PIN3==12 || UART_SOFT_RX_PIN3==13 || UART_SOFT_RX_PIN3==14 || UART_SOFT_RX_PIN3==15))
|
||||
#if (defined(UART_SOFT_RX0_GPIO) && NVIC_EXTI15_10_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX0_GPIO)) || (defined(UART_SOFT_RX1_GPIO) && NVIC_EXTI15_10_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX1_GPIO)) || (defined(UART_SOFT_RX2_GPIO) && NVIC_EXTI15_10_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX2_GPIO)) || (defined(UART_SOFT_RX3_GPIO) && NVIC_EXTI15_10_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX3_GPIO))
|
||||
void exti15_10_isr(void)
|
||||
{
|
||||
exti_reset_request(EXTI10 | EXTI11 | EXTI12 | EXTI13 | EXTI14 | EXTI15); // clear interrupt flag for pin triggers this ISR (pin state will be checked independently)
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to control up to 4 independent receive and transmit software UART ports
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
* @note peripherals used: GPIO @ref uart_soft_gpio, timer @ref uart_soft_timer
|
||||
*/
|
||||
@ -27,7 +14,7 @@ extern volatile bool uart_soft_received[4];
|
||||
* @param[in] tx_baudrates baud rates of the 4 UART TX ports (0 if unused)
|
||||
* @return is setup succeeded, else the configuration is wrong
|
||||
*/
|
||||
bool uart_soft_setup(uint32_t *rx_baudrates, uint32_t *tx_baudrates);
|
||||
bool uart_soft_setup(const uint32_t* rx_baudrates, const uint32_t* tx_baudrates);
|
||||
/** get received byte from UART port
|
||||
* @param[in] uart UART receive port to read byte from
|
||||
* @return received byte (0 if no byte is available)
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library for enhanced USART communication (code)
|
||||
* @file usart_enhanced.c
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2018
|
||||
* @details the USART peripherals only support 8 or 9-bit word and even or odd parity (included in the data bits). The library adds support for 5 to 8-bit words, none/even/odd/mark/space parity (on top of the data bits)
|
||||
* @note since parity is handled in software, the parity error (PE) flag is unused and should be replaced by the value return by usart_enhanced_parity_error
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library for enhanced USART communication (API)
|
||||
* @file usart_enhanced.h
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2018
|
||||
* @details the USART peripherals only support 8 or 9-bit word and even or odd parity (included in the data bits). The library adds support for 5 to 8-bit words, none/even/odd/mark/space parity (on top of the data bits)
|
||||
* @note since parity is handled in software, the parity error (PE) flag is unused and should be replaced by the value return by usart_enhanced_parity_error
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library for USB CDC ACM communication (code)
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2019
|
||||
*/
|
||||
|
||||
@ -492,7 +479,13 @@ void usb_cdcacm_putchar(char c)
|
||||
usb_cdcacm_data_tx_cb(NULL, 0); // send data over USB when possible
|
||||
}
|
||||
|
||||
void usb_cdcacm_flush(void)
|
||||
{
|
||||
while (tx_used);
|
||||
}
|
||||
|
||||
/** USB interrupt service routine called when data is received */
|
||||
void usb_lp_can_rx0_isr(void) {
|
||||
void usb_lp_can_rx0_isr(void)
|
||||
{
|
||||
usbd_poll(usb_device);
|
||||
}
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library for USB CDC ACM communication (API)
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2019
|
||||
*/
|
||||
#pragma once
|
||||
@ -29,3 +16,6 @@ void usb_cdcacm_setup(void);
|
||||
* @note blocks if transmit buffer is full, else puts in buffer and returns
|
||||
*/
|
||||
void usb_cdcacm_putchar(char c);
|
||||
/** wait until all is transmitted (blocking)
|
||||
*/
|
||||
void usb_cdcacm_flush(void);
|
||||
|
@ -1,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library for USB DFU to write on internal flash (code)
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2020
|
||||
*/
|
||||
|
||||
@ -247,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,20 +1,7 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library for USB DFU to write on internal flash (API)
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2019
|
||||
*/
|
||||
#pragma once
|
||||
|
@ -1,21 +1,8 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to drive vacuum fluorescent display using supertex HV518 shift register VFD drivers
|
||||
* @details the current configuration is for a VFD extracted from a Samsung SER-6500 cash register
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
* @note peripherals used: SPI @ref vfd_hv518_spi , GPIO @ref vfd_hv518_gpio , timer @ref vfd_hv518_timer
|
||||
*/
|
||||
|
@ -1,21 +1,8 @@
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library to drive vacuum fluorescent display using supertex HV518 shift register VFD drivers
|
||||
* @details the current configuration is for a VFD extracted from a Samsung SER-6500 cash register
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
* @note peripherals used: SPI @ref vfd_hv518_spi , GPIO @ref vfd_hv518_gpio , timer @ref vfd_hv518_timer
|
||||
*/
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit cb0661f81de5b1cae52ca99c7b5985b678176db7
|
||||
Subproject commit 664701d7a7f169235c457a3dd117415647aac61b
|
Loading…
Reference in New Issue
Block a user