Compare commits
23 Commits
master
...
thermocycl
Author | SHA1 | Date | |
---|---|---|---|
18f541adbb | |||
b9df8e7e30 | |||
b531e4cde3 | |||
9fbd4d377e | |||
ea5dc74c88 | |||
dde56c996c | |||
51cf8b0f95 | |||
517f58d463 | |||
6d93633587 | |||
8b62d16436 | |||
b071eb6737 | |||
d9229026e5 | |||
b8cc7d7591 | |||
eb55880296 | |||
36456ba45f | |||
89ba66646f | |||
a107c60e78 | |||
c0a137e546 | |||
685b1450f6 | |||
2120adb231 | |||
b5a37148d4 | |||
4ade159586 | |||
b9b9eea2a0 |
321
README.md
321
README.md
@ -1,3 +1,6 @@
|
||||
This is the firmware for the ThermoHybaid MBS 0.2G MBLK001 issue 2 (short HBMBSG02) thermo-cycler replacement controller board.
|
||||
|
||||
This is the firmware for the thermo-cycler
|
||||
This firmware template is designed for development boards based around [STM32 F1 series micro-controller](http://www.st.com/web/en/catalog/mmc/FM141/SC1169/SS1031).
|
||||
|
||||
project
|
||||
@ -6,50 +9,314 @@ project
|
||||
summary
|
||||
-------
|
||||
|
||||
*describe project purpose*
|
||||
I bought a HBMBSG02 thermo-cycler, with unknown defect, and wanted to use to make PCR tests.
|
||||
|
||||
technology
|
||||
----------
|
||||
|
||||
*described electronic details*
|
||||
I replaced the controller board with custom electronics to be able to operate it again.
|
||||
|
||||
HBMBSG02
|
||||
========
|
||||
|
||||
The device did power up.
|
||||
After a couple of seconds it shut down the fan and probably waits for further instructions.
|
||||
No defect was visible.
|
||||
I did not get any software to operate it, or check for errors.
|
||||
This is a rather old device, from 2003.
|
||||
The manufacturer did not reply to my request for any software or further documentation.
|
||||
|
||||
There are two RJ11 ports, for RS-485 communication.
|
||||
No traffic has been seen during boot up (this probably uses a master slave protocol since the devices can be chained).
|
||||
I did not get any reply after sending random bytes.
|
||||
|
||||
The device is made out of the following parts.
|
||||
|
||||
MBLK002
|
||||
-------
|
||||
|
||||
The heating (and cooling) bed for 8x12 PCR tubes.
|
||||
Made of black anodized aluminium.
|
||||
|
||||
lid
|
||||
---
|
||||
|
||||
there is a heated lid with two connectors.
|
||||
|
||||
temperature sensor, 12 kOhm NTC thermistor, 3-pin (pin 1 has notch):
|
||||
1. thermistor lead 1
|
||||
2. thermistor lead 2
|
||||
3. chassis
|
||||
connected to MBLK005.
|
||||
|
||||
heating element, 150 Ohm resistor, 2-pin connector.
|
||||
connected to MBLK078.
|
||||
this is power by AC 220V, controller by main board.
|
||||
I don't recommend to have is on at full power for too long.
|
||||
The heating mat gets hot very fast.
|
||||
I even blew to inline fuse (196 °C) although I only tried to heat it up to 94 °C.
|
||||
I replaced the fuse.
|
||||
|
||||
MBLK005
|
||||
-------
|
||||
|
||||
this board provides power to the controller board and lid.
|
||||
|
||||
power + heater, 2x2 connector:
|
||||
1. red, 12V (13-77 V)
|
||||
2. black, ground
|
||||
3. yellow/red, optocoupler anode for triac controlling the lid heater
|
||||
4. yellow/black, optocoupler cathode for triac controlling the lid heater
|
||||
|
||||
1x2 connector to lid heater, controller by BTA06 triac.
|
||||
|
||||
it also allows to select between 110 V and 220 V, and provides AC power to the TEC power supply.
|
||||
|
||||
MBLK041
|
||||
-------
|
||||
|
||||
RS485 adapter board.
|
||||
it provides 2 interconnected RJ11 ports.
|
||||
this allows to chain thermocyclers.
|
||||
|
||||
1x4 connector, to main board:
|
||||
1. yellow, RS-485-2 A (9/Y on MAX489)
|
||||
2. green, RS-485-2 B (10/Z on MAX489)
|
||||
3. brown, RS-485-1 A (12/A on MAX489)
|
||||
4. orange, RS-485-1 B (11/B on MAX489)
|
||||
|
||||
A4-246
|
||||
------
|
||||
|
||||
power supply for the TECs (e.g. peltier elements) though yellow/orange cables.
|
||||
I think this provides a programmable constant current to operate the TECs efficiently.
|
||||
|
||||
2x5 IDC connector to main board:
|
||||
1. IC2 anode
|
||||
2. IC2 cathode with R23
|
||||
3. IC4 anode through R24
|
||||
4. IC4 anode
|
||||
5. IC6 collector
|
||||
6. IC6 emitter
|
||||
7. IC3 anode
|
||||
8. IC3 cathode through R15
|
||||
9. IC5 anode through R25
|
||||
10. IC5 cathode
|
||||
|
||||
by applying any combination on ID2-5 I was not able to have it output power.
|
||||
IC6 is active when IC5 gets power, but that's the only reaction I got.
|
||||
|
||||
test points:
|
||||
- T3-8 -- TP9 -- TR4 -- TP8
|
||||
- TP8: 38 kHz @ 1V
|
||||
- TP9: 38 kHz @ 60V
|
||||
- TP10: ground
|
||||
- TP11: PWM 38 kHz @ 13V
|
||||
- TP12: 6V
|
||||
- TP13: 16.6V
|
||||
|
||||
A4-250
|
||||
------
|
||||
|
||||
H-bridge module mounted on A4-246
|
||||
|
||||
PL2 connection:
|
||||
1. ground
|
||||
2. orange wire (H-bridge output)
|
||||
3. yellow wire (H-bridge output)
|
||||
4. VCC (6V)
|
||||
|
||||
PL5:
|
||||
5. IC3 collector
|
||||
7. VCC
|
||||
8. ground
|
||||
9. ground
|
||||
10. IC6 anode
|
||||
12. IC5 collector
|
||||
13. IC2 emitter (pulled down)
|
||||
14. IC4 emmiter (pulled down)
|
||||
|
||||
front panel
|
||||
-----------
|
||||
|
||||
1x5 connector, to main board:
|
||||
1. ground
|
||||
2. play/pause indicator, green LED anode
|
||||
3. play/pause indicator, orange LED anode
|
||||
4. power indicator, red LED
|
||||
5. play/pause button, connected to ground when pressed
|
||||
|
||||
fan
|
||||
---
|
||||
|
||||
1x2 connector, to main board:
|
||||
1. red: 12V
|
||||
2. black: ground
|
||||
|
||||
MBLK019/MBLK020
|
||||
---------------
|
||||
|
||||
controls which TECs get power, and how.
|
||||
|
||||
2x3 pin plug (IDC numbering):
|
||||
1. red, VCC
|
||||
2. red/green, sink to control IC/TR 2/6
|
||||
3. red/black, sink to control IC/TR 1/4
|
||||
4. red/blue, connected to pin 6
|
||||
5. red/brown, sink to control IC/TR 3/5
|
||||
6. black, connected to pin 4
|
||||
|
||||
1x6 conenctor, to TEC and power supply.
|
||||
|
||||
MBLK009
|
||||
-------
|
||||
|
||||
ST 339 is a quad voltage comparator.
|
||||
I don't know what it compares.
|
||||
|
||||
LN 393 is a dual comparator.
|
||||
I don't know what it compares.
|
||||
|
||||
there is also an external reference (REF1).
|
||||
this is probably for the MAX1247 (only the MAX1246 has an internal voltage reference).
|
||||
|
||||
MAX1247 is a 4-channel 12-bit ADC.
|
||||
it is used to measure the temperatures.
|
||||
|
||||
there are four 2.2 kOhm thermistors.
|
||||
one side is connected to REF1, the other to a MAX1247 channel.
|
||||
- TH4: on the sink, connected to CH2 through R8 (2702), and COM through R4
|
||||
- TH3: in the tube, connected to CH3 through R7 (2702), and COM through R3
|
||||
- in the top of the tub heating bed, connected to CH0
|
||||
- in the bottom of the tub heating bed, connected to CH1
|
||||
|
||||
2x7 IDC connector:
|
||||
01. NC
|
||||
02. VCC
|
||||
03. LN393 OUTPUT-A
|
||||
04. MAX1247 DIN
|
||||
05. MAX1247 DOUT
|
||||
06. MAX1247 SCLK
|
||||
07. MAX1247 nCS
|
||||
08. ST339 OUTPUT-3
|
||||
09. LK1 jumper (missing), other side connected to ground
|
||||
10. LK2 jumper (missing), other side connected to ground
|
||||
11. LK3 jumper (present), other side connected to ground
|
||||
12. LK4 jumper (present), other side connected to ground
|
||||
13. ground
|
||||
14. LN393 OUTPUT-A through 10 kOhm resistor (not sure what this is used for, probably not just to pull)
|
||||
|
||||
1x6 conenctor to TECs:
|
||||
- 1 -- free standing 93 °C fuse -- fuse in heat sink -- 2x top TECs in series -- 2
|
||||
- 2 -- 2x middle-top TECs in series -- 3
|
||||
- 4 -- 2x middle-bottom TECs in series -- 5
|
||||
- 5 -- 2x bottom TECs in series -- 6
|
||||
|
||||
when 3 is + and 1 is -, the top half bed heats up.
|
||||
when 6 is + and 4 is -, the bottom half bed heats up.
|
||||
|
||||
MBLK005
|
||||
-------
|
||||
|
||||
this is the main controller board.
|
||||
it is connected to all other parts through individual connectors, or the card edge connector.
|
||||
|
||||
the 2.4V rechargeable battery was empty and leaking, with spades corroded.
|
||||
changing the battery did not change anything: the thermo-cycle goes to sleep after power up.
|
||||
I don't know it it hold the calibration data in SRAM and the reason why the device has been sold as defective.
|
||||
|
||||
This is replaced by a custom controller board for which this firmware is.
|
||||
|
||||
board
|
||||
=====
|
||||
|
||||
The current implementation uses a [core board](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#core_board).
|
||||
The MBLK005-replacement board uses a [blue pill](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#blue_pill), based on a STM32F103C8T6.
|
||||
|
||||
The underlying template also supports following board:
|
||||
peripherals
|
||||
===========
|
||||
|
||||
- [Maple Mini](http://leaflabs.com/docs/hardware/maple-mini.html), based on a STM32F103CBT6
|
||||
- [System Board](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#system_board), based on a STM32F103C8T6
|
||||
- [blue pill](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#blue_pill), based on a STM32F103C8T6
|
||||
- [black pill](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#black_pill), based on a STM32F103C8T6
|
||||
- [core board](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#core_board), based on a STM32F103C8T6
|
||||
- [ST-LINK V2 mini](https://wiki.cuvoodoo.info/doku.php?id=jtag#mini_st-link_v2), a ST-LINK/V2 clone based on a STM32F101C8T6
|
||||
- [USB-Blaster](https://wiki.cuvoodoo.info/doku.php?id=jtag#armjishu_usb-blaster), an Altera USB-Blaster clone based on a STM32F101C8T6
|
||||
additional peripherals to operate the thermo cycler:
|
||||
|
||||
**Which board is used is defined in the Makefile**.
|
||||
This is required to map the user LED and button provided on the board
|
||||
SSD1306 OLED screen to show the detailed state:
|
||||
1. GND
|
||||
2. VDD
|
||||
3. SCK
|
||||
4. SDA
|
||||
|
||||
The ST-LINK V2 mini clone has SWD test points on the board.
|
||||
Because read protection is enabled, you will first need to remove the protection to be able to flash the firmware.
|
||||
To remove the read protection (and erase flash), run `rake remove_protection` while a SWD adapter is connected.
|
||||
|
||||
The Altera USB-Blaster clone has a pin header for SWD and UART1 on the board.
|
||||
SWD is disabled in the main firmware, and it has read protection.
|
||||
To be able to flash using SWD (or the serial port), the BOOT0 pin must be set to 1 to boot the system memory install of the flash memory.
|
||||
To set BOOT0 to 1, apply 3.3 V on R11, between the resistor and the reference designator, when powering the device.
|
||||
The red LED should stay off while the green LED is on.
|
||||
Now you can remove the read protection (and erase flash), run `rake remove_protection` while a SWD adapter is connected.
|
||||
24V 12A power supply replacing the A4-246.
|
||||
ideally it would be an adjustable constant current supply (probably what the original power supply was), but using PWM on constant voltage is good enough for thermocouples, even if less efficient.
|
||||
12V is not enough to heat the TECs rapidly up to 94 °C.
|
||||
|
||||
connections
|
||||
===========
|
||||
|
||||
Connect the peripherals the following way (STM32F10X signal; STM32F10X pin; peripheral pin; peripheral signal; comment):
|
||||
|
||||
- *list board to peripheral pin connections*
|
||||
|
||||
All pins are configured using `define`s in the corresponding source code.
|
||||
Connect the peripherals the following way.
|
||||
|
||||
heating block MBLK-008 CO IDC 7x2:
|
||||
01. NC
|
||||
02. 3.3V
|
||||
03. PB3
|
||||
04. PB15 SPI2_MOSI
|
||||
05. PB14 SPI2_MISO
|
||||
06. PB13 SPI2_SCK
|
||||
07. PB12 SPI2_NSS
|
||||
08. PB4
|
||||
09. PB5
|
||||
10. PC14
|
||||
11. PC15
|
||||
12. PB1
|
||||
13. ground
|
||||
|
||||
heated lid, 12 kOhm NTC thermistor, 3-pin (pin 1 has notch):
|
||||
1. PA0/ADC1_CH0, pulled up to 5.0V by 10 kOhm resistor
|
||||
2. ground
|
||||
3. earth
|
||||
|
||||
thermocouple controller board, MBLK019, 2x3 IDC connector:
|
||||
1. 3.3 V
|
||||
2. PA1
|
||||
3. PA2
|
||||
4. PA3
|
||||
5. PA4
|
||||
6. ground
|
||||
|
||||
front panel:
|
||||
1. ground
|
||||
2. PA5, with 330 Ohm inline resistor
|
||||
3. PA6, with 330 Ohm inline resistor
|
||||
4. PA7, with 330 Ohm inline resistor
|
||||
5. PB0
|
||||
|
||||
fan:
|
||||
1. 12V front board power supply
|
||||
2. power nMOS drain
|
||||
power nMOS source. ground
|
||||
power nMOS gate. PA15, pulled up externally to 5V
|
||||
|
||||
MBLK-078, power + heater, 2x2 connector:
|
||||
1. LM7805 in
|
||||
2. ground
|
||||
3. 5V though 330 Ohm
|
||||
4. PA10
|
||||
|
||||
SSD1306 OLED screen:
|
||||
1. ground
|
||||
2. 3.3 V
|
||||
3. PB6/I2C1_SCL, pulled up to 3.3V by external 10 kOhm resistor
|
||||
4. PB7/I2C1_SDA, pulled up to 3.3V by external 10 kOhm resistor
|
||||
|
||||
thermocouple power supply control board. IDC 2x5:
|
||||
- 1,2: 5V
|
||||
- 3,4: PB10
|
||||
- 5,6: PB11
|
||||
- 7,8: PB9
|
||||
- 9,10: ground
|
||||
|
||||
DS18B20 1-Wire temperature sensor, 1x3:
|
||||
1. OWD, PB8
|
||||
2. ground
|
||||
3. 3.3V
|
||||
|
||||
code
|
||||
====
|
||||
|
1109
application.c
1109
application.c
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,7 @@
|
||||
/* define memory regions. */
|
||||
MEMORY
|
||||
{
|
||||
rom (rx) : ORIGIN = 0x08000000 + 8K, LENGTH = 64K - 8K
|
||||
rom (rx) : ORIGIN = 0x08000000 + 8K, LENGTH = 128K - 8K
|
||||
ram (rwx) : ORIGIN = 0x20000000 + 4, LENGTH = 20K - 4
|
||||
}
|
||||
PROVIDE(__application_beginning = ORIGIN(rom));
|
||||
@ -21,8 +21,8 @@ PROVIDE(__flash_end = 0);
|
||||
PROVIDE(__application_end = ORIGIN(rom) + LENGTH(rom));
|
||||
PROVIDE(__flash_end = ORIGIN(rom) + LENGTH(rom));
|
||||
*/
|
||||
PROVIDE(__application_end = 0);
|
||||
PROVIDE(__flash_end = 0);
|
||||
PROVIDE(__application_end = ORIGIN(rom) + LENGTH(rom));
|
||||
PROVIDE(__flash_end = ORIGIN(rom) + LENGTH(rom));
|
||||
/* RAM location reserved so application can talk to bootloader and tell to start DFU */
|
||||
PROVIDE(__dfu_magic = ORIGIN(ram) - 4);
|
||||
|
||||
|
@ -21,8 +21,8 @@ PROVIDE(__flash_end = 0);
|
||||
PROVIDE(__application_end = ORIGIN(rom) + LENGTH(rom));
|
||||
PROVIDE(__flash_end = ORIGIN(rom) + LENGTH(rom));
|
||||
*/
|
||||
PROVIDE(__application_end = 0);
|
||||
PROVIDE(__flash_end = 0);
|
||||
PROVIDE(__application_end = ORIGIN(rom) + 128K);
|
||||
PROVIDE(__flash_end = ORIGIN(rom) + 128K);
|
||||
/* RAM location reserved so application can talk to bootloader and tell to start DFU */
|
||||
PROVIDE(__dfu_magic = ORIGIN(ram) - 4);
|
||||
|
||||
|
78
global.c
78
global.c
@ -28,6 +28,84 @@ static volatile uint8_t user_input_used = 0; /**< how much data has been receive
|
||||
|
||||
static volatile uint32_t sleep_duration = 0; /**< sleep duration count down (in SysTick interrupts) */
|
||||
|
||||
uint8_t addu8_safe(uint8_t a, uint8_t b)
|
||||
{
|
||||
if (a > UINT8_MAX - b) {
|
||||
return UINT8_MAX;
|
||||
} else {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t addu16_safe(uint16_t a, uint16_t b)
|
||||
{
|
||||
if (a > UINT16_MAX - b) {
|
||||
return UINT16_MAX;
|
||||
} else {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t addu32_safe(uint32_t a, uint32_t b)
|
||||
{
|
||||
if (a > UINT32_MAX - b) {
|
||||
return UINT32_MAX;
|
||||
} else {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
|
||||
int8_t adds8_safe(int8_t a, int8_t b)
|
||||
{
|
||||
if (b > 0) {
|
||||
if (a > INT8_MAX - b) {
|
||||
return INT8_MAX;
|
||||
} else {
|
||||
return a + b;
|
||||
}
|
||||
} else {
|
||||
if (a < INT8_MIN + b) {
|
||||
return INT8_MIN;
|
||||
} else {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int16_t adds16_safe(int16_t a, int16_t b)
|
||||
{
|
||||
if (b > 0) {
|
||||
if (a > INT16_MAX - b) {
|
||||
return INT16_MAX;
|
||||
} else {
|
||||
return a + b;
|
||||
}
|
||||
} else {
|
||||
if (a < INT16_MIN + b) {
|
||||
return INT16_MIN;
|
||||
} else {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t adds32_safe(int32_t a, int32_t b)
|
||||
{
|
||||
if (b > 0) {
|
||||
if (a > INT32_MAX - b) {
|
||||
return INT32_MAX;
|
||||
} else {
|
||||
return a + b;
|
||||
}
|
||||
} else {
|
||||
if (a < INT32_MIN + b) {
|
||||
return INT32_MIN;
|
||||
} else {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char* b2s(uint64_t binary, uint8_t rjust)
|
||||
{
|
||||
static char string[64 + 1] = {0}; // the string representation to return
|
||||
|
43
global.h
43
global.h
@ -24,11 +24,48 @@
|
||||
/** integer underflow/overflow safe uint32_t addition (result to min/max on underflow/overflow) */
|
||||
#define ADDU32_SAFE(a,b) {__typeof__ (a) _a = (a); __typeof__ (b) _b = (b); a = (_b > 0 ? ((_a > UINT32_MAX - _b) ? UINT32_MAX : (_a + _b)) : ((_a < _b) ? 0 : (_a - _b)));}
|
||||
/** integer underflow/overflow safe int8_t addition (result to min/max on underflow/overflow) */
|
||||
#define ADDS8_SAFE(a,b) {__typeof__ (a) _a = (a); __typeof__ (b) _b = (b); a = (_b > 0 ? ((_a > INT8_MAX - _b) ? INT8_MAX : (_a + _b)) : ((_a < INT8_MAX + _b) ? INT8_MAX : (_a - _b)));}
|
||||
#define ADDS8_SAFE(a,b) {__typeof__ (a) _a = (a); __typeof__ (b) _b = (b); a = (_b > 0 ? ((_a > INT8_MAX - _b) ? INT8_MAX : (_a + _b)) : ((_a < INT8_MAX + _b) ? INT8_MAX : (_a + _b)));}
|
||||
/** integer underflow/overflow safe int16_t addition (result to min/max on underflow/overflow) */
|
||||
#define ADDS16_SAFE(a,b) {__typeof__ (a) _a = (a); __typeof__ (b) _b = (b); a = (_b > 0 ? ((_a > INT16_MAX - _b) ? INT16_MAX : (_a + _b)) : ((_a < INT16_MIN + _b) ? INT16_MIN : (_a - _b)));}
|
||||
#define ADDS16_SAFE(a,b) {__typeof__ (a) _a = (a); __typeof__ (b) _b = (b); a = (_b > 0 ? ((_a > INT16_MAX - _b) ? INT16_MAX : (_a + _b)) : ((_a < INT16_MIN + _b) ? INT16_MIN : (_a + _b)));}
|
||||
/** integer underflow/overflow safe int32_t addition (result to min/max on underflow/overflow) */
|
||||
#define ADDS32_SAFE(a,b) {__typeof__ (a) _a = (a); __typeof__ (b) _b = (b); a = (_b > 0 ? ((_a > UINT32_MAX - _b) ? UINT32_MAX : (_a + _b)) : ((_a < INT32_MIN + _b) ? INT32_MIN : (_a - _b)));}
|
||||
#define ADDS32_SAFE(a,b) {__typeof__ (a) _a = (a); __typeof__ (b) _b = (b); a = (_b > 0 ? ((_a > INT32_MAX - _b) ? INT32_MAX : (_a + _b)) : ((_a < INT32_MIN + _b) ? INT32_MIN : (_a + _b)));}
|
||||
|
||||
/** unsigned 8-bit integer overflow safe addition
|
||||
* @param[in] a first part of addition
|
||||
* @param[in] b second part of addition
|
||||
* return result of addition, or type max on overflow
|
||||
*/
|
||||
uint8_t addu8_safe(uint8_t a, uint8_t b);
|
||||
/** unsigned 16-bit integer overflow safe addition
|
||||
* @param[in] a first part of addition
|
||||
* @param[in] b second part of addition
|
||||
* return result of addition, or type max on overflow
|
||||
*/
|
||||
uint16_t addu16_safe(uint16_t a, uint16_t b);
|
||||
/** unsigned 8-bit integer overflow safe addition
|
||||
* @param[in] a first part of addition
|
||||
* @param[in] b second part of addition
|
||||
* return result of addition, or type max on overflow
|
||||
*/
|
||||
uint32_t addu32_safe(uint32_t a, uint32_t b);
|
||||
/** signed 8-bit integer underflow/overflow safe addition
|
||||
* @param[in] a first part of addition
|
||||
* @param[in] b second part of addition
|
||||
* return result of addition, or type max on overflow
|
||||
*/
|
||||
int8_t adds8_safe(int8_t a, int8_t b);
|
||||
/** signed 16-bit integer underflow/overflow safe addition
|
||||
* @param[in] a first part of addition
|
||||
* @param[in] b second part of addition
|
||||
* return result of addition, or type max on overflow
|
||||
*/
|
||||
int16_t adds16_safe(int16_t a, int16_t b);
|
||||
/** signed 32-bit integer underflow/overflow safe addition
|
||||
* @param[in] a first part of addition
|
||||
* @param[in] b second part of addition
|
||||
* return result of addition, or type max on overflow
|
||||
*/
|
||||
int32_t adds32_safe(int32_t a, int32_t b);
|
||||
|
||||
/** build year as number */
|
||||
#define COMPUTE_BUILD_YEAR \
|
||||
|
337
lib/font.c
Normal file
337
lib/font.c
Normal file
@ -0,0 +1,337 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
/** monospace pixel fonts collection (code)
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @date 2018
|
||||
*/
|
||||
#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,
|
||||
},
|
||||
};
|
41
lib/font.h
Normal file
41
lib/font.h
Normal file
@ -0,0 +1,41 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
/** monospace pixel fonts collection (API)
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @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];
|
146
lib/oled_text.c
Normal file
146
lib/oled_text.c
Normal file
@ -0,0 +1,146 @@
|
||||
/* 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 show display text on SSD1306 OLED display
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @date 2020
|
||||
* @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
|
||||
}
|
||||
}
|
45
lib/oled_text.h
Normal file
45
lib/oled_text.h
Normal file
@ -0,0 +1,45 @@
|
||||
/* 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 show display text on SSD1306 OLED display
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @date 2018-2020
|
||||
* @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);
|
@ -29,13 +29,13 @@
|
||||
* @note external pull-up resistor on pin is required (< 5 kOhm)
|
||||
* @{
|
||||
*/
|
||||
#define ONEWIRE_MASTER_PIN PC9 /**< GPIO pin */
|
||||
#define ONEWIRE_MASTER_PIN PB8 /**< GPIO pin */
|
||||
/** @} */
|
||||
|
||||
/** @defgroup onewire_master_timer timer used to measure 1-wire signal timing
|
||||
* @{
|
||||
*/
|
||||
#define ONEWIRE_MASTER_TIMER 5 /**< timer ID */
|
||||
#define ONEWIRE_MASTER_TIMER 3 /**< timer ID */
|
||||
/** @} */
|
||||
|
||||
/** set if the timer ISR should be set in the interrupt table instead of the vector table
|
||||
|
@ -2,7 +2,7 @@
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017
|
||||
* @date 2017-2020
|
||||
* @note peripherals used: 1-Wire (timer @ref onewire_master_timer, GPIO @ref onewire_master_gpio)
|
||||
* @warning this library does not support parasite power mode and alarms
|
||||
*/
|
||||
@ -42,10 +42,10 @@ uint64_t sensor_ds18b20_number(void)
|
||||
return 0; // no slave presence detected
|
||||
}
|
||||
more = onewire_master_rom_search(&code, false); // get next slave ROM code (without alarm)
|
||||
if (0==code) { // error occurred
|
||||
if (0 == code) { // error occurred
|
||||
return 0;
|
||||
}
|
||||
if (0x28==(code&0xff)) { // family code (8-LSb) for DS18B20 sensors is 0x28
|
||||
if (0x28 == (code & 0xff)) { // family code (8-LSb) for DS18B20 sensors is 0x28
|
||||
last = code; // save last found code
|
||||
sensors++; // we found an additional sensor
|
||||
} else {
|
||||
@ -68,12 +68,12 @@ bool sensor_ds18b20_list(uint64_t* code)
|
||||
return false; // no slave presence detected
|
||||
}
|
||||
onewire_master_rom_search(code, false); // get next code
|
||||
return (last!=*code); // verify if the last has been found
|
||||
return (last != *code); // verify if the last has been found
|
||||
}
|
||||
|
||||
bool sensor_ds18b20_convert(uint64_t code)
|
||||
{
|
||||
if (0==code && !only) { // asked for broadcast but there are different slaves on bus
|
||||
if (0 == code && !only) { // asked for broadcast but there are different slaves on bus
|
||||
return false; // broadcast not possible when there are also different slaves on bus
|
||||
}
|
||||
|
||||
@ -83,7 +83,7 @@ bool sensor_ds18b20_convert(uint64_t code)
|
||||
}
|
||||
|
||||
// send ROM command to select slave(s)
|
||||
if (0==code) { // broadcast convert
|
||||
if (0 == code) { // broadcast convert
|
||||
if (!onewire_master_rom_skip()) { // select all slaves
|
||||
return false; // ROM command failed
|
||||
}
|
||||
@ -99,7 +99,7 @@ bool sensor_ds18b20_convert(uint64_t code)
|
||||
|
||||
float sensor_ds18b20_temperature(uint64_t code)
|
||||
{
|
||||
if (0==code && (sensors>1 || !only)) { // broadcast read requested
|
||||
if (0 == code && (sensors > 1 || !only)) { // broadcast read requested
|
||||
return NAN; // this function is not possible when several sensors or other devices are present
|
||||
}
|
||||
|
||||
@ -109,13 +109,19 @@ float sensor_ds18b20_temperature(uint64_t code)
|
||||
}
|
||||
|
||||
// send ROM command to select slave
|
||||
if (!onewire_master_rom_match(code)) { // select specific slave
|
||||
return NAN; // ROM command failed
|
||||
if (0 == code) { // broadcast convert
|
||||
if (!onewire_master_rom_skip()) { // select all slaves
|
||||
return false; // ROM command failed
|
||||
}
|
||||
} else {
|
||||
if (!onewire_master_rom_match(code)) { // select specific slave
|
||||
return false; // ROM command failed
|
||||
}
|
||||
}
|
||||
|
||||
// read scratchpad to get temperature (on byte 0 and 1)
|
||||
uint8_t scratchpad[9] = {0}; // to store read scratchpad
|
||||
if (!onewire_master_function_read(0xbe, scratchpad, sizeof(scratchpad)*8)) { // read complete scratchpad
|
||||
if (!onewire_master_function_read(0xbe, scratchpad, sizeof(scratchpad) * 8)) { // read complete scratchpad
|
||||
return NAN; // error occurred during read
|
||||
}
|
||||
|
||||
@ -125,16 +131,16 @@ float sensor_ds18b20_temperature(uint64_t code)
|
||||
}
|
||||
|
||||
// calculate temperature (stored as int16_t but on 0.125 C steps)
|
||||
return ((int16_t)(scratchpad[1]<<8)+scratchpad[0])/16.0; // get temperature (on < 16 precision the last bits are undefined, but that doesn't matter for the end result since the lower precision is still provided)
|
||||
return ((int16_t)(scratchpad[1] << 8) + scratchpad[0]) / 16.0; // get temperature (on < 16 precision the last bits are undefined, but that doesn't matter for the end result since the lower precision is still provided)
|
||||
}
|
||||
|
||||
bool sensor_ds18b20_precision(uint64_t code, uint8_t precision)
|
||||
{
|
||||
if (precision<9 || precision>12) { // check input
|
||||
if (precision < 9 || precision > 12) { // check input
|
||||
return false; // wrong precision value
|
||||
}
|
||||
|
||||
if (0==code && !only) { // asked for broadcast but there are different slaves on bus
|
||||
if (0 == code && !only) { // asked for broadcast but there are different slaves on bus
|
||||
return false; // broadcast not possible when there are also different slaves on bus
|
||||
}
|
||||
|
||||
@ -144,7 +150,7 @@ bool sensor_ds18b20_precision(uint64_t code, uint8_t precision)
|
||||
}
|
||||
|
||||
// send ROM command to select slave(s)
|
||||
if (0==code) { // broadcast convert
|
||||
if (0 == code) { // broadcast convert
|
||||
if (!onewire_master_rom_skip()) { // select all slaves
|
||||
return false; // ROM command failed
|
||||
}
|
||||
@ -156,7 +162,7 @@ bool sensor_ds18b20_precision(uint64_t code, uint8_t precision)
|
||||
|
||||
// read scratchpad to get alarm values (on byte 2 and 3)
|
||||
uint8_t scratchpad[9] = {0}; // to store read scratchpad
|
||||
if (!onewire_master_function_read(0xbe, scratchpad, sizeof(scratchpad)*8)) { // read complete scratchpad
|
||||
if (!onewire_master_function_read(0xbe, scratchpad, sizeof(scratchpad) * 8)) { // read complete scratchpad
|
||||
return false; // error occurred during read
|
||||
}
|
||||
|
||||
@ -169,8 +175,8 @@ bool sensor_ds18b20_precision(uint64_t code, uint8_t precision)
|
||||
uint8_t configuration[3] = {0}; // to store T_HIGH, T_LOW, and configuration
|
||||
configuration[0] = scratchpad[2]; // keep T_HIGH
|
||||
configuration[1] = scratchpad[3]; // keep T_LOW
|
||||
configuration[2] = 0x1f+((precision-9)<<5); // set precision bit (R1-R0 on bit 6-5)
|
||||
if (!onewire_master_function_write(0x4e, configuration, sizeof(configuration)*8)) { // write scratchpad with new configuration (all three bytes must be written)
|
||||
configuration[2] = 0x1f + ((precision - 9) << 5); // set precision bit (R1-R0 on bit 6-5)
|
||||
if (!onewire_master_function_write(0x4e, configuration, sizeof(configuration) * 8)) { // write scratchpad with new configuration (all three bytes must be written)
|
||||
return false; // error occurred during write
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017
|
||||
* @date 2017-2020
|
||||
* @note peripherals used: 1-Wire (timer @ref onewire_master_timer, GPIO @ref onewire_master_gpio)
|
||||
* @warning this library does not support parasite power mode and alarms
|
||||
*/
|
||||
@ -36,7 +36,7 @@ bool sensor_ds18b20_convert(uint64_t code);
|
||||
*/
|
||||
float sensor_ds18b20_temperature(uint64_t code);
|
||||
/** set conversion precision
|
||||
* @param[in] code ROM code of sensor to start conversion on (0 for all, if only DS18B20 sensors are on the bus)
|
||||
* @param[in] code ROM code of sensor to start conversion on (0 for single DS18B20 sensor are on the bus)
|
||||
* @param[in] precision precision in bits (9-12)
|
||||
* @return if operation succeeded
|
||||
*/
|
||||
|
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);
|
@ -34,7 +34,7 @@ static usbd_device *usb_device = NULL; /**< structure holding all the info relat
|
||||
static volatile bool first_connection = false; /**< used to detect when the first connection occurred */
|
||||
|
||||
/* output ring buffer, index, and available memory */
|
||||
static uint8_t tx_buffer[256] = {0}; /**< ring buffer for data to transmit */
|
||||
static uint8_t tx_buffer[1024] = {0}; /**< ring buffer for data to transmit */
|
||||
static volatile uint16_t tx_i = 0; /**< current position if transmitted data */
|
||||
static volatile uint16_t tx_used = 0; /**< how much data needs to be transmitted */
|
||||
static volatile bool tx_lock = false; /**< if the transmit buffer is currently being written */
|
||||
|
Loading…
Reference in New Issue
Block a user