add math test
This commit is contained in:
parent
d2c3f3770c
commit
db0af9c275
34 changed files with 17384 additions and 0 deletions
55
math/Makefile
Normal file
55
math/Makefile
Normal file
|
@ -0,0 +1,55 @@
|
|||
TARGET?= ch32v003
|
||||
#TARGET?= stm32f051
|
||||
#TARGET?= linux
|
||||
#TARGET?= avr
|
||||
|
||||
TOOL ?= gcc
|
||||
|
||||
PRJ = example
|
||||
|
||||
VPATH = . ./$(TARGET) ./common
|
||||
BLD = ./build/
|
||||
DFLAGS = -d
|
||||
LFLAGS = -g
|
||||
LDLIBS =
|
||||
BFLAGS = --strip-unneeded
|
||||
|
||||
CFLAGS = -MMD -Wall -Wno-parentheses -ggdb -fno-exceptions -ffunction-sections -fdata-sections
|
||||
CFLAGS+= -I. -I./$(TARGET) -I./common
|
||||
DEL = rm -f
|
||||
|
||||
# zdrojaky
|
||||
OBJS = main.o compute.o
|
||||
OBJS += usart.o print.o float.o
|
||||
|
||||
include $(TARGET)/$(TOOL).mk
|
||||
BOBJS = $(addprefix $(BLD),$(OBJS))
|
||||
|
||||
all: $(BLD) $(PRJ).elf
|
||||
# ... atd.
|
||||
-include $(BLD)*.d
|
||||
# linker
|
||||
$(PRJ).elf: $(BOBJS)
|
||||
-@echo [LD $(TOOL),$(TARGET)] $@
|
||||
@$(LD) $(LFLAGS) -o $(PRJ).elf $(BOBJS) $(LDLIBS)
|
||||
-@echo "size:"
|
||||
@$(SIZE) $(PRJ).elf
|
||||
-@echo "listing:"
|
||||
$(DUMP) $(DFLAGS) $(PRJ).elf > $(PRJ).lst
|
||||
-@echo "OK."
|
||||
$(COPY) $(BFLAGS) -O binary $(PRJ).elf $(PRJ).bin
|
||||
# preloz co je potreba
|
||||
$(BLD)%.o: %.c
|
||||
-@echo [CC $(TOOL),$(TARGET)] $@
|
||||
@$(CC) -std=gnu99 -c $(CFLAGS) $< -o $@
|
||||
$(BLD)%.o: %.cpp
|
||||
-@echo [CX $(TOOL),$(TARGET)] $@
|
||||
@$(CXX) -std=c++17 -fno-rtti -c $(CFLAGS) $< -o $@
|
||||
$(BLD):
|
||||
mkdir $(BLD)
|
||||
flash: $(PRJ).elf
|
||||
minichlink -w $(PRJ).bin flash -b
|
||||
# vycisti
|
||||
clean:
|
||||
$(DEL) $(BLD)* *.lst *.bin *.elf *.map *~
|
||||
.PHONY: all clean flash run
|
332
math/avr/avr_mcu_section.h
Normal file
332
math/avr/avr_mcu_section.h
Normal file
|
@ -0,0 +1,332 @@
|
|||
/*
|
||||
avr_mcu_section.h
|
||||
|
||||
Copyright 2008-2013 Michel Pollet <buserror@gmail.com>
|
||||
|
||||
This file is part of simavr.
|
||||
|
||||
simavr 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.
|
||||
|
||||
simavr 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 simavr. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __AVR_MCU_SECTION_H__
|
||||
#define __AVR_MCU_SECTION_H__
|
||||
|
||||
/*
|
||||
* This header is used to pass "parameters" to the programmer or the simulator,
|
||||
* it tags the ELF file with a section that contains parameters about the physical
|
||||
* AVR this was compiled for, including the speed, model, and signature bytes.
|
||||
*
|
||||
* A programmer software can read this and verify fuses values for example, and a
|
||||
* simulator can instantiate the proper "model" of AVR, the speed and so on without
|
||||
* command line parameters.
|
||||
*
|
||||
* Example of use:
|
||||
*
|
||||
* #include "avr_mcu_section.h"
|
||||
* AVR_MCU(F_CPU, "atmega88");
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
AVR_MMCU_TAG = 0,
|
||||
AVR_MMCU_TAG_NAME,
|
||||
AVR_MMCU_TAG_FREQUENCY,
|
||||
AVR_MMCU_TAG_VCC,
|
||||
AVR_MMCU_TAG_AVCC,
|
||||
AVR_MMCU_TAG_AREF,
|
||||
AVR_MMCU_TAG_LFUSE,
|
||||
AVR_MMCU_TAG_HFUSE,
|
||||
AVR_MMCU_TAG_EFUSE,
|
||||
AVR_MMCU_TAG_SIGNATURE,
|
||||
AVR_MMCU_TAG_SIMAVR_COMMAND,
|
||||
AVR_MMCU_TAG_SIMAVR_CONSOLE,
|
||||
AVR_MMCU_TAG_VCD_FILENAME,
|
||||
AVR_MMCU_TAG_VCD_PERIOD,
|
||||
AVR_MMCU_TAG_VCD_TRACE,
|
||||
AVR_MMCU_TAG_VCD_PORTPIN,
|
||||
AVR_MMCU_TAG_VCD_IRQ,
|
||||
AVR_MMCU_TAG_PORT_EXTERNAL_PULL,
|
||||
};
|
||||
|
||||
enum {
|
||||
SIMAVR_CMD_NONE = 0,
|
||||
SIMAVR_CMD_VCD_START_TRACE,
|
||||
SIMAVR_CMD_VCD_STOP_TRACE,
|
||||
SIMAVR_CMD_UART_LOOPBACK,
|
||||
};
|
||||
|
||||
#if __AVR__
|
||||
/*
|
||||
* WARNING. Due to newer GCC being stupid, they introduced a bug that
|
||||
* prevents us introducing variable length strings in the declaration
|
||||
* of structs. Worked for a million years, and no longer.
|
||||
* So the new method declares the string as fixed size, and the parser
|
||||
* is forced to skip the zeroes in padding. Dumbo.
|
||||
*/
|
||||
#define _MMCU_ __attribute__((section(".mmcu")))
|
||||
struct avr_mmcu_long_t {
|
||||
uint8_t tag;
|
||||
uint8_t len;
|
||||
uint32_t val;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct avr_mmcu_string_t {
|
||||
uint8_t tag;
|
||||
uint8_t len;
|
||||
char string[64];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct avr_mmcu_addr_t {
|
||||
uint8_t tag;
|
||||
uint8_t len;
|
||||
void * what;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct avr_mmcu_vcd_trace_t {
|
||||
uint8_t tag;
|
||||
uint8_t len;
|
||||
uint8_t mask;
|
||||
void * what;
|
||||
char name[32];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#define AVR_MCU_STRING(_tag, _str) \
|
||||
const struct avr_mmcu_string_t _##_tag _MMCU_ = {\
|
||||
.tag = _tag,\
|
||||
.len = sizeof(struct avr_mmcu_string_t) - 2,\
|
||||
.string = _str,\
|
||||
}
|
||||
/*
|
||||
* This trick allows concatenation of tokens. We need a macro redirection
|
||||
* for it to work.
|
||||
* The goal is to make unique variable names (they don't matter anyway)
|
||||
*/
|
||||
#define DO_CONCAT2(_a, _b) _a##_b
|
||||
#define DO_CONCAT(_a, _b) DO_CONCAT2(_a,_b)
|
||||
|
||||
#define AVR_MCU_LONG(_tag, _val) \
|
||||
const struct avr_mmcu_long_t DO_CONCAT(DO_CONCAT(_, _tag), __LINE__) _MMCU_ = {\
|
||||
.tag = _tag,\
|
||||
.len = sizeof(struct avr_mmcu_long_t) - 2,\
|
||||
.val = _val,\
|
||||
}
|
||||
|
||||
#define AVR_MCU_BYTE(_tag, _val) \
|
||||
const uint8_t _##_tag _MMCU_ = { _tag, 1, _val }
|
||||
|
||||
/*!
|
||||
* This Macro allows you to specify traces for the VCD file output
|
||||
* engine. This specifies a default header, and let you fill in the
|
||||
* relevant bits.
|
||||
* Example:
|
||||
* const struct avr_mmcu_vcd_trace_t _mytrace[] _MMCU_ = {
|
||||
* { AVR_MCU_VCD_SYMBOL("UDR0"), .what = (void*)&UDR0, },
|
||||
* { AVR_MCU_VCD_SYMBOL("UDRE0"), .mask = (1 << UDRE0), .what = (void*)&UCSR0A, },
|
||||
* };
|
||||
* This structure will automatically tell simavr to add a VCD trace
|
||||
* for the UART register, and the UDRE0 bit, so you can trace exactly
|
||||
* the timing of the changed using gtkwave.
|
||||
*/
|
||||
#define AVR_MCU_VCD_SYMBOL(_name) \
|
||||
.tag = AVR_MMCU_TAG_VCD_TRACE, \
|
||||
.len = sizeof(struct avr_mmcu_vcd_trace_t) - 2,\
|
||||
.name = _name
|
||||
|
||||
/*!
|
||||
* Specifies the name and wanted period (in usec) for a VCD file
|
||||
* this is not mandatory for the VCD output to work, if this tag
|
||||
* is not used, a VCD file will still be created with default values
|
||||
*/
|
||||
#define AVR_MCU_VCD_FILE(_name, _period) \
|
||||
AVR_MCU_STRING(AVR_MMCU_TAG_VCD_FILENAME, _name);\
|
||||
AVR_MCU_LONG(AVR_MMCU_TAG_VCD_PERIOD, _period)
|
||||
|
||||
/*!
|
||||
* It is possible to send "commands" to simavr from the
|
||||
* firmware itself. For this to work you need to specify
|
||||
* an IO register that is to be used for a write-only
|
||||
* bridge. A favourite is one of the usual "GPIO register"
|
||||
* that most (all ?) AVR have.
|
||||
* See definition of SIMAVR_CMD_* to see what commands can
|
||||
* be used from your firmware.
|
||||
*/
|
||||
#define AVR_MCU_SIMAVR_COMMAND(_register) \
|
||||
const struct avr_mmcu_addr_t _simavr_command_register _MMCU_ = {\
|
||||
.tag = AVR_MMCU_TAG_SIMAVR_COMMAND,\
|
||||
.len = sizeof(void *),\
|
||||
.what = (void*)_register, \
|
||||
}
|
||||
/*!
|
||||
* Similar to AVR_MCU_SIMAVR_COMMAND, The CONSOLE allows the AVR code
|
||||
* to declare a register (typically a GPIO register, but any unused
|
||||
* register can work...) that will allow printing on the host's console
|
||||
* without using a UART to do debug.
|
||||
*/
|
||||
#define AVR_MCU_SIMAVR_CONSOLE(_register) \
|
||||
const struct avr_mmcu_addr_t _simavr_console_register _MMCU_ = {\
|
||||
.tag = AVR_MMCU_TAG_SIMAVR_CONSOLE,\
|
||||
.len = sizeof(void *),\
|
||||
.what = (void*)_register, \
|
||||
}
|
||||
/*!
|
||||
* Allows the firmware to hint simavr as to wether there are external
|
||||
* pullups/down on PORT pins. It helps if the firmware uses "open drain"
|
||||
* pins by toggling the DDR pins to switch between an output state and
|
||||
* a "default" state.
|
||||
* The value passed here will be output on the PORT IRQ when the DDR
|
||||
* pin is set to input again
|
||||
*/
|
||||
#define AVR_MCU_EXTERNAL_PORT_PULL(_port, _mask, _val) \
|
||||
AVR_MCU_LONG(AVR_MMCU_TAG_PORT_EXTERNAL_PULL, \
|
||||
(((unsigned long)((_port)&0xff) << 16) | \
|
||||
((unsigned long)((_mask)&0xff) << 8) | \
|
||||
((_val)&0xff)));
|
||||
/*!
|
||||
* Add this port/pin to the VCD file. The syntax uses the name of the
|
||||
* port as a character, and not a pointer to a register.
|
||||
* AVR_MCU_VCD_PORT_PIN('B', 5);
|
||||
*/
|
||||
#define AVR_MCU_VCD_PORT_PIN(_port, _pin, _name) \
|
||||
const struct avr_mmcu_vcd_trace_t DO_CONCAT(DO_CONCAT(_, _tag), __LINE__) _MMCU_ = {\
|
||||
.tag = AVR_MMCU_TAG_VCD_PORTPIN, \
|
||||
.len = sizeof(struct avr_mmcu_vcd_trace_t) - 2,\
|
||||
.mask = _port, \
|
||||
.what = (void*)_pin, \
|
||||
.name = _name, \
|
||||
}
|
||||
|
||||
/*!
|
||||
* These allows you to add a trace showing how long an IRQ vector is pending,
|
||||
* and also how long it is running. You can specify the IRQ as a vector name
|
||||
* straight from the firmware file, and it will be named properly in the trace
|
||||
*/
|
||||
|
||||
#define AVR_MCU_VCD_IRQ_TRACE(_vect_number, __what, _trace_name) \
|
||||
const struct avr_mmcu_vcd_trace_t DO_CONCAT(DO_CONCAT(_, _tag), __LINE__) _MMCU_ = {\
|
||||
.tag = AVR_MMCU_TAG_VCD_IRQ, \
|
||||
.len = sizeof(struct avr_mmcu_vcd_trace_t) - 2,\
|
||||
.mask = _vect_number, \
|
||||
.what = (void*)__what, \
|
||||
.name = _trace_name, \
|
||||
};
|
||||
#define AVR_MCU_VCD_IRQ(_irq_name) \
|
||||
AVR_MCU_VCD_IRQ_TRACE(_irq_name##_vect_num, 1, #_irq_name)
|
||||
#define AVR_MCU_VCD_IRQ_PENDING(_irq_name) \
|
||||
AVR_MCU_VCD_IRQ_TRACE(_irq_name##_vect_num, 0, #_irq_name "_pend")
|
||||
#define AVR_MCU_VCD_ALL_IRQ() \
|
||||
AVR_MCU_VCD_IRQ_TRACE(0xff, 1, "IRQ")
|
||||
#define AVR_MCU_VCD_ALL_IRQ_PENDING() \
|
||||
AVR_MCU_VCD_IRQ_TRACE(0xff, 0, "IRQ_PENDING")
|
||||
|
||||
/*!
|
||||
* This tag allows you to specify the voltages used by your board
|
||||
* It is optional in most cases, but you will need it if you use
|
||||
* ADC module's IRQs. Not specifying it in this case might lead
|
||||
* to a divide-by-zero crash.
|
||||
* The units are Volts*1000 (millivolts)
|
||||
*/
|
||||
#define AVR_MCU_VOLTAGES(_vcc, _avcc, _aref) \
|
||||
AVR_MCU_LONG(AVR_MMCU_TAG_VCC, (_vcc));\
|
||||
AVR_MCU_LONG(AVR_MMCU_TAG_AVCC, (_avcc));\
|
||||
AVR_MCU_LONG(AVR_MMCU_TAG_AREF, (_aref));
|
||||
|
||||
/*!
|
||||
* This the has to be used if you want to add other tags to the .mmcu section
|
||||
* the _mmcu symbol is used as an anchor to make sure it stays linked in.
|
||||
*/
|
||||
#define AVR_MCU(_speed, _name) \
|
||||
AVR_MCU_STRING(AVR_MMCU_TAG_NAME, _name);\
|
||||
AVR_MCU_LONG(AVR_MMCU_TAG_FREQUENCY, _speed);\
|
||||
const uint8_t _mmcu[2] _MMCU_ = { AVR_MMCU_TAG, 0 }
|
||||
|
||||
/*
|
||||
* The following MAP macros where copied from
|
||||
* https://github.com/swansontec/map-macro/blob/master/map.h
|
||||
*
|
||||
* The license header for that file is reproduced below:
|
||||
*
|
||||
* Copyright (C) 2012 William Swanson
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Except as contained in this notice, the names of the authors or
|
||||
* their institutions shall not be used in advertising or otherwise to
|
||||
* promote the sale, use or other dealings in this Software without
|
||||
* prior written authorization from the authors.
|
||||
*/
|
||||
|
||||
#define _EVAL0(...) __VA_ARGS__
|
||||
#define _EVAL1(...) _EVAL0 (_EVAL0 (_EVAL0 (__VA_ARGS__)))
|
||||
#define _EVAL2(...) _EVAL1 (_EVAL1 (_EVAL1 (__VA_ARGS__)))
|
||||
#define _EVAL3(...) _EVAL2 (_EVAL2 (_EVAL2 (__VA_ARGS__)))
|
||||
#define _EVAL4(...) _EVAL3 (_EVAL3 (_EVAL3 (__VA_ARGS__)))
|
||||
#define _EVAL(...) _EVAL4 (_EVAL4 (_EVAL4 (__VA_ARGS__)))
|
||||
|
||||
#define _MAP_END(...)
|
||||
#define _MAP_OUT
|
||||
|
||||
#define _MAP_GET_END() 0, _MAP_END
|
||||
#define _MAP_NEXT0(test, next, ...) next _MAP_OUT
|
||||
#define _MAP_NEXT1(test, next) _MAP_NEXT0 (test, next, 0)
|
||||
#define _MAP_NEXT(test, next) _MAP_NEXT1 (_MAP_GET_END test, next)
|
||||
|
||||
#define _MAP0(f, x, peek, ...) f(x) _MAP_NEXT (peek, _MAP1) (f, peek, __VA_ARGS__)
|
||||
#define _MAP1(f, x, peek, ...) f(x) _MAP_NEXT (peek, _MAP0) (f, peek, __VA_ARGS__)
|
||||
#define _MAP(f, ...) _EVAL (-MAP1 (f, __VA_ARGS__, (), 0))
|
||||
|
||||
/* End of original MAP macros. */
|
||||
|
||||
// Define MAP macros with one additional argument
|
||||
#define _MAP0_1(f, a, x, peek, ...) f(a, x) _MAP_NEXT (peek, _MAP1_1) (f, a, peek, __VA_ARGS__)
|
||||
#define _MAP1_1(f, a, x, peek, ...) f(a, x) _MAP_NEXT (peek, _MAP0_1) (f, a, peek, __VA_ARGS__)
|
||||
#define _MAP_1(f, a, ...) _EVAL (_MAP1_1 (f, a, __VA_ARGS__, (), 0))
|
||||
|
||||
#define _SEND_SIMAVR_CMD_BYTE(reg, b) reg = b;
|
||||
|
||||
// A helper macro for sending multi-byte commands
|
||||
#define SEND_SIMAVR_CMD(reg, ...) \
|
||||
do { \
|
||||
_MAP_1(_SEND_SIMAVR_CMD_BYTE, reg, __VA_ARGS__) \
|
||||
} while(0)
|
||||
|
||||
#endif /* __AVR__ */
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
20
math/avr/gcc.mk
Normal file
20
math/avr/gcc.mk
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Use gcc / binutils toolchain
|
||||
PREFIX = avr-
|
||||
CC = $(PREFIX)gcc
|
||||
CXX = $(PREFIX)g++
|
||||
LD = $(PREFIX)g++
|
||||
SIZE = $(PREFIX)size
|
||||
DUMP = $(PREFIX)objdump
|
||||
COPY = $(PREFIX)objcopy
|
||||
MCU = atmega128
|
||||
|
||||
OBJS += simulate.o
|
||||
CFLAGS+= -Os -DHAVE_STDLIB
|
||||
CFLAGS+= -mmcu=$(MCU)
|
||||
LFLAGS+= -mmcu=$(MCU) -Wl,--Map=$(@:%.elf=%.map),--relax,--gc-sections
|
||||
LFLAGS+= -Wl,--undefined=_mmcu,--section-start=.mmcu=0x910000
|
||||
#LFLAGS+= -O3
|
||||
LDLIBS+= -lc
|
||||
|
||||
run: $(PRJ).elf
|
||||
simavr ./$(PRJ).elf
|
42
math/avr/simulate.c
Normal file
42
math/avr/simulate.c
Normal file
|
@ -0,0 +1,42 @@
|
|||
#ifndef F_CPU
|
||||
#define F_CPU 8000000
|
||||
#endif
|
||||
#include <avr/io.h>
|
||||
#include <stdio.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/eeprom.h>
|
||||
#include <avr/sleep.h>
|
||||
|
||||
/*
|
||||
* This demonstrate how to use the avr_mcu_section.h file
|
||||
* The macro adds a section to the ELF file with useful
|
||||
* information for the simulator
|
||||
*/
|
||||
#include "avr_mcu_section.h"
|
||||
AVR_MCU (F_CPU, "atmega128");
|
||||
#if 0
|
||||
/*
|
||||
* This small section tells simavr to generate a VCD trace dump with changes to these
|
||||
* registers.
|
||||
* Opening it with gtkwave will show you the data being pumped out into the data register
|
||||
* UDR0, and the UDRE0 bit being set, then cleared
|
||||
*/
|
||||
const struct avr_mmcu_vcd_trace_t _mytrace[] _MMCU_ = {
|
||||
{ AVR_MCU_VCD_SYMBOL ("UDR0"), .what = (void*)&UDR0, },
|
||||
{ AVR_MCU_VCD_SYMBOL ("UDRE0"), .mask = (1 << UDRE0), .what = (void*)&UCSR0A, },
|
||||
};
|
||||
#endif // 0
|
||||
|
||||
static int uart_putchar (char c, FILE *stream) {
|
||||
// if (c == '\n') uart_putchar ('\r', stream);
|
||||
loop_until_bit_is_set (UCSR0A, UDRE0);
|
||||
UDR0 = c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
FILE mystdout = FDEV_SETUP_STREAM (uart_putchar, NULL, _FDEV_SETUP_WRITE);
|
||||
|
||||
int terminate () {
|
||||
sleep_cpu ();
|
||||
return 0;
|
||||
}
|
22
math/avr/usart.cpp
Normal file
22
math/avr/usart.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
#include <stdio.h>
|
||||
#include "usart.h"
|
||||
extern "C" FILE mystdout;
|
||||
|
||||
Usart::Usart(const uint32_t _baud) noexcept : BaseLayer (), tx_ring () {
|
||||
stdout = &mystdout;
|
||||
}
|
||||
void Usart::irq () {
|
||||
}
|
||||
void Usart::SetRS485 (const bool polarity) const {
|
||||
}
|
||||
void Usart::SetHalfDuplex (const bool on) const {
|
||||
}
|
||||
|
||||
uint32_t Usart::Down (const char * data, const uint32_t len) {
|
||||
//int n = fwrite (data, 1, len, stdout);
|
||||
for (unsigned n=0; n<len; n++) putchar(data[n]);
|
||||
return len;
|
||||
}
|
||||
extern "C" {
|
||||
void __cxa_pure_virtual() { while (1); }
|
||||
};
|
4180
math/ch32v003/CH32V00xxx.h
Normal file
4180
math/ch32v003/CH32V00xxx.h
Normal file
File diff suppressed because it is too large
Load diff
18
math/ch32v003/gcc.mk
Normal file
18
math/ch32v003/gcc.mk
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Use gcc / binutils toolchain
|
||||
PREFIX = riscv64-unknown-elf-
|
||||
CC = $(PREFIX)gcc
|
||||
CXX = $(PREFIX)g++
|
||||
LD = $(PREFIX)gcc
|
||||
SIZE = $(PREFIX)size
|
||||
DUMP = $(PREFIX)objdump
|
||||
COPY = $(PREFIX)objcopy
|
||||
CFLAGS+= -Os -I/usr/include/newlib
|
||||
|
||||
OBJS += startup.o system.o
|
||||
CCPU = -march=rv32ec -mabi=ilp32e
|
||||
MCPU = $(CCPU)
|
||||
CFLAGS+= $(MCPU)
|
||||
LFLAGS+= -Wl,--Map=$(@:%.elf=%.map),--gc-sections
|
||||
#LFLAGS+= -Wl,--print-sysroot -- chyba ld ?
|
||||
LFLAGS+= -O3 $(MCPU) -nostartfiles -nostdlib
|
||||
LDLIBS+= -lgcc -L./$(TARGET) -T generated_ch32v003.ld
|
115
math/ch32v003/generated_ch32v003.ld
Normal file
115
math/ch32v003/generated_ch32v003.ld
Normal file
|
@ -0,0 +1,115 @@
|
|||
ENTRY( InterruptVector )
|
||||
MEMORY
|
||||
{
|
||||
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 16K
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 2K
|
||||
}
|
||||
SECTIONS
|
||||
{
|
||||
.init :
|
||||
{
|
||||
_sinit = .;
|
||||
. = ALIGN(4);
|
||||
KEEP(*(SORT_NONE(.init)))
|
||||
. = ALIGN(4);
|
||||
_einit = .;
|
||||
} >FLASH AT>FLASH
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
*(.rodata)
|
||||
*(.rodata*)
|
||||
*(.gnu.linkonce.t.*)
|
||||
. = ALIGN(4);
|
||||
} >FLASH AT>FLASH
|
||||
.fini :
|
||||
{
|
||||
KEEP(*(SORT_NONE(.fini)))
|
||||
. = ALIGN(4);
|
||||
} >FLASH AT>FLASH
|
||||
PROVIDE( _etext = . );
|
||||
PROVIDE( _eitcm = . );
|
||||
.preinit_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array))
|
||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||
} >FLASH AT>FLASH
|
||||
.init_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__init_array_start = .);
|
||||
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
|
||||
KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
|
||||
PROVIDE_HIDDEN (__init_array_end = .);
|
||||
} >FLASH AT>FLASH
|
||||
.fini_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
|
||||
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
|
||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||
} >FLASH AT>FLASH
|
||||
.ctors :
|
||||
{
|
||||
KEEP (*crtbegin.o(.ctors))
|
||||
KEEP (*crtbegin?.o(.ctors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
|
||||
KEEP (*(SORT(.ctors.*)))
|
||||
KEEP (*(.ctors))
|
||||
} >FLASH AT>FLASH
|
||||
.dtors :
|
||||
{
|
||||
KEEP (*crtbegin.o(.dtors))
|
||||
KEEP (*crtbegin?.o(.dtors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
|
||||
KEEP (*(SORT(.dtors.*)))
|
||||
KEEP (*(.dtors))
|
||||
} >FLASH AT>FLASH
|
||||
.dalign :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
PROVIDE(_data_vma = .);
|
||||
} >RAM AT>FLASH
|
||||
.dlalign :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
PROVIDE(_data_lma = .);
|
||||
} >FLASH AT>FLASH
|
||||
.data :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.gnu.linkonce.r.*)
|
||||
*(.data .data.*)
|
||||
*(.gnu.linkonce.d.*)
|
||||
. = ALIGN(8);
|
||||
PROVIDE( __global_pointer$ = . + 0x800 );
|
||||
*(.sdata .sdata.*)
|
||||
*(.sdata2*)
|
||||
*(.gnu.linkonce.s.*)
|
||||
. = ALIGN(8);
|
||||
*(.srodata.cst16)
|
||||
*(.srodata.cst8)
|
||||
*(.srodata.cst4)
|
||||
*(.srodata.cst2)
|
||||
*(.srodata .srodata.*)
|
||||
. = ALIGN(4);
|
||||
PROVIDE( _edata = .);
|
||||
} >RAM AT>FLASH
|
||||
.bss :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
PROVIDE( _sbss = .);
|
||||
*(.sbss*)
|
||||
*(.gnu.linkonce.sb.*)
|
||||
*(.bss*)
|
||||
*(.gnu.linkonce.b.*)
|
||||
*(COMMON*)
|
||||
. = ALIGN(4);
|
||||
PROVIDE( _ebss = .);
|
||||
} >RAM AT>FLASH
|
||||
PROVIDE( _end = _ebss);
|
||||
PROVIDE( end = . );
|
||||
PROVIDE( _eusrstack = ORIGIN(RAM) + LENGTH(RAM));
|
||||
}
|
43
math/ch32v003/gpio.h
Normal file
43
math/ch32v003/gpio.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
#ifndef _GPIO_CLASS_H_
|
||||
#define _GPIO_CLASS_H_
|
||||
#include "CH32V00xxx.h"
|
||||
|
||||
enum GPIO_MODE : uint32_t {
|
||||
GPIO_Speed_In = 0u,
|
||||
GPIO_Speed_10MHz = 1u,
|
||||
GPIO_Speed_2MHz = 2u,
|
||||
GPIO_Speed_50MHz = 3u,
|
||||
};
|
||||
enum GPIO_CNF : uint32_t {
|
||||
GPIO_AI_PPO = 0u,
|
||||
GPIO_FI_ODO = 1u << 2,
|
||||
GPIO_UPDI_MPPO = 2u << 2,
|
||||
GPIO_none_MPDO = 3u << 2,
|
||||
};
|
||||
|
||||
class GpioClass {
|
||||
GPIOA_Type & port;
|
||||
const uint32_t pin;
|
||||
public:
|
||||
explicit constexpr GpioClass (GPIOA_Type & _port, const uint32_t _pin, const uint32_t _mode = GPIO_AI_PPO | GPIO_Speed_10MHz) noexcept
|
||||
: port(_port), pin(_pin) {
|
||||
/* Zapneme vše, ono je to dost jedno. */
|
||||
RCC.APB2PCENR.modify([](RCC_Type::APB2PCENR_DEF & r)->auto {
|
||||
r.B.IOPAEN = SET;
|
||||
r.B.IOPCEN = SET;
|
||||
r.B.IOPDEN = SET;
|
||||
return r.R;
|
||||
});
|
||||
const uint32_t pos = pin << 2;
|
||||
port.CFGLR.R &= ~(0xFu << pos);
|
||||
port.CFGLR.R |= _mode << pos;
|
||||
}
|
||||
void operator<< (const bool b) const {
|
||||
port.BSHR.R = b ? 1u << pin : 1u << (pin + 16);
|
||||
}
|
||||
operator bool () const {
|
||||
return port.INDR.R & (1u << pin);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _GPIO_CLASS_H_
|
173
math/ch32v003/startup.cpp
Normal file
173
math/ch32v003/startup.cpp
Normal file
|
@ -0,0 +1,173 @@
|
|||
#include "CH32V00xxx.h"
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
extern "C" {
|
||||
extern void handle_reset () __attribute__((naked,nothrow,used));
|
||||
extern void user_prog () __attribute__((used,noreturn));
|
||||
extern int main () __attribute__((used));
|
||||
extern void SystemInit() __attribute__((used));
|
||||
// This is required to allow pure virtual functions to be defined.
|
||||
void __cxa_pure_virtual() { while (1); }
|
||||
void * memset(void * s, int c, size_t n) {
|
||||
char * p = (char *) s;
|
||||
for (unsigned i=0u; i<n; i++) p[i] = c;
|
||||
return s;
|
||||
}
|
||||
|
||||
// These magic symbols are provided by the linker.
|
||||
extern uint32_t _sbss;
|
||||
extern uint32_t _ebss;
|
||||
extern uint32_t _data_lma;
|
||||
extern uint32_t _data_vma;
|
||||
extern uint32_t _edata;
|
||||
|
||||
extern void (*__preinit_array_start[]) (void) __attribute__((weak));
|
||||
extern void (*__preinit_array_end[]) (void) __attribute__((weak));
|
||||
extern void (*__init_array_start[]) (void) __attribute__((weak));
|
||||
extern void (*__init_array_end[]) (void) __attribute__((weak));
|
||||
|
||||
static void __init_array () {
|
||||
uint32_t * dst, * end;
|
||||
|
||||
/* Zero fill the bss section */
|
||||
dst = &_sbss;
|
||||
end = &_ebss;
|
||||
while (dst < end) * dst++ = 0U;
|
||||
/* Copy data section from flash to RAM */
|
||||
uint32_t * src;
|
||||
src = &_data_lma;
|
||||
dst = &_data_vma;
|
||||
end = &_edata;
|
||||
if (src != dst) {
|
||||
while (dst < end) * dst++ = * src++;
|
||||
}
|
||||
|
||||
size_t count;
|
||||
/* Pro Cortex-Mx bylo toto zbytečné, lze předpokládat, že je to tak i zde.
|
||||
count = __preinit_array_end - __preinit_array_start;
|
||||
for (unsigned i = 0; i < count; i++) __preinit_array_start[i]();
|
||||
*/
|
||||
count = __init_array_end - __init_array_start;
|
||||
for (unsigned i = 0; i < count; i++) __init_array_start[i]();
|
||||
}
|
||||
// If you don't override a specific handler, it will just spin forever.
|
||||
void DefaultIRQHandler( void ) {
|
||||
// Infinite Loop
|
||||
for (;;);
|
||||
}
|
||||
void NMI_RCC_CSS_IRQHandler( void ) {
|
||||
RCC.INTR.B.CSSC = RESET; // clear the clock security int flag
|
||||
}
|
||||
#define ALIAS(f) __attribute__((nothrow,weak,alias(#f),used))
|
||||
|
||||
void NMI_Handler( void ) ALIAS(NMI_RCC_CSS_IRQHandler);
|
||||
void HardFault_Handler( void ) ALIAS(DefaultIRQHandler);
|
||||
void SysTick_Handler( void ) ALIAS(DefaultIRQHandler);
|
||||
void SW_Handler( void ) ALIAS(DefaultIRQHandler);
|
||||
void WWDG_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void PVD_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void FLASH_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void RCC_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void EXTI7_0_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void AWU_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void DMA1_Channel1_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void DMA1_Channel2_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void DMA1_Channel3_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void DMA1_Channel4_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void DMA1_Channel5_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void DMA1_Channel6_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void DMA1_Channel7_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void ADC1_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void TIM1_BRK_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void TIM1_UP_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void TIM1_TRG_COM_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void TIM1_CC_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void TIM2_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void I2C1_EV_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void I2C1_ER_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void USART1_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
void SPI1_IRQHandler( void ) ALIAS(DefaultIRQHandler);
|
||||
|
||||
void InterruptVector() __attribute__((nothrow,naked,section(".init"),weak,alias("InterruptVectorDefault")));
|
||||
void InterruptVectorDefault() __attribute__((nothrow,naked,section(".init")));
|
||||
};
|
||||
void InterruptVectorDefault() noexcept {
|
||||
asm volatile( R"---(
|
||||
.align 2
|
||||
.option push
|
||||
.option norvc
|
||||
j handle_reset
|
||||
|
||||
.word 0
|
||||
.word NMI_Handler /* NMI Handler */
|
||||
.word HardFault_Handler /* Hard Fault Handler */
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word SysTick_Handler /* SysTick Handler */
|
||||
.word 0
|
||||
.word SW_Handler /* SW Handler */
|
||||
.word 0
|
||||
/* External Interrupts */
|
||||
.word WWDG_IRQHandler /* Window Watchdog */
|
||||
.word PVD_IRQHandler /* PVD through EXTI Line detect */
|
||||
.word FLASH_IRQHandler /* Flash */
|
||||
.word RCC_IRQHandler /* RCC */
|
||||
.word EXTI7_0_IRQHandler /* EXTI Line 7..0 */
|
||||
.word AWU_IRQHandler /* AWU */
|
||||
.word DMA1_Channel1_IRQHandler /* DMA1 Channel 1 */
|
||||
.word DMA1_Channel2_IRQHandler /* DMA1 Channel 2 */
|
||||
.word DMA1_Channel3_IRQHandler /* DMA1 Channel 3 */
|
||||
.word DMA1_Channel4_IRQHandler /* DMA1 Channel 4 */
|
||||
.word DMA1_Channel5_IRQHandler /* DMA1 Channel 5 */
|
||||
.word DMA1_Channel6_IRQHandler /* DMA1 Channel 6 */
|
||||
.word DMA1_Channel7_IRQHandler /* DMA1 Channel 7 */
|
||||
.word ADC1_IRQHandler /* ADC1 */
|
||||
.word I2C1_EV_IRQHandler /* I2C1 Event */
|
||||
.word I2C1_ER_IRQHandler /* I2C1 Error */
|
||||
.word USART1_IRQHandler /* USART1 */
|
||||
.word SPI1_IRQHandler /* SPI1 */
|
||||
.word TIM1_BRK_IRQHandler /* TIM1 Break */
|
||||
.word TIM1_UP_IRQHandler /* TIM1 Update */
|
||||
.word TIM1_TRG_COM_IRQHandler /* TIM1 Trigger and Commutation */
|
||||
.word TIM1_CC_IRQHandler /* TIM1 Capture Compare */
|
||||
.word TIM2_IRQHandler /* TIM2 */
|
||||
.option pop
|
||||
)---");
|
||||
}
|
||||
void handle_reset() noexcept {
|
||||
asm volatile(R"---(
|
||||
.option push
|
||||
.option norelax
|
||||
la gp, __global_pointer$
|
||||
.option pop
|
||||
la sp, _eusrstack
|
||||
)---"
|
||||
#if __GNUC__ > 10
|
||||
".option arch, +zicsr\n"
|
||||
#endif
|
||||
// Setup the interrupt vector, processor status and INTSYSCR.
|
||||
R"---(
|
||||
li a0, 0x80
|
||||
csrw mstatus, a0
|
||||
li a3, 0x3
|
||||
la a0, InterruptVector
|
||||
or a0, a0, a3
|
||||
csrw mtvec, a0
|
||||
/* Takhle RISC-V přejde do uživatelského programu. */
|
||||
csrw mepc, %[user]
|
||||
mret
|
||||
)---"
|
||||
: : [user]"r"(user_prog)
|
||||
: "a0", "a3", "memory" );
|
||||
}
|
||||
void user_prog () {
|
||||
SystemInit ();
|
||||
__init_array();
|
||||
main();
|
||||
for (;;);
|
||||
}
|
24
math/ch32v003/system.cpp
Normal file
24
math/ch32v003/system.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include "CH32V00xxx.h"
|
||||
extern "C" void SystemInit ();
|
||||
enum CLKSRC : uint32_t {
|
||||
CLK_HSI = 0u,
|
||||
CLK_HSE,
|
||||
CLK_PLL,
|
||||
};
|
||||
|
||||
void SystemInit(void) {
|
||||
RCC.CFGR0.R = 0u; // prescaler OFF
|
||||
RCC.CTLR.modify([](RCC_Type::CTLR_DEF & r) -> auto {
|
||||
r.B.HSITRIM = 0x10u;
|
||||
r.B.HSION = SET;
|
||||
r.B.HSEBYP = SET;
|
||||
r.B.CSSON = SET;
|
||||
r.B.PLLON = SET;
|
||||
return r.R;
|
||||
});
|
||||
FLASH.ACTLR.B.LATENCY = SET;
|
||||
RCC.INTR.R = 0x009F0000u; // clear interrupts
|
||||
while (RCC.CTLR.B.PLLRDY == RESET);
|
||||
RCC.CFGR0.B.SW = CLK_PLL;
|
||||
while (RCC.CFGR0.B.SWS != CLK_PLL);
|
||||
}
|
73
math/ch32v003/system.h
Normal file
73
math/ch32v003/system.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
#ifndef SYSTEM_H
|
||||
#define SYSTEM_H
|
||||
#include "CH32V00xxx.h"
|
||||
struct NVIC_Type {
|
||||
__I uint32_t ISR[8];
|
||||
__I uint32_t IPR[8];
|
||||
__IO uint32_t ITHRESDR;
|
||||
__IO uint32_t RESERVED;
|
||||
__IO uint32_t CFGR;
|
||||
__I uint32_t GISR;
|
||||
__IO uint8_t VTFIDR[4];
|
||||
uint8_t RESERVED0[12];
|
||||
__IO uint32_t VTFADDR[4];
|
||||
uint8_t RESERVED1[0x90];
|
||||
__O uint32_t IENR[8];
|
||||
uint8_t RESERVED2[0x60];
|
||||
__O uint32_t IRER[8];
|
||||
uint8_t RESERVED3[0x60];
|
||||
__O uint32_t IPSR[8];
|
||||
uint8_t RESERVED4[0x60];
|
||||
__O uint32_t IPRR[8];
|
||||
uint8_t RESERVED5[0x60];
|
||||
__IO uint32_t IACTR[8];
|
||||
uint8_t RESERVED6[0xE0];
|
||||
__IO uint8_t IPRIOR[256];
|
||||
uint8_t RESERVED7[0x810];
|
||||
__IO uint32_t SCTLR;
|
||||
void EnableIRQ (IRQn IRQ) {
|
||||
IENR [((uint32_t)(IRQ) >> 5)] = (1 << ((uint32_t)(IRQ) & 0x1F));
|
||||
}
|
||||
void DisableIRQ (IRQn IRQ) {
|
||||
IRER [((uint32_t)(IRQ) >> 5)] = (1 << ((uint32_t)(IRQ) & 0x1F));
|
||||
}
|
||||
};
|
||||
NVIC_Type & NVIC = * reinterpret_cast<NVIC_Type * const> (0xE000E000);
|
||||
struct SysTick_Type {
|
||||
union CTLR_DEF {
|
||||
struct {
|
||||
__IO ONE_BIT STE : 1; //!<[00] System counter enable
|
||||
__IO ONE_BIT STIE : 1; //!<[01] System counter interrupt enable
|
||||
__IO ONE_BIT STCLK : 1; //!<[02] System selects the clock source
|
||||
__IO ONE_BIT STRE : 1; //!<[03] System reload register
|
||||
uint32_t UNUSED0 : 27; //!<[06]
|
||||
__IO ONE_BIT SWIE : 1; //!<[31] System software triggered interrupts enable
|
||||
} B;
|
||||
__IO uint32_t R;
|
||||
template<typename F> void modify (F f) volatile {
|
||||
CTLR_DEF r; r.R = R;
|
||||
R = f (r);
|
||||
}
|
||||
};
|
||||
__IO CTLR_DEF CTLR;
|
||||
__IO uint32_t SR;
|
||||
__IO uint32_t CNT;
|
||||
uint32_t RESERVED0;
|
||||
__IO uint32_t CMP;
|
||||
uint32_t RESERVED1;
|
||||
void Config (const uint32_t ticks) {
|
||||
CNT = 0u;
|
||||
CMP = ticks - 1u;
|
||||
CTLR.modify ([] (CTLR_DEF & r) -> auto {
|
||||
r.B.STE = SET;
|
||||
r.B.STIE = SET;
|
||||
r.B.STCLK = SET;
|
||||
r.B.STRE = SET;
|
||||
return r.R;
|
||||
});
|
||||
NVIC.EnableIRQ (SysTicK_IRQn);
|
||||
}
|
||||
};
|
||||
SysTick_Type & SysTick = * reinterpret_cast<SysTick_Type * const> (0xE000F000);
|
||||
|
||||
#endif // SYSTEM_H
|
84
math/ch32v003/usart.cpp
Normal file
84
math/ch32v003/usart.cpp
Normal file
|
@ -0,0 +1,84 @@
|
|||
#include "system.h"
|
||||
#include "usart.h"
|
||||
static Usart * pInstance = nullptr;
|
||||
static constexpr unsigned HCLK = 48'000'000u;
|
||||
|
||||
extern "C" void USART1_IRQHandler (void) __attribute__((interrupt));
|
||||
void USART1_IRQHandler (void) {
|
||||
if (pInstance) pInstance->irq();
|
||||
};
|
||||
|
||||
Usart::Usart(const uint32_t _baud) noexcept : BaseLayer (), tx_ring () {
|
||||
pInstance = this;
|
||||
// 1. Clock Enable
|
||||
RCC.APB2PCENR.modify([](RCC_Type::APB2PCENR_DEF & r) -> auto {
|
||||
r.B.USART1EN = SET;
|
||||
r.B.IOPDEN = SET;
|
||||
return r.R;
|
||||
});
|
||||
// 2. GPIO Alternate Config - default TX/PD5, RX/PD6
|
||||
GPIOD.CFGLR.modify([](GPIOA_Type::CFGLR_DEF & r) -> auto {
|
||||
r.B.MODE5 = 1u;
|
||||
r.B.CNF5 = 2u; // or 3u for open drain
|
||||
r.B.MODE6 = 0u;
|
||||
r.B.CNF6 = 1u; // floating input
|
||||
return r.R;
|
||||
});
|
||||
// 4. NVIC
|
||||
NVIC.EnableIRQ (USART1_IRQn);
|
||||
// 5. USART registry 8.bit bez parity
|
||||
USART1.CTLR1.modify([] (USART1_Type::CTLR1_DEF & r) -> auto {
|
||||
r.B.RE = SET;
|
||||
r.B.TE = SET;
|
||||
r.B.RXNEIE = SET;
|
||||
return r.R;
|
||||
});
|
||||
USART1.CTLR2.R = 0;
|
||||
//USART1.CTLR3.B.OVRDIS = SET;
|
||||
const uint32_t tmp = HCLK / _baud;
|
||||
USART1.BRR.R = tmp;
|
||||
USART1.CTLR1.B.UE = SET; // nakonec povolit globálně
|
||||
}
|
||||
void Usart::irq () {
|
||||
volatile USART1_Type::STATR_DEF status (USART1.STATR); // načti status přerušení
|
||||
char rdata, tdata;
|
||||
|
||||
if (status.B.TXE) { // od vysílače
|
||||
if (tx_ring.Read (tdata)) { // pokud máme data
|
||||
USART1.DATAR.B.DR = (uint8_t) tdata; // zapíšeme do výstupu
|
||||
} else { // pokud ne
|
||||
// Předpoklad je half-duplex i.e. RS485, jinak jen zakázat TXEIE
|
||||
rdata = (USART1.DATAR.B.DR); // dummy read
|
||||
USART1.CTLR1.modify([](USART1_Type::CTLR1_DEF & r) -> auto {
|
||||
r.B.RE = SET; // povol prijem
|
||||
r.B.TXEIE = RESET; // je nutné zakázat přerušení od vysílače
|
||||
return r.R;
|
||||
});
|
||||
}
|
||||
}
|
||||
if (status.B.RXNE) { // od přijímače
|
||||
rdata = (USART1.DATAR.B.DR); // načteme data
|
||||
Up (&rdata, 1u); // a pošleme dál
|
||||
}
|
||||
}
|
||||
uint32_t Usart::Down(const char * data, const uint32_t len) {
|
||||
unsigned n = 0u;
|
||||
for (n=0u; n<len; n++) {
|
||||
if (!tx_ring.Write(data[n])) break;
|
||||
}
|
||||
USART1.CTLR1.modify([](USART1_Type::CTLR1_DEF & r) -> auto {
|
||||
r.B.RE = RESET;
|
||||
r.B.TXEIE = SET; // po povolení přerušení okamžitě přeruší
|
||||
return r.R;
|
||||
});
|
||||
return n;
|
||||
}
|
||||
void Usart::SetRS485 (const bool polarity) const {
|
||||
}
|
||||
void Usart::SetHalfDuplex (const bool on) const {
|
||||
}
|
||||
extern "C" {
|
||||
int terminate () {
|
||||
return 0;
|
||||
}
|
||||
};
|
74
math/common/baselayer.h
Normal file
74
math/common/baselayer.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
#ifndef BASELAYER_H
|
||||
#define BASELAYER_H
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __arm__
|
||||
#define debug(...)
|
||||
#else // ARCH_CM0
|
||||
#ifdef DEBUG
|
||||
#define debug printf
|
||||
#else // DEBUG
|
||||
#define debug(...)
|
||||
#endif // DEBUG
|
||||
#endif // ARCH_CM0
|
||||
/** @brief Bázová třída pro stack trochu obecnějšího komunikačního protokolu.
|
||||
*
|
||||
* @class BaseLayer
|
||||
* @brief Od této třídy budeme dále odvozovat ostatní.
|
||||
*
|
||||
*/
|
||||
class BaseLayer {
|
||||
public:
|
||||
/** Konstruktor
|
||||
*/
|
||||
explicit constexpr BaseLayer () noexcept : pUp(nullptr), pDown(nullptr) {};
|
||||
/** Virtuální metoda, přesouvající data směrem nahoru, pokud s nimi nechceme dělat něco jiného.
|
||||
@param data ukazatel na pole dat
|
||||
@param len delka dat v bytech
|
||||
@return počet přenesených bytů
|
||||
*/
|
||||
virtual uint32_t Up (const char * data, const uint32_t len) {
|
||||
if (pUp) return pUp->Up (data, len);
|
||||
return 0;
|
||||
};
|
||||
/** Virtuální metoda, přesouvající data směrem dolů, pokud s nimi nechceme dělat něco jiného.
|
||||
@param data ukazatel na pole dat
|
||||
@param len delka dat v bytech
|
||||
@return počet přenesených bytů
|
||||
*/
|
||||
virtual uint32_t Down (const char * data, const uint32_t len) {
|
||||
if (pDown) return pDown->Down (data, len);
|
||||
return len;
|
||||
};
|
||||
/** @brief Zřetězení stacku.
|
||||
* Tohle je vlastně to nejdůležitější. V čistém C by se musely
|
||||
* nastavovat ukazatele na callback funkce, tady je to čitší - pro uživatele neviditelné,
|
||||
* ale je to to samé.
|
||||
@param bl Třída, ležící pod, spodní
|
||||
@return Odkaz na tuto třídu (aby se to dalo řetězit)
|
||||
*/
|
||||
virtual BaseLayer & operator += (BaseLayer & bl) {
|
||||
bl.setUp (this); // ta spodní bude volat při Up tuto třídu
|
||||
setDown (& bl); // a tato třída bude volat při Down tu spodní
|
||||
return * this;
|
||||
};
|
||||
/** Getter pro pDown
|
||||
@return pDown
|
||||
*/
|
||||
BaseLayer * getDown (void) const { return pDown; };
|
||||
protected:
|
||||
/** Lokální setter pro pUp
|
||||
@param p Co budeme do pUp dávat
|
||||
*/
|
||||
void setUp (BaseLayer * p) { pUp = p; };
|
||||
/** Lokální setter pro pDown
|
||||
@param p Co budeme do pDown dávat
|
||||
*/
|
||||
void setDown (BaseLayer * p) { pDown = p; };
|
||||
private:
|
||||
// Ono to je vlastně oboustranně vázaný spojový seznam.
|
||||
BaseLayer * pUp; //!< Ukazatel na třídu, která bude dále volat Up
|
||||
BaseLayer * pDown; //!< Ukazatel na třídu, která bude dále volat Down
|
||||
};
|
||||
|
||||
#endif // BASELAYER_H
|
73
math/common/fifo.h
Normal file
73
math/common/fifo.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
#ifndef FIFO_H
|
||||
#define FIFO_H
|
||||
/** Typ dbus_w_t je podobně definován jako sig_atomic_t v hlavičce signal.h.
|
||||
* Je to prostě největší typ, ke kterému je "atomický" přístup. V GCC je definováno
|
||||
* __SIG_ATOMIC_TYPE__, šlo by použít, ale je znaménkový.
|
||||
* */
|
||||
#ifdef __SIG_ATOMIC_TYPE__
|
||||
typedef unsigned __SIG_ATOMIC_TYPE__ dbus_w_t;
|
||||
#else
|
||||
typedef unsigned int dbus_w_t; // pro AVR by to měl být uint8_t (šířka datové sběrnice)
|
||||
#endif //__SIG_ATOMIC_TYPE__
|
||||
/// Tahle podivná rekurzívní formule je použita pro validaci délky bufferu.
|
||||
static constexpr bool isValidM (const int N, const dbus_w_t M) {
|
||||
// constexpr má raději rekurzi než cyklus (c++11)
|
||||
return (N > 12) ? false : (((1u << N) == M) ? true : isValidM (N+1, M));
|
||||
}
|
||||
/** @class FIFO
|
||||
* @brief Jednoduchá fronta (kruhový buffer).
|
||||
*
|
||||
* V tomto přikladu je vidět, že synchronizace mezi přerušením a hlavní smyčkou programu
|
||||
* může být tak jednoduchá, že je v podstatě neviditelná. Využívá se toho, že pokud
|
||||
* do kruhového buferu zapisujeme jen z jednoho bodu a čteme také jen z jednoho bodu
|
||||
* (vlákna), zápis probíhá nezávisle pomocí indexu m_head a čtení pomocí m_tail.
|
||||
* Délka dat je dána rozdílem tt. indexů, pokud v průběhu výpočtu délky dojde k přerušení,
|
||||
* v zásadě se nic špatného neděje, maximálně je délka určena špatně a to tak,
|
||||
* že zápis nebo čtení je nutné opakovat. Důležité je, že po výpočtu se nová délka zapíše
|
||||
* do paměti "atomicky". Takže např. pro 8-bit procesor musí být indexy jen 8-bitové.
|
||||
* To není moc velké omezení, protože tyto procesory obvykle mají dost malou RAM, takže
|
||||
* velikost bufferu stejně nebývá být větší než nějakých 64 položek.
|
||||
* Opět nijak nevadí že přijde přerušení při zápisu nebo čtení položky - to se provádí
|
||||
* dříve než změna indexu, zápis a čtení je vždy na jiném místě RAM. Celé je to uděláno
|
||||
* jako šablona, takže je možné řadit do fronty i složitější věci než je pouhý byte.
|
||||
* Druhým parametrem šablony je délka bufferu (aby to šlo konstruovat jako statický objekt),
|
||||
* musí to být mocnina dvou v rozsahu 8 až 4096, default je 64. Mocnina 2 je zvolena proto,
|
||||
* aby se místo zbytku po dělení mohl použít jen bitový and, což je rychlejší.
|
||||
* */
|
||||
template<typename T, const dbus_w_t M = 64> class FIFO {
|
||||
T m_data [M];
|
||||
volatile dbus_w_t m_head; //!< index pro zápis (hlava)
|
||||
volatile dbus_w_t m_tail; //!< index pro čtení (ocas)
|
||||
/// vrací skutečnou délku dostupných dat
|
||||
constexpr dbus_w_t lenght () const { return (M + m_head - m_tail) & (M - 1); };
|
||||
/// zvětší a saturuje index, takže se tento motá v kruhu @param n index
|
||||
void sat_inc (volatile dbus_w_t & n) const { n = (n + 1) & (M - 1); };
|
||||
public:
|
||||
/// Konstruktor
|
||||
explicit constexpr FIFO<T,M> () noexcept {
|
||||
// pro 8-bit architekturu může být byte jako index poměrně malý
|
||||
static_assert (1ul << (8 * sizeof(dbus_w_t) - 1) >= M, "atomic type too small");
|
||||
// a omezíme pro jistotu i delku buferu na nějakou rozumnou delku
|
||||
static_assert (isValidM (3, M), "M must be power of two in range <8,4096> or <8,128> for 8-bit data bus (AVR)");
|
||||
m_head = 0;
|
||||
m_tail = 0;
|
||||
}
|
||||
/// Čtení položky
|
||||
/// @return true, pokud se úspěšně provede
|
||||
const bool Read (T & c) {
|
||||
if (lenght() == 0) return false;
|
||||
c = m_data [m_tail];
|
||||
sat_inc (m_tail);
|
||||
return true;
|
||||
}
|
||||
/// Zápis položky
|
||||
/// @return true, pokud se úspěšně provede
|
||||
const bool Write (const T & c) {
|
||||
if (lenght() >= (M - 1)) return false;
|
||||
m_data [m_head] = c;
|
||||
sat_inc (m_head);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // FIFO_H
|
114
math/common/float.cpp
Normal file
114
math/common/float.cpp
Normal file
|
@ -0,0 +1,114 @@
|
|||
/* Reprezentace float podle IEEE 754 v little endien */
|
||||
#include <stdint.h>
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
typedef union {
|
||||
float f;
|
||||
struct {
|
||||
uint32_t frac : 23;
|
||||
uint32_t exp : 8;
|
||||
uint32_t sign : 1;
|
||||
} u;
|
||||
} float_unsigned;
|
||||
typedef union {
|
||||
uint32_t u;
|
||||
struct {
|
||||
uint32_t l : 28;
|
||||
uint32_t h : 4;
|
||||
} e;
|
||||
} u2m;
|
||||
// výstupní znaky
|
||||
static const char * dec = "0123456789";
|
||||
// decimální exponent e do bufferu buff
|
||||
static size_t exp_str (char * buf, const int e) {
|
||||
size_t n = 0;
|
||||
int exp = 0, res;
|
||||
buf [n++] = 'E';
|
||||
if (e > 0) {
|
||||
buf [n++] = '+';
|
||||
exp = e;
|
||||
} else if (e < 0) {
|
||||
buf [n++] = '-';
|
||||
exp = -e;
|
||||
} else {
|
||||
buf [n++] = '+';
|
||||
buf [n++] = '0';
|
||||
buf [n++] = '0';
|
||||
return n;
|
||||
}
|
||||
n += 2;
|
||||
res = exp % 10;
|
||||
buf [n - 1] = dec [res];
|
||||
exp = exp / 10;
|
||||
res = exp % 10;
|
||||
buf [n - 2] = dec [res];
|
||||
return n;
|
||||
}
|
||||
// konstanty pro decimální normalizaci
|
||||
static const float exp_plus [] = {
|
||||
1.0e+1f, 1.0e+2f, 1.0e+4f, 1.0e+8f, 1.0e+16f, 1.0e+32f,
|
||||
};
|
||||
static const float exp_minus [] = {
|
||||
1.0e-1f, 1.0e-2f, 1.0e-4f, 1.0e-8f, 1.0e-16f, 1.0e-32f,
|
||||
};
|
||||
// decimální normalizace f do rozsahu <0.1, 1.0)
|
||||
static float f_norm (const float f, int * pe) {
|
||||
float_unsigned res;
|
||||
res.f = f;
|
||||
uint32_t s = res.u.sign; // schovej znaménko
|
||||
res.u.sign = 0u; // dál už počítej jen s absolutní hodnotou
|
||||
unsigned n = 5u;
|
||||
if (res.f >= 1.0f) {
|
||||
do {
|
||||
if (res.f >= exp_plus [n]) {
|
||||
res.f *= exp_minus [n];
|
||||
* pe += 1 << n;
|
||||
}
|
||||
} while (n--);
|
||||
res.f *= 0.1f;
|
||||
* pe += 1;
|
||||
} else {
|
||||
do {
|
||||
if (res.f < exp_minus [n]) {
|
||||
res.f *= exp_plus [n];
|
||||
* pe -= 1 << n;
|
||||
}
|
||||
} while (n--);
|
||||
}
|
||||
res.u.sign = s; // obnov znaménko
|
||||
// printf("normalized = %f, decimal exponent = %d\n", res.f, * pe);
|
||||
return res.f;
|
||||
}
|
||||
static unsigned to_str (const float f, char * buf) {
|
||||
int dec_exp = 0;
|
||||
float_unsigned fu;
|
||||
fu.f = f_norm (f, & dec_exp);
|
||||
u2m um;
|
||||
// převod formátu pro výstup číslic pomocí celočíselného násobení 10
|
||||
um.u = (fu.u.frac | (1lu << 23)) << (fu.u.exp - 122u);
|
||||
unsigned n = 0;
|
||||
if (fu.u.sign) buf [n++] = '-';
|
||||
else buf [n++] = '+';
|
||||
if (fu.f != 0.0f) {
|
||||
for (;;) { // exportuj decimální číslice
|
||||
um.u *= 10u;
|
||||
const char c = dec [um.e.h];
|
||||
um.e.h = 0u;
|
||||
if (n == 2) buf [n++] = '.';
|
||||
buf [n++] = c;
|
||||
if (n >= 10) break; // 8 platnych cislic
|
||||
}
|
||||
n += exp_str (buf + n, dec_exp - 1);
|
||||
} else { // 0.0f
|
||||
buf [n++] = '0';
|
||||
for (unsigned i=0; i<12; i++) buf[n++] = ' ';
|
||||
}
|
||||
buf [n] = '\0'; // ukončení
|
||||
return n;
|
||||
}
|
||||
#include "print.h"
|
||||
Print & Print::operator<< (const float num) {
|
||||
const unsigned n = to_str(num, buf);
|
||||
BlockDown (buf, n);
|
||||
return * this;
|
||||
}
|
||||
|
85
math/common/print.cpp
Normal file
85
math/common/print.cpp
Normal file
|
@ -0,0 +1,85 @@
|
|||
#include "print.h"
|
||||
|
||||
#define sleep()
|
||||
|
||||
static const char * hexStr = "0123456789ABCDEF";
|
||||
static const uint16_t numLen[] = {1, 32, 1, 11, 8, 0};
|
||||
|
||||
Print::Print (PrintBases b) : BaseLayer () {
|
||||
base = b;
|
||||
}
|
||||
// Výstup blokujeme podle toho, co se vrací ze spodní vrstvy
|
||||
uint32_t Print::BlockDown (const char * buf, uint32_t len) {
|
||||
uint32_t n, ofs = 0, req = len;
|
||||
for (;;) {
|
||||
// spodní vrstva může vrátit i nulu, pokud je FIFO plné
|
||||
n = BaseLayer::Down (buf + ofs, req);
|
||||
ofs += n; // Posuneme ukazatel
|
||||
req -= n; // Zmenšíme další požadavek
|
||||
if (!req) break;
|
||||
sleep(); // A klidně můžeme spát
|
||||
}
|
||||
return ofs;
|
||||
}
|
||||
|
||||
Print& Print::operator<< (const char * str) {
|
||||
uint32_t i = 0;
|
||||
while (str[i++]); // strlen
|
||||
BlockDown (str, --i);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Print& Print::operator<< (const int num) {
|
||||
uint32_t i = BUFLEN;
|
||||
|
||||
if (base == DEC) {
|
||||
unsigned int u;
|
||||
if (num < 0) u = -num;
|
||||
else u = num;
|
||||
do {
|
||||
// Knihovní div() je nevhodné - dělí 2x.
|
||||
// Přímočaré a funkční řešení
|
||||
uint32_t rem;
|
||||
rem = u % (unsigned) DEC; // 1.dělení
|
||||
u = u / (unsigned) DEC; // 2.dělení
|
||||
buf [--i] = hexStr [rem];
|
||||
} while (u);
|
||||
if (num < 0) buf [--i] = '-';
|
||||
} else {
|
||||
uint32_t m = (1U << (uint32_t) base) - 1U;
|
||||
uint32_t l = (uint32_t) numLen [(int) base];
|
||||
uint32_t u = (uint32_t) num;
|
||||
for (unsigned n=0; n<l; n++) {
|
||||
buf [--i] = hexStr [u & m];
|
||||
u >>= (unsigned) base;
|
||||
}
|
||||
if (base == BIN) buf [--i] = 'b';
|
||||
if (base == HEX) buf [--i] = 'x';
|
||||
buf [--i] = '0';
|
||||
}
|
||||
BlockDown (buf+i, BUFLEN-i);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Print& Print::operator<< (const PrintBases num) {
|
||||
base = num;
|
||||
return *this;
|
||||
}
|
||||
void Print::out (const void * p, uint32_t l) {
|
||||
const unsigned char* q = (const unsigned char*) p;
|
||||
unsigned char uc;
|
||||
uint32_t k, n = 0;
|
||||
for (uint32_t i=0; i<l; i++) {
|
||||
uc = q[i];
|
||||
buf[n++] = '<';
|
||||
k = uc >> 4;
|
||||
buf[n++] = hexStr [k];
|
||||
k = uc & 0x0f;
|
||||
buf[n++] = hexStr [k];
|
||||
buf[n++] = '>';
|
||||
}
|
||||
buf[n++] = '\r';
|
||||
buf[n++] = '\n';
|
||||
BlockDown (buf, n);
|
||||
}
|
||||
|
75
math/common/print.h
Normal file
75
math/common/print.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
#ifndef PRINT_H
|
||||
#define PRINT_H
|
||||
|
||||
#include "baselayer.h"
|
||||
|
||||
#define EOL "\r\n"
|
||||
#define BUFLEN 64
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Něco jako ostream.
|
||||
*
|
||||
*/
|
||||
|
||||
/// Základy pro zobrazení čísla.
|
||||
enum PrintBases {
|
||||
BIN=1, OCT=3, DEC=10, HEX=4
|
||||
};
|
||||
|
||||
/**
|
||||
* @class Print
|
||||
* @brief Třída pro výpisy do Down().
|
||||
*
|
||||
*
|
||||
* V main pak přibude jen definice instance této třídy
|
||||
* @code
|
||||
static Print print;
|
||||
* @endcode
|
||||
* a ukázka, jak se s tím pracuje:
|
||||
* @snippet main.cpp Main print example
|
||||
* Nic na tom není - operátor << má přetížení pro string, číslo a volbu formátu čísla (enum PrintBases).
|
||||
* Výstup je pak do bufferu a aby nám to "neutíkalo", tedy aby se vypsalo vše,
|
||||
* zavedeme blokování, vycházející z toho, že spodní třída vrátí jen počet bytů,
|
||||
* které skutečně odeslala. Při čekání spí, takže nepoužívat v přerušení.
|
||||
* @snippet src/print.cpp Block example
|
||||
* Toto blokování pak není použito ve vrchních třídách stacku,
|
||||
* blokovaná metoda je BlockDown(). Pokud bychom použili přímo Down(), blokování by pak
|
||||
* používaly všechny vrstvy nad tím. A protože mohou Down() používat v přerušení, byl by problém.
|
||||
*
|
||||
* Metody pro výpisy jsou sice dost zjednodušené, ale zase to nezabere
|
||||
* moc místa - pro ladění se to použít dá. Délka vypisovaného stringu není omezena
|
||||
* délkou použitého buferu.
|
||||
*
|
||||
*/
|
||||
|
||||
class Print : public BaseLayer {
|
||||
public:
|
||||
/// Konstruktor @param b Default decimální výpisy.
|
||||
Print (PrintBases b = DEC);
|
||||
/// Blokování výstupu
|
||||
/// @param buf Ukazatel na data
|
||||
/// @param len Délka přenášených dat
|
||||
/// @return Počet přenesených bytů (rovno len)
|
||||
uint32_t BlockDown (const char * buf, uint32_t len);
|
||||
/// Výstup řetězce bytů
|
||||
/// @param str Ukazatel na řetězec
|
||||
/// @return Odkaz na tuto třídu kvůli řetězení.
|
||||
Print & operator << (const char * str);
|
||||
/// Výstup celého čísla podle base
|
||||
/// @param num Číslo
|
||||
/// @return Odkaz na tuto třídu kvůli řetězení.
|
||||
Print & operator << (const int num);
|
||||
// external in float.cpp, can skip
|
||||
Print & operator << (const float num);
|
||||
/// Změna základu pro výstup čísla
|
||||
/// @param num enum PrintBases
|
||||
/// @return Odkaz na tuto třídu kvůli řetězení.
|
||||
Print & operator << (const PrintBases num);
|
||||
void out (const void* p, uint32_t l);
|
||||
private:
|
||||
PrintBases base; //!< Základ pro výstup čísla.
|
||||
char buf[BUFLEN]; //!< Buffer pro výstup čísla.
|
||||
};
|
||||
|
||||
#endif // PRINT_H
|
21
math/common/usart.h
Normal file
21
math/common/usart.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#ifndef USART_H
|
||||
#define USART_H
|
||||
#include "fifo.h"
|
||||
#include "baselayer.h"
|
||||
/** @class Usart
|
||||
* @brief Sériový port.
|
||||
*
|
||||
* Zde RS485, jen výstup.
|
||||
*/
|
||||
class Usart : public BaseLayer {
|
||||
FIFO<char, 64> tx_ring;
|
||||
public:
|
||||
explicit Usart (const uint32_t baud = 9600) noexcept;
|
||||
uint32_t Down (const char * data, const uint32_t len) override;
|
||||
void SetRS485 (const bool polarity) const;
|
||||
|
||||
void irq (void);
|
||||
void SetHalfDuplex (const bool on) const;
|
||||
};
|
||||
|
||||
#endif // USART_H
|
41
math/compute.cpp
Normal file
41
math/compute.cpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
#include "compute.h"
|
||||
void ReadData (int * data, const int len) {
|
||||
static int t = 0;
|
||||
for (int n=0; n<len; n++) data [n] = t++;
|
||||
}
|
||||
float msqrtf(const float x) {
|
||||
if (x <= 0.0f) return 0.0f;
|
||||
float y = x;
|
||||
for (unsigned n=0; n<10; n++) {
|
||||
y = 0.5f * (y + x/y);
|
||||
}
|
||||
// Debug ("delta=%f\n", fabs(y - sqrtf(x)));
|
||||
return y;
|
||||
}
|
||||
static constexpr float mfabs (const float a) { return a < 0.0f ? -a : +a; }
|
||||
//static constexpr int iround (const float a) { return a < 0.0f ? int (a - 0.5f) : int (a + 0.5f); }
|
||||
static constexpr float D_PI = 2.0f * 3.141592653f;
|
||||
/**
|
||||
* @brief sinus nebo kosinus
|
||||
* @param x úhel v radiánech
|
||||
* @param even počítá sinus, pokud even=true i kosinus, pokud even=false
|
||||
* @return float výsledek
|
||||
*/
|
||||
float sincos (const float x, const bool even) {
|
||||
if (x > +D_PI) return sincos (x - D_PI, even);
|
||||
if (x < -D_PI) return sincos (x + D_PI, even);
|
||||
float result (0.0f), element(1.0f), divider(0.0f);
|
||||
if (even) { element *= x; divider += 1.0f; }
|
||||
constexpr float eps = 1.0e-8f; // maximální chyba výpočtu
|
||||
const float aa = - (x * x);
|
||||
for (;;) {
|
||||
result += element;
|
||||
if (mfabs (element) < eps) break;
|
||||
divider += 1.0f;
|
||||
float fact = divider;
|
||||
divider += 1.0f;
|
||||
fact *= divider;
|
||||
element *= aa / fact;
|
||||
}
|
||||
return result;
|
||||
}
|
89
math/compute.h
Normal file
89
math/compute.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
#ifndef COMPUTE_H
|
||||
#define COMPUTE_H
|
||||
#include "usart.h"
|
||||
#include "print.h"
|
||||
#ifdef __linux__
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#define Debug ::printf
|
||||
#else
|
||||
#define Debug(...);
|
||||
#endif
|
||||
|
||||
|
||||
extern void ReadData (int * data, const int len);
|
||||
extern float msqrtf (const float x);
|
||||
extern float sincos (const float x, const bool even);
|
||||
|
||||
static constexpr unsigned N = 3u;
|
||||
|
||||
class Vector {
|
||||
float data [N];
|
||||
public:
|
||||
explicit Vector () { for (unsigned n=0u; n<N; n++) data [n] = 0.0f; }
|
||||
explicit Vector (const int * ptr, const unsigned n = N) {
|
||||
for (unsigned i=0u; i<n; i++) {
|
||||
data [i] = static_cast<float>(ptr [i]);
|
||||
}
|
||||
}
|
||||
float & operator[] (const unsigned n) { return data [n]; }
|
||||
float abs () const {
|
||||
float r = 0.0f;
|
||||
for (unsigned n=0u; n<N; n++) {
|
||||
const float e = data [n];
|
||||
r += e * e;
|
||||
}
|
||||
return msqrtf (r);
|
||||
}
|
||||
};
|
||||
struct Matrix {
|
||||
float data [N][N];
|
||||
Vector operator* (Vector & v) const {
|
||||
Vector r;
|
||||
for (unsigned i=0; i<N; i++) {
|
||||
for (unsigned j=0; j<N; j++) {
|
||||
r[j] += data [i][j] * v [i];
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
class Compute {
|
||||
Usart usart;
|
||||
Print cout;
|
||||
unsigned passcnt;
|
||||
public:
|
||||
explicit Compute () noexcept : usart (115200), cout (DEC), passcnt (0u) {
|
||||
cout += usart;
|
||||
}
|
||||
void multiply_test () {
|
||||
cout << " \rBegin tests ...\r\n";
|
||||
// Rotace o 45 deg v rovině xy.
|
||||
const Matrix m { .data = {{0.707f, 0.707f, 0.0f}, {-0.707f, 0.707f, 0.0f}, {0.0f, 0.0f, 1.0f}} };
|
||||
int x [N] = {1, 1, 2};
|
||||
Vector v (x);
|
||||
Vector r = m * v;
|
||||
cout << "origin: x=" << v[0] << ", y=" << v[1] << ", z=" << v[2] << EOL;
|
||||
cout << "result: x=" << r[0] << ", y=" << r[1] << ", z=" << r[2] << EOL;
|
||||
}
|
||||
void sincos_test () {
|
||||
constexpr float x = 3.1415926f / 6.0f;
|
||||
const float s = sincos(x, true);
|
||||
const float c = sincos(x, false);
|
||||
cout << "sin (" << x << ") = " << s << EOL;
|
||||
cout << "cos (" << x << ") = " << c << EOL;
|
||||
cout << "ctrl: sin^2 + cos^2 = " << s * s + c * c << EOL;
|
||||
}
|
||||
bool pass () {
|
||||
int x [N];
|
||||
ReadData(x, N);
|
||||
Vector v (x);
|
||||
cout << v.abs() << EOL;
|
||||
passcnt += 1;
|
||||
if (passcnt > 20) return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // COMPUTE_H
|
14
math/linux/gcc.mk
Normal file
14
math/linux/gcc.mk
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Use gcc / binutils toolchain
|
||||
PREFIX =
|
||||
CC = $(PREFIX)gcc
|
||||
CXX = $(PREFIX)g++
|
||||
LD = $(PREFIX)g++
|
||||
SIZE = $(PREFIX)size
|
||||
DUMP = $(PREFIX)objdump
|
||||
COPY = $(PREFIX)objcopy
|
||||
CFLAGS+= -Os
|
||||
|
||||
LFLAGS+= -Wl,--Map=$(@:%.elf=%.map),--gc-sections
|
||||
#LFLAGS+= -Wl,--print-sysroot -- chyba ld ?
|
||||
#LFLAGS+= -O3
|
||||
LDLIBS+= -lc
|
22
math/linux/usart.cpp
Normal file
22
math/linux/usart.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
#include <unistd.h>
|
||||
#include "usart.h"
|
||||
|
||||
Usart::Usart(const uint32_t _baud) noexcept : BaseLayer (), tx_ring () {
|
||||
}
|
||||
void Usart::irq () {
|
||||
}
|
||||
void Usart::SetRS485 (const bool polarity) const {
|
||||
}
|
||||
void Usart::SetHalfDuplex (const bool on) const {
|
||||
}
|
||||
|
||||
uint32_t Usart::Down (const char * data, const uint32_t len) {
|
||||
const int n = ::write (1, data, len);
|
||||
return n;
|
||||
}
|
||||
extern "C" {
|
||||
void __cxa_pure_virtual() { while (1); }
|
||||
int terminate () {
|
||||
return 0;
|
||||
}
|
||||
};
|
25
math/main.cpp
Normal file
25
math/main.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include "compute.h"
|
||||
extern "C" int terminate (); // needed only for simavr
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
/* DEMO pro otestování výpočtů v jednoduché přesnosti float.
|
||||
* Funkce: sin, cos, sqrt, násobení matice s vektorem
|
||||
* Pro srovnání jsem přidal cílové platformy stm32f051, avr, linux.
|
||||
* AVR používá simavr (https://github.com/buserror/simavr), na linuxu
|
||||
* se to lépe ladí.
|
||||
* CH32V003 má kód nejdelší, stm32f051 o málo kratší, avr asi poloviční.
|
||||
* I tak nějaké rozumné výpočty se do těch 16KiB mohou vejít.
|
||||
* Všechny platformy dávají stejné výsledky.
|
||||
*
|
||||
* Toto je v podstatě samostatný projekt, jsou zde kopie všech potřebných
|
||||
* souborů. Jinak by v tom byl už velký guláš.
|
||||
* */
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
static Compute comp;
|
||||
int main () {
|
||||
comp.multiply_test();
|
||||
comp.sincos_test ();
|
||||
for (;;) {
|
||||
if (comp.pass()) break;
|
||||
}
|
||||
return terminate();
|
||||
}
|
139
math/stm32f051/CortexM0.h
Normal file
139
math/stm32f051/CortexM0.h
Normal file
|
@ -0,0 +1,139 @@
|
|||
#ifndef ARMCM0_HDEF
|
||||
#define ARMCM0_HDEF
|
||||
|
||||
/** @brief SYSTICK for Cortex-M0
|
||||
* Není to moc domyšlené, před tt. hlavičkou je nutné mít definován NVIC a IRQn,
|
||||
* což je v STM generované hlavičce většinou uděláno. NVIC_EnableIRQ je zjednodušen
|
||||
* jen pro CM0, jinak se tam čaruje s PRIO_BITS, tady to není potřeba.
|
||||
*/
|
||||
|
||||
// tohle je jediné, co je potřeba z core_cm0.h
|
||||
static inline void NVIC_EnableIRQ (IRQn irq) {
|
||||
NVIC.ISER.R = ((1 << (static_cast<uint32_t>(irq) & 0x1F)));
|
||||
}
|
||||
static constexpr uint32_t SysTick_LOAD_RELOAD_Msk = (0xFFFFFFUL); /*!< SysTick LOAD: RELOAD Mask */
|
||||
// ////////////////////+++ SysTick +-+//////////////////// //
|
||||
struct SysTick_DEF { /*!< 24Bit System Tick Timer for use in RTOS */
|
||||
union CSR_DEF { //!< [0000](04)[0x00000004] SysTick Control and Status Register
|
||||
enum ENABLE_ENUM /*: uint32_t */ {
|
||||
ENABLE_0 = 0, //!< disabled
|
||||
ENABLE_1 = 1, //!< enabled
|
||||
};
|
||||
enum TICKINT_ENUM /*: uint32_t */ {
|
||||
TICKINT_0 = 0, //!< Enable SysTick Exception
|
||||
TICKINT_1 = 1, //!< Disable SysTick Exception
|
||||
};
|
||||
enum CLKSOURCE_ENUM /*: uint32_t */ {
|
||||
CLKSOURCE_0 = 0, //!< External Clock
|
||||
CLKSOURCE_1 = 1, //!< CPU Clock
|
||||
};
|
||||
struct {
|
||||
__IO ENABLE_ENUM ENABLE : 1; //!<[00] Enable SysTick Timer
|
||||
__IO TICKINT_ENUM TICKINT : 1; //!<[01] Generate Tick Interrupt
|
||||
__IO CLKSOURCE_ENUM CLKSOURCE : 1; //!<[02] Source to count from
|
||||
uint32_t UNUSED0 : 13; //!<[03]
|
||||
__IO ONE_BIT COUNTFLAG : 1; //!<[16] SysTick counted to zero
|
||||
} B;
|
||||
__IO uint32_t R;
|
||||
|
||||
explicit CSR_DEF () noexcept { R = 0x00000004u; }
|
||||
template<typename F> void setbit (F f) volatile {
|
||||
CSR_DEF r;
|
||||
R = f (r);
|
||||
}
|
||||
|
||||
template<typename F> void modify (F f) volatile {
|
||||
CSR_DEF r; r.R = R;
|
||||
R = f (r);
|
||||
}
|
||||
};
|
||||
__IO CSR_DEF CSR ; //!< register definition
|
||||
|
||||
union RVR_DEF { //!< [0004](04)[0x00000000] SysTick Reload Value Register
|
||||
struct {
|
||||
__IO uint32_t RELOAD : 24; //!<[00] Value to auto reload SysTick after reaching zero
|
||||
} B;
|
||||
__IO uint32_t R;
|
||||
|
||||
explicit RVR_DEF () noexcept { R = 0x00000000u; }
|
||||
template<typename F> void setbit (F f) volatile {
|
||||
RVR_DEF r;
|
||||
R = f (r);
|
||||
}
|
||||
|
||||
template<typename F> void modify (F f) volatile {
|
||||
RVR_DEF r; r.R = R;
|
||||
R = f (r);
|
||||
}
|
||||
};
|
||||
__IO RVR_DEF RVR ; //!< register definition
|
||||
|
||||
union CVR_DEF { //!< [0008](04)[0x00000000] SysTick Current Value Register
|
||||
struct {
|
||||
__IO uint32_t CURRENT : 24; //!<[00] Current value
|
||||
} B;
|
||||
__IO uint32_t R;
|
||||
|
||||
explicit CVR_DEF () noexcept { R = 0x00000000u; }
|
||||
template<typename F> void setbit (F f) volatile {
|
||||
CVR_DEF r;
|
||||
R = f (r);
|
||||
}
|
||||
|
||||
template<typename F> void modify (F f) volatile {
|
||||
CVR_DEF r; r.R = R;
|
||||
R = f (r);
|
||||
}
|
||||
};
|
||||
__IO CVR_DEF CVR ; //!< register definition
|
||||
|
||||
union CALIB_DEF { //!< [000c](04)[0x00000000] SysTick Calibration Value Register
|
||||
enum SKEW_ENUM /*: uint32_t */ {
|
||||
SKEW_0 = 0, //!< 10ms calibration value is exact
|
||||
SKEW_1 = 1, //!< 10ms calibration value is inexact, because of the clock frequency
|
||||
};
|
||||
enum NOREF_ENUM /*: uint32_t */ {
|
||||
NOREF_0 = 0, //!< Ref Clk available
|
||||
NOREF_1 = 1, //!< Ref Clk not available
|
||||
};
|
||||
struct {
|
||||
__I uint32_t TENMS : 24; //!<[00] Reload value to use for 10ms timing
|
||||
uint32_t UNUSED0 : 6; //!<[24]
|
||||
__I SKEW_ENUM SKEW : 1; //!<[30] Clock Skew
|
||||
__I NOREF_ENUM NOREF : 1; //!<[31] No Ref
|
||||
} B;
|
||||
__IO uint32_t R;
|
||||
|
||||
explicit CALIB_DEF () noexcept { R = 0x00000000u; }
|
||||
template<typename F> void setbit (F f) volatile {
|
||||
CALIB_DEF r;
|
||||
R = f (r);
|
||||
}
|
||||
|
||||
template<typename F> void modify (F f) volatile {
|
||||
CALIB_DEF r; r.R = R;
|
||||
R = f (r);
|
||||
}
|
||||
};
|
||||
__IO CALIB_DEF CALIB ; //!< register definition
|
||||
// methods :
|
||||
bool Config (const uint32_t ticks) {
|
||||
if (ticks > SysTick_LOAD_RELOAD_Msk) return false; // Reload value impossible
|
||||
RVR.B.RELOAD = ticks - 1u; // set reload register
|
||||
NVIC_EnableIRQ (SysTick_IRQn); // Enable Interrupt
|
||||
CVR.B.CURRENT = 0; // Load the SysTick Counter Value
|
||||
CSR.modify([](CSR_DEF & r) -> auto { // Enable SysTick IRQ and SysTick Timer
|
||||
r.B.CLKSOURCE = CSR_DEF::CLKSOURCE_ENUM::CLKSOURCE_1;
|
||||
r.B.TICKINT = CSR_DEF::TICKINT_ENUM ::TICKINT_1;
|
||||
r.B.ENABLE = CSR_DEF::ENABLE_ENUM ::ENABLE_1;
|
||||
return r.R;
|
||||
});
|
||||
return true; // Function successful
|
||||
}
|
||||
|
||||
}; /* total size = 0x0010, struct size = 0x0010 */
|
||||
static SysTick_DEF & SysTick = * reinterpret_cast<SysTick_DEF *> (0xe000e010);
|
||||
|
||||
static_assert (sizeof(struct SysTick_DEF) == 16, "size error SysTick");
|
||||
|
||||
#endif
|
10621
math/stm32f051/STM32F0x1.h
Normal file
10621
math/stm32f051/STM32F0x1.h
Normal file
File diff suppressed because it is too large
Load diff
21
math/stm32f051/gcc.mk
Normal file
21
math/stm32f051/gcc.mk
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Use gcc / binutils toolchain
|
||||
PREFIX = arm-none-eabi-
|
||||
CC = $(PREFIX)gcc
|
||||
CXX = $(PREFIX)g++
|
||||
# linker je ld
|
||||
LD = $(PREFIX)g++
|
||||
SIZE = $(PREFIX)size
|
||||
DUMP = $(PREFIX)objdump
|
||||
COPY = $(PREFIX)objcopy
|
||||
CFLAGS+= -Os -flto
|
||||
|
||||
CCPU = -mcpu=cortex-m0
|
||||
MCPU = -mthumb $(CCPU)
|
||||
CFLAGS+= $(MCPU)
|
||||
LFLAGS+= $(MCPU)
|
||||
LFLAGS+= -Wl,--Map=$(@:%.elf=%.map),--gc-sections
|
||||
LFLAGS+= -nostartfiles -flto -O3
|
||||
LDLIBS+= -L./stm32f051 -T script.ld
|
||||
OBJS += startup.o system.o gpio.o
|
||||
|
||||
|
26
math/stm32f051/gpio.cpp
Normal file
26
math/stm32f051/gpio.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
#include "gpio.h"
|
||||
|
||||
static constexpr uint32_t RCC_AHBENR_GPIOAEN = 1u << 17; /*!< GPIOA clock enable */
|
||||
static constexpr uint32_t RCC_AHBENR_GPIOBEN = 1u << 18; /*!< GPIOB clock enable */
|
||||
static constexpr uint32_t RCC_AHBENR_GPIOCEN = 1u << 19; /*!< GPIOC clock enable */
|
||||
static constexpr uint32_t RCC_AHBENR_GPIODEN = 1u << 20; /*!< GPIOD clock enable */
|
||||
static constexpr uint32_t RCC_AHBENR_GPIOFEN = 1u << 22; /*!< GPIOF clock enable */
|
||||
|
||||
static const GpioAssocPort cPortTab[] = {
|
||||
{&GPIOA, RCC_AHBENR_GPIOAEN},
|
||||
{&GPIOB, RCC_AHBENR_GPIOBEN},
|
||||
{&GPIOC, RCC_AHBENR_GPIOCEN},
|
||||
{&GPIOD, RCC_AHBENR_GPIODEN},
|
||||
{&GPIOF, RCC_AHBENR_GPIOFEN},
|
||||
};
|
||||
GpioClass::GpioClass (GpioPortNum const port, const uint32_t no, const GPIOMode_TypeDef type) noexcept :
|
||||
io(cPortTab[port].portAdr), pos(1UL << no), num(no) {
|
||||
// Povol hodiny
|
||||
RCC.AHBENR.R |= cPortTab[port].clkMask;
|
||||
// A nastav pin (pořadí dle ST knihovny).
|
||||
setSpeed (GPIO_Speed_Level_3);
|
||||
setOType (GPIO_OType_PP);
|
||||
setMode (type);
|
||||
setPuPd (GPIO_PuPd_NOPULL);
|
||||
}
|
||||
|
142
math/stm32f051/gpio.h
Normal file
142
math/stm32f051/gpio.h
Normal file
|
@ -0,0 +1,142 @@
|
|||
#ifndef GPIO_H
|
||||
#define GPIO_H
|
||||
|
||||
#include "STM32F0x1.h"
|
||||
|
||||
/**
|
||||
* @brief General Purpose IO
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
GPIO_Mode_IN = 0x00, /*!< GPIO Input Mode */
|
||||
GPIO_Mode_OUT = 0x01, /*!< GPIO Output Mode */
|
||||
GPIO_Mode_AF = 0x02, /*!< GPIO Alternate function Mode */
|
||||
GPIO_Mode_AN = 0x03 /*!< GPIO Analog In/Out Mode */
|
||||
} GPIOMode_TypeDef;
|
||||
|
||||
typedef enum {
|
||||
GPIO_OType_PP = 0x00,
|
||||
GPIO_OType_OD = 0x01
|
||||
} GPIOOType_TypeDef;
|
||||
|
||||
typedef enum {
|
||||
GPIO_Speed_Level_1 = 0x01, /*!< Medium Speed */
|
||||
GPIO_Speed_Level_2 = 0x02, /*!< Fast Speed */
|
||||
GPIO_Speed_Level_3 = 0x03 /*!< High Speed */
|
||||
} GPIOSpeed_TypeDef;
|
||||
|
||||
typedef enum {
|
||||
GPIO_PuPd_NOPULL = 0x00,
|
||||
GPIO_PuPd_UP = 0x01,
|
||||
GPIO_PuPd_DOWN = 0x02
|
||||
} GPIOPuPd_TypeDef;
|
||||
/// Enum pro PortNumber
|
||||
typedef enum {
|
||||
GpioPortA,
|
||||
GpioPortB,
|
||||
GpioPortC,
|
||||
GpioPortD,
|
||||
GpioPortF
|
||||
} GpioPortNum;
|
||||
/// Asociace port Adress a RCC clock
|
||||
struct GpioAssocPort {
|
||||
GPIOF_Type * const portAdr;
|
||||
const uint32_t clkMask;
|
||||
};
|
||||
/** @file
|
||||
* @brief Obecný GPIO pin.
|
||||
*
|
||||
* @class GpioClass
|
||||
* @brief Obecný GPIO pin.
|
||||
*
|
||||
* Ukázka přetížení operátorů. Návratové hodnoty jsou v tomto případě celkem zbytečné,
|
||||
* ale umožňují řetězení, takže je možné napsat např.
|
||||
@code
|
||||
+-+-+-led;
|
||||
@endcode
|
||||
* a máme na led 3 pulsy. Je to sice blbost, ale funguje.
|
||||
* Všechny metody jsou konstantní, protože nemění data uvnitř třídy.
|
||||
* Vlastně ani nemohou, protože data jsou konstantní.
|
||||
*/
|
||||
class GpioClass {
|
||||
public:
|
||||
/** Konstruktor
|
||||
@param port GpioPortA | GpioPortB | GpioPortC | GpioPortD | GpioPortF
|
||||
@param no číslo pinu na portu
|
||||
@param type IN, OUT, AF, AN default OUT
|
||||
*/
|
||||
explicit GpioClass (GpioPortNum const port, const uint32_t no, const GPIOMode_TypeDef type = GPIO_Mode_OUT) noexcept;
|
||||
/// Nastav pin @param b na tuto hodnotu
|
||||
const GpioClass& operator<< (const bool b) const {
|
||||
if (b) io->BSRR.R = pos;
|
||||
else io->BRR.R = pos;
|
||||
return *this;
|
||||
}
|
||||
//![Gpio example]
|
||||
/// Nastav pin na log. H
|
||||
const GpioClass& operator+ (void) const {
|
||||
io->BSRR.R = (uint32_t) pos;
|
||||
return *this;
|
||||
}
|
||||
/// Nastav pin na log. L
|
||||
const GpioClass& operator- (void) const {
|
||||
io->BRR.R = pos;
|
||||
return *this;
|
||||
}
|
||||
/// Změň hodnotu pinu
|
||||
const GpioClass& operator~ (void) const {
|
||||
io->ODR.R ^= pos;
|
||||
return *this;
|
||||
};
|
||||
/// Načti logickou hodnotu na pinu
|
||||
const bool get (void) const {
|
||||
if (io->IDR.R & pos) return true;
|
||||
else return false;
|
||||
};
|
||||
/// A to samé jako operátor
|
||||
const GpioClass& operator>> (bool& b) const {
|
||||
b = get();
|
||||
return *this;
|
||||
}
|
||||
//![Gpio example]
|
||||
void setMode (GPIOMode_TypeDef p) {
|
||||
uint32_t dno = num * 2;
|
||||
io->MODER.R &= ~(3UL << dno);
|
||||
io->MODER.R |= (p << dno);
|
||||
}
|
||||
void setOType (GPIOOType_TypeDef p) {
|
||||
io->OTYPER.R &= (uint16_t)~(1UL << num);
|
||||
io->OTYPER.R |= (uint16_t) (p << num);
|
||||
}
|
||||
void setSpeed (GPIOSpeed_TypeDef p) {
|
||||
uint32_t dno = num * 2;
|
||||
io->OSPEEDR.R &= ~(3UL << dno);
|
||||
io->OSPEEDR.R |= (p << dno);
|
||||
}
|
||||
void setPuPd (GPIOPuPd_TypeDef p) {
|
||||
uint32_t dno = num * 2;
|
||||
io->PUPDR.R &= ~(3UL << dno);
|
||||
io->PUPDR.R |= (p << dno);
|
||||
}
|
||||
void setAF (unsigned af) {
|
||||
unsigned int pd,pn = num;
|
||||
pd = (pn & 7) << 2; pn >>= 3;
|
||||
if (pn) {
|
||||
io->AFRH.R &= ~(0xFU << pd);
|
||||
io->AFRH.R |= ( af << pd);
|
||||
} else {
|
||||
io->AFRL.R &= ~(0xFU << pd);
|
||||
io->AFRL.R |= ( af << pd);
|
||||
}
|
||||
}
|
||||
private:
|
||||
/// Port.
|
||||
GPIOF_Type * const io;
|
||||
/// A pozice pinu na něm, stačí 16.bit
|
||||
const uint16_t pos;
|
||||
/// pro funkce setXXX necháme i číslo pinu
|
||||
const uint16_t num;
|
||||
|
||||
};
|
||||
|
||||
#endif // GPIO_H
|
94
math/stm32f051/script.ld
Normal file
94
math/stm32f051/script.ld
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
*/
|
||||
|
||||
/* Entry Point */
|
||||
ENTRY(Vectors)
|
||||
|
||||
/* Generate a link error if heap and stack don't fit into RAM */
|
||||
_Min_Heap_Size = 0; /* required amount of heap */
|
||||
_Min_Stack_Size = 0; /* required amount of stack */
|
||||
|
||||
/* Specify the memory areas */
|
||||
MEMORY {
|
||||
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 8K
|
||||
}
|
||||
|
||||
/* Highest address of the user mode stack */
|
||||
_estack = ORIGIN(RAM) + LENGTH(RAM);
|
||||
|
||||
/* Define output sections */
|
||||
SECTIONS {
|
||||
/* The startup code goes first into FLASH */
|
||||
/* The program code and other data goes into FLASH */
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
KEEP(*(.isr_vector)) /* Startup code */
|
||||
. = ALIGN(4);
|
||||
*(.text) /* .text sections (code) */
|
||||
*(.text*) /* .text* sections (code) */
|
||||
*(.rodata) /* .rodata sections (constants, strings, etc.) */
|
||||
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
||||
*(.glue_7) /* glue arm to thumb code */
|
||||
*(.glue_7t) /* glue thumb to arm code */
|
||||
*(.eh_frame)
|
||||
|
||||
*(.init)
|
||||
*(.fini)
|
||||
/* Pro použití statických konstruktorů v C++, KEEP musí být použit při gc */
|
||||
. = ALIGN(4);
|
||||
PROVIDE_HIDDEN (__init_array_start = .);
|
||||
KEEP (*(.ctors)) /* for clang */
|
||||
KEEP (*(.init_array*))
|
||||
PROVIDE_HIDDEN (__init_array_end = .);
|
||||
|
||||
. = ALIGN(4);
|
||||
_etext = .; /* define a global symbols at end of code */
|
||||
} >FLASH
|
||||
|
||||
/* used by the startup to initialize data */
|
||||
_sidata = .;
|
||||
|
||||
/* Initialized data sections goes into RAM, load LMA copy after code */
|
||||
.data : AT ( _sidata )
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sdata = .; /* create a global symbol at data start */
|
||||
*(.data) /* .data sections */
|
||||
*(.data*) /* .data* sections */
|
||||
|
||||
. = ALIGN(4);
|
||||
_edata = .; /* define a global symbol at data end */
|
||||
} >RAM
|
||||
|
||||
/* Uninitialized data section */
|
||||
. = ALIGN(4);
|
||||
.bss :
|
||||
{
|
||||
/* This is used by the startup in order to initialize the .bss secion */
|
||||
_sbss = .; /* define a global symbol at bss start */
|
||||
__bss_start__ = _sbss;
|
||||
*(.bss)
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
_ebss = .;
|
||||
__bss_end__ = _ebss;
|
||||
} >RAM
|
||||
|
||||
_end = .;
|
||||
/* Remove information from the standard libraries */
|
||||
/DISCARD/ :
|
||||
{
|
||||
/*
|
||||
libc.a ( * )
|
||||
libm.a ( * )
|
||||
libgcc.a ( * )
|
||||
*(.debug*)
|
||||
*/
|
||||
*(.comment*)
|
||||
*(.ARM.*)
|
||||
}
|
||||
|
||||
.ARM.attributes 0 : { *(.ARM.attributes) }
|
||||
}
|
163
math/stm32f051/startup.c
Normal file
163
math/stm32f051/startup.c
Normal file
|
@ -0,0 +1,163 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
//! [InitStaticConstructors]
|
||||
extern void (*__init_array_start)(); // definováno v linker skriptu
|
||||
extern void (*__init_array_end) (); // definováno v linker skriptu
|
||||
void static_init() {
|
||||
void (**p)();
|
||||
for (p = &__init_array_start; p < &__init_array_end; p++) (*p)();
|
||||
}
|
||||
//! [InitStaticConstructors]
|
||||
#define WEAK __attribute__ ((weak))
|
||||
#define ALIAS(f) __attribute__ ((weak, alias (#f)))
|
||||
|
||||
extern unsigned int _estack;
|
||||
extern unsigned int _sidata;
|
||||
extern unsigned int _sdata;
|
||||
extern unsigned int _edata;
|
||||
extern unsigned int _sbss;
|
||||
extern unsigned int _ebss;
|
||||
|
||||
WEAK void Reset_Handler (void);
|
||||
WEAK void DefaultHandler (void);
|
||||
void NonMaskableInt_Handler (void) ALIAS(Default_Handler);
|
||||
void HardFault_Handler (void) ALIAS(Default_Handler);
|
||||
void MemoryManagement_Handler (void) ALIAS(Default_Handler);
|
||||
void BusFault_Handler (void) ALIAS(Default_Handler);
|
||||
void UsageFault_Handler (void) ALIAS(Default_Handler);
|
||||
void SVCall_Handler (void) ALIAS(Default_Handler);
|
||||
void DebugMonitor_Handler (void) ALIAS(Default_Handler);
|
||||
void PendSV_Handler (void) ALIAS(Default_Handler);
|
||||
void SysTick_Handler (void) ALIAS(Default_Handler);
|
||||
|
||||
void WWDG_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void PVD_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void RTC_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void FLASH_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void RCC_CRS_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void EXTI0_1_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void EXTI2_3_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void EXTI4_15_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void TSC_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void DMA1_CH1_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void DMA1_CH2_3_DMA2_CH1_2_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void DMA1_CH4_5_6_7_DMA2_CH3_4_5_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void ADC_COMP_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void TIM1_BRK_UP_TRG_COM_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void TIM1_CC_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void TIM2_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void TIM3_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void TIM6_DAC_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void TIM7_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void TIM14_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void TIM15_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void TIM16_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void TIM17_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void I2C1_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void I2C2_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void SPI1_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void SPI2_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void USART1_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void USART2_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void USART3_4_5_6_7_8_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void CEC_CAN_IRQHandler (void) ALIAS(Default_Handler);
|
||||
void USB_IRQHandler (void) ALIAS(Default_Handler);
|
||||
|
||||
extern int main (void);
|
||||
extern void SystemInit (void);
|
||||
extern void SystemCoreClockUpdate (void);
|
||||
|
||||
#if defined (__cplusplus)
|
||||
}; // extern "C"
|
||||
#endif
|
||||
typedef void (*handler) (void);
|
||||
__attribute__ ((section(".isr_vector")))
|
||||
handler Vectors[] = {
|
||||
(handler) &_estack,
|
||||
Reset_Handler,
|
||||
NonMaskableInt_Handler,
|
||||
HardFault_Handler,
|
||||
MemoryManagement_Handler,
|
||||
BusFault_Handler,
|
||||
UsageFault_Handler,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
SVCall_Handler,
|
||||
DebugMonitor_Handler,
|
||||
0,
|
||||
PendSV_Handler,
|
||||
SysTick_Handler,
|
||||
|
||||
WWDG_IRQHandler,
|
||||
PVD_IRQHandler,
|
||||
RTC_IRQHandler,
|
||||
FLASH_IRQHandler,
|
||||
RCC_CRS_IRQHandler,
|
||||
EXTI0_1_IRQHandler,
|
||||
EXTI2_3_IRQHandler,
|
||||
EXTI4_15_IRQHandler,
|
||||
TSC_IRQHandler,
|
||||
DMA1_CH1_IRQHandler,
|
||||
DMA1_CH2_3_DMA2_CH1_2_IRQHandler,
|
||||
DMA1_CH4_5_6_7_DMA2_CH3_4_5_IRQHandler,
|
||||
ADC_COMP_IRQHandler,
|
||||
TIM1_BRK_UP_TRG_COM_IRQHandler,
|
||||
TIM1_CC_IRQHandler,
|
||||
TIM2_IRQHandler,
|
||||
TIM3_IRQHandler,
|
||||
TIM6_DAC_IRQHandler,
|
||||
TIM7_IRQHandler,
|
||||
TIM14_IRQHandler,
|
||||
TIM15_IRQHandler,
|
||||
TIM16_IRQHandler,
|
||||
TIM17_IRQHandler,
|
||||
I2C1_IRQHandler,
|
||||
I2C2_IRQHandler,
|
||||
SPI1_IRQHandler,
|
||||
SPI2_IRQHandler,
|
||||
USART1_IRQHandler,
|
||||
USART2_IRQHandler,
|
||||
USART3_4_5_6_7_8_IRQHandler,
|
||||
CEC_CAN_IRQHandler,
|
||||
USB_IRQHandler,
|
||||
|
||||
};
|
||||
static inline void fillStack (void) {
|
||||
register unsigned int *dst, *end;
|
||||
dst = &_ebss;
|
||||
end = &_estack;
|
||||
while (dst < end) *dst++ = 0xDEADBEEFU;
|
||||
}
|
||||
void Reset_Handler(void) {
|
||||
|
||||
fillStack();
|
||||
|
||||
register unsigned int *src, *dst, *end;
|
||||
/* Zero fill the bss section */
|
||||
dst = &_sbss;
|
||||
end = &_ebss;
|
||||
while (dst < end) *dst++ = 0U;
|
||||
|
||||
/* Copy data section from flash to RAM */
|
||||
src = &_sidata;
|
||||
dst = &_sdata;
|
||||
end = &_edata;
|
||||
while (dst < end) *dst++ = *src++;
|
||||
|
||||
SystemInit();
|
||||
SystemCoreClockUpdate(); // Potřebné pro USART
|
||||
static_init(); // Zde zavolám globální konstruktory
|
||||
|
||||
main();
|
||||
|
||||
for (;;);
|
||||
}
|
||||
|
||||
void Default_Handler (void) {
|
||||
asm volatile ("bkpt 1");
|
||||
}
|
232
math/stm32f051/system.cpp
Normal file
232
math/stm32f051/system.cpp
Normal file
|
@ -0,0 +1,232 @@
|
|||
#include "STM32F0x1.h"
|
||||
#include "system.h"
|
||||
|
||||
#if !defined (HSE_VALUE)
|
||||
#define HSE_VALUE ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */
|
||||
#endif /* HSE_VALUE */
|
||||
#if !defined (HSI_VALUE)
|
||||
#define HSI_VALUE ((uint32_t)8000000) /*!< Value of the Internal High Speed oscillator in Hz. */
|
||||
#endif /* HSI_VALUE */
|
||||
|
||||
#define HSE_STARTUP_TIMEOUT ((uint16_t)0x5000) /*!< Time out for HSE start up */
|
||||
//! [EnumExampleSW_EN_Def]
|
||||
typedef enum {
|
||||
USEHSI = 0, USEHSE, USEPLL
|
||||
} SW_EN;
|
||||
//! [EnumExampleSW_EN_Def]
|
||||
typedef enum {
|
||||
RCC_CFGR_PLLMUL2 = 0,
|
||||
RCC_CFGR_PLLMUL3,
|
||||
RCC_CFGR_PLLMUL4,
|
||||
RCC_CFGR_PLLMUL5,
|
||||
RCC_CFGR_PLLMUL6,
|
||||
RCC_CFGR_PLLMUL7,
|
||||
RCC_CFGR_PLLMUL8,
|
||||
RCC_CFGR_PLLMUL9,
|
||||
RCC_CFGR_PLLMUL10,
|
||||
RCC_CFGR_PLLMUL11,
|
||||
RCC_CFGR_PLLMUL12,
|
||||
RCC_CFGR_PLLMUL13,
|
||||
RCC_CFGR_PLLMUL14,
|
||||
RCC_CFGR_PLLMUL15,
|
||||
RCC_CFGR_PLLMUL16,
|
||||
} PLLML_EN;
|
||||
|
||||
/* Select the PLL clock source */
|
||||
//#define PLL_SOURCE_HSI // HSI (~8MHz) used to clock the PLL, and the PLL is used as system clock source
|
||||
#define PLL_SOURCE_HSE // HSE (8MHz) used to clock the PLL, and the PLL is used as system clock source
|
||||
//#define PLL_SOURCE_HSE_BYPASS // HSE bypassed with an external clock (8MHz, coming from ST-Link) used to clock
|
||||
// the PLL, and the PLL is used as system clock source
|
||||
|
||||
uint32_t SystemCoreClock = 48000000;
|
||||
const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
|
||||
|
||||
|
||||
static void SetSysClock (void);
|
||||
|
||||
/**
|
||||
* @brief Setup the microcontroller system.
|
||||
* Initialize the Embedded Flash Interface, the PLL and update the
|
||||
* SystemCoreClock variable.
|
||||
* @param None
|
||||
* @retval None
|
||||
*/
|
||||
extern "C"
|
||||
void SystemInit (void) {
|
||||
/* Set HSION bit */
|
||||
RCC.CR.R |= (uint32_t) 0x00000001;
|
||||
/* Reset SW[1:0], HPRE[3:0], PPRE[2:0], ADCPRE and MCOSEL[2:0] bits */
|
||||
RCC.CFGR.R &= (uint32_t) 0xF8FFB80C;
|
||||
/* Reset HSEON, CSSON and PLLON bits */
|
||||
RCC.CR.R &= (uint32_t) 0xFEF6FFFF;
|
||||
/* Reset HSEBYP bit */
|
||||
RCC.CR.R &= (uint32_t) 0xFFFBFFFF;
|
||||
/* Reset PLLSRC, PLLXTPRE and PLLMUL[3:0] bits */
|
||||
RCC.CFGR.R &= (uint32_t) 0xFFC0FFFF;
|
||||
/* Reset PREDIV1[3:0] bits */
|
||||
RCC.CFGR2.R &= (uint32_t) 0xFFFFFFF0;
|
||||
/* Reset USARTSW[1:0], I2CSW, CECSW and ADCSW bits */
|
||||
RCC.CFGR3.R &= (uint32_t) 0xFFFFFEAC;
|
||||
/* Reset HSI14 bit */
|
||||
RCC.CR2.R &= (uint32_t) 0xFFFFFFFE;
|
||||
/* Disable all interrupts */
|
||||
RCC.CIR.R = 0x00000000u;
|
||||
/* Configure the System clock frequency, AHB/APBx prescalers and Flash settings */
|
||||
SetSysClock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update SystemCoreClock according to Clock Register Values
|
||||
* The SystemCoreClock variable contains the core clock (HCLK), it can
|
||||
* be used by the user application to setup the SysTick timer or configure
|
||||
* other parameters.
|
||||
*
|
||||
* @note Each time the core clock (HCLK) changes, this function must be called
|
||||
* to update SystemCoreClock variable value. Otherwise, any configuration
|
||||
* based on this variable will be incorrect.
|
||||
*
|
||||
* @note - The system frequency computed by this function is not the real
|
||||
* frequency in the chip. It is calculated based on the predefined
|
||||
* constant and the selected clock source:
|
||||
*
|
||||
* - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*)
|
||||
*
|
||||
* - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**)
|
||||
*
|
||||
* - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**)
|
||||
* or HSI_VALUE(*) multiplied/divided by the PLL factors.
|
||||
*
|
||||
* (*) HSI_VALUE is a constant defined in stm32f0xx.h file (default value
|
||||
* 8 MHz) but the real value may vary depending on the variations
|
||||
* in voltage and temperature.
|
||||
*
|
||||
* (**) HSE_VALUE is a constant defined in stm32f0xx.h file (default value
|
||||
* 8 MHz), user has to ensure that HSE_VALUE is same as the real
|
||||
* frequency of the crystal used. Otherwise, this function may
|
||||
* have wrong result.
|
||||
*
|
||||
* - The result of this function could be not correct when using fractional
|
||||
* value for HSE crystal.
|
||||
* @param None
|
||||
* @retval None
|
||||
*/
|
||||
extern "C"
|
||||
void SystemCoreClockUpdate (void) {
|
||||
uint32_t prediv1factor, pllmull;
|
||||
|
||||
//! [EnumExampleSW_EN_Use]
|
||||
switch (RCC.CFGR.B.SWS) {
|
||||
case USEHSI: /* HSI used as system clock */
|
||||
SystemCoreClock = HSI_VALUE;
|
||||
break;
|
||||
case USEHSE: /* HSE used as system clock */
|
||||
SystemCoreClock = HSE_VALUE;
|
||||
break;
|
||||
case USEPLL: /* PLL used as system clock */
|
||||
/* Get PLL clock source and multiplication factor */
|
||||
pllmull = RCC.CFGR.B.PLLMUL + 2u;
|
||||
// ...
|
||||
//! [EnumExampleSW_EN_Use]
|
||||
if (RCC.CFGR.B.PLLSRC == RESET) {
|
||||
/* HSI oscillator clock divided by 2 selected as PLL clock entry */
|
||||
SystemCoreClock = (HSI_VALUE >> 1) * pllmull;
|
||||
} else {
|
||||
prediv1factor = RCC.CFGR2.B.PREDIV + 1;
|
||||
/* HSE oscillator clock selected as PREDIV1 clock entry */
|
||||
SystemCoreClock = (HSE_VALUE / prediv1factor) * pllmull;
|
||||
}
|
||||
break;
|
||||
default: /* HSI used as system clock */
|
||||
SystemCoreClock = HSI_VALUE;
|
||||
break;
|
||||
}
|
||||
/* Compute HCLK clock frequency */
|
||||
/* Get HCLK prescaler */
|
||||
pllmull = AHBPrescTable[RCC.CFGR.B.HPRE];
|
||||
/* HCLK clock frequency */
|
||||
SystemCoreClock >>= pllmull;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures the System clock frequency, AHB/APBx prescalers and Flash
|
||||
* settings.
|
||||
* @note This function should be called only once the RCC clock configuration
|
||||
* is reset to the default reset state (done in SystemInit() function).
|
||||
* @param None
|
||||
* @retval None
|
||||
*/
|
||||
|
||||
static void SetSysClock (void) {
|
||||
/* SYSCLK, HCLK, PCLK configuration */
|
||||
#if defined (PLL_SOURCE_HSI)
|
||||
/* At this stage the HSI is already enabled */
|
||||
/* Enable Prefetch Buffer and set Flash Latency */
|
||||
Flash.ACR.setbit([] (auto & r) -> auto { // C++14
|
||||
r.B.PRFTBE = SET;
|
||||
r.B.LATENCY = SET;
|
||||
return r.R;
|
||||
});
|
||||
RCC.CFGR.modify([] (auto & r) -> auto {
|
||||
r.B.HPRE = 0;
|
||||
r.B.PPRE = 0;
|
||||
r.B.PLLSRC = RESET;
|
||||
r.B.PLLXTPRE = RESET;
|
||||
r.B.PLLMUL = RCC_CFGR_PLLMUL12;
|
||||
return r.R;
|
||||
});
|
||||
/* Enable PLL */
|
||||
RCC.CR.B.PLLON = SET;
|
||||
/* Wait till PLL is ready */
|
||||
while ((RCC.CR.B.PLLRDY) == RESET);
|
||||
/* Select PLL as system clock source */
|
||||
RCC.CFGR.B.SW = USEPLL;
|
||||
/* Wait till PLL is used as system clock source */
|
||||
while (RCC.CFGR.B.SWS != USEPLL);
|
||||
#else
|
||||
#if defined (PLL_SOURCE_HSE)
|
||||
/* Enable HSE */
|
||||
RCC.CR.B.HSEON = SET;
|
||||
#elif defined (PLL_SOURCE_HSE_BYPASS)
|
||||
/* HSE oscillator bypassed with external clock */
|
||||
RCC.CR.B.HSEON = SET;
|
||||
RCC.CR.B.HSEBYP = SET;
|
||||
#endif /* PLL_SOURCE_HSE */
|
||||
__IO uint32_t StartUpCounter = 0;
|
||||
__IO uint32_t HSEStatus;
|
||||
/* Wait till HSE is ready and if Time out is reached exit */
|
||||
do {
|
||||
HSEStatus = RCC.CR.B.HSERDY;
|
||||
StartUpCounter++;
|
||||
} while ((HSEStatus == RESET) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
|
||||
|
||||
HSEStatus = RCC.CR.B.HSERDY;
|
||||
|
||||
if (HSEStatus == SET) {
|
||||
/* Enable Prefetch Buffer and set Flash Latency */
|
||||
Flash.ACR.setbit([] (auto & r) -> uint32_t {
|
||||
r.B.PRFTBE = SET;
|
||||
r.B.LATENCY = SET;
|
||||
return r.R;
|
||||
});
|
||||
RCC.CFGR.modify([] (auto & r) -> uint32_t {
|
||||
r.B.HPRE = 0;
|
||||
r.B.PPRE = 0;
|
||||
r.B.PLLSRC = SET;
|
||||
r.B.PLLXTPRE = RESET;
|
||||
r.B.PLLMUL = RCC_CFGR_PLLMUL12;
|
||||
return r.R;
|
||||
});
|
||||
/* Enable PLL */
|
||||
RCC.CR.B.PLLON = SET;
|
||||
/* Wait till PLL is ready */
|
||||
while ((RCC.CR.B.PLLRDY) == RESET);
|
||||
/* Select PLL as system clock source */
|
||||
RCC.CFGR.B.SW = USEPLL;
|
||||
/* Wait till PLL is used as system clock source */
|
||||
while (RCC.CFGR.B.SWS != USEPLL);
|
||||
} else {
|
||||
/* If HSE fails to start-up, the application will have wrong clock
|
||||
configuration. User can add here some code to deal with this error */
|
||||
}
|
||||
#endif /* PLL_SOURCE_HSI */
|
||||
}
|
17
math/stm32f051/system.h
Normal file
17
math/stm32f051/system.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#ifndef __SYSTEM_STM32F0XX_H
|
||||
#define __SYSTEM_STM32F0XX_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern uint32_t SystemCoreClock; /*!< System Clock Frequency (Core Clock) */
|
||||
extern void SystemInit(void);
|
||||
extern void SystemCoreClockUpdate(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*__SYSTEM_STM32F0XX_H */
|
||||
|
115
math/stm32f051/usart.cpp
Normal file
115
math/stm32f051/usart.cpp
Normal file
|
@ -0,0 +1,115 @@
|
|||
#include "STM32F0x1.h"
|
||||
#include "CortexM0.h" // NVIC_EnableIRQ
|
||||
#include "gpio.h"
|
||||
#include "usart.h"
|
||||
|
||||
extern "C" uint32_t SystemCoreClock;
|
||||
static Usart * Instance = nullptr;
|
||||
|
||||
void Usart::irq (void) {
|
||||
volatile USART1_Type::ISR_DEF status (USART1.ISR); // načti status přerušení
|
||||
char tdata;
|
||||
volatile char rdata;
|
||||
if (status.B.TC) { // od vysílače
|
||||
if (tx_ring.Read (tdata)) { // pokud máme data
|
||||
USART1.TDR.R = (uint32_t) tdata & 0xFFu;// zapíšeme do výstupu
|
||||
} else { // pokud ne
|
||||
//USART1.CR1.B.RE = SET; // povol prijem
|
||||
USART1.CR1.B.TCIE = RESET; // je nutné zakázat přerušení od vysílače
|
||||
}
|
||||
}
|
||||
if (status.B.RXNE) { // od přijímače
|
||||
rdata = (USART1.RDR.R) & 0xFFu; // načteme data
|
||||
(void) rdata; // zahodime
|
||||
}
|
||||
}
|
||||
/// Voláno z čistého C - startup.c
|
||||
extern "C" void USART1_IRQHandler (void) {
|
||||
if (Instance) Instance->irq();
|
||||
};
|
||||
//! [MembersConstructorExample]
|
||||
Usart::Usart(const uint32_t baud) noexcept : BaseLayer(), tx_ring() {
|
||||
//! [MembersConstructorExample]
|
||||
if (Instance) return; // Chyba - jedina instance
|
||||
Instance = this;
|
||||
// 1. Clock Enable
|
||||
RCC.APB2ENR.B.USART1EN = SET;
|
||||
// 2. GPIO Alternate Config
|
||||
GpioClass txp (GpioPortA, 9, GPIO_Mode_AF);
|
||||
GpioClass rxp (GpioPortA, 10, GPIO_Mode_AF);
|
||||
txp.setAF (1);
|
||||
rxp.setAF (1);
|
||||
// 4. NVIC
|
||||
NVIC_EnableIRQ (USART1_IRQn);
|
||||
uint32_t tmp = 0;
|
||||
// 5. USART registry 8.bit bez parity
|
||||
USART1.CR1.modify([] (USART1_Type::CR1_DEF & r) -> uint32_t { // pro ilustraci, co by bylo auto
|
||||
r.B.TE = SET;
|
||||
//r.B.RE = SET; // příjem je zde zbytečný
|
||||
//r.B.RXNEIE = SET;
|
||||
return r.R;
|
||||
});
|
||||
USART1.CR2.R = 0;
|
||||
USART1.CR3.B.OVRDIS = SET;
|
||||
// Tuhle část už vezmeme přímo z knihovny, jen ty hodiny zjednodušíme na SystemCoreClock
|
||||
uint32_t apbclock = SystemCoreClock;
|
||||
uint32_t integerdivider, fractionaldivider;
|
||||
/* Determine the integer part */
|
||||
if (USART1.CR1.B.OVER8 != RESET) {
|
||||
/* Integer part computing in case Oversampling mode is 8 Samples */
|
||||
integerdivider = ((25u * apbclock) / (2u * (baud)));
|
||||
} else {
|
||||
/* Integer part computing in case Oversampling mode is 16 Samples */
|
||||
integerdivider = ((25u * apbclock) / (4u * (baud)));
|
||||
}
|
||||
tmp = (integerdivider / 100u) << 4;
|
||||
/* Determine the fractional part */
|
||||
fractionaldivider = integerdivider - (100u * (tmp >> 4));
|
||||
/* Implement the fractional part in the register */
|
||||
if (USART1.CR1.B.OVER8 != RESET) {
|
||||
tmp |= ((((fractionaldivider * 8u ) + 50u) / 100u)) & ((uint8_t)0x07u);
|
||||
} else {
|
||||
tmp |= ((((fractionaldivider * 16u) + 50u) / 100u)) & ((uint8_t)0x0Fu);
|
||||
}
|
||||
/* Write to USART BRR */
|
||||
USART1.BRR.R = (uint16_t)tmp;
|
||||
USART1.CR1.B.UE = SET; // nakonec povolit globálně
|
||||
}
|
||||
//! [VirtualMethodBottom]
|
||||
uint32_t Usart::Down (const char * data, const uint32_t len) {
|
||||
uint32_t res; // výsledek, musí žít i po ukončení smyčky
|
||||
for (res=0; res<len; res++) if (!tx_ring.Write(data[res])) break;
|
||||
USART1.CR1.B.TCIE = SET; // po povolení přerušení okamžitě přeruší
|
||||
return res;
|
||||
}
|
||||
//! [VirtualMethodBottom]
|
||||
void Usart::SetHalfDuplex (const bool on) const {
|
||||
USART1.CR1.B.UE = RESET; // zakázat, jinak nelze nastavovat
|
||||
if (on) USART1.CR3.B.HDSEL = SET; // poloduplex on
|
||||
else USART1.CR3.B.HDSEL = RESET; // poloduplex off
|
||||
USART1.CR1.B.UE = SET; // nakonec povolit globálně
|
||||
}
|
||||
void Usart::SetRS485 (const bool polarity) const {
|
||||
USART1.CR1.B.UE = RESET; // zakázat, jinak nelze nastavovat
|
||||
// Nastavit pin DE (RTS)
|
||||
GpioClass de (GpioPortA, 12u, GPIO_Mode_AF);
|
||||
de.setAF (1u);
|
||||
// Nastavení driveru
|
||||
USART1.CR3.B.DEM = SET; // povolit DE v USARTu
|
||||
if (polarity) USART1.CR3.B.DEP = SET;
|
||||
else USART1.CR3.B.DEP = RESET;
|
||||
// A nakonec doby vybavení (přesah) - to je hodně užitečné
|
||||
//! [LambdaExampleUsage]
|
||||
USART1.CR1.modify([] (auto & r) -> auto {
|
||||
r.B.DEAT = 1u; // doba vybavení před start bitem - 16 ~= 1 bit, 0..31
|
||||
r.B.DEDT = 1u; // doba vybavení po stop bitu - 16 ~= 1 bit, 0..31
|
||||
return r.R;
|
||||
});
|
||||
//! [LambdaExampleUsage]
|
||||
USART1.CR1.B.UE = SET;
|
||||
}
|
||||
extern "C" {
|
||||
int terminate () {
|
||||
return 0;
|
||||
}
|
||||
};
|
Loading…
Reference in a new issue