169 Commits

Author SHA1 Message Date
a858223c35 Fix #3: Merge branch 'issue/3-load-oven-pid-params' into dev 2020-11-08 21:38:21 +01:00
f2405e23b4 Add settings load function for PID parameters 2020-11-08 21:35:41 +01:00
2e640fa7fa Update base 64 library 2020-11-02 23:47:32 +01:00
1a3889b72d Update base64 lib to newest version. Now supports decoding 2020-11-02 23:44:37 +01:00
a7e376deab Add safety memory dump to file functionality 2020-11-02 18:21:24 +01:00
bb39c4b6e0 Fix bug in settings parser 2020-11-02 18:20:57 +01:00
7b1ae3bdc6 Improve code in calibration module 2020-11-02 18:20:17 +01:00
e37001e3c4 improve SDIO driver 2020-11-02 18:19:42 +01:00
09ea84beaf Increase watchdog timeout because some SD card operations need a lot of time 2020-11-02 18:17:50 +01:00
dc9fc2f814 Fix code warnings 2020-11-01 21:24:13 +01:00
32c8e2e937 Add define to move uart on debug header even when compiled in release mode 2020-11-01 21:22:28 +01:00
88360fe307 close config parser after use for reading calibration 2020-11-01 21:03:00 +01:00
57308e18e3 Delete cmsis math library. It is not needed anymore 2020-11-01 21:00:47 +01:00
731cb4fec3 Remove cmsis dsp library from project 2020-11-01 20:59:17 +01:00
8ff276e30d see last commit 2020-11-01 20:58:32 +01:00
1166477a6c Calibration routines adapted
* Implement custom mean and std functions
* Use standard deviation in calibration measurment instead of peak-peak noise.
2020-11-01 20:55:57 +01:00
7aa0b62012 Implement saftey memory dump feature and increase heap space 2020-11-01 20:43:59 +01:00
10596cdbf0 Add base64 library to Makefile 2020-11-01 18:03:52 +01:00
fc6fb1aee0 Add base 64 library 2020-11-01 18:01:28 +01:00
1eeaf3d892 Use new config parser for calibration 2020-11-01 00:59:46 +01:00
a3778fcb6e Change double numbers to float in order to prevent unwanted double to float conversion 2020-10-31 01:11:17 +01:00
86f153bf69 Fix code style 2020-10-31 01:10:46 +01:00
3ca5e41602 Implement config read function 2020-10-30 23:12:39 +01:00
dcec366b0a Implement config_parser_reset_to_start() 2020-10-30 22:24:03 +01:00
14b7bdbf19 Start config parser 2020-10-30 22:21:31 +01:00
af9845cbba Improve style 2020-09-27 23:23:44 +02:00
034ecaa60f Fix bug in menu 2020-09-27 23:02:38 +02:00
ae91affc30 issue #5: Implement safety weight checking in control loop 2020-09-27 22:54:06 +02:00
5fd2db319d Configure safety weight defaults 2020-09-27 22:22:54 +02:00
eec15df271 Fix code style 2020-09-27 22:13:49 +02:00
84c747c81c silence compiler warning about unused parameter 2020-09-22 23:47:18 +02:00
b869ed9c45 Improve comments and doxygen headers 2020-09-22 23:45:22 +02:00
42f6d0270d Issue #6: Implement safety flag ack in shell command safety-flags 2020-09-21 21:51:00 +02:00
e8f59b6dc6 Implement automatic resotre of error mem corrupt flag 2020-09-21 21:10:26 +02:00
d91a1b1da0 Fix #15: Make safety controller use CRC checked settings arrays for weights and persistencies. Weights not yet checked. 2020-09-08 21:46:57 +02:00
004be4ea5c Move safety weights and persistencies to config file 2020-09-08 21:15:23 +02:00
a838bf3af8 Add new Flag: ERR_FLAG_SAFETY_TAB_CORRUPT 2020-09-08 20:15:40 +02:00
1c1d1c4c97 Issue #18: Store correct flag number in error memory 2020-09-08 19:23:14 +02:00
e0f61af709 Issue #18: Protect safety memory heder with CRC 2020-09-08 18:24:10 +02:00
b619fc5600 Restructure panic mode and hardfault calls 2020-09-08 18:23:47 +02:00
2f6590416d Improve documentation of Stack Checking 2020-09-07 23:52:34 +02:00
a877ef5f28 Merge branch 'issue/18-Backup-RAM' into issue/15-safety-controller-hardening 2020-09-07 21:58:25 +02:00
af555aba21 Issue #20: Move stack checker to safety folder 2020-09-07 21:56:04 +02:00
dd0ee47d86 Issue #20: Add stack checking based on protection area between stack and heap 2020-09-07 21:52:53 +02:00
452abfdd5c Merge branch 'issue/18-Backup-RAM' into issue/20-implement-better-stack-checking 2020-09-07 21:04:37 +02:00
ba41c0911d Add necessary sections for stack protection area and implement fill function 2020-09-07 21:03:37 +02:00
cbbd97e1bd Issue #20: Implement driver for RNG 2020-09-07 20:47:56 +02:00
ab8228f712 Fix bug in doxygen xml generation for sphinx regarding the IN_SECTION() macro 2020-09-06 22:14:06 +02:00
20e2a2b84b Doxygen: Fix bug of IN_SECTION macro and __atribute__ 2020-09-06 22:13:51 +02:00
039a35522e Fix bug in doxygen xml generation for sphinx regarding the IN_SECTION() macro 2020-09-06 22:11:12 +02:00
72668472a0 Doxygen: Fix bug of IN_SECTION macro and __atribute__ 2020-09-06 22:09:14 +02:00
569d42bbe9 Issue #18: Fix wrong documentation 2020-09-06 21:12:57 +02:00
c7ebe441c7 Merge branch 'issue/15-safety-controller-hardening' into issue/18-Backup-RAM 2020-09-06 21:10:30 +02:00
403786e0c6 Issue #15: Implement safety weight table
* CRC protected flag weight table.
* Currently only filled with dummy values. Has to be finished in issue #5
* Config overrides from safety memor ynot yet implemented
2020-09-06 21:05:00 +02:00
192bcf01f6 Merge branch 'issue/18-Backup-RAM' into issue/15-safety-controller-hardening 2020-09-06 19:54:09 +02:00
9880c701b1 Issue #15: Introduce safety weigths 2020-09-06 19:52:44 +02:00
910037a562 Issue #18: Write doxygen headers for safety memory 2020-09-06 19:45:45 +02:00
6232e2f330 Issue #18: Store permanent errors in safety backup RAM 2020-09-06 01:40:10 +02:00
7ea0e73869 Merge branch 'issue/15-safety-controller-hardening' into issue/18-Backup-RAM 2020-09-05 20:31:23 +02:00
c4fe006efa Issue #15: Implement redundancy for error flags 2020-09-05 20:29:21 +02:00
b2b1702670 Issue #15: add redundant invers error flag 2020-09-05 20:17:35 +02:00
1f8a6347e9 Issue #15: Move safety controller working pages to CCMRAM 2020-09-05 20:14:08 +02:00
d0cf95db49 Issue #18: further iomplementation of safety memory 2020-09-05 20:06:13 +02:00
331b049868 Issue #18: Documentation of boot status flags 2020-09-05 19:00:57 +02:00
e50602611c Issue #18: Documentation of boot status flags 2020-09-05 19:00:01 +02:00
95382d9ab8 Make interrupt default handler trigger panic mode. 2020-09-05 18:23:53 +02:00
e96a710576 Make interrupt default handler trigger panic mode. 2020-09-05 18:23:17 +02:00
2673112a9c Reflow menu: Stop rendering the menu although nothing has changed. This reduces the current consumption. 2020-09-05 18:04:52 +02:00
b8b8e19206 Reflow menu: Stop rendering the menu although nothing has changed. This reduces the current consumption. 2020-09-05 18:03:05 +02:00
325fb24ed8 Issue #18: Improve documentation 2020-09-05 18:02:03 +02:00
ea26f56545 Issue #18: Check error memory entries at safety ram init 2020-09-05 17:37:56 +02:00
77c88c69cd Issue #18: Redefine error memory entries 2020-09-05 16:57:25 +02:00
e85a85d9c3 Issue #18: ommand to shell in order to test panic mode 2020-09-05 16:56:56 +02:00
3df0631ffc Issue #18: Implement writing and reading boot status structure from backup RAM 2020-09-05 16:32:31 +02:00
7434554319 Issue #18: Fix bugs in safety memory handling 2020-09-05 15:56:52 +02:00
c9a5a2c2ff Issue #18: Write init of safety memory 2020-09-05 15:15:46 +02:00
04008a07c0 Issue #18: Implement CRC calculation module 2020-09-05 12:17:36 +02:00
928dbfb9f3 Issue #18: Firther improve documentation 2020-09-04 23:51:51 +02:00
0f0afcf359 Issue #18: Add safety mem corrupt error flag 2020-09-04 23:04:27 +02:00
5d437f3a9f Fix documentation and add safety RAM module to Makefile 2020-09-04 23:02:23 +02:00
cb3b42aece Start documentation for safety RAM. Will be implemented afterwards 2020-09-04 22:55:34 +02:00
a12648ff7a Issue #18: Backup RAM: Make use of backup regulator optional 2020-09-04 21:33:54 +02:00
d3c4e1bffc Issue #18: Implement driver for backup RAM 2020-09-04 21:03:53 +02:00
45c0625864 fixup style issues 2020-08-31 22:58:00 +02:00
03e1ccf97e Fix style 2020-08-31 22:50:39 +02:00
0fd738f37e Fix startup code bug in release mode 2020-08-30 19:40:33 +02:00
3dfe59482e Fix #17: Merge branch 'issue/17-rotary-emulation-cmd' into dev 2020-08-30 19:04:01 +02:00
9024402a3b Issue #17: Write documentation for emulation function 2020-08-30 19:03:17 +02:00
f32d1afde5 Issue #17: Add ui-emulate command to shell 2020-08-30 18:44:36 +02:00
0da6925119 Issue #17: Add override function for deltas of rotary encoder 2020-08-30 18:25:10 +02:00
914abd8562 Issue #17: Add override function for button 2020-08-30 18:20:58 +02:00
80edd09528 temperature converter: Replace division with multiplication. This makes the code faster 2020-08-29 08:53:23 +02:00
ab4499a284 Fix mrpropewr make target to clean debug and release projects 2020-08-29 08:52:44 +02:00
cc6e922d1b Move variables to CCM RAM 2020-08-23 21:51:34 +02:00
149c5715c6 Add fault modes and start implementation of backup SRAM. However, this will probably never be used 2020-08-23 21:40:16 +02:00
627da0def5 Fix-up documentation 2020-08-23 00:04:02 +02:00
324e6d506d Issue #5: Write documentation for error flags and their handling 2020-08-22 23:57:26 +02:00
dcd1fa9605 remove executable flag from CCM memory because it is not executable 2020-08-22 13:45:17 +02:00
3cc49fb764 Remove unneeded variable 2020-08-22 13:33:53 +02:00
13ac487ddb remove unneeded variables 2020-08-22 13:30:59 +02:00
ec117e0627 Change linker script and startup code.
This is my own code from my template. It is much cleaner than the old code.
2020-08-21 23:47:55 +02:00
95de84fa85 Fix #13: Add hang command to shell 2020-08-21 00:29:19 +02:00
432d30cc34 Fix #12: Timing monitors are now correctly displayed in flags command 2020-08-21 00:20:16 +02:00
0395cd19d4 SD card settings folder: Use f_stat to determine if directory is present 2020-08-21 00:06:56 +02:00
04994bff51 Issue #9: Fix timeout bug in write block command 2020-08-21 00:00:08 +02:00
45564a7789 Fix safety issue #11 2020-08-18 20:47:08 +02:00
fa20304df8 Makefile: Add linker script as dependency for linking step 2020-08-18 20:23:44 +02:00
86ba94a3f3 Makefile: Add linker flag to show memory usage after linking 2020-08-18 20:10:15 +02:00
60e990632b Fix #10: Moved static and global variablöes that are 0 initialized to CCMRAM in order to make room and increase performance. 2020-08-18 19:57:13 +02:00
64ef7b4a3c Issue #9: Increase SDIO clock speed to 4.2 MHz 2020-08-18 19:30:51 +02:00
637ac77a09 Add license to docu 2020-08-18 01:34:45 +02:00
6c980721b2 Add GPLv2 license to project 2020-08-18 01:07:37 +02:00
107c676084 Documentation: Add part of the GPL license text to the disclaimer in the documentation. 2020-08-18 01:04:57 +02:00
34b6af3627 Documentation: Write some docu
* Safety notice
* HW modifications
* Links to PCB repo
2020-08-18 01:00:45 +02:00
277b28d7f5 Make green LED flash instead of orange one, when inserting SD card 2020-08-17 22:26:29 +02:00
bdfaa67070 Fix typo 2020-08-17 22:26:05 +02:00
4e9b28ce15 Improve SDIO handling 2020-08-17 22:10:04 +02:00
543127b187 Fix bugs in settings saving 2020-08-16 22:11:57 +02:00
6c92048de5 Remove emptry line 2020-08-16 20:35:11 +02:00
227562cf3c Load calibration when SD card is inserted.
* Load calibration when SD card is inserted and controller is not yet calibrated
* Fix #7: LED0 blinks when SD is inserted / removed.
2020-08-16 20:33:25 +02:00
58937b46f6 Improve code 2020-08-16 20:33:17 +02:00
e06c9f7ddc Remove toml. Write calibration to dat files. Implement first draft for reading function 2020-08-16 19:37:41 +02:00
a5402d3f04 Add gitignore to build directory of documentation 2020-08-16 13:18:04 +02:00
d04d8ebf9d Change save fuinction for SD card calibration data 2020-08-16 13:15:35 +02:00
42ca1a01b5 Merge branch 'issue/1-shell-function-save-calibration-data' into dev 2020-08-16 12:53:47 +02:00
68883735ec Remove global error state from main file 2020-08-16 12:53:05 +02:00
15d255778c Fix #1: Implement save function for calibration 2020-08-16 12:52:37 +02:00
2f39b5eb69 Merge branch 'dev' of git.shimatta.de:mhu/reflow-oven-control-sw into dev 2020-08-16 12:35:45 +02:00
d1d2d514bd Add timing monitor for main loop and add monitors to safety flag command 2020-08-16 12:34:41 +02:00
4a441a9c44 Documentation: Create build folder in case it does not exist. However, add it to the git repo by placing a .gitignore inside 2020-08-16 11:50:20 +02:00
fa3c980207 shell: Add dummy function save-calibration. Not yet correcly implemented! 2020-08-16 01:24:59 +02:00
3c6200e08c Settings: Add preliminary functions to store Claibration data on SD Card. Not yet implemented correctly 2020-08-16 01:24:20 +02:00
e7d150e8f5 FatFs: Enable relative paths 2020-08-16 01:23:50 +02:00
c5667c6895 Toml: Add TOML interpreter for config files 2020-08-16 01:22:57 +02:00
b7ccd8542e Increase safety ADC timing monitor to over a second. It might take a while for the ADC to be called if a demanding operation is taking place 2020-08-16 01:22:26 +02:00
1ad68a2c43 SDIO Driver: Fix bug created in commit 3705cc09d1 that makes writing to disk impossible 2020-08-16 01:19:56 +02:00
4ab91ace5f Remove redundant define macro 2020-08-11 23:37:26 +02:00
97c32b0443 Write documentation 2020-08-11 23:21:24 +02:00
a68b9176cb Move ADC_TO_RES macro to header file 2020-08-11 23:21:14 +02:00
cb3c989683 Docu 2020-08-04 00:55:16 +02:00
f6f01b0510 Improve sphinx 2020-08-03 21:13:04 +02:00
d6815f8285 Documentation: Breathe: Detect c/h files correctly as C Files and not C++ 2020-08-02 22:29:18 +02:00
62ea995c2d fix typo in sphinx doc 2020-08-02 22:20:41 +02:00
e8e3d71bbe doxygen: Remove latex output and add optional flags to buildscript 2020-08-02 22:15:33 +02:00
75d4af84c4 Basic template for documentation 2020-08-02 22:14:49 +02:00
6cde956c31 Merge branch 'safety-controller' into dev 2020-08-02 19:00:35 +02:00
bedf231550 Use the RTD theme for sphinx 2020-07-30 23:08:40 +02:00
a112cd80bf Add sphinx docu 2020-07-30 22:58:42 +02:00
fdf3f2c7d6 Merge branch 'dev' into safety-controller 2020-07-30 22:51:08 +02:00
21ad2ace4a Remove temp profile submodule 2020-07-30 22:50:34 +02:00
464c247e32 Fix a few bugs and implement flags command further 2020-07-30 20:29:48 +02:00
6c4b698fd7 Add safety flag for debugbuild 2020-07-28 23:29:35 +02:00
6498aaf8b8 Add color to flag monitor command and set oven output to 0 2020-07-28 23:26:28 +02:00
b65d94b0e8 Fix ADC measurement to run at 1000 Hz and fix wrong error handling for PT1000 Watchdog. Add function for flags to shell 2020-07-28 22:55:02 +02:00
97fc04399e Fix recursion loop in safety controller 2020-07-28 21:00:37 +02:00
da96daa767 Reworked measurement ADC to use safety controller 2020-07-27 22:15:01 +02:00
a9e300bf5b Make error structures volatile 2020-07-27 21:32:25 +02:00
4f3016649d First draft of safety controller 2020-07-27 21:29:15 +02:00
a04e894518 Further work on safety controller 2020-07-26 21:40:09 +02:00
9136dc196c Further rewrite safety handling 2020-07-09 22:31:42 +02:00
5eb51f08b6 Start safety implementation. Completely dumped old stuff 2020-07-07 20:47:22 +02:00
06a75559f0 Add structure for safety controller config. 2020-07-07 19:26:00 +02:00
248707055e Fix watchdog init code 2020-07-06 21:37:36 +02:00
67a32cdc20 Safety Controller:
* Add watchdog code
* Add file structure for safety controller
* Lay groundstones to move all error flags to the safety controller
* Improve doxygen
2020-07-06 21:12:18 +02:00
8a365ab5e0 Move safety ADC to safety subfolder 2020-07-06 20:13:01 +02:00
7cd05e1582 Update shellmatta 2020-06-25 23:54:36 +02:00
0e97d57883 Improve menu function 2020-06-25 23:52:58 +02:00
cced874460 Add Pullup to uart RX pin 2020-06-21 01:29:50 +02:00
49927a25cf Add Omega as unit for Ohm in LCD menu 2020-06-16 20:05:32 +02:00
103 changed files with 29172 additions and 8825 deletions

7
.gitmodules vendored
View File

@@ -5,7 +5,8 @@
path = c-style-checker
url = https://git.shimatta.de/mhu/c-style-checker.git
branch = master
[submodule "reflow-controller-temp-profile-lang"]
path = reflow-controller-temp-profile-lang
url = https://git.shimatta.de/mhu/reflow-controller-temp-profile-lang.git
[submodule "stm-firmware/base64-lib"]
path = stm-firmware/base64-lib
url = https://git.shimatta.de/mhu/base64-lib.git
branch = master

361
LICENSE.md Normal file
View File

@@ -0,0 +1,361 @@
### GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
### Preamble
The licenses for most software are designed to take away your freedom
to share and change it. By contrast, the GNU General Public License is
intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if
you distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on,
we want its recipients to know that what they have is not the
original, so that any problems introduced by others will not reflect
on the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at
all.
The precise terms and conditions for copying, distribution and
modification follow.
### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
**0.** This License applies to any program or other work which
contains a notice placed by the copyright holder saying it may be
distributed under the terms of this General Public License. The
"Program", below, refers to any such program or work, and a "work
based on the Program" means either the Program or any derivative work
under copyright law: that is to say, a work containing the Program or
a portion of it, either verbatim or with modifications and/or
translated into another language. (Hereinafter, translation is
included without limitation in the term "modification".) Each licensee
is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the Program
(independent of having been made by running the Program). Whether that
is true depends on what the Program does.
**1.** You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a
fee.
**2.** You may modify your copy or copies of the Program or any
portion of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
**a)** You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
**b)** You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any part
thereof, to be licensed as a whole at no charge to all third parties
under the terms of this License.
**c)** If the modified program normally reads commands interactively
when run, you must cause it, when started running for such interactive
use in the most ordinary way, to print or display an announcement
including an appropriate copyright notice and a notice that there is
no warranty (or else, saying that you provide a warranty) and that
users may redistribute the program under these conditions, and telling
the user how to view a copy of this License. (Exception: if the
Program itself is interactive but does not normally print such an
announcement, your work based on the Program is not required to print
an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
**3.** You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
**a)** Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections 1
and 2 above on a medium customarily used for software interchange; or,
**b)** Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your cost of
physically performing source distribution, a complete machine-readable
copy of the corresponding source code, to be distributed under the
terms of Sections 1 and 2 above on a medium customarily used for
software interchange; or,
**c)** Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is allowed
only for noncommercial distribution and only if you received the
program in object code or executable form with such an offer, in
accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
**4.** You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt otherwise
to copy, modify, sublicense or distribute the Program is void, and
will automatically terminate your rights under this License. However,
parties who have received copies, or rights, from you under this
License will not have their licenses terminated so long as such
parties remain in full compliance.
**5.** You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
**6.** Each time you redistribute the Program (or any work based on
the Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
**7.** If, as a consequence of a court judgment or allegation of
patent infringement or for any other reason (not limited to patent
issues), conditions are imposed on you (whether by court order,
agreement or otherwise) that contradict the conditions of this
License, they do not excuse you from the conditions of this License.
If you cannot distribute so as to satisfy simultaneously your
obligations under this License and any other pertinent obligations,
then as a consequence you may not distribute the Program at all. For
example, if a patent license would not permit royalty-free
redistribution of the Program by all those who receive copies directly
or indirectly through you, then the only way you could satisfy both it
and this License would be to refrain entirely from distribution of the
Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
**8.** If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
**9.** The Free Software Foundation may publish revised and/or new
versions of the General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Program does not specify a
version number of this License, you may choose any version ever
published by the Free Software Foundation.
**10.** If you wish to incorporate parts of the Program into other
free programs whose distribution conditions are different, write to
the author to ask for permission. For software which is copyrighted by
the Free Software Foundation, write to the Free Software Foundation;
we sometimes make exceptions for this. Our decision will be guided by
the two goals of preserving the free status of all derivatives of our
free software and of promoting the sharing and reuse of software
generally.
**NO WARRANTY**
**11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
**12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
### END OF TERMS AND CONDITIONS
### How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these
terms.
To do so, attach the following notices to the program. It is safest to
attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
one line to give the program's name and an idea of what it does.
Copyright (C) yyyy name of author
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Also add information on how to contact you by electronic and paper
mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
type `show w'. This is free software, and you are welcome
to redistribute it under certain conditions; type `show c'
for details.
The hypothetical commands \`show w' and \`show c' should show the
appropriate parts of the General Public License. Of course, the
commands you use may be called something other than \`show w' and
\`show c'; they could even be mouse-clicks or menu items--whatever
suits your program.
You should also get your employer (if you work as a programmer) or
your school, if any, to sign a "copyright disclaimer" for the program,
if necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright
interest in the program `Gnomovision'
(which makes passes at compilers) written
by James Hacker.
signature of Ty Coon, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library,
you may consider it more useful to permit linking proprietary
applications with the library. If this is what you want to do, use the
[GNU Lesser General Public
License](https://www.gnu.org/licenses/lgpl.html) instead of this
License.

0
doc/.gitignore vendored Normal file
View File

22
doc/Makefile Normal file
View File

@@ -0,0 +1,22 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
livehtml:
sphinx-autobuild -b html $(SPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/html

3
doc/build/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*
*/*
!.gitignore

35
doc/make.bat Normal file
View File

@@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

2557
doc/source/Doxyfile.in Normal file

File diff suppressed because it is too large Load Diff

3432
doc/source/_static/ibom.html Normal file

File diff suppressed because one or more lines are too long

84
doc/source/conf.py Normal file
View File

@@ -0,0 +1,84 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os, subprocess
import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
import re
project = 'Shimatta Reflow Controller'
copyright = '2020, Mario Hüttel'
author = 'Mario Hüttel'
# The full version, including alpha/beta/rc tags.
release = re.sub('^v', '', os.popen('git describe --always --tags --dirty').read().strip())
# The short X.Y version.
version = release
try:
os.mkdir('../build/_doxygen')
except FileExistsError:
pass
subprocess.call('doxygen Doxyfile.in', shell=True)
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx_rtd_theme',
'sphinx.ext.autodoc',
'sphinx.ext.todo',
'sphinxcontrib.blockdiag',
'breathe'
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
breathe_projects = {
"Reflow Controller Firmware": "../build/_doxygen/xml/"
}
breathe_domain_by_extension = { "h" : "c",
"c" : "c" }
breathe_default_project = "Reflow Controller Firmware"
breathe_default_members = ('members', 'undoc-members')
blockdiag_html_image_format = 'SVG'
breathe_show_define_initializer = True
blockdiag_latex_image_format = 'PDF'
todo_include_todos = True

View File

@@ -0,0 +1,8 @@
.. _api_dmas:
Peripheral DMA Library Code
====================================
.. doxygengroup:: dma-ring-buffer
:project: Reflow Controller Firmware

View File

@@ -0,0 +1,11 @@
.. _api:
Important Code APIs
===================
.. toctree::
:maxdepth: 2
:glob:
*
safety/safety-controller

View File

@@ -0,0 +1,8 @@
.. _api_main:
Reflow Controller Firmware Main File
====================================
.. doxygenfile:: main.c
:project: Reflow Controller Firmware

View File

@@ -0,0 +1,8 @@
.. _dox_safety_adc:
Safety ADC
====================================
.. doxygengroup:: safety-adc
:project: Reflow Controller Firmware

View File

@@ -0,0 +1,14 @@
.. _dox_safety_controller:
Safety Controller
====================================
.. toctree::
:maxdepth: 1
safety-adc
.. doxygengroup:: safety-controller
:project: Reflow Controller Firmware

View File

@@ -0,0 +1,16 @@
.. _firmware:
Reflow Controller Firmware
==========================
This chapter describes the reflow controller's firmware.
This is in most cases not intended to be a code documentation but an overview over the functional
mechanisms and the behavior. For a detailed code documentation see the doxygen output.
.. toctree::
:maxdepth: 2
pt1000-processing
safety/index
code/index

View File

@@ -0,0 +1,196 @@
.. _pt1000_processing:
PT1000 Temperature Value Processing
===================================
The PT1000 temperature sensor is the sensing element used for determining the Reflow Oven Temperature.
The PT1000 value processing is enabled by default and not intended to be turned off.
PT1000 Value Sampling
---------------------
The following block diagram shows the processing chain of the temperature signal.
.. blockdiag::
:desctable:
blockdiag {
orientation = portrait;
FRONTEND[description=":ref:`hw_analog_fe`", label="Frontend"];
ADC[description="`Analog to Digital Converter <ADC_>`_"];
WATCHDOG [label = "WDT", shape=endpoint, description="`Hardware Value Watchdog <Watchdog_>`_"];
PREFILTER [label=Prefilter, description="`Prefiltering and Downsampling <Prefilter_>`_"];
ADC2RES [label= "Val -> Ohm", description="`Conversion from ADC value to resistance in Ohms <ADC Value to Ohm_>`_"]
MAVG [label="MAVG Filter", description="`Exponential Moving Average Filter`_"];
RAW_HF [label="HF", shape = endpoint, description="High Frequency raw value reading"];
PT1000 [label = "LF", shape = endpoint, description="Low Frequency PT1000 resistance value (see: `MAVG Filter <Exponential Moving Average Filter_>`_)"]
RAW_STREAM [label = "Stream", shape = endpoint, description="Raw value streaming"];
FRONTEND -> ADC -> WATCHDOG;
ADC -> PREFILTER [label="1 kHz"];
PREFILTER -> ADC2RES [label="1/6 kHz"];
ADC2RES -> MAVG;
MAVG -> PT1000 [label="1/6 kHz"];
PREFILTER -> RAW_HF [label="1/6 kHz"];
PREFILTER -> RAW_STREAM [label="1/6 kHz"];
}
ADC
~~~
The internal ADC of the STM32F407 controller is used to sample the analog signal from the :ref:`hw_analog_fe`. The ADC is triggered by the hardware Timer *TIM2* each millisecond, which results in a sampling frequency of
1 kHz. The ADC module provides an analog value `watchdog <Watchdog_>`_, which is used to detect wirebreaks and other hardware errors that result in a wrong resistance measurement.
The sample frequency is controlled by
.. doxygendefine:: ADC_PT1000_SAMPLE_CNT_DELAY
whereas the ADC Peripheral module is defined by
.. doxygendefine:: ADC_PT1000_PERIPH
Prefilter
~~~~~~~~~
The analog value prefilter is used to filter outliers. It is triggered after a certain amount ``n`` of values have been sampled by the `ADC`_.
The filter then removes the two most extreme values and computes the average of the remaining ``n - 2`` values. By default ``n`` is configured to:
.. doxygendefine:: ADC_PT1000_DMA_AVG_SAMPLES
Therefore, by default, the resulting datastream has a sampling rate of 1/6 kHz. This depends on the :c:macro:`ADC_PT1000_SAMPLE_CNT_DELAY` and ``n``
.. _firmware_meas_adc_watchdog:
Watchdog
~~~~~~~~
The analog watchdog supervises the measured value of the `ADC`_. It is configured by the following defines:
.. doxygendefine:: ADC_PT1000_LOWER_WATCHDOG
.. doxygendefine:: ADC_PT1000_UPPER_WATCHDOG
.. doxygendefine:: ADC_PT1000_WATCHDOG_SAMPLE_COUNT
The watchdog will set the :ref:`safety_flags_adc_watchdog` error flag.
ADC Value to Ohm
~~~~~~~~~~~~~~~~
This block converts the analog value to an Ohm resistance value.
The formula is:
.. math::
R(V) = \frac{V}{4096} \cdot 2500~\Omega
The equation is implemented in
.. doxygendefine:: ADC_TO_RES
and applied during the `Exponential Moving Average Filter`_.
.. _firmware_meas_adc_filter:
Exponential Moving Average Filter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The external moving average filter filters the measured resistance value. It's equation is:
.. math::
y[n] = (1-\alpha) y[n-1] + \alpha x[n]
The filter constant *alpha* defaults to the define
.. doxygendefine:: ADC_PT1000_FILTER_WEIGHT
and can be changed in code using
.. doxygenfunction:: adc_pt1000_set_moving_average_filter_param
After initial startup and after each change of the filter constant, the filter will set the :ref:`safety_flags_adc_unstable` flag for a defined sample count of:
.. doxygendefine:: ADC_FILTER_STARTUP_CYCLES
The moving average filter's output signal is the Low Frequency (LF) PT1000 resistance signal used for internal PT1000 measurements.
Reading and Converting the PT1000 Value
---------------------------------------
Calibration
~~~~~~~~~~~
The functions
.. doxygenfunction:: adc_pt1000_set_resistance_calibration
:outline:
and
.. doxygenfunction:: adc_pt1000_get_resistance_calibration
:outline:
are used to set the reistance calibration internally. For a guide on how to calibrate the deivce, see the corresponding :ref:`usage_calibration` usage page.
The calibration is calculated the following way:
.. blockdiag::
:desctable:
blockdiag {
orientation = portrait;
LF [label="LF", shape=beginpoint, description="Low Frequency PT1000 Value"];
SENS [label="Sens", description="Sensitivity Correction :math:`\sigma`"];
OFFSET [label="Offset", description="Offset Correction :math:`O`"];
OUT [shape=endpoint, description="Corrected Value"];
LF -> SENS -> OFFSET -> OUT
}
The final calibrated PT1000 resistance is calculated as:
.. math::
R_{PT1000_{corr}} = R_{PT1000_{LF}} \cdot (1 + \sigma) + O
The default values, if no calibration is loaded / executed, are:
============== =========
:math:`\sigma` :math:`O`
============== =========
0 0
============== =========
Get Calibration Corrected Value
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The PT1000 value is available through the following function.
If a calibration is set, it is applied.
.. doxygenfunction:: adc_pt1000_get_current_resistance
Converting the Value
~~~~~~~~~~~~~~~~~~~~
The valid range for conversion is between
.. doxygendefine:: TEMP_CONVERSION_MIN_RES
:outline:
and
.. doxygendefine:: TEMP_CONVERSION_MAX_RES
:outline:
By default, the valid range is:
.. math::
1000~\Omega \le R_{PT1000} \le 2200~\Omega
.. doxygenfunction:: temp_converter_convert_resistance_to_temp
The cvonversion function is based on a lookup table with linear interpolation between the data points.
The lookuptable is stored as a header file and can, if necessary, be recreated using the ``create-temp-lookup-table.py`` script.

View File

@@ -0,0 +1,112 @@
.. _backup_ram:
Safety Backup RAM
=================
Overview
--------
The STM controller's backup RAM is used to store different kinds of information that shall be preserved if the controller resets.
The hardware setup is missing a separate powersupply for the controller's backup domain. Therefore, the backup RAM is cleared, when the power is cut.
The backup RAM is used to store permanent error flags (See :ref:`safety_flags`). This ensures the flags that trigger hard faults / the panic mode, can be identified, although the wathcoog resets the controller. The only way to clear them is by cutting the power.
Because cutting the power is a way to clear the backup RAM, no separate method for clearing the error entries in the backup RAM is defined.
The backup RAM contents are protected by a `CRC Checksum`_.
The backup RAM is initialized and checked after boot. If the controller starts from a powered down state,
the backup RAM is empty. This is detected by an invalid `Header`_ at the beginning of the backup RAM. If this is the case, the safety ocntoller
will create a valid backup RAM image with a `Header`_, empty `Boot Status Flag Entries`_, empty `Config Overrides`_, an empty `Error Memory`_, and a valid `CRC Checksum`_.
If the Header is valid during boot (verified by plausible values and correct magic numbers), the backup RAM is CRC checked and the error memory is
checked for valid entries.
In case of a CRC error or invalid entries in the error memory, the Backup RAM is wiped and reinitialized. On top of that, the error flag :ref:`safety_flags_safety_mem_corrupt` is set.
.. note:: It may be possible that future versions of the hardware include a backup RAM battery / Goldcap. In this case, a way to clear the error memory will be implemented,
because it will no longer be possible to clear the error memory by cutting the power.
On top of that, the backup memory will also contain the calibration data.
.. note:: The firmware will not use the ``NOP`` entries of the error memory by default, but they will be respected by the validity checker.
Partitioning and Entries
------------------------
The backup RAM consists of multiple sections. The memory section are listed below.
Header
~~~~~~
The backup memory header is located at offset address:
.. doxygendefine:: SAFETY_MEMORY_HEADER_ADDRESS
The header is defined by the following structure:
.. doxygenstruct:: safety_memory_header
The validity of the header is checked, if the magic and inverse amgic fields contain the correct values, and if the offset address pointers
have values that are located inside the error memory and are not ``0`` or the same value.
The safety memory header magic is:
.. doxygendefine:: SAFETY_MEMORY_MAGIC
.. _backup_ram_boot_flags:
Boot Status Flag Entries
~~~~~~~~~~~~~~~~~~~~~~~~
The boot status flag entries are use to store system states over resets.
The flags are stored in memory using the follwoing structure:
.. doxygenstruct:: safety_memory_boot_status
Flags are evaluated active, if the corresponding word is unequal to ``0``.
Config Overrides
~~~~~~~~~~~~~~~~
Config overrides are used to override persistance and flag weights dynamically. The safety controller will parse the entries on
startup.
======================= ============ ================= ===================== =====================================
Entry Byte 1 (LSB) Byte 2 Byte 3 Byte 4 (MSB)
======================= ============ ================= ===================== =====================================
Weight override ``0xA2`` ``Weight`` ``Flag Number`` reserved don't care (written as 0xAA)
Persistance override ``0x8E`` ``Persistance`` ``Flag Number`` reserved don't care (written as 0xBB)
======================= ============ ================= ===================== =====================================
All words, not matching the table above are ignored and do not cause an error. By default the firmware fills this memory area with zeroes.
Error Memory
~~~~~~~~~~~~
The error memory contains error entries in form of 32 bit words. The entries are coded as stated below.
``Error Flag`` entries are used to restore error flags after boot. In theory, all flags can be set using this entry type.
However, only persistent flags are stored in the error memory by the firmware.
``NOP`` entries have no meaning. They are used as a filler. When adding a new error memory entry, the error memory is scanned until the first ``NOP`` entry is found.
It is replaced with a valid entry. If the error memory contains a word, that is not defined below, it is considered invalid and will trigger the RAM checker on boot.
``NOP`` entries can be used to preallocate the error memory in advance. if the end of the error memory is reached, it is expanded by 1 word to first
the new error entry, until the backup RAM is full. After this, no further errors are stored.
If the same persistent error is triggered mutliple times, the ``COUNTER`` in the error entry is incremented.
======================= ============ ================= ===================== =====================================
Entry Byte 1 (LSB) Byte 2 Byte 3 Byte 4 (MSB)
======================= ============ ================= ===================== =====================================
Error Flag ``0x51`` ``Flag Number`` ``COUNTER 7:0`` ``COUNTER 15:8``
NOP Entry ``0x22`` ``0x12`` ``0xAA`` ``0xC1``
======================= ============ ================= ===================== =====================================
CRC Checksum
~~~~~~~~~~~~
The CRC checksum is located after the error memory. The checksum is calculated by the internal peripheral module of the STM32F4 controller.
Therefore, the CRC calculation is fixed.
The polynomial is ``0x4C11DB7`` (Ethernet CRC32):
.. math:: P_{CRC}(x) = x^{32}+x^{26}+x^{23}+x^{22}+x^{16}+x^{12}+x^{11}+x^{10}+x^{8}+x^{7}+x^{5}+x^{4}+x^{2}+x+1

View File

@@ -0,0 +1,20 @@
.. _safety_handling:
Error Handling
==============
.. _safety_panic:
Panic Mode
----------
.. _safety_error_mem:
Error memory
------------
Permanent errors are stored in the backup RAM of the STM. This ensures, that errors can be read even after a full system reset has occured.
.. seealso:: :ref:`backup_ram`

View File

@@ -0,0 +1,114 @@
.. _safety_flags:
Safety Flags
============
The safety flags are represented in software by the following enums
.. doxygenenum:: safety_flag
The safety flags can be temporarily or permanent. Some temporary flags are reset automatically, once the error condition disappears. Others have to be explicitly cleared.
The safety weights (if a flag stops the PID controller, or triggers the panic mode) are configured by default as described below. However, it will be possible to override these weights by
setting config entries in the safety memory.
.. todo:: Change docu of config entires in memory
----------------------------------------------------------------------------------------------------------------------------------
.. _safety_flags_adc_overflow:
ERR_FLAG_MEAS_ADC_OVERFLOW
--------------------------
``ERR_FLAG_MEAS_ADC_OVERFLOW`` is triggered in case of an overflow in the signal path of the measurement ADC. This should never happen unless there is a bug in the software.
========== ============= ============= ===========
persistent self-clearing Stops PID Panic Mode
========== ============= ============= ===========
yes no yes no
========== ============= ============= ===========
----------------------------------------------------------------------------------------------------------------------------------
.. _safety_flags_adc_off:
ERR_FLAG_MEAS_ADC_OFF
---------------------
``ERR_FLAG_MEAS_ADC_OFF`` signals that the measurement ADC for the PT1000 sensor is deactivated. This flag is automatically cleared by the firmware
once the ADC is started.
========== ============= ============= ===========
persistent self-clearing Stops PID Panic Mode
========== ============= ============= ===========
no yes yes no
========== ============= ============= ===========
----------------------------------------------------------------------------------------------------------------------------------
.. _safety_flags_adc_watchdog:
ERR_FLAG_MEAS_ADC_WATCHDOG
--------------------------
``ERR_FLAG_MEAS_ADC_WATCHDOG`` is used as a wire break detection mechanism. This flag is set when the PT1000 measurement ADC detects an invalid resistance measurement.
.. seealso:: :ref:`ADC Watchdog<firmware_meas_adc_watchdog>`
========== ============= ============= ===========
persistent self-clearing Stops PID Panic Mode
========== ============= ============= ===========
no no yes no
========== ============= ============= ===========
----------------------------------------------------------------------------------------------------------------------------------
.. _safety_flags_adc_unstable:
ERR_FLAG_MEAS_ADC_UNSTABLE
--------------------------
``ERR_FLAG_MEAS_ADC_UNSTABLE`` is set after startup of the PT1000 measuremnt or after reconfiguring the filter settings.
.. seealso:: :ref:`firmware_meas_adc_filter`
========== ============= ============= ===========
persistent self-clearing Stops PID Panic Mode
========== ============= ============= ===========
no yes no no
========== ============= ============= ===========
.. _safety_flags_safety_mem_corrupt:
ERR_FLAG_SAFETY_MEM_CORRUPT
---------------------------
``ERR_FLAG_SAFETY_MEM_CORRUPT`` is set during the initialization of the controller, in case a corrupted safety memory is encountered.
In this case the error memory is reinitialized and the flag is set in the error memory. Afer a reboot it will stay asserted until the
safety backup memory is cleared
.. seealso:: :ref:`backup_ram`
========== ============= ============= ===========
persistent self-clearing Stops PID Panic Mode
========== ============= ============= ===========
yes no yes no
========== ============= ============= ===========
.. _safety_flags_stack:
ERR_FLAG_STACK
---------------------------
``ERR_FLAG_STACK`` ialization of the controller, in case a corrupted safety memory is encountered.
This error is not recoverable and will trigger the panic mode.
.. seealso:: :ref:`safety_stack_checking`
========== ============= ============= ===========
persistent self-clearing Stops PID Panic Mode
========== ============= ============= ===========
yes no yes yes
========== ============= ============= ===========

View File

@@ -0,0 +1,24 @@
.. _firmware_safety:
Safety Controller
=================
The safety controller is the software component that monitors the overall condition of the reflow controller,
and stops the output driver in case of an error.
Severe error flags, like a drifting reference voltage, stop the PID controller and force the output to zero.
The controller stays in a usable state. After the errors have been cleared, normal operation may continue.
On the other hand, fatal errors like an over-temperature error, or memory problem, lead to the activation of the :ref:`safety_panic`,
which forces the output zero, but does not allow any further interaction.
On top of this, a :ref:`backup_ram` is implemented. It stores permantent errors, which are reset at a restart. On top of that, it stores the :ref:`backup_ram_boot_flags`,
which are used to retain boot information across resets, for example to communicate with the firmware updater etc. The RAM also contains entries, that allow overrides of flag weights and persistance.
.. toctree::
:maxdepth: 3
flags
backup-ram
error-handling
stack-checking

View File

@@ -0,0 +1,39 @@
.. _safety_stack_checking:
Safety Stack Checking
=====================
To ensure correct operation of the controller, the stack is continuously monitored. For this, the :ref:`firmware_safety` checks the stack in each run.
These checks include:
1. Checking of used stack space and limit to end of stack
2. Checking a protection area between heap and stack for memory corruption
Any detected error will set the :ref:`safety_flags_stack` error flag.
Stack Pointer Checking
----------------------
The stack pointer is checked using :c:func:`stack_check_get_free`. The returned value for the remaining stack space is checked against
.. doxygendefine:: SAFETY_MIN_STACK_FREE
.. doxygenfunction:: stack_check_get_free
Stack and Heap Corruption Checking
----------------------------------
A section of memory is located between the stack and the heap. It is defined inside the linker script. It's size is configured by the linker script parameter ``__stack_corruption_area_size``, which is set to ``128`` by default.
This section is filled at the initializazion of the safety controller by a call to
.. doxygenfunction:: stack_check_init_corruption_detect_area
On each run of the safety controller's handling function (:c:func:`safety_controller_handle`) the following function is called:
.. doxygenfunction:: stack_check_corruption_detect_area
This function constantly checks the memory area for write modifications, and therefore detects, if the stack or heap have grown outside their boundaries.

View File

@@ -0,0 +1,8 @@
.. _hw_bom:
Interactive BoM of Controller Board
===================================
.. raw:: html
<iframe src="../_static/ibom.html" width="100%" height="1500"></iframe>

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 284 KiB

View File

@@ -0,0 +1,10 @@
.. _hw_analog_fe:
Analog Frontend
===============
Schematic
---------
.. image:: frontend-schematic_v1.2.svg
:target: /_images/frontend-schematic_v1.2.svg

View File

@@ -0,0 +1,20 @@
.. _hw:
Hardware
========
This guide on the reflow controller's hardware is based on the ``reflow-oven-control-pcb`` -- Version ``v1.2``
.. toctree::
:maxdepth: 2
:glob:
*
Links
-----
- `Reflow Oven Control PCB v1.2 schematic <https://git.shimatta.de/attachments/788fde4e-a560-445b-87ea-de1d67f6846a>`__
- `Git Repository for Controller PCB <https://git.shimatta.de/pcb/reflow-oven-control-pcb>`__

View File

@@ -0,0 +1,15 @@
.. _hardware_modifications:
Hardware Modifications
======================
Analog Frontend
---------------
.. note::
Solder a ``100 nF`` capacitor parallel to ``R315`` in order to implement a low pass characteristic of the difference amplifier in the :ref:`analog frontend<hw_analog_fe>`. This massively increases EMI performance which prevents the :ref:`ADC Watchdog<firmware_meas_adc_watchdog>` from triggering.
Power Supply
------------
.. note::
Replace transformer ``BV-EI-303-2010`` with ``BV-EI-303-2050``, which has the same footprint but a little more power and does not heat that much.

26
doc/source/index.rst Normal file
View File

@@ -0,0 +1,26 @@
.. Shimatta Reflow Controller documentation master file, created by
sphinx-quickstart on Thu Jul 30 22:56:09 2020.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to Shimatta Reflow Controller's documentation!
======================================================
Quick Links
===========
* :ref:`genindex`
* :ref:`hardware_modifications`
.. warning::
Although the Shimatta Reflow Controller provides a bunch of safety mechanisms that -- in theory -- prevent your house from burning down, NEVER leave the oven controller running unattended. This project is published 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.
.. toctree::
:maxdepth: 2
:caption: Contents
self
usage/index
hardware/index
firmware/index
license

399
doc/source/license.rst Normal file
View File

@@ -0,0 +1,399 @@
.. _license:
License
=======
The firmware of the reflow controller and its hardware are distributed under the GPLv2 license (see below).
The firmware includes following third-party software libraries:
- ``shellmatta`` by ``Stefan Strobel`` <https://git.shimatta.net/shimatta/shellmatta>, licensed under ``Mozilla Public License Version 2.0``
- ``CMSIS`` by ``ARM Limited`` <https://github.com/ARM-software/CMSIS_5>, licensed under ``Apache License Version 2.0, January 2004``
- ``STM Header files and startup code`` by ``ST Microelectronics``, licensed under a `custom license <ST License for Used Header Files_>`_
ST License for Used Header Files
--------------------------------
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of STMicroelectronics nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
GNU GENERAL PUBLIC LICENSE
--------------------------
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
~~~~~~~~
The licenses for most software are designed to take away your freedom
to share and change it. By contrast, the GNU General Public License is
intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if
you distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on,
we want its recipients to know that what they have is not the
original, so that any problems introduced by others will not reflect
on the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at
all.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**0.** This License applies to any program or other work which
contains a notice placed by the copyright holder saying it may be
distributed under the terms of this General Public License. The
"Program", below, refers to any such program or work, and a "work
based on the Program" means either the Program or any derivative work
under copyright law: that is to say, a work containing the Program or
a portion of it, either verbatim or with modifications and/or
translated into another language. (Hereinafter, translation is
included without limitation in the term "modification".) Each licensee
is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the Program
(independent of having been made by running the Program). Whether that
is true depends on what the Program does.
**1.** You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a
fee.
**2.** You may modify your copy or copies of the Program or any
portion of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
**a)** You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
**b)** You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any part
thereof, to be licensed as a whole at no charge to all third parties
under the terms of this License.
**c)** If the modified program normally reads commands interactively
when run, you must cause it, when started running for such interactive
use in the most ordinary way, to print or display an announcement
including an appropriate copyright notice and a notice that there is
no warranty (or else, saying that you provide a warranty) and that
users may redistribute the program under these conditions, and telling
the user how to view a copy of this License. (Exception: if the
Program itself is interactive but does not normally print such an
announcement, your work based on the Program is not required to print
an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
**3.** You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
**a)** Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections 1
and 2 above on a medium customarily used for software interchange; or,
**b)** Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your cost of
physically performing source distribution, a complete machine-readable
copy of the corresponding source code, to be distributed under the
terms of Sections 1 and 2 above on a medium customarily used for
software interchange; or,
**c)** Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is allowed
only for noncommercial distribution and only if you received the
program in object code or executable form with such an offer, in
accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
**4.** You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt otherwise
to copy, modify, sublicense or distribute the Program is void, and
will automatically terminate your rights under this License. However,
parties who have received copies, or rights, from you under this
License will not have their licenses terminated so long as such
parties remain in full compliance.
**5.** You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
**6.** Each time you redistribute the Program (or any work based on
the Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
**7.** If, as a consequence of a court judgment or allegation of
patent infringement or for any other reason (not limited to patent
issues), conditions are imposed on you (whether by court order,
agreement or otherwise) that contradict the conditions of this
License, they do not excuse you from the conditions of this License.
If you cannot distribute so as to satisfy simultaneously your
obligations under this License and any other pertinent obligations,
then as a consequence you may not distribute the Program at all. For
example, if a patent license would not permit royalty-free
redistribution of the Program by all those who receive copies directly
or indirectly through you, then the only way you could satisfy both it
and this License would be to refrain entirely from distribution of the
Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
**8.** If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
**9.** The Free Software Foundation may publish revised and/or new
versions of the General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Program does not specify a
version number of this License, you may choose any version ever
published by the Free Software Foundation.
**10.** If you wish to incorporate parts of the Program into other
free programs whose distribution conditions are different, write to
the author to ask for permission. For software which is copyrighted by
the Free Software Foundation, write to the Free Software Foundation;
we sometimes make exceptions for this. Our decision will be guided by
the two goals of preserving the free status of all derivatives of our
free software and of promoting the sharing and reuse of software
generally.
**NO WARRANTY**
**11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
**12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these
terms.
To do so, attach the following notices to the program. It is safest to
attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
one line to give the program's name and an idea of what it does.
Copyright (C) yyyy name of author
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Also add information on how to contact you by electronic and paper
mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
type ``show w``. This is free software, and you are welcome
to redistribute it under certain conditions; type ``show c``
for details.
The hypothetical commands \`show w' and \`show c' should show the
appropriate parts of the General Public License. Of course, the
commands you use may be called something other than \`show w' and
\`show c'; they could even be mouse-clicks or menu items--whatever
suits your program.
You should also get your employer (if you work as a programmer) or
your school, if any, to sign a "copyright disclaimer" for the program,
if necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright
interest in the program ``Gnomovision``
(which makes passes at compilers) written
by James Hacker.
signature of Ty Coon, 1 April 1989
Ty Coon, President of Vice``
This General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library,
you may consider it more useful to permit linking proprietary
applications with the library. If this is what you want to do, use the
[GNU Lesser General Public
License](<https://www.gnu.org/licenses/lgpl.html>) instead of this
License.

View File

@@ -0,0 +1,17 @@
.. _usage_calibration:
Calibration
===========
In order to provide higher measurement accuracy, the PT1000 measurement can be calibrated. The calibration only calibrates the internal :ref:`hw_analog_fe` and not the PT1000 Sensor element itself.
The Sensor element must be conform with the standard PT1000 norms.
Tests have shown, that a calibration is most likely not necessary, because the resolution of the 12 bit analog measurement is far worse than the reistance reading error produced by the :ref:`hw_analog_fe`.
Calibration might only be necessary if no precission reistors in the frontend hardware are used.
Calibration can be performed the following ways:
Command Line Calibration
------------------------
Use the :ref:`command_line` to invoke the :ref:`shell_command_calibrate` command.

View File

@@ -0,0 +1,88 @@
.. _command_line:
Command Line Interface
======================
This section describes the command line interface located on the UART interface.
The command line interface is implemented using a "shellmatta" shell module <https://git.shimatta.net/shimatta/shellmatta>
Hardware Settings
-----------------
General Settings
~~~~~~~~~~~~~~~~
The UART is configured for the following settings:
- 115200 Baud
- 1 Stopbit
- No parity
- 8 data bits
Setup in Debug Build
~~~~~~~~~~~~~~~~~~~~
If the Reflow controller is build in **debug** mode, the UART is located on the internal spring contact connector, which is also used for the SWD interface.
Setup in Release Build
~~~~~~~~~~~~~~~~~~~~~~
In case of a **release** build, the UART is externally accessible on the DIGIO Header. The voltage level is 3.3 Volt LVCMOS. The inputs are ESD protected. Overvoltage is interally clamped and may dammage the clamping diodes!
- DIGIO2: Reflow Controller's TX
- DIGIO3: Reflow Controller's RX
Shell Commands
--------------
The following shell commands are available.
- `safety-flags <safety-flags_>`_ (alias: flags)
- `calibrate`_ (alias: cal)
safety-flags
~~~~~~~~~~~~
The ``safety-flags`` (``flags``) command displays the status of all safety flags and analog monitors. See: :ref:`safety_flags`
.. _shell_command_calibrate:
calibrate
~~~~~~~~~
The ``calibrate`` (``cal``) command is used to calibrate the :ref:`hw_analog_fe`, in order to ensure correct resistance measurement.
Calibration is most likely not necessary! See the :ref:`usage_calibration` page.
The command will guide you through the calibration process and will ask for two reference resistors with ``1000 Ohm`` and ``2000 Ohm`` values.
Calibration can be aborted using ``CTRL + C``.
.. _shell_command_hang:
hang
~~~~
The ``hang`` command hangs the main-loop in an infinite loop. This function tests, whether the controller is correctly rescued by the watchdog.
.. _shell_command_ui_emulate:
ui-emulate
~~~~~~~~~~
The ``ui-emulate`` command emulates the rotary encoder and button from the shell. The following keys are available:
========== ================================
Key Emulation
========== ================================
``CTRL+C`` Exit the command
``ENTER`` Button press: short released
``s`` Rotary Encoder: anti-clockwise
``w`` Rotary Encoder: clockwise
``l`` Button press: long
``k`` Button press: short
``r`` Button press: long released
========== ================================

View File

@@ -0,0 +1,12 @@
.. _usage:
Reflow Controller Usage Guide
=============================
.. toctree::
:maxdepth: 2
command-line
calibration

View File

@@ -3,17 +3,18 @@
#Compiler: arm-none-eabi
#####################################################################################
#Add Files and Folders below#########################################################
CFILES = main.c syscalls.c setup/system_stm32f4xx.c systick.c
ASFILES = boot/startup_stm32f4xx.S
CFILES = main.c syscalls.c setup/system_stm32f4xx.c systick.c boot/startup_stm32f407vx.c
ASFILES =
INCLUDEPATH = -Iinclude
OBJDIR_BASE = obj
TARGET_BASE = reflow-controller
LIBRARYPATH = -L. -Lmathlib
LIBRARIES = -larm_cortexM4lf_math -lm
LIBRARYPATH = -L.
LIBRARIES =
DEFINES = -DSTM32F407xx -DSTM32F4XX -DARM_MATH_CM4 -DHSE_VALUE=8000000UL
MAPFILE_BASE = memory-mapping
LINKER_SCRIPT=stm32f407vet6_flash.ld
export GIT_VER = $(shell git describe --always --dirty --tags)
DEFINES += -DGIT_VER=$(GIT_VER)
@@ -34,22 +35,30 @@ DEFINES += -DSHELLMATTA_HELP_ALIAS=\"?\"
# RCC Manager
CFILES += stm-periph/clock-enable-manager.c
CFILES += stm-periph/uart.c stm-periph/dma-ring-buffer.c
CFILES += stm-periph/uart.c stm-periph/dma-ring-buffer.c stm-periph/backup-ram.c
CFILES += stm-periph/rng.c
CFILES += digio.c
CFILES += stm-periph/unique-id.c
CFILES += calibration.c
CFILES += temp-converter.c
CFILES += rotary-encoder.c button.c
CFILES += stack-check.c
CFILES += ui/lcd.c ui/menu.c reflow-menu.c
#CFILES += onewire-temp-sensors.c
CFILES += fatfs/diskio.c fatfs/ff.c fatfs/ffsystem.c fatfs/ffunicode.c fatfs/shimatta_sdio_driver/shimatta_sdio.c
CFILES += pid-controller.c oven-driver.c
CFILES += settings/settings.c settings/settings-sd-card.c
CFILES += safety-adc.c
CFILES += stm-periph/crc-unit.c
CFILES += safety/safety-adc.c safety/safety-controller.c safety/watchdog.c safety/fault.c safety/safety-memory.c safety/stack-check.c
CFILES += config-parser/config-parser.c
INCLUDEPATH += -Iconfig-parser/include
CFILES += base64-lib/src/base64-lib.c
INCLUDEPATH += -Ibase64-lib/include
DEFINES += -DBASE64_LOOKUP_TABLE_SECTION="\".ccm.bss\""
DEBUG_DEFINES = -DDEBUGBUILD
RELEASE_DEFINES =
RELEASE_DEFINES = -DUART_ON_DEBUG_HEADER
###################################################################################
ifeq ($(CROSS_COMPILE),)
@@ -88,7 +97,7 @@ endif
LFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork
LFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 --disable-newlib-supplied-syscalls -nostartfiles
LFLAGS += -Tstm32f407vet6_flash.ld -Wl,-Map=$(MAPFILE).map
LFLAGS += -T$(LINKER_SCRIPT) -Wl,-Map=$(MAPFILE).map -Wl,--print-memory-usage
CFLAGS += -c -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork
CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16 -nostartfiles
@@ -115,9 +124,9 @@ debug:
$(QUIET)$(OBJCOPY) -O ihex $^ $@
#Linking
$(target).elf: $(OBJ) $(ASOBJ)
$(target).elf: $(OBJ) $(ASOBJ) $(LINKER_SCRIPT)
@echo [LD] $@
$(QUIET)$(CC) $(LFLAGS) $(LIBRARYPATH) -o $@ $^ $(LIBRARIES)
$(QUIET)$(CC) $(LFLAGS) $(LIBRARYPATH) -o $@ $(OBJ) $(ASOBJ) $(LIBRARIES)
$(QUIET)$(SIZE) $@
@echo "Built Version $(GIT_VER)"
@@ -148,8 +157,16 @@ disassemble: $(target).elf
objcopy: $(target).bin $(target).hex
mrproper: clean
@echo "Purging project files..."
ifneq ($(DEBUGBUILD),true)
@echo "Purging RELEASE project files"
else
@echo "Purging DEBUG project files"
endif
$(QUIET)rm -f $(target).pro $(target).creator $(target).files $(target).cflags $(target).cxxflags $(target).includes $(target).config
ifneq ($(DEBUGBUILD),true)
$(QUIET)$(MAKE) DEBUGBUILD=true mrproper
endif
clean:
@echo -n "Cleaning up derived files for "

View File

@@ -1,4 +1,4 @@
/* Reflow Oven Controller
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
@@ -18,44 +18,54 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file adc-meas.c
* @brief Implementation of the PT1000 measurement ADC and filtering functions
*/
#include <reflow-controller/adc-meas.h>
#include <stm32/stm32f4xx.h>
#include <cmsis/core_cm4.h>
#include <stm-periph/stm32-gpio-macros.h>
#include <stdlib.h>
#include <helper-macros/helper-macros.h>
#include <stm-periph/clock-enable-manager.h>
#include <reflow-controller/safety/safety-controller.h>
static float pt1000_offset;
static float pt1000_sens_dev;
static bool calibration_active;
static float filter_alpha;
static volatile float pt1000_res_raw_lf;
static volatile bool filter_ready;
static volatile enum adc_pt1000_error pt1000_error = ADC_PT1000_INACTIVE;
static volatile int * volatile streaming_flag_ptr = NULL;
static uint32_t filter_startup_cnt;
static volatile float adc_pt1000_raw_reading_hf;
static float IN_SECTION(.ccm.bss) pt1000_offset;
static float IN_SECTION(.ccm.bss) pt1000_sens_dev;
static bool IN_SECTION(.ccm.bss) calibration_active;
static float IN_SECTION(.ccm.bss) filter_alpha;
/**
* @brief Filtered PT1000 resistance value.
* @note This value is not yet calibrated.
* Use @ref adc_pt1000_get_current_resistance to get this value with calibration.
*/
static volatile float IN_SECTION(.ccm.bss) pt1000_res_raw_lf;
static volatile int * volatile streaming_flag_ptr;
static uint32_t IN_SECTION(.ccm.bss) filter_startup_cnt;
static volatile float IN_SECTION(.ccm.bss) adc_pt1000_raw_reading_hf;
static volatile uint16_t dma_sample_buffer[ADC_PT1000_DMA_AVG_SAMPLES];
static volatile uint32_t adc_watchdog_counter = 0UL;
static volatile uint32_t IN_SECTION(.ccm.bss) adc_watchdog_counter;
volatile float * volatile stream_buffer = NULL;
volatile float * volatile stream_buffer;
volatile uint32_t stream_count;
volatile uint32_t stream_pos;
#define ADC_TO_RES(adc) ((float)(adc) / 4096.0f * 2500.0f)
static inline void adc_pt1000_stop_sample_frequency_timer()
static inline void adc_pt1000_stop_sample_frequency_timer(void)
{
TIM2->CR1 &= ~TIM_CR1_CEN;
rcc_manager_disable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(RCC_APB1ENR_TIM2EN));
}
static inline void adc_pt1000_setup_sample_frequency_timer()
static inline void adc_pt1000_setup_sample_frequency_timer(void)
{
rcc_manager_enable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(RCC_APB1ENR_TIM2EN));
/* Divide 42 MHz peripheral clock by 42 */
TIM2->PSC = (42UL-1UL);
/* Divide 2*42 MHz peripheral clock by 42 */
TIM2->PSC = (84UL-1UL);
/* Reload value */
TIM2->ARR = ADC_PT1000_SAMPLE_CNT_DELAY;
@@ -68,13 +78,13 @@ static inline void adc_pt1000_setup_sample_frequency_timer()
}
static inline void adc_pt1000_disable_adc()
static inline void adc_pt1000_disable_adc(void)
{
ADC_PT1000_PERIPH->CR2 &= ~ADC_CR2_ADON;
DMA2_Stream0->CR = 0;
pt1000_error |= ADC_PT1000_INACTIVE;
safety_controller_report_error_with_key(ERR_FLAG_MEAS_ADC_OFF, MEAS_ADC_SAFETY_FLAG_KEY);
safety_controller_enable_timing_mon(ERR_TIMING_MEAS_ADC, false);
rcc_manager_disable_clock(&RCC->APB2ENR, BITMASK_TO_BITNO(RCC_APB2ENR_ADC3EN));
rcc_manager_disable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(ADC_PT1000_PORT_RCC_MASK));
}
@@ -90,7 +100,7 @@ static inline void adc_pt1000_disable_adc()
* After that, the moving average filter is fed with the values.
*
*/
static inline void adc_pt1000_enable_dma_stream()
static inline void adc_pt1000_enable_dma_stream(void)
{
/* Enable peripheral clock for DMA2 */
rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(RCC_AHB1ENR_DMA2EN));
@@ -115,7 +125,7 @@ static inline void adc_pt1000_enable_dma_stream()
DMA_SxCR_CIRC | DMA_SxCR_TCIE | DMA_SxCR_TEIE | DMA_SxCR_EN | ((ADC_PT1000_CHANNEL & 0x7)<<25);
}
static inline void adc_pt1000_disable_dma_stream()
static inline void adc_pt1000_disable_dma_stream(void)
{
/* Disable the stream */
DMA2_Stream0->CR = 0;
@@ -127,7 +137,7 @@ static inline void adc_pt1000_disable_dma_stream()
NVIC_DisableIRQ(DMA2_Stream0_IRQn);
}
void adc_pt1000_setup_meas()
void adc_pt1000_setup_meas(void)
{
rcc_manager_enable_clock(&RCC->APB2ENR, BITMASK_TO_BITNO(RCC_APB2ENR_ADC3EN));
rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(ADC_PT1000_PORT_RCC_MASK));
@@ -154,7 +164,8 @@ void adc_pt1000_setup_meas()
ADC_PT1000_PERIPH->SQR3 = (ADC_PT1000_CHANNEL<<0);
ADC_PT1000_PERIPH->CR1 = ADC_CR1_OVRIE | ADC_CR1_AWDEN | ADC_CR1_AWDIE;
ADC_PT1000_PERIPH->CR2 = ADC_CR2_EXTEN_0 | ADC_CR2_EXTSEL_2 | ADC_CR2_EXTSEL_1 | ADC_CR2_ADON | ADC_CR2_DMA | ADC_CR2_DDS;
ADC_PT1000_PERIPH->CR2 = ADC_CR2_EXTEN_0 | ADC_CR2_EXTSEL_2 | ADC_CR2_EXTSEL_1 |
ADC_CR2_ADON | ADC_CR2_DMA | ADC_CR2_DDS;
adc_pt1000_set_moving_average_filter_param(ADC_PT1000_FILTER_WEIGHT);
adc_pt1000_set_resistance_calibration(0, 0, false);
@@ -166,13 +177,17 @@ void adc_pt1000_setup_meas()
adc_pt1000_setup_sample_frequency_timer();
pt1000_error &= ~ADC_PT1000_INACTIVE;
safety_controller_ack_flag_with_key(ERR_FLAG_MEAS_ADC_OFF, MEAS_ADC_SAFETY_FLAG_KEY);
streaming_flag_ptr = NULL;
adc_watchdog_counter = 0UL;
stream_buffer = NULL;
}
void adc_pt1000_set_moving_average_filter_param(float alpha)
{
filter_alpha = alpha;
filter_ready = false;
safety_controller_report_error_with_key(ERR_FLAG_MEAS_ADC_UNSTABLE, MEAS_ADC_SAFETY_FLAG_KEY);
filter_startup_cnt = ADC_FILTER_STARTUP_CYCLES;
}
@@ -181,16 +196,21 @@ void adc_pt1000_set_resistance_calibration(float offset, float sensitivity_devia
pt1000_offset = offset;
pt1000_sens_dev = sensitivity_deviation;
calibration_active = active;
if (!calibration_active)
safety_controller_report_error_with_key(ERR_FLAG_UNCAL, MEAS_ADC_SAFETY_FLAG_KEY);
else
safety_controller_ack_flag_with_key(ERR_FLAG_UNCAL, MEAS_ADC_SAFETY_FLAG_KEY);
}
void adc_pt1000_get_resistance_calibration(float *offset, float *sensitivity_deviation, bool *active)
{
if (!offset || !sensitivity_deviation || !active)
return;
*offset = pt1000_offset;
*sensitivity_deviation = pt1000_sens_dev;
*active = calibration_active;
if (offset)
*offset = pt1000_offset;
if (sensitivity_deviation)
*sensitivity_deviation = pt1000_sens_dev;
if (active)
*active = calibration_active;
}
static inline float adc_pt1000_apply_calibration(float raw_resistance)
@@ -205,18 +225,23 @@ static inline float adc_pt1000_apply_calibration(float raw_resistance)
int adc_pt1000_get_current_resistance(float *resistance)
{
int ret_val = 0;
bool flag = true;
if (!resistance)
return -1001;
*resistance = adc_pt1000_apply_calibration(pt1000_res_raw_lf);
if (adc_pt1000_check_error()) {
if (safety_controller_get_flags_by_mask(ERR_FLAG_MEAS_ADC_OFF | ERR_FLAG_MEAS_ADC_OVERFLOW |
ERR_FLAG_MEAS_ADC_WATCHDOG)) {
ret_val = -100;
goto return_value;
}
if (!filter_ready) {
(void)safety_controller_get_flag(ERR_FLAG_MEAS_ADC_UNSTABLE, &flag, false);
if (flag) {
ret_val = 2;
goto return_value;
}
@@ -260,25 +285,15 @@ void adc_pt1000_convert_raw_value_array_to_resistance(float *resistance_dest, fl
resistance_dest[i] = ADC_TO_RES(raw_source[i]);
}
enum adc_pt1000_error adc_pt1000_check_error()
{
return pt1000_error;
}
void adc_pt1000_clear_error()
{
pt1000_error &= ~ADC_PT1000_OVERFLOW & ~ADC_PT1000_WATCHDOG_ERROR;
}
void adc_pt1000_disable()
void adc_pt1000_disable(void)
{
adc_pt1000_disable_adc();
adc_pt1000_stop_sample_frequency_timer();
adc_pt1000_disable_dma_stream();
filter_ready = false;
pt1000_res_raw_lf = 0.0f;
pt1000_error |= ADC_PT1000_INACTIVE;
safety_controller_report_error_with_key(ERR_FLAG_MEAS_ADC_OFF, MEAS_ADC_SAFETY_FLAG_KEY);
safety_controller_report_error_with_key(ERR_FLAG_MEAS_ADC_UNSTABLE, MEAS_ADC_SAFETY_FLAG_KEY);
if (streaming_flag_ptr) {
*streaming_flag_ptr = -3;
@@ -288,13 +303,19 @@ void adc_pt1000_disable()
static inline __attribute__((optimize("O3"))) void adc_pt1000_filter(float adc_prefiltered_value)
{
if (!filter_ready && --filter_startup_cnt <= 0)
filter_ready = true;
if (filter_startup_cnt > 0) {
filter_startup_cnt--;
if (filter_startup_cnt == 0)
safety_controller_ack_flag_with_key(ERR_FLAG_MEAS_ADC_UNSTABLE, MEAS_ADC_SAFETY_FLAG_KEY);
}
pt1000_res_raw_lf = (1.0f-filter_alpha) * pt1000_res_raw_lf + filter_alpha * ADC_TO_RES(adc_prefiltered_value);
pt1000_res_raw_lf = (1.0f - filter_alpha) * pt1000_res_raw_lf +
filter_alpha * ADC_TO_RES(adc_prefiltered_value);
safety_controller_report_timing(ERR_TIMING_MEAS_ADC);
}
static inline __attribute__((optimize("O3"))) float adc_pt1000_dma_avg_pre_filter()
static inline __attribute__((optimize("O3"))) float adc_pt1000_dma_avg_pre_filter(void)
{
unsigned int i;
uint32_t sum = 0;
@@ -328,7 +349,7 @@ void ADC_IRQHandler(void)
if (adc1_sr & ADC_SR_OVR) {
ADC_PT1000_PERIPH->SR &= ~ADC_SR_OVR;
pt1000_error |= ADC_PT1000_OVERFLOW;
safety_controller_report_error(ERR_FLAG_MEAS_ADC_OVERFLOW);
/* Disable ADC in case of overrrun*/
adc_pt1000_disable();
}
@@ -337,7 +358,7 @@ void ADC_IRQHandler(void)
ADC_PT1000_PERIPH->SR &= ~ADC_SR_AWD;
adc_watchdog_counter++;
if (adc_watchdog_counter >= ADC_PT1000_WATCHDOG_SAMPLE_COUNT)
pt1000_error |= ADC_PT1000_WATCHDOG_ERROR;
safety_controller_report_error(ERR_FLAG_MEAS_ADC_WATCHDOG);
}
}
@@ -356,7 +377,7 @@ static void append_stream_buffer(float val)
}
void DMA2_Stream0_IRQHandler()
void DMA2_Stream0_IRQHandler(void)
{
uint32_t lisr;
float adc_val;
@@ -379,8 +400,7 @@ void DMA2_Stream0_IRQHandler()
adc_pt1000_filter(adc_val);
}
if (lisr & DMA_LISR_TEIF0) {
if (lisr & DMA_LISR_TEIF0)
adc_pt1000_disable();
}
}

View File

@@ -0,0 +1,329 @@
/*
* STM32F4 Startup Code for STM32F407 devices
* Copyright (C) 2017 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of 'STM32F4 code template'.
*
* It 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, version 2 of the License.
*
* This code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this template. If not, see <http://www.gnu.org/licenses/>.
* ------------------------------------------------------------------------
*/
#include <stdint.h>
/* C++ library init */
# if defined(__cplusplus)
extern "C" {
extern void __libc_init_array(void);
}
#endif
/* Defines for weak default handlers */
#define WEAK __attribute__((weak))
#define ALIAS(func) __attribute__ ((weak, alias (#func)))
/* Define for section mapping */
#define SECTION(sec) __attribute__((section(sec)))
/* Handler prototypes */
#if defined(_cplusplus)
extern "C" {
#endif
/* Interrupt Defualt handler */
WEAK void __int_default_handler(void);
/* Core Interrupts */
void Reset_Handler(void);
void NMI_Handler(void) ALIAS(__int_default_handler);
void HardFault_Handler(void) ALIAS(__int_default_handler);
void MemManage_Handler(void) ALIAS(__int_default_handler);
void BusFault_Handler(void) ALIAS(__int_default_handler);
void UsageFault_Handler(void) ALIAS(__int_default_handler);
void SVC_Handler(void) ALIAS(__int_default_handler);
void DebugMon_Handler(void) ALIAS(__int_default_handler);
void PendSV_Handler(void) ALIAS(__int_default_handler);
void SysTick_Handler(void) ALIAS(__int_default_handler);
/* Peripheral Interrupts (by default mapped onto Default Handler) */
void WWDG_IRQHandler(void) ALIAS(__int_default_handler);
void PVD_IRQHandler(void) ALIAS(__int_default_handler);
void TAMP_STAMP_IRQHandler(void) ALIAS(__int_default_handler);
void RTC_WKUP_IRQHandler(void) ALIAS(__int_default_handler);
void FLASH_IRQHandler(void) ALIAS(__int_default_handler);
void RCC_IRQHandler(void) ALIAS(__int_default_handler);
void EXTI0_IRQHandler(void) ALIAS(__int_default_handler);
void EXTI1_IRQHandler(void) ALIAS(__int_default_handler);
void EXTI2_IRQHandler(void) ALIAS(__int_default_handler);
void EXTI3_IRQHandler(void) ALIAS(__int_default_handler);
void EXTI4_IRQHandler(void) ALIAS(__int_default_handler);
void DMA1_Stream0_IRQHandler(void) ALIAS(__int_default_handler);
void DMA1_Stream1_IRQHandler(void) ALIAS(__int_default_handler);
void DMA1_Stream2_IRQHandler(void) ALIAS(__int_default_handler);
void DMA1_Stream3_IRQHandler(void) ALIAS(__int_default_handler);
void DMA1_Stream4_IRQHandler(void) ALIAS(__int_default_handler);
void DMA1_Stream5_IRQHandler(void) ALIAS(__int_default_handler);
void DMA1_Stream6_IRQHandler(void) ALIAS(__int_default_handler);
void ADC_IRQHandler(void) ALIAS(__int_default_handler);
void CAN1_TX_IRQHandler(void) ALIAS(__int_default_handler);
void CAN1_RX0_IRQHandler(void) ALIAS(__int_default_handler);
void CAN1_RX1_IRQHandler(void) ALIAS(__int_default_handler);
void CAN1_SCE_IRQHandler(void) ALIAS(__int_default_handler);
void EXTI9_5_IRQHandler(void) ALIAS(__int_default_handler);
void TIM1_BRK_TIM9_IRQHandler(void) ALIAS(__int_default_handler);
void TIM1_UP_TIM10_IRQHandler(void) ALIAS(__int_default_handler);
void TIM1_TRG_COM_TIM11_IRQHandler(void) ALIAS(__int_default_handler);
void TIM1_CC_IRQHandler(void) ALIAS(__int_default_handler);
void TIM2_IRQHandler(void) ALIAS(__int_default_handler);
void TIM3_IRQHandler(void) ALIAS(__int_default_handler);
void TIM4_IRQHandler(void) ALIAS(__int_default_handler);
void I2C1_EV_IRQHandler(void) ALIAS(__int_default_handler);
void I2C1_ER_IRQHandler(void) ALIAS(__int_default_handler);
void I2C2_EV_IRQHandler(void) ALIAS(__int_default_handler);
void I2C2_ER_IRQHandler(void) ALIAS(__int_default_handler);
void SPI1_IRQHandler(void) ALIAS(__int_default_handler);
void SPI2_IRQHandler(void) ALIAS(__int_default_handler);
void USART1_IRQHandler(void) ALIAS(__int_default_handler);
void USART2_IRQHandler(void) ALIAS(__int_default_handler);
void USART3_IRQHandler(void) ALIAS(__int_default_handler);
void EXTI15_10_IRQHandler(void) ALIAS(__int_default_handler);
void RTC_Alarm_IRQHandler(void) ALIAS(__int_default_handler);
void OTG_FS_WKUP_IRQHandler(void) ALIAS(__int_default_handler);
void TIM8_BRK_TIM12_IRQHandler(void) ALIAS(__int_default_handler);
void TIM8_UP_TIM13_IRQHandler(void) ALIAS(__int_default_handler);
void TIM8_TRG_COM_TIM14_IRQHandler(void) ALIAS(__int_default_handler);
void TIM8_CC_IRQHandler(void) ALIAS(__int_default_handler);
void DMA1_Stream7_IRQHandler(void) ALIAS(__int_default_handler);
void FSMC_IRQHandler(void) ALIAS(__int_default_handler);
void SDIO_IRQHandler(void) ALIAS(__int_default_handler);
void TIM5_IRQHandler(void) ALIAS(__int_default_handler);
void SPI3_IRQHandler(void) ALIAS(__int_default_handler);
void UART4_IRQHandler(void) ALIAS(__int_default_handler);
void UART5_IRQHandler(void) ALIAS(__int_default_handler);
void TIM6_DAC_IRQHandler(void) ALIAS(__int_default_handler);
void TIM7_IRQHandler(void) ALIAS(__int_default_handler);
void DMA2_Stream0_IRQHandler(void) ALIAS(__int_default_handler);
void DMA2_Stream1_IRQHandler(void) ALIAS(__int_default_handler);
void DMA2_Stream2_IRQHandler(void) ALIAS(__int_default_handler);
void DMA2_Stream3_IRQHandler(void) ALIAS(__int_default_handler);
void DMA2_Stream4_IRQHandler(void) ALIAS(__int_default_handler);
void ETH_IRQHandler(void) ALIAS(__int_default_handler);
void ETH_WKUP_IRQHandler(void) ALIAS(__int_default_handler);
void CAN2_TX_IRQHandler(void) ALIAS(__int_default_handler);
void CAN2_RX0_IRQHandler(void) ALIAS(__int_default_handler);
void CAN2_RX1_IRQHandler(void) ALIAS(__int_default_handler);
void CAN2_SCE_IRQHandler(void) ALIAS(__int_default_handler);
void OTG_FS_IRQHandler(void) ALIAS(__int_default_handler);
void DMA2_Stream5_IRQHandler(void) ALIAS(__int_default_handler);
void DMA2_Stream6_IRQHandler(void) ALIAS(__int_default_handler);
void DMA2_Stream7_IRQHandler(void) ALIAS(__int_default_handler);
void USART6_IRQHandler(void) ALIAS(__int_default_handler);
void I2C3_EV_IRQHandler(void) ALIAS(__int_default_handler);
void I2C3_ER_IRQHandler(void) ALIAS(__int_default_handler);
void OTG_HS_EP1_OUT_IRQHandler(void) ALIAS(__int_default_handler);
void OTG_HS_EP1_IN_IRQHandler(void) ALIAS(__int_default_handler);
void OTG_HS_WKUP_IRQHandler(void) ALIAS(__int_default_handler);
void OTG_HS_IRQHandler(void) ALIAS(__int_default_handler);
void DCMI_IRQHandler(void) ALIAS(__int_default_handler);
void CRYP_IRQHandler(void) ALIAS(__int_default_handler);
void HASH_RNG_IRQHandler(void) ALIAS(__int_default_handler);
void FPU_IRQHandler(void) ALIAS(__int_default_handler);
extern int main(void);
extern void SystemInit(void);
extern void __ld_top_of_stack(void);
#if defined(_cplusplus)
extern "C" }
#endif
void (* const vector_table[])(void) SECTION(".vectors") = {
&__ld_top_of_stack,
/* Core Interrupts */
Reset_Handler,
NMI_Handler,
HardFault_Handler,
MemManage_Handler,
BusFault_Handler,
UsageFault_Handler,
0,
0,
0,
0,
SVC_Handler,
DebugMon_Handler,
0,
PendSV_Handler,
SysTick_Handler,
/* Peripheral Interrupts */
WWDG_IRQHandler,
PVD_IRQHandler,
TAMP_STAMP_IRQHandler,
RTC_WKUP_IRQHandler,
FLASH_IRQHandler,
RCC_IRQHandler,
EXTI0_IRQHandler,
EXTI1_IRQHandler,
EXTI2_IRQHandler,
EXTI3_IRQHandler,
EXTI4_IRQHandler,
DMA1_Stream0_IRQHandler,
DMA1_Stream1_IRQHandler,
DMA1_Stream2_IRQHandler,
DMA1_Stream3_IRQHandler,
DMA1_Stream4_IRQHandler,
DMA1_Stream5_IRQHandler,
DMA1_Stream6_IRQHandler,
ADC_IRQHandler,
CAN1_TX_IRQHandler,
CAN1_RX0_IRQHandler,
CAN1_RX1_IRQHandler,
CAN1_SCE_IRQHandler,
EXTI9_5_IRQHandler,
TIM1_BRK_TIM9_IRQHandler,
TIM1_UP_TIM10_IRQHandler,
TIM1_TRG_COM_TIM11_IRQHandler,
TIM1_CC_IRQHandler,
TIM2_IRQHandler,
TIM3_IRQHandler,
TIM4_IRQHandler,
I2C1_EV_IRQHandler,
I2C1_ER_IRQHandler,
I2C2_EV_IRQHandler,
I2C2_ER_IRQHandler,
SPI1_IRQHandler,
SPI2_IRQHandler,
USART1_IRQHandler,
USART2_IRQHandler,
USART3_IRQHandler,
EXTI15_10_IRQHandler,
RTC_Alarm_IRQHandler,
OTG_FS_WKUP_IRQHandler,
TIM8_BRK_TIM12_IRQHandler,
TIM8_UP_TIM13_IRQHandler,
TIM8_TRG_COM_TIM14_IRQHandler,
TIM8_CC_IRQHandler,
DMA1_Stream7_IRQHandler,
FSMC_IRQHandler,
SDIO_IRQHandler,
TIM5_IRQHandler,
SPI3_IRQHandler,
UART4_IRQHandler,
UART5_IRQHandler,
TIM6_DAC_IRQHandler,
TIM7_IRQHandler,
DMA2_Stream0_IRQHandler,
DMA2_Stream1_IRQHandler,
DMA2_Stream2_IRQHandler,
DMA2_Stream3_IRQHandler,
DMA2_Stream4_IRQHandler,
ETH_IRQHandler,
ETH_WKUP_IRQHandler,
CAN2_TX_IRQHandler,
CAN2_RX0_IRQHandler,
CAN2_RX1_IRQHandler,
CAN2_SCE_IRQHandler,
OTG_FS_IRQHandler,
DMA2_Stream5_IRQHandler,
DMA2_Stream6_IRQHandler,
DMA2_Stream7_IRQHandler,
USART6_IRQHandler,
I2C3_EV_IRQHandler,
I2C3_ER_IRQHandler,
OTG_HS_EP1_OUT_IRQHandler,
OTG_HS_EP1_IN_IRQHandler,
OTG_HS_WKUP_IRQHandler,
OTG_HS_IRQHandler,
DCMI_IRQHandler,
CRYP_IRQHandler,
HASH_RNG_IRQHandler,
FPU_IRQHandler
};
static void __init_section(unsigned int *src_start, unsigned int *dest_start, unsigned int *dest_end) {
unsigned int *get, *put;
put = dest_start;
get = src_start;
while ((unsigned int)put < (unsigned int)dest_end) {
*(put++) = *(get++);
}
}
static void __fill_zero(unsigned int *start, unsigned int *end) {
while ((unsigned int) start < (unsigned int)end) {
*(start++) = 0x00000000;
}
}
extern unsigned int __ld_load_data;
extern unsigned int __ld_sdata_ccm;
extern unsigned int __ld_edata_ccm;
extern unsigned int __ld_load_ccm_data;
extern unsigned int __ld_sdata_ccm;
extern unsigned int __ld_edata_ccm;
extern unsigned int __ld_sbss_ccm;
extern unsigned int __ld_ebss_ccm;
extern unsigned int __ld_sdata;
extern unsigned int __ld_edata;
extern unsigned int __ld_sbss;
extern unsigned int __ld_ebss;
extern unsigned int __ld_sheap;
extern unsigned int __ld_eheap;
#ifdef CPACR
#undef CPACR
#endif
#define CPACR (*((volatile uint32_t *)0xE000ED88))
void Reset_Handler(void) {
/* Stack is already initialized by hardware */
/* The first thing we do here, is to initialize the FPU
* When this code is compiled optimized with hardfpu abi,
* GCC tends to generate FPU instructions for data copying
*/
CPACR |= (0xF << 20);
/* Copy .data section */
__init_section(&__ld_load_data, &__ld_sdata, &__ld_edata);
/* Fill bss with zero */
__fill_zero(&__ld_sbss, &__ld_ebss);
/* Fill Heap with zero */
__fill_zero(&__ld_sheap, &__ld_eheap);
/* Fill static CCM memory with zeroes */
__fill_zero(&__ld_sbss_ccm, &__ld_ebss_ccm);
/* Init CCM RAM data section */
__init_section(&__ld_load_ccm_data, &__ld_sdata_ccm, &__ld_edata_ccm);
/* Set clocks, waitstates, ART operation etc. */
SystemInit();
/* C++ init function */
#if defined(__cplusplus)
__libc_init_array();
#endif
/* Call main */
main();
/* Catch return from main() */
while(1);
}
WEAK void __int_default_handler(void)
{
while(1);
}

View File

@@ -1,519 +0,0 @@
/**
******************************************************************************
* @file startup_stm32f4xx.s
* @author MCD Application Team
* @version V1.0.0
* @date 30-September-2011
* @brief STM32F4xx Devices vector table for Atollic TrueSTUDIO toolchain.
* This module performs:
* - Set the initial SP
* - Set the initial PC == Reset_Handler,
* - Set the vector table entries with the exceptions ISR address
* - Configure the clock system and the external SRAM mounted on
* STM324xG-EVAL board to be used as data memory (optional,
* to be enabled by user)
* - Branches to main in the C library (which eventually
* calls main()).
* After Reset the Cortex-M4 processor is in Thread mode,
* priority is Privileged, and the Stack is set to Main.
******************************************************************************
* @attention
*
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
* TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
*
* <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
******************************************************************************
*/
.syntax unified
.cpu cortex-m4
.fpu softvfp
.thumb
.global g_pfnVectors
.global Default_Handler
/* start address for the initialization values of the .data section.
defined in linker script */
.word _sidata
/* start address for the .data section. defined in linker script */
.word _sdata
/* end address for the .data section. defined in linker script */
.word _edata
/* start address for the .bss section. defined in linker script */
.word _sbss
/* end address for the .bss section. defined in linker script */
.word _ebss
/* stack used for SystemInit_ExtMemCtl; always internal RAM used */
/**
* @brief This is the code that gets called when the processor first
* starts execution following a reset event. Only the absolutely
* necessary set is performed, after which the application
* supplied main() routine is called.
* @param None
* @retval : None
*/
.section .text.Reset_Handler
.weak Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
/* Copy the data segment initializers from flash to SRAM */
movs r1, #0
b LoopCopyDataInit
CopyDataInit:
ldr r3, =_sidata
ldr r3, [r3, r1]
str r3, [r0, r1]
adds r1, r1, #4
LoopCopyDataInit:
ldr r0, =_sdata
ldr r3, =_edata
adds r2, r0, r1
cmp r2, r3
bcc CopyDataInit
ldr r2, =_sbss
b LoopFillZerobss
/* Zero fill the bss segment. */
FillZerobss:
movs r3, #0
str r3, [r2], #4
LoopFillZerobss:
ldr r3, = _ebss
cmp r2, r3
bcc FillZerobss
/* Call the clock system intitialization function.*/
bl SystemInit
/* Call static constructors */
//bl __libc_init_array
/* Call the application's entry point.*/
/* Enable FPU hard */
LDR.W R0, =0xE000ED88
LDR R1, [R0]
ORR R1, R1, #(0xF << 20)
STR R1, [R0]
bl main
bx lr
.size Reset_Handler, .-Reset_Handler
/**
* @brief This is the code that gets called when the processor receives an
* unexpected interrupt. This simply enters an infinite loop, preserving
* the system state for examination by a debugger.
* @param None
* @retval None
*/
.section .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
b Infinite_Loop
.size Default_Handler, .-Default_Handler
/******************************************************************************
*
* The minimal vector table for a Cortex M3. Note that the proper constructs
* must be placed on this to ensure that it ends up at physical address
* 0x0000.0000.
*
*******************************************************************************/
.section .isr_vector,"a",%progbits
.type g_pfnVectors, %object
.size g_pfnVectors, .-g_pfnVectors
g_pfnVectors:
.word _estack
.word Reset_Handler
.word NMI_Handler
.word HardFault_Handler
.word MemManage_Handler
.word BusFault_Handler
.word UsageFault_Handler
.word 0
.word 0
.word 0
.word 0
.word SVC_Handler
.word DebugMon_Handler
.word 0
.word PendSV_Handler
.word SysTick_Handler
/* External Interrupts */
.word WWDG_IRQHandler /* Window WatchDog */
.word PVD_IRQHandler /* PVD through EXTI Line detection */
.word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXTI line */
.word RTC_WKUP_IRQHandler /* RTC Wakeup through the EXTI line */
.word FLASH_IRQHandler /* FLASH */
.word RCC_IRQHandler /* RCC */
.word EXTI0_IRQHandler /* EXTI Line0 */
.word EXTI1_IRQHandler /* EXTI Line1 */
.word EXTI2_IRQHandler /* EXTI Line2 */
.word EXTI3_IRQHandler /* EXTI Line3 */
.word EXTI4_IRQHandler /* EXTI Line4 */
.word DMA1_Stream0_IRQHandler /* DMA1 Stream 0 */
.word DMA1_Stream1_IRQHandler /* DMA1 Stream 1 */
.word DMA1_Stream2_IRQHandler /* DMA1 Stream 2 */
.word DMA1_Stream3_IRQHandler /* DMA1 Stream 3 */
.word DMA1_Stream4_IRQHandler /* DMA1 Stream 4 */
.word DMA1_Stream5_IRQHandler /* DMA1 Stream 5 */
.word DMA1_Stream6_IRQHandler /* DMA1 Stream 6 */
.word ADC_IRQHandler /* ADC1, ADC2 and ADC3s */
.word CAN1_TX_IRQHandler /* CAN1 TX */
.word CAN1_RX0_IRQHandler /* CAN1 RX0 */
.word CAN1_RX1_IRQHandler /* CAN1 RX1 */
.word CAN1_SCE_IRQHandler /* CAN1 SCE */
.word EXTI9_5_IRQHandler /* External Line[9:5]s */
.word TIM1_BRK_TIM9_IRQHandler /* TIM1 Break and TIM9 */
.word TIM1_UP_TIM10_IRQHandler /* TIM1 Update and TIM10 */
.word TIM1_TRG_COM_TIM11_IRQHandler /* TIM1 Trigger and Commutation and TIM11 */
.word TIM1_CC_IRQHandler /* TIM1 Capture Compare */
.word TIM2_IRQHandler /* TIM2 */
.word TIM3_IRQHandler /* TIM3 */
.word TIM4_IRQHandler /* TIM4 */
.word I2C1_EV_IRQHandler /* I2C1 Event */
.word I2C1_ER_IRQHandler /* I2C1 Error */
.word I2C2_EV_IRQHandler /* I2C2 Event */
.word I2C2_ER_IRQHandler /* I2C2 Error */
.word SPI1_IRQHandler /* SPI1 */
.word SPI2_IRQHandler /* SPI2 */
.word USART1_IRQHandler /* USART1 */
.word USART2_IRQHandler /* USART2 */
.word USART3_IRQHandler /* USART3 */
.word EXTI15_10_IRQHandler /* External Line[15:10]s */
.word RTC_Alarm_IRQHandler /* RTC Alarm (A and B) through EXTI Line */
.word OTG_FS_WKUP_IRQHandler /* USB OTG FS Wakeup through EXTI line */
.word TIM8_BRK_TIM12_IRQHandler /* TIM8 Break and TIM12 */
.word TIM8_UP_TIM13_IRQHandler /* TIM8 Update and TIM13 */
.word TIM8_TRG_COM_TIM14_IRQHandler /* TIM8 Trigger and Commutation and TIM14 */
.word TIM8_CC_IRQHandler /* TIM8 Capture Compare */
.word DMA1_Stream7_IRQHandler /* DMA1 Stream7 */
.word FSMC_IRQHandler /* FSMC */
.word SDIO_IRQHandler /* SDIO */
.word TIM5_IRQHandler /* TIM5 */
.word SPI3_IRQHandler /* SPI3 */
.word UART4_IRQHandler /* UART4 */
.word UART5_IRQHandler /* UART5 */
.word TIM6_DAC_IRQHandler /* TIM6 and DAC1&2 underrun errors */
.word TIM7_IRQHandler /* TIM7 */
.word DMA2_Stream0_IRQHandler /* DMA2 Stream 0 */
.word DMA2_Stream1_IRQHandler /* DMA2 Stream 1 */
.word DMA2_Stream2_IRQHandler /* DMA2 Stream 2 */
.word DMA2_Stream3_IRQHandler /* DMA2 Stream 3 */
.word DMA2_Stream4_IRQHandler /* DMA2 Stream 4 */
.word ETH_IRQHandler /* Ethernet */
.word ETH_WKUP_IRQHandler /* Ethernet Wakeup through EXTI line */
.word CAN2_TX_IRQHandler /* CAN2 TX */
.word CAN2_RX0_IRQHandler /* CAN2 RX0 */
.word CAN2_RX1_IRQHandler /* CAN2 RX1 */
.word CAN2_SCE_IRQHandler /* CAN2 SCE */
.word OTG_FS_IRQHandler /* USB OTG FS */
.word DMA2_Stream5_IRQHandler /* DMA2 Stream 5 */
.word DMA2_Stream6_IRQHandler /* DMA2 Stream 6 */
.word DMA2_Stream7_IRQHandler /* DMA2 Stream 7 */
.word USART6_IRQHandler /* USART6 */
.word I2C3_EV_IRQHandler /* I2C3 event */
.word I2C3_ER_IRQHandler /* I2C3 error */
.word OTG_HS_EP1_OUT_IRQHandler /* USB OTG HS End Point 1 Out */
.word OTG_HS_EP1_IN_IRQHandler /* USB OTG HS End Point 1 In */
.word OTG_HS_WKUP_IRQHandler /* USB OTG HS Wakeup through EXTI */
.word OTG_HS_IRQHandler /* USB OTG HS */
.word DCMI_IRQHandler /* DCMI */
.word CRYP_IRQHandler /* CRYP crypto */
.word HASH_RNG_IRQHandler /* Hash and Rng */
.word FPU_IRQHandler /* FPU */
/*******************************************************************************
*
* Provide weak aliases for each Exception handler to the Default_Handler.
* As they are weak aliases, any function with the same name will override
* this definition.
*
*******************************************************************************/
.weak NMI_Handler
.thumb_set NMI_Handler,Default_Handler
.weak HardFault_Handler
.thumb_set HardFault_Handler,Default_Handler
.weak MemManage_Handler
.thumb_set MemManage_Handler,Default_Handler
.weak BusFault_Handler
.thumb_set BusFault_Handler,Default_Handler
.weak UsageFault_Handler
.thumb_set UsageFault_Handler,Default_Handler
.weak SVC_Handler
.thumb_set SVC_Handler,Default_Handler
.weak DebugMon_Handler
.thumb_set DebugMon_Handler,Default_Handler
.weak PendSV_Handler
.thumb_set PendSV_Handler,Default_Handler
.weak SysTick_Handler
.thumb_set SysTick_Handler,Default_Handler
.weak WWDG_IRQHandler
.thumb_set WWDG_IRQHandler,Default_Handler
.weak PVD_IRQHandler
.thumb_set PVD_IRQHandler,Default_Handler
.weak TAMP_STAMP_IRQHandler
.thumb_set TAMP_STAMP_IRQHandler,Default_Handler
.weak RTC_WKUP_IRQHandler
.thumb_set RTC_WKUP_IRQHandler,Default_Handler
.weak FLASH_IRQHandler
.thumb_set FLASH_IRQHandler,Default_Handler
.weak RCC_IRQHandler
.thumb_set RCC_IRQHandler,Default_Handler
.weak EXTI0_IRQHandler
.thumb_set EXTI0_IRQHandler,Default_Handler
.weak EXTI1_IRQHandler
.thumb_set EXTI1_IRQHandler,Default_Handler
.weak EXTI2_IRQHandler
.thumb_set EXTI2_IRQHandler,Default_Handler
.weak EXTI3_IRQHandler
.thumb_set EXTI3_IRQHandler,Default_Handler
.weak EXTI4_IRQHandler
.thumb_set EXTI4_IRQHandler,Default_Handler
.weak DMA1_Stream0_IRQHandler
.thumb_set DMA1_Stream0_IRQHandler,Default_Handler
.weak DMA1_Stream1_IRQHandler
.thumb_set DMA1_Stream1_IRQHandler,Default_Handler
.weak DMA1_Stream2_IRQHandler
.thumb_set DMA1_Stream2_IRQHandler,Default_Handler
.weak DMA1_Stream3_IRQHandler
.thumb_set DMA1_Stream3_IRQHandler,Default_Handler
.weak DMA1_Stream4_IRQHandler
.thumb_set DMA1_Stream4_IRQHandler,Default_Handler
.weak DMA1_Stream5_IRQHandler
.thumb_set DMA1_Stream5_IRQHandler,Default_Handler
.weak DMA1_Stream6_IRQHandler
.thumb_set DMA1_Stream6_IRQHandler,Default_Handler
.weak ADC_IRQHandler
.thumb_set ADC_IRQHandler,Default_Handler
.weak CAN1_TX_IRQHandler
.thumb_set CAN1_TX_IRQHandler,Default_Handler
.weak CAN1_RX0_IRQHandler
.thumb_set CAN1_RX0_IRQHandler,Default_Handler
.weak CAN1_RX1_IRQHandler
.thumb_set CAN1_RX1_IRQHandler,Default_Handler
.weak CAN1_SCE_IRQHandler
.thumb_set CAN1_SCE_IRQHandler,Default_Handler
.weak EXTI9_5_IRQHandler
.thumb_set EXTI9_5_IRQHandler,Default_Handler
.weak TIM1_BRK_TIM9_IRQHandler
.thumb_set TIM1_BRK_TIM9_IRQHandler,Default_Handler
.weak TIM1_UP_TIM10_IRQHandler
.thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler
.weak TIM1_TRG_COM_TIM11_IRQHandler
.thumb_set TIM1_TRG_COM_TIM11_IRQHandler,Default_Handler
.weak TIM1_CC_IRQHandler
.thumb_set TIM1_CC_IRQHandler,Default_Handler
.weak TIM2_IRQHandler
.thumb_set TIM2_IRQHandler,Default_Handler
.weak TIM3_IRQHandler
.thumb_set TIM3_IRQHandler,Default_Handler
.weak TIM4_IRQHandler
.thumb_set TIM4_IRQHandler,Default_Handler
.weak I2C1_EV_IRQHandler
.thumb_set I2C1_EV_IRQHandler,Default_Handler
.weak I2C1_ER_IRQHandler
.thumb_set I2C1_ER_IRQHandler,Default_Handler
.weak I2C2_EV_IRQHandler
.thumb_set I2C2_EV_IRQHandler,Default_Handler
.weak I2C2_ER_IRQHandler
.thumb_set I2C2_ER_IRQHandler,Default_Handler
.weak SPI1_IRQHandler
.thumb_set SPI1_IRQHandler,Default_Handler
.weak SPI2_IRQHandler
.thumb_set SPI2_IRQHandler,Default_Handler
.weak USART1_IRQHandler
.thumb_set USART1_IRQHandler,Default_Handler
.weak USART2_IRQHandler
.thumb_set USART2_IRQHandler,Default_Handler
.weak USART3_IRQHandler
.thumb_set USART3_IRQHandler,Default_Handler
.weak EXTI15_10_IRQHandler
.thumb_set EXTI15_10_IRQHandler,Default_Handler
.weak RTC_Alarm_IRQHandler
.thumb_set RTC_Alarm_IRQHandler,Default_Handler
.weak OTG_FS_WKUP_IRQHandler
.thumb_set OTG_FS_WKUP_IRQHandler,Default_Handler
.weak TIM8_BRK_TIM12_IRQHandler
.thumb_set TIM8_BRK_TIM12_IRQHandler,Default_Handler
.weak TIM8_UP_TIM13_IRQHandler
.thumb_set TIM8_UP_TIM13_IRQHandler,Default_Handler
.weak TIM8_TRG_COM_TIM14_IRQHandler
.thumb_set TIM8_TRG_COM_TIM14_IRQHandler,Default_Handler
.weak TIM8_CC_IRQHandler
.thumb_set TIM8_CC_IRQHandler,Default_Handler
.weak DMA1_Stream7_IRQHandler
.thumb_set DMA1_Stream7_IRQHandler,Default_Handler
.weak FSMC_IRQHandler
.thumb_set FSMC_IRQHandler,Default_Handler
.weak SDIO_IRQHandler
.thumb_set SDIO_IRQHandler,Default_Handler
.weak TIM5_IRQHandler
.thumb_set TIM5_IRQHandler,Default_Handler
.weak SPI3_IRQHandler
.thumb_set SPI3_IRQHandler,Default_Handler
.weak UART4_IRQHandler
.thumb_set UART4_IRQHandler,Default_Handler
.weak UART5_IRQHandler
.thumb_set UART5_IRQHandler,Default_Handler
.weak TIM6_DAC_IRQHandler
.thumb_set TIM6_DAC_IRQHandler,Default_Handler
.weak TIM7_IRQHandler
.thumb_set TIM7_IRQHandler,Default_Handler
.weak DMA2_Stream0_IRQHandler
.thumb_set DMA2_Stream0_IRQHandler,Default_Handler
.weak DMA2_Stream1_IRQHandler
.thumb_set DMA2_Stream1_IRQHandler,Default_Handler
.weak DMA2_Stream2_IRQHandler
.thumb_set DMA2_Stream2_IRQHandler,Default_Handler
.weak DMA2_Stream3_IRQHandler
.thumb_set DMA2_Stream3_IRQHandler,Default_Handler
.weak DMA2_Stream4_IRQHandler
.thumb_set DMA2_Stream4_IRQHandler,Default_Handler
.weak ETH_IRQHandler
.thumb_set ETH_IRQHandler,Default_Handler
.weak ETH_WKUP_IRQHandler
.thumb_set ETH_WKUP_IRQHandler,Default_Handler
.weak CAN2_TX_IRQHandler
.thumb_set CAN2_TX_IRQHandler,Default_Handler
.weak CAN2_RX0_IRQHandler
.thumb_set CAN2_RX0_IRQHandler,Default_Handler
.weak CAN2_RX1_IRQHandler
.thumb_set CAN2_RX1_IRQHandler,Default_Handler
.weak CAN2_SCE_IRQHandler
.thumb_set CAN2_SCE_IRQHandler,Default_Handler
.weak OTG_FS_IRQHandler
.thumb_set OTG_FS_IRQHandler,Default_Handler
.weak DMA2_Stream5_IRQHandler
.thumb_set DMA2_Stream5_IRQHandler,Default_Handler
.weak DMA2_Stream6_IRQHandler
.thumb_set DMA2_Stream6_IRQHandler,Default_Handler
.weak DMA2_Stream7_IRQHandler
.thumb_set DMA2_Stream7_IRQHandler,Default_Handler
.weak USART6_IRQHandler
.thumb_set USART6_IRQHandler,Default_Handler
.weak I2C3_EV_IRQHandler
.thumb_set I2C3_EV_IRQHandler,Default_Handler
.weak I2C3_ER_IRQHandler
.thumb_set I2C3_ER_IRQHandler,Default_Handler
.weak OTG_HS_EP1_OUT_IRQHandler
.thumb_set OTG_HS_EP1_OUT_IRQHandler,Default_Handler
.weak OTG_HS_EP1_IN_IRQHandler
.thumb_set OTG_HS_EP1_IN_IRQHandler,Default_Handler
.weak OTG_HS_WKUP_IRQHandler
.thumb_set OTG_HS_WKUP_IRQHandler,Default_Handler
.weak OTG_HS_IRQHandler
.thumb_set OTG_HS_IRQHandler,Default_Handler
.weak DCMI_IRQHandler
.thumb_set DCMI_IRQHandler,Default_Handler
.weak CRYP_IRQHandler
.thumb_set CRYP_IRQHandler,Default_Handler
.weak HASH_RNG_IRQHandler
.thumb_set HASH_RNG_IRQHandler,Default_Handler
.weak FPU_IRQHandler
.thumb_set FPU_IRQHandler,Default_Handler
/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/

View File

@@ -23,11 +23,13 @@
#include <stm-periph/stm32-gpio-macros.h>
#include <stm-periph/clock-enable-manager.h>
#include <stdint.h>
#include <helper-macros/helper-macros.h>
#include <cmsis/core_cm4.h>
#include <reflow-controller/systick.h>
static volatile uint64_t to_active_timestamp;
static volatile enum button_state int_state;
static volatile uint64_t IN_SECTION(.ccm.bss) to_active_timestamp;
static volatile enum button_state IN_SECTION(.ccm.bss) int_state;
static volatile enum button_state IN_SECTION(.ccm.bss) override_state;
void button_init()
{
@@ -39,6 +41,7 @@ void button_init()
to_active_timestamp = 0ULL;
int_state = BUTTON_IDLE;
override_state = BUTTON_IDLE;
SYSCFG->EXTICR[1] |= 0x3;
EXTI->IMR |= (1U<<4);
@@ -52,6 +55,12 @@ enum button_state button_read_event()
uint64_t time_delta;
enum button_state temp_state;
if (override_state != BUTTON_IDLE) {
temp_state = override_state;
override_state = BUTTON_IDLE;
return temp_state;
}
if (BUTTON_PORT->IDR & (1U<<BUTTON_PIN)) {
temp_state = int_state;
int_state = BUTTON_IDLE;
@@ -95,3 +104,8 @@ void EXTI4_IRQHandler(void)
to_active_timestamp = systick_get_global_tick();
}
}
void button_override_event(enum button_state state)
{
override_state = state;
}

View File

@@ -23,9 +23,9 @@
#include <reflow-controller/adc-meas.h>
#include <stm-periph/uart.h>
#include <helper-macros/helper-macros.h>
#include <arm_math.h>
#include <stdlib.h>
#include <float.h>
#include <reflow-controller/safety/safety-controller.h>
enum calibration_shell_state {CAL_START = 0, CAL_WAIT_RES1, CAL_MEAS_RES1, CAL_WAIT_RES2, CAL_MEAS_RES2};
@@ -74,14 +74,77 @@ free_mem:
return NULL;
}
static int calibration_poll_data_acquisition(float *mem_array, uint32_t count, volatile int *flag, float *mu, float *max_dev)
static float calculate_mean(float *values, uint32_t count)
{
uint32_t loop_cnt = (count + 7) / 8;
uint32_t remainder = count % 8;
float sum = 0;
switch (remainder) {
case 0: do { sum += *values++; /* FALLTHRU */
case 7: sum += *values++; /* FALLTHRU */
case 6: sum += *values++; /* FALLTHRU */
case 5: sum += *values++; /* FALLTHRU */
case 4: sum += *values++; /* FALLTHRU */
case 3: sum += *values++; /* FALLTHRU */
case 2: sum += *values++; /* FALLTHRU */
case 1: sum += *values++;
} while (--loop_cnt > 0);
}
return sum / (float)count;
}
static float calculate_standard_deviation(float *values, uint32_t count, float mean)
{
uint32_t loop_cnt = (count + 7) / 8;
uint32_t remainder = count % 8;
float sum = 0;
float res;
switch (remainder) {
case 0: do {
sum += (*values - mean) * (*values - mean);
values++;
/* FALLTHRU */
case 7: sum += (*values - mean) * (*values - mean);
values++;
/* FALLTHRU */
case 6: sum += (*values - mean) * (*values - mean);
values++;
/* FALLTHRU */
case 5: sum += (*values - mean) * (*values - mean);
values++;
/* FALLTHRU */
case 4: sum += (*values - mean) * (*values - mean);
values++;
/* FALLTHRU */
case 3: sum += (*values - mean) * (*values - mean);
values++;
/* FALLTHRU */
case 2: sum += (*values - mean) * (*values - mean);
values++;
/* FALLTHRU */
case 1: sum += (*values - mean) * (*values - mean);
values++;
/* FALLTHRU */
} while (--loop_cnt > 0);
}
sum /= (float)count;
/* Compute the square root using the FPU.
* The constraint 't' tells GCC to use a floating point register
*/
__asm__ __volatile__("vsqrt.f32 %0, %1" : "=t"(res) : "t"(sum));
return res;
}
static int calibration_poll_data_acquisition(float *mem_array, uint32_t count, volatile int *flag, float *mu, float *std_dev)
{
int ret_val = 0;
float min_val = FLT_MAX;
float max_val = -FLT_MAX;
uint32_t i;
if (!flag || !mem_array || !mu || !max_dev)
if (!flag || !mem_array || !mu || !std_dev)
return -1000;
if (*flag == 0) {
@@ -98,19 +161,9 @@ static int calibration_poll_data_acquisition(float *mem_array, uint32_t count, v
/* Convert the stream memory to Ohm readings */
adc_pt1000_convert_raw_value_array_to_resistance(NULL, mem_array, count);
/* Do not compute std-deviation. Too imprecise
* arm_std_f32(stream_mem, count, sigma);
*/
arm_mean_f32(mem_array, count, mu);
/* Find min and max values of array */
for (i = 0U; i < count; i++) {
min_val = MIN(min_val, mem_array[i]);
max_val = MAX(max_val, mem_array[i]);
}
/* Compute maximum deviation range */
*max_dev = max_val - min_val;
/* Do not compute std-deviation. Too imprecise */
*mu = calculate_mean(mem_array, count);
*std_dev = calculate_standard_deviation(mem_array, count, *mu);
ret_free_mem:
free(mem_array);
@@ -121,6 +174,8 @@ shellmatta_retCode_t calibration_sequence_shell_cmd(shellmatta_handle_t shell, c
{
(void)arg;
(void)len;
bool error_occured;
const enum safety_flag meas_adc_err_mask = ERR_FLAG_MEAS_ADC_OFF | ERR_FLAG_MEAS_ADC_WATCHDOG;
/* This stores the current state of the calibration process */
static enum calibration_shell_state cal_state = CAL_START;
@@ -139,7 +194,7 @@ shellmatta_retCode_t calibration_sequence_shell_cmd(shellmatta_handle_t shell, c
switch (cal_state) {
case CAL_START:
/* Clear errors of PT1000 reading */
adc_pt1000_clear_error();
safety_controller_ack_flag(ERR_FLAG_MEAS_ADC_WATCHDOG);
shellmatta_printf(shell, "Starting calibration: Insert 1000 Ohm calibration resistor and press ENTER\r\n");
cal_state = CAL_WAIT_RES1;
ret_val = SHELLMATTA_CONTINUE;
@@ -154,7 +209,7 @@ shellmatta_retCode_t calibration_sequence_shell_cmd(shellmatta_handle_t shell, c
cal_state = CAL_MEAS_RES1;
ret_val = SHELLMATTA_BUSY;
shellmatta_printf(shell, "Measurement...\r\n");
adc_pt1000_clear_error();
safety_controller_ack_flag(ERR_FLAG_MEAS_ADC_WATCHDOG);
data_buffer = calibration_acquire_data_start(512UL, &flag);
break;
} else if (stdin_data[i] == '\x03') {
@@ -178,9 +233,10 @@ shellmatta_retCode_t calibration_sequence_shell_cmd(shellmatta_handle_t shell, c
ret_val = SHELLMATTA_BUSY;
cal_state = CAL_MEAS_RES1;
} else if (res == 0) {
shellmatta_printf(shell, "R=%.2f, Noise peak-peak: %.2f\r\n", mu, dev);
if (adc_pt1000_check_error() != ADC_PT1000_NO_ERR) {
shellmatta_printf(shell, "Error in resistance measurement: %d", adc_pt1000_check_error());
shellmatta_printf(shell, "R=%.2f, Std-Dev: %.2f\r\n", mu, dev);
error_occured = safety_controller_get_flags_by_mask(meas_adc_err_mask);
if (error_occured) {
shellmatta_printf(shell, "Error in resistance measurement");
ret_val = SHELLMATTA_OK;
cal_state = CAL_START;
} else {
@@ -189,7 +245,7 @@ shellmatta_retCode_t calibration_sequence_shell_cmd(shellmatta_handle_t shell, c
cal_state = CAL_WAIT_RES2;
}
} else {
shellmatta_printf(shell, "Error in resistance measurement: %d", adc_pt1000_check_error());
shellmatta_printf(shell, "Error in resistance measurement");
ret_val = SHELLMATTA_OK;
cal_state = CAL_START;
}
@@ -204,7 +260,7 @@ shellmatta_retCode_t calibration_sequence_shell_cmd(shellmatta_handle_t shell, c
cal_state = CAL_MEAS_RES2;
ret_val = SHELLMATTA_BUSY;
shellmatta_printf(shell, "Measurement...\r\n");
adc_pt1000_clear_error();
safety_controller_ack_flag(ERR_FLAG_MEAS_ADC_WATCHDOG);
data_buffer = calibration_acquire_data_start(512UL, &flag);
break;
} else if (stdin_data[i] == '\x03') {
@@ -228,17 +284,18 @@ shellmatta_retCode_t calibration_sequence_shell_cmd(shellmatta_handle_t shell, c
ret_val = SHELLMATTA_BUSY;
cal_state = CAL_MEAS_RES2;
} else if (res == 0) {
shellmatta_printf(shell, "R=%.2f, Noise peak-peak: %.2f\r\n", mu2, dev2);
if (adc_pt1000_check_error() != ADC_PT1000_NO_ERR) {
shellmatta_printf(shell, "Error in resistance measurement: %d", adc_pt1000_check_error());
shellmatta_printf(shell, "R=%.2f, Std-Dev: %.2f\r\n", mu2, dev2);
error_occured = safety_controller_get_flags_by_mask(meas_adc_err_mask);
if (error_occured) {
shellmatta_printf(shell, "Error in resistance measurement");
ret_val = SHELLMATTA_OK;
cal_state = CAL_START;
} else {
ret_val = SHELLMATTA_OK;
cal_state = CAL_START;
if (dev > CALIBRATION_MAX_PEAK_PEAK_NOISE_OHM ||
dev2 > CALIBRATION_MAX_PEAK_PEAK_NOISE_OHM) {
if (dev > CALIBRATION_MAX_NOISE_OHM ||
dev2 > CALIBRATION_MAX_NOISE_OHM) {
shellmatta_printf(shell, "Calibration failed! Too much noise. Check your hardware.\r\n");
break;
}
@@ -250,7 +307,7 @@ shellmatta_retCode_t calibration_sequence_shell_cmd(shellmatta_handle_t shell, c
adc_pt1000_set_resistance_calibration(offset, sens_dev, true);
}
} else {
shellmatta_printf(shell, "Error in resistance measurement: %d", adc_pt1000_check_error());
shellmatta_printf(shell, "Error in resistance measurement");
ret_val = SHELLMATTA_OK;
cal_state = CAL_START;
}

View File

@@ -0,0 +1,191 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @addtogroup config-parser
* @{
*/
#include <config-parser/config-parser.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define CONFIG_PARSER_MAGIC 0x464a6e2bUL
#define CONFIG_PARSER(p) ((struct config_parser *)(p))
#define config_parser_check_handle(handle) do { if (!(handle) || \
((struct config_parser *)(handle))->magic != CONFIG_PARSER_MAGIC) \
return CONFIG_PARSER_PARAM_ERR; \
} while (0)
config_parser_handle_t config_parser_open_file(struct config_parser *config_parser, bool write, const char *file_name,
char *working_buffer, size_t buff_size)
{
FRESULT res;
if (!config_parser || !file_name || !working_buffer)
return NULL;
config_parser->magic = CONFIG_PARSER_MAGIC;
config_parser->write = write;
config_parser->buffer = working_buffer;
config_parser->buff_size = buff_size;
res = f_open(&config_parser->file, file_name, (write ? FA_CREATE_ALWAYS | FA_WRITE : FA_READ));
if (res != FR_OK)
return NULL;
return (config_parser_handle_t)config_parser;
}
static const char * const token_delim = " \t";
static int parse_value(struct config_parser_entry *entry, char *value_start_token)
{
char *dot;
char *endptr;
/* Check if token is a float number */
dot = strstr(value_start_token, ".");
if (dot) {
/* Try parsing as float */
entry->value.float_val = strtof(value_start_token, &endptr);
if (endptr == value_start_token)
return -1;
entry->type = CONFIG_PARSER_TYPE_FLOAT;
goto exit;
}
if (value_start_token[0] != '-') {
/* Try parsing as ul */
/* Try parsing as int */
entry->value.uint_val = strtoul(value_start_token, &endptr, 0);
if (endptr == value_start_token) {
return -1;
}
entry->type = CONFIG_PARSER_TYPE_UINT;
goto exit;
} else {
/* Try parsing as int */
entry->value.int_val = strtod(value_start_token, &endptr);
if (endptr == value_start_token) {
return -1;
}
entry->type = CONFIG_PARSER_TYPE_INT;
}
exit:
return 0;
}
enum config_parser_ret config_parser_get_line(config_parser_handle_t handle, struct config_parser_entry *entry, bool force_float)
{
struct config_parser *p;
config_parser_check_handle(handle);
p = CONFIG_PARSER(handle);
char *token;
int token_round = 0;
if (!entry)
return CONFIG_PARSER_PARAM_ERR;
p->buffer[0] = '\0';
if (f_gets(p->buffer, (int)p->buff_size, &p->file) == NULL)
return CONFIG_PARSER_IOERR;
token = strtok(p->buffer, token_delim);
while (token != NULL) {
/* Check for comment */
if (token[0] == '#') {
if (token_round == 0)
return CONFIG_PARSER_LINE_COMMENT;
else
break;
}
switch (token_round) {
case 0: /* KEY */
entry->name = token;
break;
case 1: /* = Symbol */
if (strcmp(token, "=")) {
return CONFIG_PARSER_LINE_MALFORM;
}
break;
case 2: /* VALUE */
if (parse_value(entry, token))
return CONFIG_PARSER_LINE_MALFORM;
break;
default:
return CONFIG_PARSER_LINE_MALFORM;
}
token_round++;
token = strtok(NULL, token_delim);
}
if (force_float) {
if (entry->type == CONFIG_PARSER_TYPE_INT)
entry->value.float_val = (float)entry->value.int_val;
if (entry->type == CONFIG_PARSER_TYPE_UINT)
entry->value.float_val = (float)entry->value.uint_val;
entry->type = CONFIG_PARSER_TYPE_FLOAT;
}
return CONFIG_PARSER_OK;
}
enum config_parser_ret config_parser_reset_to_start(config_parser_handle_t handle)
{
FRESULT res;
struct config_parser *p;
config_parser_check_handle(handle);
p = CONFIG_PARSER(handle);
res = f_rewind(&p->file);
if (res != FR_OK)
return CONFIG_PARSER_IOERR;
return CONFIG_PARSER_OK;
}
enum config_parser_ret config_parser_write_entry(config_parser_handle_t handle, const struct config_parser_entry *entry)
{
(void)entry;
config_parser_check_handle(handle);
return CONFIG_PARSER_OK;
}
enum config_parser_ret config_parser_close_file(config_parser_handle_t handle)
{
struct config_parser *p;
FRESULT res;
config_parser_check_handle(handle);
p = CONFIG_PARSER(handle);
res = f_close(&p->file);
return (res == FR_OK ? CONFIG_PARSER_OK : CONFIG_PARSER_IOERR);
}
/** @} */

View File

@@ -0,0 +1,95 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file config-parser.h
* @brief Header file for the key-value pair config parser
* @addtogroup config-parser
* @{
*/
#ifndef _CONFIG_PARSER_H_
#define _CONFIG_PARSER_H_
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <fatfs/ff.h>
struct config_parser {
uint32_t magic;
bool write;
FIL file;
char *buffer;
size_t buff_size;
};
typedef void * config_parser_handle_t;
enum config_parser_value_type {
CONFIG_PARSER_TYPE_UINT = 0,
CONFIG_PARSER_TYPE_INT,
CONFIG_PARSER_TYPE_FLOAT,
};
enum config_parser_ret {
CONFIG_PARSER_OK = 0,
CONFIG_PARSER_PARAM_ERR,
CONFIG_PARSER_GENERIC_ERR,
CONFIG_PARSER_IOERR,
CONFIG_PARSER_LINE_COMMENT,
CONFIG_PARSER_LINE_TOO_LONG,
CONFIG_PARSER_LINE_MALFORM,
CONFIG_PARSER_END_REACHED,
CONFIG_PARSER_WRONG_MODE,
};
struct config_parser_entry {
const char *name;
enum config_parser_value_type type;
union {
uint32_t uint_val;
int32_t int_val;
float float_val;
} value;
};
config_parser_handle_t config_parser_open_file(struct config_parser *config_parser, bool write, const char *file_name,
char *working_buffer, size_t buff_size);
/**
* @brief Parse the current line in the config file.
* @param handle Config parser handle
* @param[out] entry Entry read from config file.
* @warning \p entry is only valid as long as no other function was called on the same \p handle. If necessary, the values
* have to be copied
* @return Config parser error
*/
enum config_parser_ret config_parser_get_line(config_parser_handle_t handle, struct config_parser_entry *entry, bool force_float);
enum config_parser_ret config_parser_reset_to_start(config_parser_handle_t handle);
enum config_parser_ret config_parser_write_entry(config_parser_handle_t handle, const struct config_parser_entry *entry);
enum config_parser_ret config_parser_close_file(config_parser_handle_t handle);
#endif /* _CONFIG_PARSER_H_ */
/** @} */

View File

@@ -47,7 +47,7 @@ static void digio_setup_pin_int(uint8_t bit_no, uint8_t in_out, uint8_t alt_func
}
void digio_setup_default_all()
void digio_setup_default_all(void)
{
unsigned int i;
@@ -90,7 +90,7 @@ int digio_get(uint8_t num)
static const uint8_t led_pins[] = {LED_PINS};
void led_setup()
void led_setup(void)
{
unsigned int i;
@@ -132,7 +132,7 @@ static void loudspeaker_freq_timer_init(void)
#endif
}
void loudspeaker_setup()
void loudspeaker_setup(void)
{
rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(LOUDSPEAKER_RCC_MASK));
@@ -179,7 +179,7 @@ void loudspeaker_set(uint16_t val)
}
}
uint16_t loudspeaker_get()
uint16_t loudspeaker_get(void)
{
return loudspeaker_val;
}

View File

@@ -1,4 +1,4 @@
# Doxyfile 1.8.17
# Doxyfile 1.8.20
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@@ -227,6 +227,14 @@ QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
# By default Python docstrings are displayed as preformatted text and doxygen's
# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the
# doxygen's special commands can be used and the contents of the docstring
# documentation blocks is shown as doxygen documentation.
# The default value is: YES.
PYTHON_DOCSTRING = YES
# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
# documentation from any documented member that it re-implements.
# The default value is: YES.
@@ -263,12 +271,6 @@ TAB_SIZE = 8
ALIASES =
# This tag can be used to specify a number of word-keyword mappings (TCL only).
# A mapping has the form "name=value". For example adding "class=itcl::class"
# will allow you to use the command class in the itcl::class meaning.
TCL_SUBST =
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For
# instance, some of the names that are used will be different. The list of all
@@ -310,13 +312,13 @@ OPTIMIZE_OUTPUT_SLICE = NO
# extension. Doxygen has a built-in mapping, but you can override or extend it
# using this tag. The format is ext=language, where ext is a file extension, and
# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,
# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,
# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
# tries to guess whether the code is fixed or free formatted code, this is the
# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat
# .inc files as Fortran files (default is PHP), and .f files as C (default is
# Fortran), use: inc=Fortran f=C.
# default for Fortran type files). For instance to make doxygen treat .inc files
# as Fortran files (default is PHP), and .f files as C (default is Fortran),
# use: inc=Fortran f=C.
#
# Note: For files without extension you can use no_extension as a placeholder.
#
@@ -455,6 +457,19 @@ TYPEDEF_HIDES_STRUCT = NO
LOOKUP_CACHE_SIZE = 0
# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use
# during processing. When set to 0 doxygen will based this on the number of
# cores available in the system. You can set it explicitly to a value larger
# than 0 to get more control over the balance between CPU load and processing
# speed. At this moment only the input processing can be done using multiple
# threads. Since this is still an experimental feature the default is set to 1,
# which efficively disables parallel processing. Please report any issues you
# encounter. Generating dot graphs in parallel is controlled by the
# DOT_NUM_THREADS setting.
# Minimum value: 0, maximum value: 32, default value: 1.
NUM_PROC_THREADS = 1
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
@@ -559,7 +574,7 @@ INTERNAL_DOCS = NO
# names in lower-case letters. If set to YES, upper-case letters are also
# allowed. This is useful if you have classes or files whose names only differ
# in case and if your file system supports case sensitive file names. Windows
# (including Cygwin) ands Mac users are advised to set this option to NO.
# (including Cygwin) and Mac users are advised to set this option to NO.
# The default value is: system dependent.
CASE_SENSE_NAMES = NO
@@ -853,7 +868,7 @@ INPUT_ENCODING = UTF-8
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen
# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f, *.for, *.tcl, *.vhd,
# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
# *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.c \
@@ -1381,7 +1396,7 @@ CHM_FILE =
HHC_LOCATION =
# The GENERATE_CHI flag controls if a separate .chi index file is generated
# (YES) or that it should be included in the master .chm file (NO).
# (YES) or that it should be included in the main .chm file (NO).
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
@@ -1543,6 +1558,17 @@ TREEVIEW_WIDTH = 250
EXT_LINKS_IN_WINDOW = NO
# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
# the HTML output. These images will generally look nicer at scaled resolutions.
# Possible values are: png (the default) and svg (looks nicer but requires the
# pdf2svg or inkscape tool).
# The default value is: png.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FORMULA_FORMAT = png
# Use this tag to change the font size of LaTeX formulas included as images in
# the HTML documentation. When you change the font size after a successful
# doxygen run you need to manually remove any form_*.png images from the HTML
@@ -1598,7 +1624,7 @@ MATHJAX_FORMAT = HTML-CSS
# Content Delivery Network so you can quickly see the result without installing
# MathJax. However, it is strongly recommended to install a local copy of
# MathJax from https://www.mathjax.org before deployment.
# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.
# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/
@@ -1711,7 +1737,7 @@ EXTRA_SEARCH_MAPPINGS =
# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
# The default value is: YES.
GENERATE_LATEX = YES
GENERATE_LATEX = NO
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
@@ -1837,9 +1863,11 @@ LATEX_EXTRA_FILES =
PDF_HYPERLINKS = YES
# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
# the PDF file directly from the LaTeX files. Set this option to YES, to get a
# higher quality PDF documentation.
# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as
# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX
# files. Set this option to YES, to get a higher quality PDF documentation.
#
# See also section LATEX_CMD_NAME for selecting the engine.
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
@@ -2013,7 +2041,7 @@ MAN_LINKS = NO
# captures the structure of the code including all documentation.
# The default value is: NO.
GENERATE_XML = NO
GENERATE_XML = YES
# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
@@ -2078,6 +2106,10 @@ DOCBOOK_PROGRAMLISTING = NO
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to Sqlite3 output
#---------------------------------------------------------------------------
#---------------------------------------------------------------------------
# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
@@ -2133,7 +2165,7 @@ ENABLE_PREPROCESSING = YES
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
MACRO_EXPANSION = NO
MACRO_EXPANSION = YES
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
# the macro expansion is limited to the macros specified with the PREDEFINED and
@@ -2141,7 +2173,7 @@ MACRO_EXPANSION = NO
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_ONLY_PREDEF = NO
EXPAND_ONLY_PREDEF = YES
# If the SEARCH_INCLUDES tag is set to YES, the include files in the
# INCLUDE_PATH will be searched if a #include is found.
@@ -2173,7 +2205,8 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED =
PREDEFINED = __attribute__(x)= \
IN_SECTION(x)=
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The

View File

@@ -11,10 +11,17 @@ cd "$DIR"
export PROJECT_NUMBER=`git describe --always --tags --dirty`
if [ $# != 1 ]; then
configfile="Doxyconfig"
if [[ -z "$1" ]]; then
export OUTPUT_DIRECTORY="./output"
else
export OUTPUT_DIRECTORY="$1"
fi
doxygen Doxyconfig
if [[ -n "$2" ]]; then
configfile="$2"
fi
doxygen "$configfile"

View File

@@ -139,7 +139,8 @@ static int sdio_switch_appmode_cmd55()
return -1;
}
enum acmd41_ret sdio_init_card_acmd41(uint8_t HCS){
enum acmd41_ret sdio_init_card_acmd41(uint8_t HCS)
{
uint32_t response;
int retry = 0x20;
if (sdio_switch_appmode_cmd55())
@@ -184,12 +185,13 @@ static int sdio_send_csd_cmd9(uint16_t rca, uint32_t *response_buffer) {
* @param blklen Log2 of block length (9 in case of 512 byte block)
* @param buff Buffer to send
*/
static void sdio_write_buffer(uint32_t dlen, uint32_t log_blklen, const unsigned char *buff)
static int sdio_write_buffer(uint32_t dlen, uint32_t log_blklen, const unsigned char *buff)
{
uint32_t count;
int byte_count;
int byte_max;
uint32_t fifo;
uint32_t status_reg;
SDIO->DLEN = dlen;
@@ -212,6 +214,8 @@ static void sdio_write_buffer(uint32_t dlen, uint32_t log_blklen, const unsigned
fifo |= (((uint32_t)*(buff++)) << 24) & 0xFF000000;
}
/* Wait as long as FIFO is full */
while (SDIO->STA & SDIO_STA_TXFIFOF);
@@ -221,6 +225,14 @@ static void sdio_write_buffer(uint32_t dlen, uint32_t log_blklen, const unsigned
/* Wait for TX to complete */
while (SDIO->STA & SDIO_STA_TXACT);
status_reg = SDIO->STA;
if (status_reg & (SDIO_STA_DTIMEOUT | SDIO_STA_TXUNDERR | SDIO_STA_DCRCFAIL)) {
SDIO->DCTRL = 0UL;
return -1;
}
return 0;
}
static int sdio_send_write_block_cmd24(uint32_t addr)
@@ -417,6 +429,26 @@ static int sdio_send_go_idle_cmd0() {
return 0;
}
static int sdio_send_stop_transmission_cmd12()
{
int res;
uint32_t response;
sdio_send_cmd(12, 0, SHORT_ANS);
res = sdio_get_response(12, SHORT_ANS, &response);
return res;
}
static int sdio_send_write_multiple_blocks_cmd25(uint32_t address)
{
int res;
uint32_t response;
sdio_send_cmd(25, address, SHORT_ANS);
res = sdio_get_response(25, SHORT_ANS, &response);
return res;
}
static enum cmd8_ret sdio_send_iface_condition_cmd8()
{
uint32_t response;
@@ -606,11 +638,25 @@ DSTATUS sdio_initialize(){
return 0;
}
void sdio_stop_clk()
{
SDIO->POWER = 0UL;
}
DRESULT sdio_disk_read(BYTE *buff, DWORD sector, UINT count){
uint32_t addr;
uint32_t sdio_status;
uint32_t fifo;
uint32_t counter;
int err;
union sdio_status_conv status;
do {
err = sdio_check_status_register_cmd13(card_info.rca, &status.value);
if (err)
return RES_ERROR;
} while (status.statusstruct.CURRENT_STATE != CURRENT_STATE_TRAN);
addr = (card_info.type == SD_V2_HC ? (sector) : (sector*512));
for (; count > 0; count--) {
@@ -633,7 +679,8 @@ DRESULT sdio_disk_read(BYTE *buff, DWORD sector, UINT count){
SDIO->DCTRL = (BLOCKSIZE<<4) | SDIO_DCTRL_DTDIR | /*SDIO_DCTRL_DMAEN |*/ SDIO_DCTRL_DTEN;
/* Init Transfer */
if (sdio_send_read_block_cmd17(addr)) {
err = sdio_send_read_block_cmd17(addr);
if (err) {
return RES_ERROR;
}
@@ -703,38 +750,51 @@ DRESULT sdio_disk_write(const BYTE *buff, DWORD sector, UINT count)
union sdio_status_conv status;
uint32_t buff_offset = 0;
int ret;
UINT count_backup = count;
uint32_t retry_counter = 512;
if (sdio_check_write_protection())
return RES_WRPRT;
addr = (card_info.type == SD_V2_HC ? (sector) : (sector * 512));
while (count) {
do {
ret = sdio_check_status_register_cmd13(card_info.rca, &status.value);
} while (status.statusstruct.CURRENT_STATE == CURRENT_STATE_PRG ||
status.statusstruct.CURRENT_STATE == CURRENT_STATE_RCV ||
!ret);
ret = sdio_check_status_register_cmd13(card_info.rca, &status.value);
if (ret)
return RES_ERROR;
if (status.statusstruct.CURRENT_STATE == CURRENT_STATE_STBY) {
if (sdio_send_select_card_cmd7(card_info.rca))
return RES_ERROR;
}
do {
sdio_check_status_register_cmd13(card_info.rca, &status.value);
} while (status.statusstruct.READY_FOR_DATA != 1);
ret = sdio_send_write_block_cmd24(addr);
if (ret) {
if (status.statusstruct.CURRENT_STATE == CURRENT_STATE_STBY) {
if (sdio_send_select_card_cmd7(card_info.rca))
return RES_ERROR;
}
sdio_write_buffer(512, 9, &buff[buff_offset]);
buff_offset += 512;
addr += (card_info.type == SD_V2_HC ? 1 : 512);
count--;
}
return RES_OK;
while (1) {
ret = sdio_check_status_register_cmd13(card_info.rca, &status.value);
if (ret)
return RES_ERROR;
if (status.statusstruct.CURRENT_STATE == CURRENT_STATE_TRAN)
break;
if (--retry_counter == 0)
return RES_ERROR;
sdio_wait_ms(1);
}
if (count > 1)
ret = sdio_send_write_multiple_blocks_cmd25(addr);
else if (count == 1)
ret = sdio_send_write_block_cmd24(addr);
else
ret = RES_PARERR;
if (ret)
return RES_ERROR;
ret = 0;
ret = sdio_write_buffer((count * 512UL), 9, &buff[buff_offset]);
if (count_backup > 1)
(void)sdio_send_stop_transmission_cmd12();
return (ret ? RES_ERROR : RES_OK);
}

View File

@@ -21,7 +21,7 @@ DRESULT sdio_disk_ioctl(BYTE cmd, void* buff);
DWORD get_fattime();
int sdio_check_inserted();
void sdio_stop_clk();
//Defines for Card Status in struct _CardStatus
#define CURRENT_STATE_IDLE 0

View File

@@ -3,6 +3,8 @@
#include <stm32/stm32f4xx.h>
#define SDIO_CLOCK_FREQ 42000000UL
//General Definitions
//Blocksize: 512 = 2^9 => 9
#define BLOCKSIZE 9 //9
@@ -12,11 +14,14 @@
//4 bit: 4
#define BUSWIDTH 4 //4
//Initial Transfer CLK (ca. 400kHz)
#define INITCLK 130 //120
#define INITCLK 140UL //120
//Working CLK (Maximum)
#define WORKCLK 50 //0
#define WORKCLK 30UL //0
//Data Timeout in CLK Cycles
#define DTIMEOUT 0x3000 //150
#define DATA_TIMEOUT_MS 250UL // 250
#define DTIMEOUT (((SDIO_CLOCK_FREQ / (WORKCLK+2))) * DATA_TIMEOUT_MS / 1000UL)
//DMA Stream used for TX and RX DMA2 Stream 3 or 6 possible
// Currently not used due to possible misalignment of the data buffer.
//#define DMASTREAM DMA2_Stream6

File diff suppressed because it is too large Load Diff

View File

@@ -150,7 +150,7 @@
*/
#define FF_FS_RPATH 0
#define FF_FS_RPATH 2
/* This option configures support for relative path.
/
/ 0: Disable relative path and remove related functions.

View File

@@ -30,10 +30,15 @@
#define CONCAT(x,y) x##y
#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
#define wordsize_of(x) ((sizeof(x) / 4U) / ((sizeof(x) % 4U) ? 0U : 1U))
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#define ABS(a) ((a) < 0 ? (-1*(a)) : (a))
#define is_power_of_two(num) ((num) && !((num) & ((num) - 1)))
#define IN_SECTION(sec) __attribute__((section(#sec)))
#endif /* __HELPER_MACROS_H__ */

View File

@@ -18,6 +18,10 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file adc-meas.h
*/
#ifndef __ADCMEAS_H__
#define __ADCMEAS_H__
@@ -90,7 +94,10 @@
*/
#define ADC_PT1000_WATCHDOG_SAMPLE_COUNT 25U
enum adc_pt1000_error {ADC_PT1000_NO_ERR= 0, ADC_PT1000_WATCHDOG_ERROR=(1UL<<0), ADC_PT1000_OVERFLOW=(1UL<<1), ADC_PT1000_INACTIVE = (1UL<<2)};
/**
* @brief Conversion macro: ADC value to resistance
*/
#define ADC_TO_RES(adc) ((float)(adc) / 4096.0f * 2500.0f)
/**
* @brief This function sets up the ADC measurement fo the external PT1000 temperature sensor
@@ -102,7 +109,7 @@ enum adc_pt1000_error {ADC_PT1000_NO_ERR= 0, ADC_PT1000_WATCHDOG_ERROR=(1UL<<0),
* The filter weight \f$\alpha\f$ is configured for @ref ADC_PT1000_FILTER_WEIGHT
*
*/
void adc_pt1000_setup_meas();
void adc_pt1000_setup_meas(void);
/**
* @brief Set moving average filter parameters
@@ -136,15 +143,13 @@ void adc_pt1000_set_resistance_calibration(float offset, float sensitivity_devia
void adc_pt1000_get_resistance_calibration(float *offset, float *sensitivity_deviation, bool *active);
/**
* @brief Get the current reistance value
* @brief Get the current resistance value
*
* If the reistance calibration is enabled, this function applies the calculations of the raw resistance reading and
* If the resistance calibration is enabled, this function applies the calculations of the raw resistance reading and
* returns the corrected value.
*
* If an ADC error is set, the status is negative. The status is 2 during the first measurements with a given filter setting. Technically, the resistance value is
* correct but the filter is not stable yet.
* Use adc_pt1000_check_error to check the error and reinitialize the ADC.
*
*
* @param[out] resistance Resistance output in Ohms
* @return Status
@@ -160,23 +165,21 @@ int adc_pt1000_get_current_resistance(float *resistance);
*/
int adc_pt1000_stream_raw_value_to_memory(volatile float *adc_array, uint32_t length, volatile int *flag_to_set);
void adc_pt1000_convert_raw_value_array_to_resistance(float *resistance_dest, float *raw_source, uint32_t count);
/**
* @brief Check if the ADC measurement experienced any kind of error (DMA, Analog Watchdog, etc...)
* @brief Convert an array of raw adc values to resistance values
*
* In case of an error, it may be necessary to call adc_pt1000_setup_meas() again in order to recover from the error
* In case \p resistance_dest is NULL, the conversion is done inplace in the
* \p raw_source array.
*
* @param resistance_dest Destination. Maybe NULL.
* @param raw_source Source array
* @param count Number of values to convert
*/
enum adc_pt1000_error adc_pt1000_check_error();
/**
* @brief Clear the error status of the PT1000 measurement
*/
void adc_pt1000_clear_error();
void adc_pt1000_convert_raw_value_array_to_resistance(float *resistance_dest, float *raw_source, uint32_t count);
/**
* @brief Disable the PT1000 measurement
*/
void adc_pt1000_disable();
void adc_pt1000_disable(void);
#endif // __ADCMEAS_H__

View File

@@ -72,5 +72,10 @@ enum button_state button_read_event();
*/
void button_deinit();
/**
* @brief This function overrides the button event.
* @param state State to set
*/
void button_override_event(enum button_state state);
#endif /* __BUTTON_H__ */

View File

@@ -21,7 +21,7 @@
#ifndef __CALIBRATION_H__
#define __CALIBRATION_H__
#define CALIBRATION_MAX_PEAK_PEAK_NOISE_OHM 8.0f
#define CALIBRATION_MAX_NOISE_OHM 3.0f
#include <stdint.h>
#include <shellmatta.h>

View File

@@ -29,7 +29,7 @@
#define DIGIO_RCC_MASK RCC_AHB1ENR_GPIOBEN
#define DIGIO_PINS 4,5,6,7
#ifdef DEBUGBUILD
#if defined(DEBUGBUILD) || defined(UART_ON_DEBUG_HEADER)
#define DIGIO_INOUT_DEFAULT 0,0,0,0
#define DIGIO_ALTFUNC_DEFAULT 0,0,0,0
#else
@@ -41,7 +41,7 @@
#define BEEPER_RCC_MASK RCC_AHB1ENR_GPIOBEN
void digio_setup_default_all();
void digio_setup_default_all(void);
void digio_setup_pin(uint8_t num, uint8_t in_out, uint8_t alt_func);
void digio_set(uint8_t num, int val);
@@ -51,7 +51,7 @@ int digio_get(uint8_t num);
#define LED_RCC_MASK RCC_AHB1ENR_GPIOBEN
#define LED_PINS 2,3
void led_setup();
void led_setup(void);
void led_set(uint8_t num, int val);
int led_get(uint8_t num);
@@ -60,9 +60,9 @@ int led_get(uint8_t num);
#define LOUDSPEAKER_PIN 1
#define LOUDSPEAKER_MULTIFREQ 1
void loudspeaker_setup();
void loudspeaker_setup(void);
void loudspeaker_set(uint16_t val);
uint16_t loudspeaker_get();
uint16_t loudspeaker_get(void);
#endif /* __DIGIO_H__ */

View File

@@ -25,32 +25,9 @@
#include <stdbool.h>
#include <reflow-controller/pid-controller.h>
enum oven_pid_error_report {
OVEN_PID_NO_ERROR = 0,
OVEN_PID_ERR_PT1000_ADC_WATCHDOG = (1<<0),
OVEN_PID_ERR_PT1000_ADC_OFF = (1<<1),
OVEN_PID_ERR_PT1000_OTHER = (1<<2),
OVEN_PID_ERR_VREF_TOL = (1<<3),
OVEN_PID_ERR_OVERTEMP = (1<<4),
};
struct oven_pid_errors {
bool generic_error;
bool pt1000_adc_watchdog;
bool pt1000_adc_off;
bool pt1000_other;
bool vref_tol;
bool controller_overtemp;
};
struct oven_pid_status {
bool active;
bool error_set;
struct oven_pid_errors error_flags;
float target_temp;
float current_temp;
uint64_t timestamp_last_run;
};
enum oven_pid_status {OVEN_PID_DEACTIVATED,
OVEN_PID_RUNNING,
OVEN_PID_ABORTED};
void oven_driver_init(void);
@@ -64,10 +41,12 @@ void oven_pid_init(struct pid_controller *controller_to_copy);
void oven_pid_handle(float target_temp);
void oven_pid_stop();
void oven_pid_stop(void);
void oven_pid_report_error(enum oven_pid_error_report report);
void oven_pid_abort(void);
const struct oven_pid_status *oven_pid_get_status(void);
void oven_driver_apply_power_level(void);
enum oven_pid_status oven_pid_get_status(void);
#endif /* __OVEN_DRIVER_H__ */

View File

@@ -41,4 +41,6 @@ void rotary_encoder_stop(void);
void rotary_encoder_zero(void);
void rotary_encoder_override_delta(int16_t delta);
#endif /* __ROTARY_ENCODER_H__ */

View File

@@ -0,0 +1,28 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @brief This function implements the panic mode
*
* This function will never return and ensures that the controller goes in a safe operating state
* The watchdog will eventually reset the controller from this state. Therefore, this function sets
* a flag in the backup SRAM that will prevent the controller from booting in normal operation.
*/
void panic_mode(void);

View File

@@ -18,32 +18,18 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @addtogroup safety-adc
* @{
*/
#ifndef __SAFETY_ADC_H__
#define __SAFETY_ADC_H__
#include <stdint.h>
#include <stdbool.h>
#define SAFETY_ADC_VREF_MVOLT (2500.0f)
#define SAFETY_ADC_VREF_TOL_MVOLT (100.0f)
#define SAFETY_ADC_TEMP_LOW_LIM (0.0f)
#define SAFETY_ADC_TEMP_HIGH_LIM (65.0f)
enum safety_adc_meas_channel {SAFETY_ADC_MEAS_VREF, SAFETY_ADC_MEAS_TEMP};
enum safety_adc_check_result {
SAFETY_ADC_CHECK_OK = 0UL,
SAFETY_ADC_CHECK_VREF_LOW = (1U<<0),
SAFETY_ADC_CHECK_VREF_HIGH = (1U<<1),
SAFETY_ADC_CHECK_TEMP_LOW = (1U<<2),
SAFETY_ADC_CHECK_TEMP_HIGH = (1U<<3),
SAFETY_ADC_INTERNAL_ERROR = (1U<<4),
};
extern enum safety_adc_check_result global_safety_adc_status;
enum safety_adc_check_result safety_adc_get_errors();
void safety_adc_clear_errors(void);
void safety_adc_init();
@@ -58,13 +44,10 @@ void safety_adc_trigger_meas(enum safety_adc_meas_channel measurement);
*/
int safety_adc_poll_result(uint16_t *adc_result);
enum safety_adc_check_result safety_adc_check_results(uint16_t vref_result, uint16_t temp_result,
float *vref_calculated, float *temp_calculated);
enum safety_adc_check_result handle_safety_adc();
float safety_adc_get_temp();
float safety_adc_get_vref();
float safety_adc_convert_channel(enum safety_adc_meas_channel channel, uint16_t analog_value);
#endif /* __SAFETY_ADC_H__ */
/** @} */

View File

@@ -0,0 +1,144 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __SAFETY_CONFIG_H__
#define __SAFETY_CONFIG_H__
enum safety_flag {
ERR_FLAG_NO_FLAG = 0,
ERR_FLAG_MEAS_ADC_OFF = (1<<0),
ERR_FLAG_MEAS_ADC_OVERFLOW = (1<<1),
ERR_FLAG_MEAS_ADC_WATCHDOG = (1<<2),
ERR_FLAG_MEAS_ADC_UNSTABLE = (1<<3),
ERR_FLAG_TIMING_PID = (1<<4),
ERR_FLAG_TIMING_MEAS_ADC = (1<<5),
ERR_FLAG_AMON_VREF = (1<<6),
ERR_FLAG_AMON_UC_TEMP = (1<<7),
ERR_FLAG_STACK = (1<<8),
ERR_FLAG_SAFETY_ADC = (1<<9),
ERR_FLAG_SYSTICK = (1<<10),
ERR_FLAG_WTCHDG_FIRED = (1<<11),
ERR_FLAG_UNCAL = (1<<12),
ERR_FLAG_DEBUG = (1<<13),
ERR_FLAG_TIMING_MAIN_LOOP = (1<<14),
ERR_FLAG_SAFETY_MEM_CORRUPT = (1<<15),
ERR_FLAG_SAFETY_TAB_CORRUPT = (1<<16),
};
enum timing_monitor {
ERR_TIMING_PID = (1<<0),
ERR_TIMING_MEAS_ADC = (1<<1),
ERR_TIMING_SAFETY_ADC = (1<<2),
ERR_TIMING_MAIN_LOOP = (1<<3),
};
enum analog_value_monitor {
ERR_AMON_VREF = (1<<0),
ERR_AMON_UC_TEMP = (1<<1),
};
#define ERR_FLAG_ENTRY(errflag) {.name=#errflag, .flag = (errflag), .error_state = false, .error_state_inv = true, .key = 0UL, .weight = NULL, .persistency = NULL}
#define TIM_MON_ENTRY(mon, min, max, flag) {.name=#mon, .monitor = (mon), .associated_flag=(flag), .min_delta = (min), .max_delta = (max), .last = 0ULL, .enabled= false}
#define ANA_MON_ENTRY(mon, min_value, max_value, flag) {.name=#mon, .monitor = (mon), .associated_flag=(flag), .min = (min_value), .max = (max_value), .value = 0.0f, .valid = false}
#define ERR_FLAG_WEIGHT_ENTRY(_flag, _weight) {.flag = (_flag), .flag_ptr = NULL, .weight = (_weight), .start_dummy = 0x11823344, .end_dummy = 0xAABBCCFD}
#define ERR_FLAG_PERSIST_ENTRY(_flag, _persist) {.flag = (_flag), .flag_ptr = NULL, .persistency = (_persist), .start_dummy = 0xFF1100BB, .end_dummy = 0xEBB439A2}
/**
* @brief Magic key used to reset the watchdog using the @ref watchdog_ack function
*/
#define WATCHDOG_MAGIC_KEY 0x1a2c56F4
#ifdef DEBUGBUILD
/**
* @brief If one, the watchdog is halted whenever the core is halted by the debugger.
*
* This is only applicable in a debug build. In release mode, the watchdog stays always enabled
*/
#define WATCHDOG_HALT_DEBUG (1)
#else
#define WATCHDOG_HALT_DEBUG (0)
#endif
#define WATCHDOG_PRESCALER 16
/**
* @brief Minimum number of bytes that have to be free on the stack. If this is not the case, an error is detected
*/
#define SAFETY_MIN_STACK_FREE 0x100
#define PID_CONTROLLER_ERR_CAREMASK (ERR_FLAG_STACK | ERR_FLAG_AMON_UC_TEMP | ERR_FLAG_AMON_VREF | \
ERR_FLAG_TIMING_PID | ERR_FLAG_TIMING_MEAS_ADC | ERR_FLAG_MEAS_ADC_OFF | \
ERR_FLAG_MEAS_ADC_OVERFLOW)
#define HALTING_CAREMASK (ERR_FLAG_STACK | ERR_FLAG_AMON_UC_TEMP)
#define SAFETY_ADC_VREF_MVOLT (2500.0f)
#define SAFETY_ADC_VREF_TOL_MVOLT (100.0f)
#define SAFETY_ADC_TEMP_LOW_LIM (0.0f)
#define SAFETY_ADC_TEMP_HIGH_LIM (65.0f)
/**
* @brief Key used to lock the safety flags from external ack'ing
*/
#define MEAS_ADC_SAFETY_FLAG_KEY 0xe554dac3UL
#define SAFETY_CONTROLLER_ADC_DELAY_MS 120
#define SAFETY_CONFIG_DEFAULT_PERSIST ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_MEAS_ADC_OFF, false), \
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_MEAS_ADC_WATCHDOG, false), \
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_MEAS_ADC_UNSTABLE, false), \
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_MEAS_ADC_OVERFLOW, true), \
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_TIMING_MEAS_ADC, false), \
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_TIMING_PID, false), \
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_AMON_UC_TEMP, true), \
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_AMON_VREF, false), \
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_STACK, true), \
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_SAFETY_ADC, true), \
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_SYSTICK, true), \
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_WTCHDG_FIRED, true), \
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_UNCAL, false), \
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_DEBUG, true), \
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_TIMING_MAIN_LOOP, true), \
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_SAFETY_MEM_CORRUPT, true), \
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_SAFETY_TAB_CORRUPT, true),
#define SAFETY_CONFIG_DEFAULT_WEIGHTS ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_MEAS_ADC_OFF, SAFETY_FLAG_CONFIG_WEIGHT_PID), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_MEAS_ADC_WATCHDOG, SAFETY_FLAG_CONFIG_WEIGHT_PID), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_MEAS_ADC_UNSTABLE, SAFETY_FLAG_CONFIG_WEIGHT_NONE), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_MEAS_ADC_OVERFLOW, SAFETY_FLAG_CONFIG_WEIGHT_PID), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_TIMING_MEAS_ADC, SAFETY_FLAG_CONFIG_WEIGHT_PID), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_TIMING_PID, SAFETY_FLAG_CONFIG_WEIGHT_PID), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_AMON_UC_TEMP, SAFETY_FLAG_CONFIG_WEIGHT_PANIC), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_AMON_VREF, SAFETY_FLAG_CONFIG_WEIGHT_PID), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_STACK, SAFETY_FLAG_CONFIG_WEIGHT_PANIC), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_SAFETY_ADC, SAFETY_FLAG_CONFIG_WEIGHT_PANIC), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_SYSTICK, SAFETY_FLAG_CONFIG_WEIGHT_PANIC), \
/* Watchdog timeout is not handled perioodically, but only on startup.
* Therefore, it is not listed here */\
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_WTCHDG_FIRED, SAFETY_FLAG_CONFIG_WEIGHT_NONE), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_UNCAL, SAFETY_FLAG_CONFIG_WEIGHT_NONE), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_DEBUG, SAFETY_FLAG_CONFIG_WEIGHT_NONE), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_TIMING_MAIN_LOOP, SAFETY_FLAG_CONFIG_WEIGHT_PANIC), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_SAFETY_MEM_CORRUPT, SAFETY_FLAG_CONFIG_WEIGHT_PID), \
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_SAFETY_TAB_CORRUPT, SAFETY_FLAG_CONFIG_WEIGHT_PANIC),
#endif /* __SAFETY_CONFIG_H__ */

View File

@@ -0,0 +1,256 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @addtogroup safety-controller
* @{
*/
#ifndef __SAFETY_CONTROLLER_H__
#define __SAFETY_CONTROLLER_H__
#include <reflow-controller/safety/safety-config.h>
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
/**
* @brief State of an analog monitor
*/
enum analog_monitor_status {ANALOG_MONITOR_OK = 0, /**< @brief Monitor set up and ok */
ANALOG_MONITOR_ERROR, /**< @brief An internal error occured */
ANALOG_MONITOR_INACTIVE, /**< @brief Monitor inactive. Reading is not valid */
ANALOG_MONITOR_OVER, /**< @brief Value too high */
ANALOG_MONITOR_UNDER}; /**< @brief Value too low */
/**
* @brief Info structure describing an analog monitor
*/
struct analog_monitor_info {
float value; /**< @brief Current analog value */
float min; /**< @brief Minumum value allowed */
float max; /**< @brief Maximum value allowed */
enum analog_monitor_status status; /**< @brief Current monitor status */
uint64_t timestamp; /**< @brief ms timestamp when @ref analog_monitor_info::value was taken. */
};
/**
* @brief Info structure describing a timing monitor
*/
struct timing_monitor_info {
uint64_t last_run; /**< @brief Timestamp, when the monitor was last triggered */
uint64_t min; /**< @brief Minimum delay between two activations in ms */
uint64_t max; /**< @brief Maximum delay between two activations in ms */
bool enabled; /**< @brief Monitor enabled */
uint64_t delta; /**< @brief Last delta between two activations */
};
/**
* @brief Initialize the safety controller.
*
* After a call to this function the controller is iniotlaized and the watchdog is set up.
* You have to call safety_controller_handle
* If this function fails, it will hang, because errors in the safety controller are not recoverable
*/
void safety_controller_init();
/**
* @brief Handle the safety controller.
* @note This function must be executed periodically in order to prevent the watchdog from resetting the firmware
* @return 0 if successful
*/
int safety_controller_handle();
/**
* @brief Report one or multiple errors to the safety controller
*
* When passing multipe error glags, the flags have to be ORed together.
*
* @param flag Error flag to report
* @return 0 if successful.
*/
int safety_controller_report_error(enum safety_flag flag);
/**
* @brief Report one or multiple error flags with a key.
*
* When setting a \p key on an error flag. The error flag can only be cleared,
* by passing the same key value to the @ref safety_controller_ack_flag_with_key function.
*
* @param flag Error flag to report
* @param key Key
* @return 0 if successful
*/
int safety_controller_report_error_with_key(enum safety_flag flag, uint32_t key);
/**
* @brief Report timing to a timing monitor.
* @param monitor Monitor to report
*/
void safety_controller_report_timing(enum timing_monitor monitor);
/**
* @brief Report an analog value to an analog value monitor
* @param monitor Monitor to report
* @param value Analog value
*/
void safety_controller_report_analog_value(enum analog_value_monitor monitor, float value);
/**
* @brief Enable or disable a timing monitor.
* @param monitor Monitor to enable
* @param enable State to set the monitor to.
* @return 0 if successful.
*/
int safety_controller_enable_timing_mon(enum timing_monitor monitor, bool enable);
/**
* @brief Get the value of an analog monitor.
* @param monitor Monitor to get value from
* @param[out] value The analog value
* @returns Status of the analog monitor. \p value only valid, if return value does not indicate an internal error,
* or an inactive monitor.
*/
enum analog_monitor_status safety_controller_get_analog_mon_value(enum analog_value_monitor monitor, float *value);
/**
* @brief Get error flag state and optionally acknowledge the flag
*
* If the flag is persistent, it cannot be ack'ed. In this case this function
* does not return an error code.
*
* @param flag Error flag
* @param[out] status state of the flag.
* @param try_ack Try to ack the flag. This might fail, if the flag is persistent.
* @return 0 if successful.
*/
int safety_controller_get_flag(enum safety_flag flag, bool *status, bool try_ack);
/**
* @brief Ack an error flag
* @param flag Error flag to ack
* @return 0 if successful, -2 if flag is persistent or keyed. All other values: Errors
*/
int safety_controller_ack_flag(enum safety_flag flag);
/**
* @brief Acknowledge error flag with a key
* @param flag Error flag
* @param key Key
* @return 0 if successful, -2 if flag is persistent or key wrong. All other values: Errors
*/
int safety_controller_ack_flag_with_key(enum safety_flag flag, uint32_t key);
/**
* @brief Get an ored status of multiple flags.
* @param mask Flags to check
* @return True if errors. False if no errors.
*/
bool safety_controller_get_flags_by_mask(enum safety_flag mask);
/**
* @brief Get the count of error flags
* @return Error flag count
*/
uint32_t safety_controller_get_flag_count();
/**
* @brief Get the count of analog monitors
* @return Analog monitor count
*/
uint32_t safety_controller_get_analog_monitor_count();
/**
* @brief Get an error flag's name by its index.
*
* The name of the flag will be cropped, if the buffersize is too small.
* Paramter \p buffsize may not be zero.
*
* @param index 0 based index.
* @param[out] buffer Buffer to write the name to.
* @param buffsize Buffer size. This has to be big enough to hold the name and the \0-terminator
* @return 0 of successful
*/
int safety_controller_get_flag_name_by_index(uint32_t index, char *buffer, size_t buffsize);
/**
* @brief Get the safety flag by its internal index.
* @param index 0 based index.
* @param[out] status Current flag state. May be NULL.
* @param[out] flag_enum Flag enum used in SW. May be NULL.
* @return 0 if successful; else: negative
*/
int safety_controller_get_flag_by_index(uint32_t index, bool *status, enum safety_flag *flag_enum);
/**
* @brief Get an analog monitor info by the monitor's index
* @param index 0 based index
* @param[out] info Info structure.
* @return 0 if successful.
* -1001, if \p index out of range
* -1002, if \p info is NULL
*/
int safety_controller_get_analog_mon_by_index(uint32_t index, struct analog_monitor_info *info);
/**
* @brief Get the name of an analog monitor by its index
*
* The buffer has to be large enough to hold the name plus a null terminator.
* If the buffer is not large enough, The name will be cropped.
* Parameter \p buffsize may not be zero.
*
* @param index 0 based index
* @param buffer Buffer to write name to
* @param buffsize Buffer size
* @return
*/
int safety_controller_get_analog_mon_name_by_index(uint32_t index, char *buffer, size_t buffsize);
/**
* @brief Get timing monitor information by index
* @param index 0 based index
* @param[out] info Info
* @return 0 if successful
*/
int safety_controller_get_timing_mon_by_index(uint32_t index, struct timing_monitor_info *info);
/**
* @brief Get the name of a timing monitor by its index
*
* The buffer has to be large enough to hold the name plus a null terminator.
* If the buffer is not large enough, The name will be cropped.
* Parameter \p buffsize may not be zero.
*
* @param index 0 based index
* @param buffer Buffer to write name to.
* @param buffsize Buffer size
* @return 0 if successful
*/
int safety_controller_get_timing_mon_name_by_index(uint32_t index, char *buffer, size_t buffsize);
/**
* @brief Get the count of timing monitors
* @return Timing monitor count
*/
uint32_t safety_controller_get_timing_monitor_count();
#endif /* __SAFETY_CONTROLLER_H__ */
/** @} */

View File

@@ -0,0 +1,260 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __SAFETY_MEMORY_H__
#define __SAFETY_MEMORY_H__
#include <stdint.h>
#include <stddef.h>
/** @addtogroup safety-memory
* @{
*/
/**
* @brief Magic number to signal a valid safety memory header.
*/
#define SAFETY_MEMORY_MAGIC 0x12AA5CB7
/**
* @brief Error memory NOP entry
*/
#define SAFETY_MEMORY_NOP_ENTRY 0xC1AA1222
/**
* @brief Offset address for the safety_memory_header.
* @note Any other value than 0UL doesn't really make sense. Therfore, this should not be changed.
*/
#define SAFETY_MEMORY_HEADER_ADDRESS 0UL
#define SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT 32UL
/**
* @brief Safety memory header
*/
struct safety_memory_header {
uint32_t magic; /**< @brief Magic. Set to @ref SAFETY_MEMORY_MAGIC */
uint32_t boot_status_offset; /**< @brief Offset of the safety_memory_boot_status struct (in 32 bit words)*/
uint32_t config_overrides_offset; /**< @brief Offset address of override entries */
uint32_t config_overrides_len; /**< @brief Length of override entries in words */
uint32_t err_memory_offset; /**< @brief Offset of the error memory */
uint32_t err_memory_end; /**< @brief End of the error memory. This points to the word after the error memory, containing the CRC of the whole backup RAM. */
uint32_t crc; /**< @brief CRC of the header */
};
struct safety_memory_boot_status {
/**
* @brief Reboot into the bootloader
*
* When this flag is set, the controller will load the bootloader to
* memory and execute it.
*/
uint32_t reboot_to_bootloader;
/**
* @brief Bootloader has updated the code
*
* This flag is set, if the firmware ahs been updated successfully
*/
uint32_t code_updated;
/**
* @brief reset_from_panic
*
* This flag is set, when entering the panic mode.
* Because the panic mode is reset by a watchdog reset,
* this flag is needed, in order to ensure, that the panic is handled correcly after
* the watchdog reset.
*/
uint32_t reset_from_panic;
};
/**
* @brief The state of the safety memory
*
* This is returned by certain functions in order to signal, if the header and CRC infos are valid.
*/
enum safety_memory_state {
SAFETY_MEMORY_INIT_FRESH = 0, /**< @brief Memory header not found */
SAFETY_MEMORY_INIT_CORRUPTED = 1, /**< @brief Header found, but corrupt memory */
SAFETY_MEMORY_INIT_VALID_MEMORY = 2, /**< @brief Valid header found and CRC check is valid */
};
/**
* @brief Types of error memory entries
*/
enum safety_memory_error_entry_type {
SAFETY_MEMORY_ERR_ENTRY_FLAG = 1, /**< @brief Flag error entry. Logs a flag */
SAFETY_MEMORY_ERR_ENTRY_NOP = 2, /**< @brief NOP entry. Has no meaning, but will be treated as a valid entry */
};
/**
* @brief Firmware internal representation of an error memory entry.
*/
struct error_memory_entry {
enum safety_memory_error_entry_type type;
uint8_t flag_num;
uint16_t counter;
};
/**
* @brief Types of conig override entries
*/
enum config_override_entry_type {
SAFETY_MEMORY_CONFIG_OVERRIDE_WEIGHT = 1,
SAFETY_MEMORY_CONFIG_OVERRIDE_PERSISTANCE = 2,
};
/**
* @brief Weights of error flags.
*/
enum config_weight {
SAFETY_FLAG_CONFIG_WEIGHT_NONE = 0, /**< @brief This flag has no global error consequence, but might be respected by certain software modules. */
SAFETY_FLAG_CONFIG_WEIGHT_PID = 1, /**< @brief This flag will force a stop of the temperature PID controller */
SAFETY_FLAG_CONFIG_WEIGHT_PANIC = 2, /**< @brief This flag will trigger the panic mode */
};
/**
* @brief representation of a config override memory entry
*/
struct config_override {
enum config_override_entry_type type;
union {
struct {
uint8_t flag;
enum config_weight weight;
} weight_override;
struct {
uint8_t flag;
uint8_t persistance;
} persistance_override;
} entry;
};
/**
* @brief First time init the safety memory. This requests all clocks etc.
*
* The error memory is always vlaid after this function. At least, if it returns without error.
* The \p found_state output tells the caller, in which state the memory was found. If it was uninitialized,
* or corrupted, it is completely wiped and a fresh memory structure is written.
*
* @param[out] found_state State the error memory was found in
* @return 0 if successful
* @warning Also check @ref safety_memory_reinit
*/
int safety_memory_init(enum safety_memory_state *found_state);
/**
* @brief Same as @ref safety_memory_init, but without specifically requesting the clock modules.
*
* Use this, if a call to @ref safety_memory_init has already been done.
*
* @param[out] found_state State the error memory was found in
* @return 0 if successful
*/
int safety_memory_reinit(enum safety_memory_state *found_state);
/**
* @brief Get the boot status structure from safety memory
* @param[out] status Status read from memory.
* @return 0 if successful
*/
int safety_memory_get_boot_status(struct safety_memory_boot_status *status);
/**
* @brief Write the boot status structure to safety memory
* @param[in] status Status to write
* @return 0 if successful
*/
int safety_memory_set_boot_status(const struct safety_memory_boot_status *status);
/**
* @brief Get the amout of error entries in the error memory. This also includes NOP entries.
* @param[out] count Count
* @return 0 if successful
*/
int safety_memory_get_error_entry_count(uint32_t *count);
/**
* @brief Check the header and CRC of the safety memory.
* @return 0 if all checks pass
*/
int safety_memory_check(void);
/**
* @brief Read an error entry from the error memory
* @param idx Index of the entry
* @param[out] entry Error entry
* @return 0 if successful
*/
int safety_memory_get_error_entry(uint32_t idx, struct error_memory_entry *entry);
/**
* @brief Insert an error entry
*
* This function inserts an error entry on the first NOP entry found in the error memory.
* If an entry is found with the same flag number, its counter is incremented by the counter value of the
* element to insert.
*
* If there are no NOPs or fitting entries in the error memory, error memory is expanded until it hits the memory
* boundary.
*
* @param entry Error entry to insert
* @returns 0 if successful, -3 if out of memory, and other negative error codes
*/
int safety_memory_insert_error_entry(struct error_memory_entry *entry);
/**
* @brief Insert a config override entry at the first free location.
*
* Free locations are entries containing 0x00000000
*
* @param config_override Config to write
* @return 0 if successful
*/
int safety_memory_insert_config_override(struct config_override *config_override);
/**
* @brief Get count of config overrides
* @param[out] count Number of overrides
* @return 0 if successful
*/
int safety_memory_get_config_override_count(uint32_t *count);
/**
* @brief Get a config ovveide entry
* @param idx Index of the requested entry
* @param[out] config_override READ override
* @return 0 if successful
*/
int safety_memory_get_config_override(uint32_t idx, struct config_override *config_override);
/**
* @brief Get a base64 dump of the whole safety memory.
* @param[out] buffer Buffer to write the base 64 dump into.
* @param buffsize Size of buffer. Must be large enough to hold the data plus a '\0' terminator
* @param[out] used_size Number of written bytes including the '\0' terminator. May be NULL.
* @return 0 if successful
*/
int safety_memory_dump_base64(char *buffer, size_t buffsize, size_t *used_size);
#endif /* __SAFETY_MEMORY_H__ */
/** @} */

View File

@@ -0,0 +1,97 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* GDSII-Converter 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __STACK_CHECK_H__
#define __STACK_CHECK_H__
#include <stdint.h>
#define STACK_CHECK_MIN_HEAP_GAP 16UL
/**
* @brief Get usage of the stack
* @return Usage of the stack in bytes
*/
int32_t stack_check_get_usage();
/**
* @brief Get free stack space
* @return free stack space in bytes. If negative, a stack overflow occured
*/
int32_t stack_check_get_free();
/**
* @brief Check if the current free stack space is bigger than @ref STACK_CHECK_MIN_HEAP_GAP
* @return 0: enough space available, -1: stack space low
*/
static inline int stack_check_collision()
{
int ret = 0;
int32_t free_space = stack_check_get_free();
if ((unsigned int)free_space < STACK_CHECK_MIN_HEAP_GAP) {
ret = -1;
}
return ret;
}
/**
* @brief Get the current stack pointer value
* @return
*/
static inline uint32_t read_stack_pointer()
{
uint32_t stack_pointer;
__asm__ __volatile__ ("mov %0, sp\n\t" : "=r"(stack_pointer) : : );
return stack_pointer;
}
/**
* @brief Init the stack corruption detection area.
*
* This function initializes the memory area between heap and stack with random values generated by the
* STM's random number generator. A 32 bit CRC generated by the CRC unit of the STM is appended for verification of the
* area.
*
*
* @return 0 if successful, else an error has occured in generating a random number. This should never happen
* @note This function turns on the CRC unit but does not disable it afterwards. Therefore, the CRC unit does not have
* to be explicitly initialized before calling @ref stack_check_corruption_detect_area.
*/
int stack_check_init_corruption_detect_area(void);
/**
* @brief Check the CRC of the stack corruption detection area
*
* This function checks the stack corruption detection area, which must be initialized by
* @ref stack_check_init_corruption_detect_area beforehand.
*
* The CRC unit must be enabled for this function to work properly.
* After calling @stack_check_init_corruption_detect_area, this is the case.
*
* @return 0 if no error is detected, all other values are an error.
* @note Make sure CRC unit is enabled.
*/
int stack_check_corruption_detect_area(void);
#endif /* __STACK_CHECK_H__ */

View File

@@ -0,0 +1,50 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __WATCHDOG_H__
#define __WATCHDOG_H__
#include <reflow-controller/safety/safety-config.h>
#include <stdint.h>
#include <stdbool.h>
/**
* @brief Setup the watchdog for the safety controller
* @param Prescaler to use for the 32 KHz LSI clock
* @return 0 if successful
* @note Once the watchdog is enabled, it cannot be turned off!
*/
int watchdog_setup(uint8_t prescaler);
/**
* @brief Reset watchdog counter
* @param magic Magic value to prevent this fuinction from being called randomly
* @return 0 if successful
*/
int watchdog_ack(uint32_t magic);
/**
* @brief Check if reset was generated by the watchdog.
* @note This also clears the relevant flag, so the function will return false when called a second time
* @return true, if reset was generated by the watchdog
*/
bool watchdog_check_reset_source(void);
#endif /* __WATCHDOG_H__ */

View File

@@ -21,4 +21,19 @@
#ifndef __SETTINGS_SETTINGS_SD_CARD_H__
#define __SETTINGS_SETTINGS_SD_CARD_H__
#include <stdbool.h>
#include <reflow-controller/settings/settings.h>
int sd_card_settings_save_calibration(float sens_deviation, float offset, bool active);
/**
* @brief Try and load calibration from SD card
* @param sens_deviation
* @param offset
* @return 0 if files found -1 if files errorneous, -2 if no files found
*/
int sd_card_settings_try_load_calibration(float *sens_deviation, float *offset);
enum settings_load_result sd_card_settings_load_pid_oven_parameters(struct oven_pid_settings *settings);
#endif /* __SETTINGS_SETTINGS_SD_CARD_H__ */

View File

@@ -22,6 +22,35 @@
#ifndef __SETTINGS_SETTINGS_H__
#define __SETTINGS_SETTINGS_H__
int settings_save_calibration();
#include <stdbool.h>
struct oven_pid_settings {
float kd;
float kp;
float ki;
float t_sample;
float max_integral;
};
enum settings_load_result {
SETT_LOAD_SUCCESS = 0,
SETT_LOAD_FILE_NOT_FOUND,
SETT_LOAD_ERR,
SETT_LOAD_DISK_ERR
};
#define SETTINGS_PID_PARAMETER_FILE "pid.conf"
/**
* @brief Save the calibration
* @param sens_deviation
* @param offset
* @return 0 if successful, -1 if generic error, -2 if medium unavailable
*/
int settings_save_calibration(float sens_deviation, float offset, bool active);
int settings_load_calibration(float *sens_dev, float *offset);
enum settings_load_result settings_load_pid_oven_parameters(struct oven_pid_settings *settings);
#endif /* __SETTINGS_SETTINGS_H__ */

View File

@@ -12,7 +12,7 @@
#define SHELL_UART_RX_DMA_TRIGGER 4U
#define SHELL_UART_TX_DMA_TRIGGER 4U
#ifdef DEBUGBUILD
#if defined(DEBUGBUILD) || defined(UART_ON_DEBUG_HEADER)
#define SHELL_UART_PORT GPIOA
#define SHELL_UART_PORT_RCC_MASK RCC_AHB1ENR_GPIOAEN

View File

@@ -1,53 +0,0 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* GDSII-Converter 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __STACK_CHECK_H__
#define __STACK_CHECK_H__
#include <stdint.h>
#define STACK_CHECK_MIN_HEAP_GAP 16UL
int32_t stack_check_get_usage();
int32_t stack_check_get_free();
static inline int stack_check_collision()
{
int ret = 0;
int32_t free_space = stack_check_get_free();
if ((unsigned int)free_space < STACK_CHECK_MIN_HEAP_GAP) {
ret = -1;
}
return ret;
}
static inline uint32_t read_stack_pointer()
{
uint32_t stack_pointer;
__asm__ __volatile__ ("mov %0, sp\n\t" : "=r"(stack_pointer) : : );
return stack_pointer;
}
#endif /* __STACK_CHECK_H__ */

View File

@@ -39,6 +39,8 @@
#define LCD_SHIMATTA_STRING "\xBC\xCF\xAF\xC0"
#define LCD_DEGREE_SYMBOL_STRING "\xDF"
#define LCD_DEGREE_SYMBOL_CHAR '\xDF'
#define LCD_OHM_SYMBOL_CHAR '\xF4'
#define LCD_OHM_SYMBOL_STRING "\xF4"
enum lcd_fsm_ret {LCD_FSM_NOP, LCD_FSM_CALL_AGAIN, LCD_FSM_WAIT_CALL};

View File

@@ -0,0 +1,59 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stdbool.h>
/**
* @brief Init the backup ram and make it accesible
* @param use_backup_regulator Enable the Backup VBAT regulator. It will be used, when VDD is powered off
*/
void backup_ram_init(bool use_backup_regulator);
/**
* @brief Disable access to the backup RAM. This saves power
*/
void backup_ram_disable(void);
/**
* @brief Whis function overwrites the backup RAM with 0x00000000
*/
void backup_ram_wipe(void);
/**
* @brief Read data from the backup RAM
* @param addr Address offset inside memory
* @param data Read data
* @param count amount of 32 bit words to read
* @return 0 if successful
*/
int backup_ram_get_data(uint32_t addr, uint32_t *data, uint32_t count);
/**
* @brief Write data structure to backup RAM
* @param[in] data Data to write.
* @param count Count of 32 bit words to write
* @return 0 if successful
*/
int backup_ram_write_data(uint32_t addr, const uint32_t *data, uint32_t count);
uint32_t backup_ram_get_size_in_words(void);
volatile void *backup_ram_get_base_ptr(void);

View File

@@ -64,5 +64,4 @@ int rcc_manager_enable_clock(volatile uint32_t *rcc_enable_register, uint8_t bit
*/
int rcc_manager_disable_clock(volatile uint32_t *rcc_enable_register, uint8_t bit_no);
#endif /* __CLOCK_ENABLE_MANAGER_H__ */

View File

@@ -0,0 +1,38 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* GDSII-Converter 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __CRC_UNIT_H__
#define __CRC_UNIT_H__
#include <stdint.h>
void crc_unit_init(void);
void crc_unit_deinit(void);
void crc_unit_reset(void);
uint32_t crc_unit_get_crc(void);
void crc_unit_input(uint32_t data);
void crc_unit_input_array(const uint32_t *data, uint32_t len);
#endif /* __CRC_UNIT_H__ */

View File

@@ -0,0 +1,42 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* GDSII-Converter 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __STM_RNG_H__
#define __STM_RNG_H__
#include <stdint.h>
#include <stdbool.h>
enum random_number_error {
RNG_ERROR_OK = 0,
RNG_ERROR_INACT,
RNG_ERROR_INTERNAL_ERROR,
RNG_ERROR_NOT_READY
};
void random_number_gen_init(bool int_enable);
void random_number_gen_deinit();
void random_number_gen_reset(bool int_en);
enum random_number_error random_number_gen_get_number(uint32_t *random_number, bool wait_for_valid_value);
#endif /* __STM_RNG_H__ */

View File

@@ -26,8 +26,6 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
/* #include <arm_math.h> */
#include <stm32/stm32f4xx.h>
#include <cmsis/core_cm4.h>
#include <setup/system_stm32f4xx.h>
@@ -41,11 +39,10 @@
#include <stm-periph/uart.h>
#include <reflow-controller/shell-uart-config.h>
#include <reflow-controller/oven-driver.h>
#include <reflow-controller/safety-adc.h>
#include <fatfs/ff.h>
#include <reflow-controller/reflow-menu.h>
bool global_error_state;
#include <reflow-controller/safety/safety-controller.h>
#include <reflow-controller/settings/settings.h>
static void setup_nvic_priorities(void)
{
@@ -54,12 +51,14 @@ static void setup_nvic_priorities(void)
/* Setup Priorities */
NVIC_SetPriority(ADC_IRQn, 2);
/* Measurement ADC DMA */
NVIC_SetPriority(DMA2_Stream0_IRQn, 1);
/* Shelmatta UART TX */
NVIC_SetPriority(DMA2_Stream7_IRQn, 3);
}
FATFS fs;
FATFS * const fs_ptr = &fs;
#define fs_ptr (&fs)
static inline void uart_gpio_config(void)
{
@@ -68,12 +67,15 @@ static inline void uart_gpio_config(void)
* else the Pins on the DIGIO header are configured in the digio module
*/
#ifdef DEBUGBUILD
#if defined(DEBUGBUILD) || defined(UART_ON_DEBUG_HEADER)
rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(SHELL_UART_PORT_RCC_MASK));
SHELL_UART_PORT->MODER &= MODER_DELETE(SHELL_UART_TX_PIN) & MODER_DELETE(SHELL_UART_RX_PIN);
SHELL_UART_PORT->MODER |= ALTFUNC(SHELL_UART_RX_PIN) | ALTFUNC(SHELL_UART_TX_PIN);
SETAF(SHELL_UART_PORT, SHELL_UART_RX_PIN, SHELL_UART_RX_PIN_ALTFUNC);
SETAF(SHELL_UART_PORT, SHELL_UART_TX_PIN, SHELL_UART_TX_PIN_ALTFUNC);
/* Setup Pullup resistor at UART RX */
SHELL_UART_PORT->PUPDR |= PULLUP(SHELL_UART_RX_PIN);
#endif
}
@@ -113,16 +115,25 @@ static inline void setup_shell_uart(struct stm_uart *uart)
static bool mount_sd_card_if_avail(bool mounted)
{
FRESULT res;
static uint8_t IN_SECTION(.ccm.bss) inserted_counter = 0;
if (sdio_check_inserted() && mounted) {
memset(fs_ptr, 0, sizeof(FATFS));
sdio_stop_clk();
inserted_counter = 0;
return false;
}
if (!sdio_check_inserted() && !mounted) {
if (!sdio_check_inserted() && inserted_counter < 255)
inserted_counter++;
if (!sdio_check_inserted() && !mounted && inserted_counter > 4) {
inserted_counter = 0;
res = f_mount(fs_ptr, "0:/", 1);
if (res == FR_OK)
if (res == FR_OK) {
led_set(1, 1);
return true;
}
else
return false;
}
@@ -138,6 +149,7 @@ static void setup_unused_pins(void)
GPIOE->MODER = 0UL;
for (i = 0; i < 16; i++)
GPIOE->PUPDR |= PULLDOWN(i);
rcc_manager_disable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(RCC_AHB1ENR_GPIOEEN));
}
static inline void setup_system(void)
@@ -145,18 +157,19 @@ static inline void setup_system(void)
setup_nvic_priorities();
systick_setup();
adc_pt1000_setup_meas();
oven_driver_init();
digio_setup_default_all();
led_setup();
loudspeaker_setup();
reflow_menu_init();
safety_adc_init();
uart_gpio_config();
setup_shell_uart(&shell_uart);
setup_unused_pins();
safety_controller_init();
adc_pt1000_setup_meas();
}
static void handle_shell_uart_input(shellmatta_handle_t shell_handle)
@@ -171,91 +184,55 @@ static void handle_shell_uart_input(shellmatta_handle_t shell_handle)
shell_handle_input(shell_handle, uart_input, uart_input_len);
}
static void zero_ccm_ram(void)
{
/* These extern variables are placed in the linker script */
extern char _sccmram;
extern char _eccmram;
uint32_t len;
uint32_t i;
uint32_t *ptr = (uint32_t *)&_sccmram;
len = (uint32_t)&_eccmram - (uint32_t)&_sccmram;
for (i = 0; i < len; i++)
ptr[i] = 0UL;
}
/**
* @brief This function sets the appropriate error flags in the oven PID controller
* depending on the Safety ADC measurements.
* The PID controller's error flags have to be cleared via the GUI by either starting a new RUN or explicitly
* ack'ing these errors.
*/
static void propagate_safety_adc_error_to_oven_pid(void)
{
enum safety_adc_check_result safety_adc_result;
safety_adc_result = safety_adc_get_errors();
if (safety_adc_result & SAFETY_ADC_CHECK_TEMP_LOW ||
safety_adc_result & SAFETY_ADC_CHECK_TEMP_HIGH)
oven_pid_report_error(OVEN_PID_ERR_OVERTEMP);
if (safety_adc_result & SAFETY_ADC_CHECK_VREF_LOW ||
safety_adc_result & SAFETY_ADC_CHECK_VREF_HIGH)
oven_pid_report_error(OVEN_PID_ERR_VREF_TOL);
if (safety_adc_result & SAFETY_ADC_INTERNAL_ERROR)
oven_pid_report_error(0);
}
int main(void)
{
bool cal_active;
float offset;
float sens;
int status;
bool sd_card_mounted = false;
bool sd_old;
shellmatta_handle_t shell_handle;
int menu_wait_request;
uint64_t quarter_sec_timestamp = 0ULL;
const struct oven_pid_status *pid_status;
enum adc_pt1000_error pt1000_status;
zero_ccm_ram();
setup_system();
global_error_state = false;
shell_handle = shell_init(write_shell_callback);
shell_print_motd(shell_handle);
while (1) {
sd_card_mounted = mount_sd_card_if_avail(sd_card_mounted);
pid_status = oven_pid_get_status();
if (systick_ticks_have_passed(quarter_sec_timestamp, 250)) {
led_set(1, 0);
sd_old = sd_card_mounted;
sd_card_mounted = mount_sd_card_if_avail(sd_card_mounted);
if (sd_card_mounted && !sd_old) {
adc_pt1000_get_resistance_calibration(NULL, NULL, &cal_active);
if (!cal_active) {
status = settings_load_calibration(&sens, &offset);
if (!status) {
adc_pt1000_set_resistance_calibration(offset, sens, true);
}
}
}
quarter_sec_timestamp = systick_get_global_tick();
(void)handle_safety_adc();
propagate_safety_adc_error_to_oven_pid();
if (global_error_state)
led_set(0, !led_get(0));
else
led_set(0, 0);
}
pt1000_status = adc_pt1000_check_error();
global_error_state = pid_status->error_set || !!safety_adc_get_errors() || !!pt1000_status;
menu_wait_request = reflow_menu_handle();
/* Deactivate oven output in case of error! */
if (!pid_status->active || global_error_state)
oven_driver_set_power(0U);
handle_shell_uart_input(shell_handle);
safety_controller_handle();
/* Todo: Remove this */
oven_driver_set_power(0);
oven_driver_apply_power_level();
safety_controller_report_timing(ERR_TIMING_MAIN_LOOP);
if (menu_wait_request)
__WFI();
else
__NOP();
}
@@ -267,6 +244,9 @@ void sdio_wait_ms(uint32_t ms)
systick_wait_ms(ms);
}
/**
* @brief Handles the TX of UART1 (Shellmatta)
*/
void DMA2_Stream7_IRQHandler(void)
{
uint32_t hisr = DMA2->HISR;

View File

@@ -1,22 +1,22 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <reflow-controller/oven-driver.h>
#include <reflow-controller/periph-config/oven-driver-hwcfg.h>
@@ -24,18 +24,15 @@
#include <reflow-controller/systick.h>
#include <reflow-controller/adc-meas.h>
#include <reflow-controller/temp-converter.h>
#include <helper-macros/helper-macros.h>
#include <reflow-controller/safety/safety-controller.h>
static struct pid_controller oven_pid;
static struct pid_controller IN_SECTION(.ccm.bss) oven_pid;
static bool oven_pid_running;
static bool oven_pid_aborted;
static uint8_t IN_SECTION(.ccm.bss) oven_driver_power_level;
static struct oven_pid_status oven_pid_current_status = {
.active = false,
.error_set = false,
.target_temp = 0.0f,
.current_temp = 0.0f,
.timestamp_last_run = 0ULL
};
void oven_driver_init()
void oven_driver_init(void)
{
rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(OVEN_CONTROLLER_PORT_RCC_MASK));
rcc_manager_enable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(OVEN_CONTROLLER_TIM_RCC_MASK));
@@ -53,6 +50,12 @@ void oven_driver_init()
OVEN_CONTROLLER_PWM_TIMER->PSC = 42000U - 1U;
OVEN_CONTROLLER_PWM_TIMER->CR1 = TIM_CR1_CMS | TIM_CR1_CEN;
/* Explicitly init global variables */
oven_pid_aborted = false;
oven_pid_running = false;
oven_driver_set_power(0U);
}
void oven_driver_set_power(uint8_t power)
@@ -60,10 +63,15 @@ void oven_driver_set_power(uint8_t power)
if (power > 100U)
power = 100U;
OVEN_CONTROLLER_PWM_TIMER->CCR3 = power * 10;
oven_driver_power_level = power;
}
void oven_driver_disable()
void oven_driver_apply_power_level(void)
{
OVEN_CONTROLLER_PWM_TIMER->CCR3 = oven_driver_power_level * 10;
}
void oven_driver_disable(void)
{
OVEN_CONTROLLER_PWM_TIMER->CR1 = 0UL;
OVEN_CONTROLLER_PWM_TIMER->CR2 = 0UL;
@@ -71,93 +79,59 @@ void oven_driver_disable()
rcc_manager_disable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(OVEN_CONTROLLER_TIM_RCC_MASK));
}
void oven_pid_ack_errors(void)
{
oven_pid_current_status.error_set = false;
oven_pid_current_status.error_flags.vref_tol = false;
oven_pid_current_status.error_flags.pt1000_other = false;
oven_pid_current_status.error_flags.generic_error = false;
oven_pid_current_status.error_flags.pt1000_adc_off = false;
oven_pid_current_status.error_flags.controller_overtemp = false;
oven_pid_current_status.error_flags.pt1000_adc_watchdog = false;
}
void oven_pid_init(struct pid_controller *controller_to_copy)
{
pid_copy(&oven_pid, controller_to_copy);
oven_pid.output_sat_min = 0.0f;
oven_pid.output_sat_max = 100.0f;
oven_pid_current_status.timestamp_last_run = 0ULL;
oven_pid_current_status.active = true;
oven_pid_ack_errors();
oven_pid_running = true;
oven_pid_aborted = false;
safety_controller_report_timing(ERR_TIMING_PID);
}
void oven_pid_handle(float target_temp)
{
float pid_out;
float current_temp;
int resistance_status;
enum adc_pt1000_error pt1000_error;
if (oven_pid_current_status.active && !oven_pid_current_status.error_set) {
if (systick_ticks_have_passed(oven_pid_current_status.timestamp_last_run,
(uint64_t)(oven_pid.sample_period * 1000))) {
resistance_status = adc_pt1000_get_current_resistance(&current_temp);
if (resistance_status < 0) {
oven_driver_set_power(0);
pt1000_error = adc_pt1000_check_error();
if (pt1000_error & ADC_PT1000_WATCHDOG_ERROR)
oven_pid_report_error(OVEN_PID_ERR_PT1000_ADC_WATCHDOG);
if (pt1000_error & ADC_PT1000_INACTIVE)
oven_pid_report_error(OVEN_PID_ERR_PT1000_ADC_OFF);
if (pt1000_error & ADC_PT1000_OVERFLOW)
oven_pid_report_error(OVEN_PID_ERR_PT1000_OTHER);
return;
}
static uint64_t timestamp_last_run;
if (oven_pid_running && !oven_pid_aborted) {
if (systick_ticks_have_passed(timestamp_last_run, (uint64_t)(oven_pid.sample_period * 1000))) {
/* No need to check. Safety controller will monitor this */
(void)adc_pt1000_get_current_resistance(&current_temp);
(void)temp_converter_convert_resistance_to_temp(current_temp, &current_temp);
pid_out = pid_sample(&oven_pid, target_temp - current_temp);
oven_driver_set_power((uint8_t)pid_out);
oven_pid_current_status.timestamp_last_run = systick_get_global_tick();
oven_pid_current_status.target_temp = target_temp;
oven_pid_current_status.current_temp = current_temp;
timestamp_last_run = systick_get_global_tick();
safety_controller_report_timing(ERR_TIMING_PID);
}
}
}
void oven_pid_report_error(enum oven_pid_error_report report)
void oven_pid_stop(void)
{
struct oven_pid_errors *e = &oven_pid_current_status.error_flags;
oven_pid_current_status.active = false;
oven_pid_current_status.error_set = true;
if (report == 0) {
e->generic_error = true;
}
if (report & OVEN_PID_ERR_OVERTEMP)
e->controller_overtemp = true;
if (report & OVEN_PID_ERR_VREF_TOL)
e->controller_overtemp = true;
if (report & OVEN_PID_ERR_PT1000_OTHER)
e->pt1000_other = true;
if (report & OVEN_PID_ERR_PT1000_ADC_OFF)
e->pt1000_adc_off = true;
if (report & OVEN_PID_ERR_PT1000_ADC_WATCHDOG)
e->pt1000_adc_watchdog = true;
oven_pid_running = false;
oven_driver_set_power(0U);
safety_controller_enable_timing_mon(ERR_TIMING_PID, false);
}
const struct oven_pid_status *oven_pid_get_status()
void oven_pid_abort(void)
{
return &oven_pid_current_status;
oven_pid_aborted = true;
oven_pid_stop();
}
void oven_pid_stop()
enum oven_pid_status oven_pid_get_status(void)
{
oven_pid_current_status.active = false;
oven_pid_current_status.target_temp = 0.0f;
oven_pid_current_status.current_temp = 0.0f;
enum oven_pid_status ret = OVEN_PID_ABORTED;
if (oven_pid_running && !oven_pid_aborted)
ret = OVEN_PID_RUNNING;
else if (!oven_pid_running && !oven_pid_aborted)
ret = OVEN_PID_DEACTIVATED;
return ret;
}

View File

@@ -1,27 +1,28 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <reflow-controller/pid-controller.h>
#include <string.h>
void pid_init(struct pid_controller *pid, float k_deriv, float k_int, float k_p, float output_sat_min, float output_sat_max, float integral_max, float sample_period)
void pid_init(struct pid_controller *pid, float k_deriv, float k_int, float k_p, float output_sat_min,
float output_sat_max, float integral_max, float sample_period)
{
if (!pid)
return;
@@ -61,11 +62,10 @@ static void calculate_integral(struct pid_controller *pid, float deviation)
pid->integral = pid->integral + pid->k_int_t * (deviation + pid->last_in);
/* Saturate integral term to spoecified maximum */
if (pid->integral > pid->integral_max) {
if (pid->integral > pid->integral_max)
pid->integral = pid->integral_max;
} else if (pid->integral < -pid->integral_max){
pid->integral = - pid->integral_max;
}
else if (pid->integral < -pid->integral_max)
pid->integral = -pid->integral_max;
}
float pid_sample(struct pid_controller *pid, float deviation)
@@ -73,12 +73,13 @@ float pid_sample(struct pid_controller *pid, float deviation)
float output;
if (!pid)
return 0.0;
return 0.0f;
output = deviation * pid->k_p;
if (!(deviation > 0.0f && pid->control_output > pid->output_sat_max - 0.5) &&
!(deviation < 0.0f && pid->control_output < pid->output_sat_min + 0.5)) {
/* PID runaway compensation */
if (!(deviation > 0.0f && pid->control_output > pid->output_sat_max - 0.5f) &&
!(deviation < 0.0f && pid->control_output < pid->output_sat_min + 0.5f)) {
calculate_integral(pid, deviation);
}

View File

@@ -24,7 +24,7 @@
#include <reflow-controller/rotary-encoder.h>
#include <reflow-controller/systick.h>
#include <reflow-controller/adc-meas.h>
#include <reflow-controller/safety-adc.h>
#include <reflow-controller/safety/safety-controller.h>
#include <reflow-controller/temp-converter.h>
#include <helper-macros/helper-macros.h>
#include <stm-periph/unique-id.h>
@@ -34,9 +34,9 @@
#include <string.h>
#include <inttypes.h>
static char __attribute__((section(".ccmram"))) display_buffer[4][21] = {0};
static struct lcd_menu reflow_menu;
static struct lcd_menu * const reflow_menu_ptr = &reflow_menu;
static char IN_SECTION(.ccm.bss) display_buffer[4][21] = {0};
static struct lcd_menu IN_SECTION(.ccm.bss) reflow_menu;
#define reflow_menu_ptr (&reflow_menu)
static void update_display_buffer(uint8_t row, const char *data)
{
@@ -71,7 +71,7 @@ static void reflow_menu_monitor(struct lcd_menu *menu, enum menu_entry_func_entr
if (systick_ticks_have_passed(my_timestamp, 250)) {
my_timestamp = systick_get_global_tick();
adc_pt1000_get_current_resistance(&tmp);
snprintf(line, sizeof(line), "Res: %.1f", tmp);
snprintf(line, sizeof(line), "Res: %.1f " LCD_OHM_SYMBOL_STRING, tmp);
menu->update_display(0, line);
res = temp_converter_convert_resistance_to_temp(tmp, &tmp);
@@ -90,11 +90,11 @@ static void reflow_menu_monitor(struct lcd_menu *menu, enum menu_entry_func_entr
snprintf(line, sizeof(line), "Temp: %s%.1f " LCD_DEGREE_SYMBOL_STRING "C", prefix, tmp);
menu->update_display(1, line);
tmp = safety_adc_get_temp();
(void)safety_controller_get_analog_mon_value(ERR_AMON_UC_TEMP, &tmp);
snprintf(line, sizeof(line), "Tj: %.1f " LCD_DEGREE_SYMBOL_STRING "C", tmp);
menu->update_display(2, line);
tmp = safety_adc_get_vref();
(void)safety_controller_get_analog_mon_value(ERR_AMON_VREF, &tmp);
snprintf(line, sizeof(line), "Vref: %.1f mV", tmp);
menu->update_display(3, line);
}
@@ -109,6 +109,7 @@ static void reflow_menu_about(struct lcd_menu *menu, enum menu_entry_func_entry
static void *my_parent;
static bool button_ready;
static int page = 0;
static int last_page = -1;
static uint32_t uptime_secs;
uint32_t new_uptime_secs;
uint32_t uptime_mins;
@@ -121,6 +122,7 @@ static void reflow_menu_about(struct lcd_menu *menu, enum menu_entry_func_entry
if (entry_type == MENU_ENTRY_FIRST_ENTER) {
uptime_secs = 0ULL;
page = 0;
last_page = -1;
my_parent = parent;
button_ready = false;
menu_display_clear(menu);
@@ -144,12 +146,18 @@ static void reflow_menu_about(struct lcd_menu *menu, enum menu_entry_func_entry
switch (page) {
case 0:
if (last_page == 0)
break;
last_page = 0;
menu_lcd_output(menu, 0, LCD_SHIMATTA_STRING " Shimatta");
menu_lcd_output(menu, 1, "Oven Controller");
menu_lcd_output(menu, 2, "(c) Mario H\xF5ttel");
menu_lcd_output(menu, 3, "Page 1/5");
break;
case 1:
if (last_page == 1)
break;
last_page = 1;
menu_lcd_output(menu, 0, "Version Number:");
menu_lcd_outputf(menu, 1, "%.*s", LCD_CHAR_WIDTH, xstr(GIT_VER));
if (strlen(xstr(GIT_VER)) > LCD_CHAR_WIDTH) {
@@ -162,12 +170,18 @@ static void reflow_menu_about(struct lcd_menu *menu, enum menu_entry_func_entry
#endif
break;
case 2:
if (last_page == 2)
break;
last_page = 2;
menu_lcd_output(menu, 0, "Compile Info");
menu_lcd_output(menu, 1, __DATE__);
menu_lcd_output(menu, 2, __TIME__);
menu_lcd_output(menu, 3, "Page 3/5");
break;
case 3:
if (last_page == 3)
break;
last_page = 3;
unique_id_get(&ser1, &ser2, &ser3);
menu_lcd_outputf(menu, 0, "Serial: %08X", ser1);
@@ -187,6 +201,7 @@ static void reflow_menu_about(struct lcd_menu *menu, enum menu_entry_func_entry
break;
default:
page = 0;
last_page = -1;
break;
}
@@ -206,6 +221,7 @@ static void reflow_menu_root_entry(struct lcd_menu *menu, enum menu_entry_func_e
(void)parent;
static struct menu_list list;
static bool button_valid;
bool menu_changed = false;
static const char * const root_entry_names[] = {
"About",
"Monitoring",
@@ -219,6 +235,7 @@ static void reflow_menu_root_entry(struct lcd_menu *menu, enum menu_entry_func_e
int16_t rot_delta;
if (entry_type != MENU_ENTRY_CONTINUE) {
menu_changed = true;
menu_display_clear(menu);
update_display_buffer(0, "Main Menu");
menu_ack_rotary_delta(menu);
@@ -245,12 +262,15 @@ static void reflow_menu_root_entry(struct lcd_menu *menu, enum menu_entry_func_e
if (rot_delta >= 4) {
menu_list_scroll_down(&list);
menu_ack_rotary_delta(menu);
menu_changed = true;
} else if (rot_delta <= -4) {
menu_list_scroll_up(&list);
menu_ack_rotary_delta(menu);
menu_changed = true;
}
menu_list_display(&list, 1, 3);
if (menu_changed)
menu_list_display(&list, 1, 3);
}
int reflow_menu_handle()

View File

@@ -23,6 +23,8 @@
#include <stm-periph/stm32-gpio-macros.h>
#include <helper-macros/helper-macros.h>
static int16_t IN_SECTION(.ccm.bss) override_delta;
static inline void rotary_encoder_setup_pins(void)
{
rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(ROTARY_ENCODER_RCC_MASK));
@@ -45,6 +47,8 @@ void rotary_encoder_setup(void)
ROTARY_ENCODER_TIMER->CCER = TIM_CCER_CC1P | TIM_CCER_CC2P;
ROTARY_ENCODER_TIMER->PSC = 0;
ROTARY_ENCODER_TIMER->CR1 = TIM_CR1_CEN;
override_delta = 0;
}
uint32_t rotary_encoder_get_abs_val(void)
@@ -72,6 +76,9 @@ int32_t rotary_encoder_get_change_val(void)
last_val = val;
diff += override_delta;
override_delta = 0;
return diff;
}
@@ -88,3 +95,8 @@ void rotary_encoder_zero(void)
{
ROTARY_ENCODER_TIMER->CNT = 0UL;
}
void rotary_encoder_override_delta(int16_t delta)
{
override_delta = delta;
}

View File

@@ -1,188 +0,0 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <reflow-controller/safety-adc.h>
#include <reflow-controller/periph-config/safety-adc-hwcfg.h>
#include <helper-macros/helper-macros.h>
#include <stm-periph/clock-enable-manager.h>
enum safety_adc_check_result global_safety_adc_status;
enum safety_adc_check_result safety_adc_get_errors()
{
return global_safety_adc_status;
}
void safety_adc_clear_errors(void)
{
global_safety_adc_status = SAFETY_ADC_CHECK_OK;
}
void safety_adc_init()
{
rcc_manager_enable_clock(&RCC->APB2ENR, BITMASK_TO_BITNO(SAFETY_ADC_ADC_RCC_MASK));
safety_adc_clear_errors();
/* Enable temperature and VREFINT measurement */
ADC->CCR |= ADC_CCR_TSVREFE;
/* Set sample time for channels 16 and 17 */
SAFETY_ADC_ADC_PERIPHERAL->SMPR1 |= ADC_SMPR1_SMP17 | ADC_SMPR1_SMP16;
/* Standard sequence. One measurement */
SAFETY_ADC_ADC_PERIPHERAL->SQR1 = 0UL;
}
void safety_adc_deinit()
{
SAFETY_ADC_ADC_PERIPHERAL->CR1 = 0UL;
SAFETY_ADC_ADC_PERIPHERAL->CR2 = 0UL;
SAFETY_ADC_ADC_PERIPHERAL->SMPR1 = 0UL;
rcc_manager_enable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(RCC_APB2ENR_ADC2EN));
}
enum safety_adc_check_result safety_adc_check_results(uint16_t vref_result, uint16_t temp_result,
float *vref_calculated, float *temp_calculated)
{
enum safety_adc_check_result res = SAFETY_ADC_CHECK_OK;
float vref;
float temp;
vref = (SAFETY_ADC_INT_REF_MV * 4095.0f) / (float)vref_result;
if (vref_calculated) {
*vref_calculated = vref;
}
temp = (((float)temp_result / 4095.0f * 2500.0f -
SAFETY_ADC_TEMP_NOM_MV) / SAFETY_ADC_TEMP_MV_SLOPE) + SAFETY_ADC_TEMP_NOM;
if (temp_calculated) {
*temp_calculated = temp;
}
if (ABS(vref - SAFETY_ADC_VREF_MVOLT) > SAFETY_ADC_VREF_TOL_MVOLT) {
if (vref > SAFETY_ADC_VREF_MVOLT)
res |= SAFETY_ADC_CHECK_VREF_HIGH;
else
res |= SAFETY_ADC_CHECK_VREF_LOW;
}
if (temp < SAFETY_ADC_TEMP_LOW_LIM)
res |= SAFETY_ADC_CHECK_TEMP_LOW;
else if (temp < SAFETY_ADC_CHECK_TEMP_HIGH)
res |= SAFETY_ADC_CHECK_TEMP_HIGH;
return res;
}
int safety_adc_poll_result(uint16_t *adc_result)
{
int ret = 0;
if (!adc_result)
return -1000;
if (!(SAFETY_ADC_ADC_PERIPHERAL->CR2 & ADC_CR2_ADON)) {
return -1;
}
if (SAFETY_ADC_ADC_PERIPHERAL->SR & ADC_SR_EOC) {
*adc_result = (uint16_t)SAFETY_ADC_ADC_PERIPHERAL->DR;
SAFETY_ADC_ADC_PERIPHERAL->CR2 &= ~ADC_CR2_ADON;
ret = 1;
}
return ret;
}
void safety_adc_trigger_meas(enum safety_adc_meas_channel measurement)
{
switch (measurement) {
case SAFETY_ADC_MEAS_TEMP:
SAFETY_ADC_ADC_PERIPHERAL->SQR3 = TEMP_CHANNEL_NUM;
break;
case SAFETY_ADC_MEAS_VREF:
SAFETY_ADC_ADC_PERIPHERAL->SQR3 = INT_REF_CHANNEL_NUM;
break;
default:
return;
}
SAFETY_ADC_ADC_PERIPHERAL->CR2 |= ADC_CR2_ADON;
SAFETY_ADC_ADC_PERIPHERAL->CR2 |= ADC_CR2_SWSTART;
}
static uint16_t safety_vref_meas_raw;
static bool safety_vref_valid = false;
static uint16_t safety_temp_meas_raw;
static bool safety_temp_valid = false;
static float safety_vref;
static float safety_temp;
enum safety_adc_check_result handle_safety_adc()
{
static enum safety_adc_meas_channel safety_meas_channel = SAFETY_ADC_MEAS_VREF;
enum safety_adc_check_result check_result;
uint16_t result;
int poll_status;
poll_status = safety_adc_poll_result(&result);
if (poll_status < 0) {
safety_adc_trigger_meas(safety_meas_channel);
} else if (poll_status > 0) {
switch (safety_meas_channel) {
case SAFETY_ADC_MEAS_TEMP:
safety_temp_meas_raw = result;
safety_temp_valid = true;
safety_meas_channel = SAFETY_ADC_MEAS_VREF;
break;
case SAFETY_ADC_MEAS_VREF:
safety_vref_meas_raw = result;
safety_vref_valid = true;
safety_meas_channel = SAFETY_ADC_MEAS_TEMP;
break;
default:
safety_meas_channel = SAFETY_ADC_MEAS_VREF;
return SAFETY_ADC_INTERNAL_ERROR;
}
}
if (safety_temp_valid && safety_vref_valid) {
check_result = safety_adc_check_results(safety_vref_meas_raw, safety_temp_meas_raw, &safety_vref, &safety_temp);
global_safety_adc_status |= check_result;
} else {
check_result = SAFETY_ADC_CHECK_OK;
}
return check_result;
}
float safety_adc_get_temp()
{
return safety_temp;
}
float safety_adc_get_vref()
{
return safety_vref;
}

View File

@@ -0,0 +1,62 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <reflow-controller/oven-driver.h>
#include <reflow-controller/digio.h>
#include <reflow-controller/safety/fault.h>
#include <reflow-controller/safety/safety-memory.h>
#include <helper-macros/helper-macros.h>
void HardFault_Handler(void)
{
/* This is a non recoverable fault. Stop the oven */
oven_driver_set_power(0);
oven_driver_apply_power_level();
/* Set the error led */
led_set(0, 1);
/* Try the real panic mode */
panic_mode();
}
/* Overwrite default handler. Go to panic mode */
void __int_default_handler(void)
{
panic_mode();
}
void panic_mode(void)
{
/* This variable is static, because I don't want it to be on the stack */
static struct safety_memory_boot_status IN_SECTION(.ccm.bss) boot_status;
oven_driver_set_power(0);
oven_driver_apply_power_level();
if (!safety_memory_get_boot_status(&boot_status)) {
boot_status.reset_from_panic = 0xFFFFFFFF;
(void)safety_memory_set_boot_status(&boot_status);
}
/* Let the watchdog do the rest */
while (1);
}

View File

@@ -0,0 +1,113 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @addtogroup safety-adc
* @{
*/
#include <reflow-controller/safety/safety-adc.h>
#include <reflow-controller/periph-config/safety-adc-hwcfg.h>
#include <helper-macros/helper-macros.h>
#include <stm-periph/clock-enable-manager.h>
void safety_adc_init()
{
rcc_manager_enable_clock(&RCC->APB2ENR, BITMASK_TO_BITNO(SAFETY_ADC_ADC_RCC_MASK));
/* Enable temperature and VREFINT measurement */
ADC->CCR |= ADC_CCR_TSVREFE;
/* Set sample time for channels 16 and 17 */
SAFETY_ADC_ADC_PERIPHERAL->SMPR1 |= ADC_SMPR1_SMP17 | ADC_SMPR1_SMP16;
/* Standard sequence. One measurement */
SAFETY_ADC_ADC_PERIPHERAL->SQR1 = 0UL;
}
void safety_adc_deinit()
{
SAFETY_ADC_ADC_PERIPHERAL->CR1 = 0UL;
SAFETY_ADC_ADC_PERIPHERAL->CR2 = 0UL;
SAFETY_ADC_ADC_PERIPHERAL->SMPR1 = 0UL;
rcc_manager_enable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(RCC_APB2ENR_ADC2EN));
}
float safety_adc_convert_channel(enum safety_adc_meas_channel channel, uint16_t analog_value)
{
float converted_val;
switch (channel) {
case SAFETY_ADC_MEAS_TEMP:
converted_val = (((float)analog_value / 4095.0f * 2500.0f - SAFETY_ADC_TEMP_NOM_MV) /
SAFETY_ADC_TEMP_MV_SLOPE) + SAFETY_ADC_TEMP_NOM;
break;
case SAFETY_ADC_MEAS_VREF:
converted_val = (SAFETY_ADC_INT_REF_MV * 4095.0f) / (float)analog_value;
break;
default:
/* Generate NaN value as default return */
converted_val = 0.0f / 0.0f;
break;
}
return converted_val;
}
int safety_adc_poll_result(uint16_t *adc_result)
{
int ret = 0;
if (!adc_result)
return -1000;
if (!(SAFETY_ADC_ADC_PERIPHERAL->CR2 & ADC_CR2_ADON)) {
return -1;
}
if (SAFETY_ADC_ADC_PERIPHERAL->SR & ADC_SR_EOC) {
*adc_result = (uint16_t)SAFETY_ADC_ADC_PERIPHERAL->DR;
SAFETY_ADC_ADC_PERIPHERAL->CR2 &= ~ADC_CR2_ADON;
ret = 1;
}
return ret;
}
void safety_adc_trigger_meas(enum safety_adc_meas_channel measurement)
{
switch (measurement) {
case SAFETY_ADC_MEAS_TEMP:
SAFETY_ADC_ADC_PERIPHERAL->SQR3 = TEMP_CHANNEL_NUM;
break;
case SAFETY_ADC_MEAS_VREF:
SAFETY_ADC_ADC_PERIPHERAL->SQR3 = INT_REF_CHANNEL_NUM;
break;
default:
return;
}
SAFETY_ADC_ADC_PERIPHERAL->CR2 |= ADC_CR2_ADON;
SAFETY_ADC_ADC_PERIPHERAL->CR2 |= ADC_CR2_SWSTART;
}
/** @} */

View File

@@ -0,0 +1,8 @@
/**
@defgroup safety-adc Safety ADC
@ingroup safety
The safety ADC continuously monitors the microcontrollers internal core temperature (and therefore the whole device's temperature) and the external reference voltage compared to its
internal bandgap reference voltage.
*/

View File

@@ -0,0 +1,872 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @addtogroup safety-controller
* @{
*/
#include <reflow-controller/safety/safety-controller.h>
#include <reflow-controller/safety/safety-config.h>
#include <reflow-controller/safety/watchdog.h>
#include <reflow-controller/safety/safety-adc.h>
#include <reflow-controller/safety/stack-check.h>
#include <helper-macros/helper-macros.h>
#include <stm-periph/crc-unit.h>
#include <reflow-controller/systick.h>
#include <reflow-controller/safety/fault.h>
#include <stm32/stm32f4xx.h>
#include <cmsis/core_cm4.h>
#include <stddef.h>
#include <string.h>
#include <reflow-controller/safety/safety-memory.h>
#include <reflow-controller/oven-driver.h>
#include <helper-macros/helper-macros.h>
#define check_flag_persistent(flag) ((flag)->persistency && (flag)->persistency->persistency)
#define get_flag_weight(flag) ((flag)->weight ? (flag->weight->weight) : SAFETY_FLAG_CONFIG_WEIGHT_NONE)
struct safety_weight {
uint32_t start_dummy;
enum config_weight weight;
enum safety_flag flag;
volatile struct error_flag *flag_ptr;
uint32_t end_dummy;
};
struct safety_persistency {
uint32_t start_dummy;
bool persistency;
enum safety_flag flag;
volatile struct error_flag *flag_ptr;
uint32_t end_dummy;
};
struct error_flag {
const char *name;
enum safety_flag flag;
bool error_state;
bool error_state_inv;
volatile struct safety_persistency *persistency;
volatile struct safety_weight *weight;
uint32_t key;
};
struct timing_mon {
const char *name;
enum timing_monitor monitor;
enum safety_flag associated_flag;
uint64_t min_delta;
uint64_t max_delta;
uint64_t last;
uint64_t calculated_delta;
bool enabled;
};
struct analog_mon {
const char *name;
enum analog_value_monitor monitor;
enum safety_flag associated_flag;
float min;
float max;
float value;
bool valid;
uint64_t timestamp;
};
static volatile struct error_flag IN_SECTION(.ccm.data) flags[] = {
ERR_FLAG_ENTRY(ERR_FLAG_MEAS_ADC_OFF),
ERR_FLAG_ENTRY(ERR_FLAG_MEAS_ADC_WATCHDOG),
ERR_FLAG_ENTRY(ERR_FLAG_MEAS_ADC_UNSTABLE),
ERR_FLAG_ENTRY(ERR_FLAG_MEAS_ADC_OVERFLOW),
ERR_FLAG_ENTRY(ERR_FLAG_TIMING_MEAS_ADC),
ERR_FLAG_ENTRY(ERR_FLAG_TIMING_PID),
ERR_FLAG_ENTRY(ERR_FLAG_AMON_UC_TEMP),
ERR_FLAG_ENTRY(ERR_FLAG_AMON_VREF),
ERR_FLAG_ENTRY(ERR_FLAG_STACK),
ERR_FLAG_ENTRY(ERR_FLAG_SAFETY_ADC),
ERR_FLAG_ENTRY(ERR_FLAG_SYSTICK),
ERR_FLAG_ENTRY(ERR_FLAG_WTCHDG_FIRED),
ERR_FLAG_ENTRY(ERR_FLAG_UNCAL),
ERR_FLAG_ENTRY(ERR_FLAG_DEBUG),
ERR_FLAG_ENTRY(ERR_FLAG_TIMING_MAIN_LOOP),
ERR_FLAG_ENTRY(ERR_FLAG_SAFETY_MEM_CORRUPT),
ERR_FLAG_ENTRY(ERR_FLAG_SAFETY_TAB_CORRUPT),
};
static volatile struct timing_mon IN_SECTION(.ccm.data) timings[] = {
TIM_MON_ENTRY(ERR_TIMING_PID, 2, 1000, ERR_FLAG_TIMING_PID),
TIM_MON_ENTRY(ERR_TIMING_MEAS_ADC, 0, 50, ERR_FLAG_TIMING_MEAS_ADC),
TIM_MON_ENTRY(ERR_TIMING_SAFETY_ADC, 10, SAFETY_CONTROLLER_ADC_DELAY_MS + 1000, ERR_FLAG_SAFETY_ADC),
TIM_MON_ENTRY(ERR_TIMING_MAIN_LOOP, 0, 1000, ERR_FLAG_TIMING_MAIN_LOOP),
};
static volatile struct analog_mon IN_SECTION(.ccm.data) analog_mons[] = {
ANA_MON_ENTRY(ERR_AMON_VREF, SAFETY_ADC_VREF_MVOLT - SAFETY_ADC_VREF_TOL_MVOLT,
SAFETY_ADC_VREF_MVOLT + SAFETY_ADC_VREF_TOL_MVOLT, ERR_FLAG_AMON_VREF),
ANA_MON_ENTRY(ERR_AMON_UC_TEMP, SAFETY_ADC_TEMP_LOW_LIM, SAFETY_ADC_TEMP_HIGH_LIM,
ERR_FLAG_AMON_UC_TEMP),
};
static const struct safety_weight default_flag_weights[] = { SAFETY_CONFIG_DEFAULT_WEIGHTS };
static const struct safety_persistency default_flag_persistencies[] = {SAFETY_CONFIG_DEFAULT_PERSIST};
static volatile struct safety_persistency IN_SECTION(.ccm.bss) flag_persistencies[COUNT_OF(default_flag_persistencies)];
static uint32_t IN_SECTION(.ccm.bss) flag_persistencies_crc;
static volatile struct safety_weight IN_SECTION(.ccm.bss) flag_weights[COUNT_OF(default_flag_weights)];
static uint32_t IN_SECTION(.ccm.bss) flag_weight_crc;
static int flag_weight_table_crc_check(void)
{
/* Check the flag weight table */
crc_unit_reset();
crc_unit_input_array((uint32_t *)flag_weights, wordsize_of(flag_weights));
if (crc_unit_get_crc() != flag_weight_crc)
return -1;
return 0;
}
static int flag_persistency_table_crc_check(void)
{
crc_unit_reset();
crc_unit_input_array((uint32_t*)flag_persistencies, wordsize_of(flag_persistencies));
if (crc_unit_get_crc() != flag_persistencies_crc)
return -1;
return 0;
}
static volatile struct error_flag *find_error_flag(enum safety_flag flag)
{
uint32_t i;
volatile struct error_flag *ret = NULL;
for (i = 0; i < COUNT_OF(flags); i++) {
if (flags[i].flag == flag)
ret = &flags[i];
}
return ret;
}
/**
* @brief This function copies the safety weigths from flash ro RAM and computes the CRC
*/
static void init_safety_flag_weight_table_from_default(void)
{
uint32_t index;
volatile struct safety_weight *current_weight;
/* Copy the table */
memcpy((void *)flag_weights, default_flag_weights, sizeof(flag_weights));
/* Fill in the flag pointers */
for (index = 0; index < COUNT_OF(flag_weights); index++) {
current_weight = &flag_weights[index];
current_weight->flag_ptr = find_error_flag(current_weight->flag);
if (current_weight->flag_ptr)
current_weight->flag_ptr->weight = current_weight;
}
crc_unit_reset();
crc_unit_input_array((uint32_t*)flag_weights, wordsize_of(flag_weights));
flag_weight_crc = crc_unit_get_crc();
}
static void init_safety_flag_persistencies_from_default(void)
{
uint32_t index;
volatile struct safety_persistency *current_persistency;
/* Copy values */
memcpy((void *)flag_persistencies, default_flag_persistencies, sizeof(flag_persistencies));
/* Fill in flag pointers */
for (index = 0; index < COUNT_OF(flag_persistencies); index++) {
current_persistency = &flag_persistencies[index];
current_persistency->flag_ptr = find_error_flag(current_persistency->flag);
if (current_persistency->flag_ptr)
current_persistency->flag_ptr->persistency = current_persistency;
}
crc_unit_reset();
crc_unit_input_array((uint32_t *)flag_persistencies, wordsize_of(flag_persistencies));
flag_persistencies_crc = crc_unit_get_crc();
}
static bool error_flag_get_status(const volatile struct error_flag *flag)
{
if (!flag)
return true;
if (flag->error_state == flag->error_state_inv) {
return true;
} else {
return flag->error_state;
}
}
static volatile struct analog_mon *find_analog_mon(enum analog_value_monitor mon)
{
uint32_t i;
volatile struct analog_mon *ret = NULL;
for (i = 0; i < COUNT_OF(analog_mons); i++) {
if (analog_mons[i].monitor == mon)
ret = &analog_mons[i];
}
return ret;
}
static volatile struct timing_mon *find_timing_mon(enum timing_monitor mon)
{
uint32_t i;
volatile struct timing_mon *ret = NULL;
for (i = 0; i < COUNT_OF(timings); i++) {
if (timings[i].monitor == mon)
ret = &timings[i];
}
return ret;
}
static void safety_controller_process_active_timing_mons()
{
uint32_t i;
volatile struct timing_mon *current_mon;
uint64_t last;
for (i = 0; i < COUNT_OF(timings); i++) {
current_mon = &timings[i];
if (current_mon->enabled) {
__disable_irq();
last = current_mon->last;
__enable_irq();
if (systick_ticks_have_passed(last, current_mon->max_delta))
safety_controller_report_error(current_mon->associated_flag);
}
}
}
static void safety_controller_process_monitor_checks()
{
static bool startup_completed = false;
enum analog_monitor_status amon_state;
float amon_value;
if (!startup_completed && systick_get_global_tick() >= 1000)
startup_completed = true;
if (startup_completed) {
amon_state = safety_controller_get_analog_mon_value(ERR_AMON_VREF, &amon_value);
if (amon_state != ANALOG_MONITOR_OK)
safety_controller_report_error(ERR_FLAG_AMON_VREF);
amon_state = safety_controller_get_analog_mon_value(ERR_AMON_UC_TEMP, &amon_value);
if (amon_state != ANALOG_MONITOR_OK)
safety_controller_report_error(ERR_FLAG_AMON_UC_TEMP);
}
safety_controller_process_active_timing_mons();
}
static uint8_t flag_enum_to_flag_no(enum safety_flag flag)
{
uint32_t flag_mask;
uint8_t i;
if (!is_power_of_two(flag))
return 0xFF;
flag_mask = (uint32_t)flag;
for (i = 0; i < 32; i++) {
if ((flag_mask >> i) & 0x1U)
break;
}
return i;
}
static enum safety_flag flag_no_to_flag_enum(uint8_t no)
{
if (no >= COUNT_OF(flags))
return ERR_FLAG_NO_FLAG;
return (1U << no);
}
static int report_error(enum safety_flag flag, uint32_t key, bool prevent_error_mem_enty)
{
uint32_t i;
int ret = -1;
bool old_state;
int res;
struct error_memory_entry err_mem_entry;
for (i = 0; i < COUNT_OF(flags); i++) {
if (flags[i].flag & flag) {
old_state = flags[i].error_state;
flags[i].error_state = true;
flags[i].error_state_inv = !flags[i].error_state;
flags[i].key = key;
if (check_flag_persistent(&flags[i]) && !old_state && !prevent_error_mem_enty) {
err_mem_entry.counter = 1;
err_mem_entry.flag_num = flag_enum_to_flag_no(flags[i].flag);
err_mem_entry.type = SAFETY_MEMORY_ERR_ENTRY_FLAG;
res = safety_memory_insert_error_entry(&err_mem_entry);
if (res)
ret = -12;
} else {
ret = 0;
}
}
}
return ret;
}
int safety_controller_report_error(enum safety_flag flag)
{
return safety_controller_report_error_with_key(flag, 0x0UL);
}
int safety_controller_report_error_with_key(enum safety_flag flag, uint32_t key)
{
return report_error(flag, key, false);
}
void safety_controller_report_timing(enum timing_monitor monitor)
{
volatile struct timing_mon *tim;
uint64_t timestamp;
timestamp = systick_get_global_tick();
tim = find_timing_mon(monitor);
if (tim) {
if (tim->enabled) {
if (!systick_ticks_have_passed(tim->last, tim->min_delta) && tim->min_delta > 0U) {
safety_controller_report_error(tim->associated_flag);
}
}
tim->calculated_delta = timestamp - tim->last;
tim->last = timestamp;
tim->enabled = true;
}
}
void safety_controller_report_analog_value(enum analog_value_monitor monitor, float value)
{
volatile struct analog_mon *ana;
/* Return if not a power of two */
if (!is_power_of_two(monitor))
return;
ana = find_analog_mon(monitor);
if (ana) {
ana->valid = true;
ana->value = value;
ana->timestamp = systick_get_global_tick();
}
}
/**
* @brief Return the flags, which are set in the error memory
* @param flags Flags read from error memory
* @return 0 if ok, != 0 if error
*/
static enum safety_flag get_safety_flags_from_error_mem(enum safety_flag *flags)
{
uint32_t count;
uint32_t idx;
int res;
enum safety_flag return_flags = 0;
struct error_memory_entry entry;
if (!flags)
return -1001;
res = safety_memory_get_error_entry_count(&count);
if (res)
return -1;
for (idx = 0; idx < count; idx++) {
res = safety_memory_get_error_entry(idx, &entry);
if (entry.type == SAFETY_MEMORY_ERR_ENTRY_FLAG) {
return_flags |= flag_no_to_flag_enum(entry.flag_num);
}
}
*flags = return_flags;
return 0;
}
void safety_controller_init()
{
enum safety_memory_state found_memory_state;
enum safety_flag flags_in_err_mem = ERR_FLAG_NO_FLAG;
int res;
/* Init the safety memory */
if (safety_memory_init(&found_memory_state)) {
/* Trigger panic mode! */
panic_mode();
}
/* This is usually done by the safety memory already. But, since this module also uses the CRC... */
crc_unit_init();
stack_check_init_corruption_detect_area();
init_safety_flag_weight_table_from_default();
init_safety_flag_persistencies_from_default();
if (found_memory_state == SAFETY_MEMORY_INIT_CORRUPTED)
safety_controller_report_error(ERR_FLAG_SAFETY_MEM_CORRUPT);
else if (found_memory_state == SAFETY_MEMORY_INIT_VALID_MEMORY) {
/* restore the corrupt flag flag */
res = get_safety_flags_from_error_mem(&flags_in_err_mem);
if (res)
panic_mode();
if (flags_in_err_mem & ERR_FLAG_SAFETY_MEM_CORRUPT)
report_error(ERR_FLAG_SAFETY_MEM_CORRUPT, 0, true);
}
/* Init default flag states */
safety_controller_report_error_with_key(ERR_FLAG_MEAS_ADC_OFF | ERR_FLAG_MEAS_ADC_UNSTABLE,
MEAS_ADC_SAFETY_FLAG_KEY);
safety_adc_init();
watchdog_setup(WATCHDOG_PRESCALER);
if (watchdog_check_reset_source())
safety_controller_report_error(ERR_FLAG_WTCHDG_FIRED);
#ifdef DEBUGBUILD
safety_controller_report_error(ERR_FLAG_DEBUG);
#endif
}
static void safety_controller_check_stack()
{
int32_t free_stack;
free_stack = stack_check_get_free();
if (free_stack < SAFETY_MIN_STACK_FREE)
safety_controller_report_error(ERR_FLAG_STACK);
if (stack_check_corruption_detect_area()) {
safety_controller_report_error(ERR_FLAG_STACK);
}
}
static void safety_controller_handle_safety_adc()
{
static enum safety_adc_meas_channel current_channel = SAFETY_ADC_MEAS_TEMP;
static uint64_t last_result_timestamp = 0;
int poll_result;
uint16_t result;
float analog_value;
poll_result = safety_adc_poll_result(&result);
if (!systick_ticks_have_passed(last_result_timestamp, SAFETY_CONTROLLER_ADC_DELAY_MS) && poll_result != 1)
return;
if (poll_result) {
if (poll_result == -1) {
switch (current_channel) {
case SAFETY_ADC_MEAS_TEMP:
current_channel = SAFETY_ADC_MEAS_VREF;
break;
case SAFETY_ADC_MEAS_VREF:
/* Expected fallthru */
default:
current_channel = SAFETY_ADC_MEAS_TEMP;
break;
}
safety_adc_trigger_meas(current_channel);
} else if (poll_result == 1) {
last_result_timestamp = systick_get_global_tick();
analog_value = safety_adc_convert_channel(current_channel, result);
safety_controller_report_timing(ERR_TIMING_SAFETY_ADC);
switch (current_channel) {
case SAFETY_ADC_MEAS_TEMP:
safety_controller_report_analog_value(ERR_AMON_UC_TEMP, analog_value);
break;
case SAFETY_ADC_MEAS_VREF:
safety_controller_report_analog_value(ERR_AMON_VREF, analog_value);
break;
default:
safety_controller_report_error(ERR_FLAG_SAFETY_ADC);
break;
}
}
}
}
/**
* @brief Check the memory structures.
*/
static void safety_controller_handle_memory_checks(void)
{
static uint64_t ts = 0;
enum safety_memory_state found_state;
if (systick_ticks_have_passed(ts, 250)) {
ts = systick_get_global_tick();
/* Check the safety memory */
if (safety_memory_check()) {
(void)safety_memory_reinit(&found_state);
if (found_state != SAFETY_MEMORY_INIT_VALID_MEMORY) {
safety_controller_report_error(ERR_FLAG_SAFETY_MEM_CORRUPT);
}
}
/* If flag weight table is broken, reinit to default and set flag */
if (flag_weight_table_crc_check()) {
safety_controller_report_error(ERR_FLAG_SAFETY_TAB_CORRUPT);
init_safety_flag_weight_table_from_default();
}
/* If persistency table is broken, reinit to default and set flag */
if(flag_persistency_table_crc_check()) {
safety_controller_report_error(ERR_FLAG_SAFETY_TAB_CORRUPT);
init_safety_flag_persistencies_from_default();
}
}
}
static void safety_controller_do_systick_checking()
{
static uint64_t last_systick;
static uint32_t same_systick_cnt = 0UL;
uint64_t systick;
systick = systick_get_global_tick();
if (systick == last_systick) {
same_systick_cnt++;
if (same_systick_cnt > 1000)
safety_controller_report_error(ERR_FLAG_SYSTICK);
} else {
same_systick_cnt = 0UL;
}
last_systick = systick;
}
static void safety_controller_handle_weighted_flags()
{
uint32_t weight_index;
volatile struct safety_weight *current_weight;
for (weight_index = 0; weight_index < COUNT_OF(flag_weights); weight_index++) {
current_weight = &flag_weights[weight_index];
if (error_flag_get_status(current_weight->flag_ptr)) {
switch (current_weight->weight) {
case SAFETY_FLAG_CONFIG_WEIGHT_NONE:
break;
case SAFETY_FLAG_CONFIG_WEIGHT_PID:
oven_pid_abort();
break;
case SAFETY_FLAG_CONFIG_WEIGHT_PANIC:
/* Expected fallthrough */
default:
oven_pid_abort();
panic_mode();
break;
}
}
}
}
int safety_controller_handle()
{
int ret = 0;
safety_controller_check_stack();
safety_controller_handle_safety_adc();
safety_controller_handle_memory_checks();
safety_controller_do_systick_checking();
safety_controller_process_monitor_checks();
safety_controller_handle_weighted_flags();
ret |= watchdog_ack(WATCHDOG_MAGIC_KEY);
return (ret ? -1 : 0);
}
int safety_controller_enable_timing_mon(enum timing_monitor monitor, bool enable)
{
volatile struct timing_mon *tim;
if (enable) {
safety_controller_report_timing(monitor);
} else {
tim = find_timing_mon(monitor);
if (!tim)
return -1;
tim->enabled = false;
}
return 0;
}
enum analog_monitor_status safety_controller_get_analog_mon_value(enum analog_value_monitor monitor, float *value)
{
volatile struct analog_mon *mon;
int ret = ANALOG_MONITOR_ERROR;
if (!is_power_of_two(monitor))
goto go_out;
if (!value)
goto go_out;
mon = find_analog_mon(monitor);
if (mon) {
if (!mon->valid) {
ret = ANALOG_MONITOR_INACTIVE;
goto go_out;
}
*value = mon->value;
if (mon->value < mon->min)
ret = ANALOG_MONITOR_UNDER;
else if (mon->value > mon->max)
ret = ANALOG_MONITOR_OVER;
else
ret = ANALOG_MONITOR_OK;
}
go_out:
return ret;
}
int safety_controller_get_flag(enum safety_flag flag, bool *status, bool try_ack)
{
volatile struct error_flag *found_flag;
int ret = -1;
if (!status)
return -1002;
if (!is_power_of_two(flag))
return -1001;
found_flag = find_error_flag(flag);
if (found_flag) {
*status = error_flag_get_status(found_flag);
if (try_ack && !check_flag_persistent(found_flag)) {
/* Flag is generally non persistent
* If key is set, this function cannot remove the flag
*/
if (found_flag->key == 0UL) {
found_flag->error_state = false;
found_flag->error_state_inv = !found_flag->error_state;
}
}
}
return ret;
}
int safety_controller_ack_flag(enum safety_flag flag)
{
return safety_controller_ack_flag_with_key(flag, 0UL);
}
int safety_controller_ack_flag_with_key(enum safety_flag flag, uint32_t key)
{
int ret = -1;
volatile struct error_flag *found_flag;
if (!is_power_of_two(flag)) {
return -1001;
}
found_flag = find_error_flag(flag);
if (found_flag) {
if (!check_flag_persistent(found_flag) && (found_flag->key == key || !found_flag->key)) {
found_flag->error_state = false;
found_flag->error_state_inv = true;
ret = 0;
} else {
ret = -2;
}
}
return ret;
}
bool safety_controller_get_flags_by_mask(enum safety_flag mask)
{
uint32_t i;
bool ret = false;
for (i = 0; i < COUNT_OF(flags); i++) {
if ((flags[i].flag & mask) && error_flag_get_status(&flags[i])) {
ret = true;
break;
}
}
return ret;
}
uint32_t safety_controller_get_flag_count()
{
return COUNT_OF(flags);
}
uint32_t safety_controller_get_analog_monitor_count()
{
return COUNT_OF(analog_mons);
}
uint32_t safety_controller_get_timing_monitor_count()
{
return COUNT_OF(timings);
}
int safety_controller_get_analog_mon_name_by_index(uint32_t index, char *buffer, size_t buffsize)
{
if (index >= COUNT_OF(analog_mons))
return -1;
if (buffsize == 0 || !buffer)
return -1000;
strncpy(buffer, analog_mons[index].name, buffsize);
buffer[buffsize - 1] = 0;
return 0;
}
int safety_controller_get_flag_name_by_index(uint32_t index, char *buffer, size_t buffsize)
{
if (index >= COUNT_OF(flags))
return -1;
if (buffsize == 0 || !buffer)
return -1000;
strncpy(buffer, flags[index].name, buffsize);
buffer[buffsize - 1] = 0;
return 0;
}
int safety_controller_get_timing_mon_name_by_index(uint32_t index, char *buffer, size_t buffsize)
{
if (index >= COUNT_OF(timings))
return -1;
if (buffsize == 0 || !buffer)
return -1000;
strncpy(buffer, timings[index].name, buffsize);
buffer[buffsize - 1] = 0;
return 0;
}
int safety_controller_get_flag_by_index(uint32_t index, bool *status, enum safety_flag *flag_enum)
{
int ret = -1;
if (!status && !flag_enum)
return -1000;
if (index < COUNT_OF(flags)) {
if (status)
*status = error_flag_get_status(&flags[index]);
if (flag_enum)
*flag_enum = flags[index].flag;
ret = 0;
}
return ret;
}
int safety_controller_get_analog_mon_by_index(uint32_t index, struct analog_monitor_info *info)
{
volatile struct analog_mon *mon;
if (!info)
return -1002;
if (index >= COUNT_OF(analog_mons)) {
info->status = ANALOG_MONITOR_ERROR;
return -1001;
}
mon = &analog_mons[index];
info->max = mon->max;
info->min = mon->min;
info->value = mon->value;
info->timestamp = mon->timestamp;
if (!mon->valid) {
info->status = ANALOG_MONITOR_INACTIVE;
} else {
if (mon->value > mon->max)
info->status = ANALOG_MONITOR_OVER;
else if (mon->value < mon->min)
info->status = ANALOG_MONITOR_UNDER;
else
info->status = ANALOG_MONITOR_OK;
}
return 0;
}
int safety_controller_get_timing_mon_by_index(uint32_t index, struct timing_monitor_info *info)
{
volatile struct timing_mon *mon;
if (!info)
return -1002;
if (index >= COUNT_OF(timings)) {
return -1001;
}
mon = &timings[index];
info->max = mon->max_delta;
info->min = mon->min_delta;
info->enabled = mon->enabled;
info->last_run = mon->last;
info->delta = mon->calculated_delta;
return 0;
}
/** @} */

View File

@@ -0,0 +1,7 @@
/**
@defgroup safety-controller Safety Controller
@ingroup safety
This is the main module for the safety part of the firmware. It monitors
analog values, error states and timeouts of timing critical sections of the firmware.
*/

View File

@@ -0,0 +1,510 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <reflow-controller/safety/safety-memory.h>
#include <helper-macros/helper-macros.h>
#include <stm-periph/crc-unit.h>
#include <stm-periph/backup-ram.h>
#include <base64-lib/base64-lib.h>
static int word_to_error_memory_entry(uint32_t entry_data, struct error_memory_entry *out)
{
int ret = 0;
if (!out)
return -1002;
if (entry_data == SAFETY_MEMORY_ERR_ENTRY_NOP) {
out->flag_num = 0U;
out->type = SAFETY_MEMORY_ERR_ENTRY_NOP;
out->counter = 0U;
} else if ((entry_data & 0xFFU) == 0x51U) {
out->flag_num = (uint8_t)((entry_data >> 8U) & 0xFFU);
out->type = SAFETY_MEMORY_ERR_ENTRY_FLAG;
out->counter = (uint16_t)((entry_data >> 16U) & 0xFFFF);
} else {
/* Invalid entry */
ret = -1;
}
return ret;
}
static uint32_t error_memory_entry_to_word(const struct error_memory_entry *entry)
{
uint32_t word = 0;
switch (entry->type) {
case SAFETY_MEMORY_ERR_ENTRY_NOP:
word = SAFETY_MEMORY_NOP_ENTRY;
break;
case SAFETY_MEMORY_ERR_ENTRY_FLAG:
word = 0x51UL | ((uint32_t)entry->flag_num << 8U) |
((uint32_t)entry->counter << 16U);
break;
}
return word;
}
static enum safety_memory_state safety_memory_get_header(struct safety_memory_header *header)
{
int res;
enum safety_memory_state ret;
if (!header)
return SAFETY_MEMORY_INIT_CORRUPTED;
res = backup_ram_get_data(0UL, (uint32_t *)header, wordsize_of(struct safety_memory_header));
if (res)
return SAFETY_MEMORY_INIT_CORRUPTED;
/* Check magic */
if (header->magic != SAFETY_MEMORY_MAGIC) {
/* Magic invalid */
ret = SAFETY_MEMORY_INIT_FRESH;
goto return_val;
}
/* Check the header crc */
crc_unit_reset();
crc_unit_input_array((uint32_t *)header, wordsize_of(struct safety_memory_header));
if (crc_unit_get_crc() != 0UL) {
ret = SAFETY_MEMORY_INIT_CORRUPTED;
goto return_val;
}
res = 0;
if (header->boot_status_offset < wordsize_of(struct safety_memory_header))
res++;
if (header->config_overrides_offset < header->boot_status_offset + wordsize_of(struct safety_memory_boot_status))
res++;
if (header->config_overrides_len > SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT)
res++;
if (header->err_memory_offset < header->config_overrides_offset + header->config_overrides_len)
res++;
if (header->err_memory_end >= backup_ram_get_size_in_words() || header->err_memory_end < header->err_memory_offset)
res++;
if (res) {
/* Error detected: Write new header */
ret = SAFETY_MEMORY_INIT_CORRUPTED;
} else {
ret = SAFETY_MEMORY_INIT_VALID_MEMORY;
}
return_val:
return ret;
}
static void safety_memory_write_and_patch_header(struct safety_memory_header *header)
{
/* Patch the CRC */
crc_unit_reset();
crc_unit_input_array((uint32_t *)header, wordsize_of(struct safety_memory_header) - 1U);
header->crc = crc_unit_get_crc();
/* Write to memory */
backup_ram_write_data(0UL, (uint32_t *)header, wordsize_of(*header));
}
static void safety_memory_write_new_header(void)
{
struct safety_memory_header header;
header.boot_status_offset = wordsize_of(struct safety_memory_header);
header.config_overrides_len = SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT;
header.config_overrides_offset = header.boot_status_offset + wordsize_of(struct safety_memory_boot_status);
header.err_memory_offset = header.config_overrides_offset + SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT;
header.err_memory_end = header.err_memory_offset;
header.magic = SAFETY_MEMORY_MAGIC;
backup_ram_wipe();
safety_memory_write_and_patch_header(&header);
}
static int safety_memory_check_crc()
{
struct safety_memory_header header;
enum safety_memory_state state = safety_memory_get_header(&header);
uint32_t crc_offset;
uint32_t data;
uint32_t addr;
int res;
if (state != SAFETY_MEMORY_INIT_VALID_MEMORY)
return -1;
crc_offset = header.err_memory_end;
crc_unit_reset();
for (addr = 0; addr < crc_offset; addr++) {
res = backup_ram_get_data(addr, &data, 1UL);
if (res)
return -2000;
crc_unit_input(data);
}
res = backup_ram_get_data(crc_offset, &data, 1UL);
if (res)
return -2001;
if (crc_unit_get_crc() != data)
return -3000;
else
return 0;
}
static int safety_memory_gen_crc()
{
struct safety_memory_header header;
uint32_t word_addr;
uint32_t data;
int res;
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY)
return -1;
crc_unit_reset();
for (word_addr = 0; word_addr < header.err_memory_end; word_addr++) {
res = backup_ram_get_data(word_addr, &data, 1);
if (res)
return -2;
crc_unit_input(data);
}
/* Write CRC */
data = crc_unit_get_crc();
res = backup_ram_write_data(header.err_memory_end, &data, 1UL);
if (res)
return -3;
return 0;
}
int safety_memory_reinit(enum safety_memory_state *found_state)
{
struct safety_memory_header header;
int res;
int ret = -1;
if (!found_state)
return -1001;
*found_state = safety_memory_get_header(&header);
switch (*found_state) {
case SAFETY_MEMORY_INIT_VALID_MEMORY:
/* Valid memory detected. Check CRC and error entries */
res = safety_memory_check();
if (res)
*found_state = SAFETY_MEMORY_INIT_CORRUPTED;
break;
case SAFETY_MEMORY_INIT_FRESH:
break;
case SAFETY_MEMORY_INIT_CORRUPTED:
break;
default:
*found_state = SAFETY_MEMORY_INIT_CORRUPTED;
break;
}
/* Check if memory header has to be written */
if (*found_state != SAFETY_MEMORY_INIT_VALID_MEMORY) {
safety_memory_write_new_header();
/* If yes, generate new CRC checksum */
res = safety_memory_gen_crc();
if (res)
ret = -100;
else
ret = 0;
} else {
ret = 0;
}
return ret;
}
int safety_memory_init(enum safety_memory_state *found_state)
{
crc_unit_init();
backup_ram_init(true);
return safety_memory_reinit(found_state);
}
int safety_memory_get_boot_status(struct safety_memory_boot_status *status)
{
struct safety_memory_header header;
int res;
if (!status)
return -1001;
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) {
return -2000;
}
if (safety_memory_check_crc())
return -2001;
res = backup_ram_get_data(header.boot_status_offset, (uint32_t *)status, wordsize_of(*status));
if (res)
return -3000;
return 0;
}
int safety_memory_set_boot_status(const struct safety_memory_boot_status *status)
{
struct safety_memory_header header;
int res;
if (!status)
return -1001;
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) {
return -2000;
}
if (safety_memory_check_crc())
return -2001;
res = backup_ram_write_data(header.boot_status_offset, (uint32_t *)status, wordsize_of(*status));
res |= safety_memory_gen_crc();
if (res)
return -3000;
return 0;
}
static int safety_memory_check_error_entries()
{
struct safety_memory_header header;
uint32_t addr;
uint32_t data;
int ret = 0;
int res;
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) {
return -2000;
}
for (addr = header.err_memory_offset; addr < header.err_memory_end; addr++) {
res = backup_ram_get_data(addr, &data, 1UL);
if (res)
return -100;
/* Valid flag entry */
if ((data & 0xFF) == 0x51)
continue;
if (data == SAFETY_MEMORY_NOP_ENTRY)
continue;
ret--;
}
return ret;
}
int safety_memory_get_error_entry_count(uint32_t *count)
{
struct safety_memory_header header;
if (!count)
return -1001;
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) {
return -2000;
}
*count = header.err_memory_end - header.err_memory_offset;
return 0;
}
int safety_memory_check(void)
{
int res;
res = safety_memory_check_crc();
if (!res) {
res |= safety_memory_check_error_entries();
}
return -!!res;
}
int safety_memory_get_error_entry(uint32_t idx, struct error_memory_entry *entry)
{
struct safety_memory_header header;
uint32_t err_mem_count;
int ret = -1;
int res;
uint32_t data;
if (!entry)
return -1001;
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) {
return -2000;
}
err_mem_count = header.err_memory_end - header.err_memory_offset;
if (idx < err_mem_count && err_mem_count > 0) {
res = backup_ram_get_data(header.err_memory_offset + idx, &data, 1UL);
if (res)
goto return_value;
res = word_to_error_memory_entry(data, entry);
if (res)
goto return_value;
ret = 0;
} else {
/* out of range */
ret = -1001;
}
return_value:
return ret;
}
int safety_memory_insert_error_entry(struct error_memory_entry *entry)
{
int res;
int ret = -0xFFFF;
uint32_t addr;
uint32_t data;
bool found;
uint32_t input_data;
struct error_memory_entry current_entry;
struct safety_memory_header header;
input_data = error_memory_entry_to_word(entry);
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) {
return -2000;
}
if (entry->type == SAFETY_MEMORY_ERR_ENTRY_NOP) {
/* Append to end */
if ((header.err_memory_end + 1U) < backup_ram_get_size_in_words()) {
/* Still fits in memory */
backup_ram_write_data(header.err_memory_end, &input_data, 1UL);
header.err_memory_end++;
safety_memory_write_and_patch_header(&header);
safety_memory_gen_crc();
ret = 0;
}
} else if (entry->type == SAFETY_MEMORY_ERR_ENTRY_FLAG) {
found = false;
for (addr = header.err_memory_offset; addr < header.err_memory_end; addr++) {
res = backup_ram_get_data(addr, &data, 1UL);
if (res) {
ret = -1;
goto return_value;
}
res = word_to_error_memory_entry(data, &current_entry);
if (res) {
ret = -2;
goto return_value;
}
if (current_entry.type == SAFETY_MEMORY_ERR_ENTRY_FLAG &&
current_entry.flag_num == entry->flag_num) {
found = true;
break;
}
if (current_entry.type == SAFETY_MEMORY_ERR_ENTRY_NOP) {
found = true;
break;
}
}
if (!found) {
/* No suitable place found in memory. Append */
if ((addr + 1) < backup_ram_get_size_in_words()) {
backup_ram_write_data(addr, &input_data, 1UL);
header.err_memory_end++;
safety_memory_write_and_patch_header(&header);
} else {
ret = -3;
goto return_value;
}
} else {
if (current_entry.type == SAFETY_MEMORY_ERR_ENTRY_NOP) {
backup_ram_write_data(addr, &input_data, 1UL);
} else {
current_entry.counter += entry->counter;
if (current_entry.counter < entry->counter)
current_entry.counter = 0xFFFF;
data = error_memory_entry_to_word(&current_entry);
backup_ram_write_data(addr, &data, 1UL);
}
}
safety_memory_gen_crc();
ret = 0;
} else {
ret = -1001;
}
return_value:
return ret;
}
int safety_memory_insert_config_override(struct config_override *config_override);
int safety_memory_get_config_override_count(uint32_t *count);
int safety_memory_get_config_override(uint32_t idx, struct config_override *config_override);
int safety_memory_dump_base64(char *buffer, size_t buffsize, size_t *used_size)
{
uint32_t safety_mem_size;
size_t output_size;
const char *backup_mem_ptr;
int res;
if (!buffer)
return -1000;
safety_mem_size = backup_ram_get_size_in_words() * 4U;
output_size = base64_calculate_encoded_size(safety_mem_size);
if (output_size + 1 > buffsize)
return -1001;
backup_mem_ptr = (const char *)backup_ram_get_base_ptr();
res = base64_encode(backup_mem_ptr, buffer, safety_mem_size, buffsize, &output_size);
if (res)
return -1;
buffer[output_size] = '\0';
if (used_size)
*used_size = output_size + 1u;
return 0;
}

View File

@@ -0,0 +1,6 @@
/**
@defgroup safety Safety Module
@brief Safety Supervisor Module
This is the safety module which ensures safe operation of the reflow controller
*/

View File

@@ -0,0 +1,110 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <reflow-controller/safety/stack-check.h>
#include <stdint.h>
#include <stm-periph/rng.h>
#include <stm-periph/crc-unit.h>
extern char __ld_top_of_stack;
extern char __ld_end_stack;
int32_t stack_check_get_usage()
{
uint32_t stack_top;
uint32_t stack_ptr;
stack_ptr = read_stack_pointer();
stack_top = (uint32_t)&__ld_top_of_stack;
return stack_top - stack_ptr;
}
int32_t stack_check_get_free()
{
uint32_t upper_heap_boundary;
uint32_t stack_ptr;
stack_ptr = read_stack_pointer();
upper_heap_boundary = (uint32_t)&__ld_end_stack;
return stack_ptr - upper_heap_boundary;
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
extern uint32_t __ld_start_stack_corruption_detect_area;
extern uint32_t __ld_end_stack_corruption_detect_area;
int stack_check_init_corruption_detect_area(void)
{
uint32_t *ptr = &__ld_start_stack_corruption_detect_area;
uint32_t *end_ptr = &__ld_end_stack_corruption_detect_area;
const uint32_t area_size_in_words = &__ld_end_stack_corruption_detect_area -
&__ld_start_stack_corruption_detect_area;
enum random_number_error rng_stat;
uint32_t rng_number;
uint32_t crc_val;
int ret = 0;
random_number_gen_init(false);
while (ptr < &end_ptr[-1]) {
rng_stat = random_number_gen_get_number(&rng_number, true);
if (rng_stat != RNG_ERROR_OK) {
ret = -1;
goto exit_deinit_rng;
}
*ptr = rng_number;
ptr++;
}
/* Init CRC unit and leave it on */
crc_unit_init();
crc_unit_reset();
crc_unit_input_array(&__ld_start_stack_corruption_detect_area, area_size_in_words - 1);
crc_val = crc_unit_get_crc();
end_ptr[-1] = crc_val;
exit_deinit_rng:
random_number_gen_deinit();
return ret;
}
#pragma GCC diagnostic pop
int stack_check_corruption_detect_area(void)
{
const uint32_t area_size_in_words = &__ld_end_stack_corruption_detect_area -
&__ld_start_stack_corruption_detect_area;
crc_unit_reset();
crc_unit_input_array(&__ld_start_stack_corruption_detect_area, area_size_in_words);
if (crc_unit_get_crc() == 0UL) {
return 0;
} else {
return -1;
}
}

View File

@@ -0,0 +1,120 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @addtogroup watchdog
* @{
*/
#include <reflow-controller/safety/watchdog.h>
#include <stm32/stm32f4xx.h>
/**
* @brief This key is expected by hardware to be written to the IWDG_KR register in order to reset the watchdog
*/
#define STM32_WATCHDOG_RESET_KEY 0xAAAA
/**
* @brief This key is expected by hardware to be written to the IWDG_KR register in order to enable the watchdog
*/
#define STM32_WATCHDOG_ENABLE_KEY 0xCCCC
/**
* @brief This key is expected by hardware to be written to the IWDG_KR register in order to enable access to config
* registers
*/
#define STM32_WATCHDOG_REGISTER_ACCESS_KEY 0x5555
int watchdog_setup(uint8_t prescaler)
{
uint32_t prescaler_reg_val;
/** - Activate the LSI oscillator */
RCC->CSR |= RCC_CSR_LSION;
__DSB();
/** - Wait for the oscillator to be ready */
while (!(RCC->CSR & RCC_CSR_LSIRDY));
if (prescaler == 4U)
prescaler_reg_val = 0UL;
else if (prescaler == 8U)
prescaler_reg_val = 1UL;
else if (prescaler == 16U)
prescaler_reg_val = 2UL;
else if (prescaler == 32U)
prescaler_reg_val = 3UL;
else if (prescaler == 64U)
prescaler_reg_val = 4UL;
else if (prescaler == 128U)
prescaler_reg_val = 5UL;
else
prescaler_reg_val = 6UL;
/** - Unlock registers */
IWDG->KR = STM32_WATCHDOG_REGISTER_ACCESS_KEY;
/** - Wait until prescaler can be written */
while (IWDG->SR & IWDG_SR_PVU);
/** - Write prescaler value */
IWDG->PR = prescaler_reg_val;
/* - Wait until reload value can be written */
while (IWDG->SR & IWDG_SR_RVU);
/** - Set reload value fixed to 0xFFF */
IWDG->RLR = 0xFFFU;
/** - Write enable key */
IWDG->KR = STM32_WATCHDOG_ENABLE_KEY;
/** - Do a first reset of the counter. This also locks the config regs */
watchdog_ack(WATCHDOG_MAGIC_KEY);
return 0;
}
int watchdog_ack(uint32_t magic)
{
int ret = -1;
/** - Check if magic key is correct */
if (magic == WATCHDOG_MAGIC_KEY) {
/** - Write reset key to watchdog */
IWDG->KR = STM32_WATCHDOG_RESET_KEY;
ret = 0;
}
return ret;
}
bool watchdog_check_reset_source(void)
{
bool ret;
ret = !!(RCC->CSR & RCC_CSR_WDGRSTF);
if (ret)
RCC->CSR |= RCC_CSR_RMVF;
return ret;
}
/** @} */

View File

@@ -0,0 +1,9 @@
/**
@defgroup watchdog Independent Watchdog
@ingroup safety
The independet watchdog module enusres that the safety controller run continuously and the whole formware does not lock.
The watchdog is entirely controlled by the safety controller and must not be used by the rest of the firmware
*/

View File

@@ -19,3 +19,200 @@
*/
#include <reflow-controller/settings/settings-sd-card.h>
#include <stm-periph/unique-id.h>
#include <fatfs/ff.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <config-parser/config-parser.h>
static char workbuff[256];
static void get_controller_folder_path(char *path, size_t size)
{
uint32_t high;
uint32_t mid;
uint32_t low;
if (!path)
return;
unique_id_get(&high, &mid, &low);
snprintf(path, size, "/%08X-%08X-%08X",
(unsigned int)high, (unsigned int)mid, (unsigned int)low);
}
static void get_controller_settings_path(char *path, size_t size, const char *setting)
{
char folder[48];
get_controller_folder_path(folder, sizeof(folder));
snprintf(path, size, "%s/%s.conf", folder, setting);
}
/**
* @brief Open or create the controller folder on the SD Card.
* @param[in,out] controller_folder
* @return 0 if opened, 1 if created and opened, -1 if error.
*/
static int create_controller_folder(void)
{
char foldername[48];
int ret = -1;
FRESULT filesystem_result;
FILINFO fno;
get_controller_folder_path(foldername, sizeof(foldername));
/* Check if folder is present */
filesystem_result = f_stat(foldername, &fno);
if (filesystem_result == FR_OK && fno.fattrib & AM_DIR) {
ret = 0;
} else {
filesystem_result = f_mkdir(foldername);
if (filesystem_result == FR_OK) {
ret = 1;
} else {
ret = -1;
}
}
return ret;
}
int sd_card_settings_save_calibration(float sens_deviation, float offset, bool active)
{
char path[200];
FRESULT res = FR_OK;
int ret = 0;
FIL file;
get_controller_settings_path(path, sizeof(path), "calibration");
if (create_controller_folder() < 0)
return -2;
if (!active) {
res = f_unlink(path);
goto check_fresult;
}
res = f_open(&file, path, FA_CREATE_ALWAYS | FA_WRITE);
if (res != FR_OK)
goto check_fresult;
snprintf(path, sizeof(path), "offset = %f\nsensitivity = %f\n", offset, sens_deviation);
ret = f_puts(path, &file);
if (ret < 0)
goto close_file;
ret = 0;
close_file:
res = f_close(&file);
check_fresult:
if (res != FR_OK)
return -2;
return ret;
}
int sd_card_settings_try_load_calibration(float *sens_deviation, float *offset)
{
char path[128];
int status = -1;
struct config_parser parser;
config_parser_handle_t p;
enum config_parser_ret res;
struct config_parser_entry entry;
bool sens_loaded = false;
bool offset_loaded = false;
if (!sens_deviation || !offset)
return -1000;
get_controller_settings_path(path, sizeof(path), "calibration");
p = config_parser_open_file(&parser, false, path, workbuff, sizeof(workbuff));
status = 0;
do {
res = config_parser_get_line(p, &entry, true);
if (res == CONFIG_PARSER_OK) {
if (!strcmp(entry.name, "offset") && entry.type == CONFIG_PARSER_TYPE_FLOAT) {
offset_loaded = true;
*offset = entry.value.float_val;
} else if (!strcmp(entry.name, "sensitivity") && entry.type == CONFIG_PARSER_TYPE_FLOAT) {
sens_loaded = true;
*sens_deviation = entry.value.float_val;
}
}
} while (res != CONFIG_PARSER_END_REACHED &&
res != CONFIG_PARSER_GENERIC_ERR &&
res != CONFIG_PARSER_IOERR &&
res != CONFIG_PARSER_PARAM_ERR);
config_parser_close_file(p);
if (sens_loaded && offset_loaded)
status = 0;
return status;
}
enum settings_load_result sd_card_settings_load_pid_oven_parameters(struct oven_pid_settings *settings)
{
enum settings_load_result ret = SETT_LOAD_ERR;
const char * const file_name = SETTINGS_PID_PARAMETER_FILE;
struct config_parser parser;
config_parser_handle_t p;
enum config_parser_ret parse_result;
struct config_parser_entry entry;
bool kp_loaded = false, kd_loaded = false, ki_loaded = false, int_max_loaded = false, t_sample = false;
if (!settings) {
ret = SETT_LOAD_ERR;
goto exit;
}
workbuff[0] = 0;
p = config_parser_open_file(&parser, false, file_name, workbuff, sizeof(workbuff));
if (!p)
goto exit;
do {
parse_result = config_parser_get_line(p, &entry, true);
if (parse_result == CONFIG_PARSER_OK) {
if (!strcmp(entry.name, "kp")) {
kp_loaded = true;
settings->kp = entry.value.float_val;
} else if (!strcmp(entry.name, "kd")) {
kd_loaded = true;
settings->kd = entry.value.float_val;
} else if (!strcmp(entry.name, "ki")) {
ki_loaded = true;
settings->ki = entry.value.float_val;
} else if (!strcmp(entry.name, "max_int")) {
int_max_loaded = true;
settings->max_integral = entry.value.float_val;
} else if (!strcmp(entry.name, "sample_period")) {
t_sample = true;
settings->t_sample = entry.value.float_val / 1000.0f;
}
}
} while (parse_result != CONFIG_PARSER_END_REACHED &&
parse_result != CONFIG_PARSER_GENERIC_ERR &&
parse_result != CONFIG_PARSER_IOERR &&
parse_result != CONFIG_PARSER_PARAM_ERR);
if (kp_loaded && kd_loaded && ki_loaded && t_sample && int_max_loaded)
ret = SETT_LOAD_SUCCESS;
config_parser_close_file(p);
exit:
return ret;
}

View File

@@ -19,8 +19,20 @@
*/
#include <reflow-controller/settings/settings.h>
#include <reflow-controller/settings/settings-sd-card.h>
int settings_save_calibration()
int settings_save_calibration(float sens_deviation, float offset, bool active)
{
return 0;
/* There is no other configuration location besides the SD card (yet) */
return sd_card_settings_save_calibration(sens_deviation, offset, active);
}
int settings_load_calibration(float *sens_dev, float *offset)
{
return sd_card_settings_try_load_calibration(sens_dev, offset);
}
enum settings_load_result settings_load_pid_oven_parameters(struct oven_pid_settings *settings)
{
return sd_card_settings_load_pid_oven_parameters(settings);
}

View File

@@ -33,9 +33,13 @@
#include <reflow-controller/calibration.h>
#include <reflow-controller/temp-converter.h>
#include <fatfs/ff.h>
#include <reflow-controller/stack-check.h>
#include <reflow-controller/safety/stack-check.h>
#include <reflow-controller/rotary-encoder.h>
#include <reflow-controller/safety-adc.h>
#include <reflow-controller/safety/safety-controller.h>
#include <reflow-controller/settings/settings.h>
#include <reflow-controller/button.h>
#include <reflow-controller/safety/fault.h>
#include <reflow-controller/safety/safety-memory.h>
#ifndef GIT_VER
#define GIT_VER "VERSION NOT SET"
@@ -44,7 +48,29 @@
extern struct stm_uart shell_uart;
static shellmatta_instance_t shell;
static char shell_buffer[512];
static char history_buffer[600];
static char IN_SECTION(.ccm.bss) history_buffer[600];
static bool check_opt(const char *args, uint32_t len, const char *opt_to_check)
{
(void)len;
char str[128];
const char *ptr;
static const char * const tokens = "\t ";
strncpy(str, args, sizeof(str));
str[sizeof(str) - 1] = 0;
/* Tokenize the string */
ptr = strtok(str, tokens);
while (ptr) {
if (strcmp(ptr, opt_to_check) == 0)
return true;
ptr = strtok(NULL, tokens);
}
return false;
}
static shellmatta_retCode_t shell_cmd_ver(const shellmatta_handle_t handle,
const char *arguments,
@@ -59,8 +85,8 @@ static shellmatta_retCode_t shell_cmd_ver(const shellmatta_handle_t handle,
unique_id_get(&high_id, &mid_id, &low_id);
shellmatta_printf(handle, "Reflow Oven Controller Firmware " xstr(GIT_VER) "\r\n"
"Compiled: " __DATE__ " at " __TIME__ "\r\n"
"Serial: %08X-%08X-%08X", high_id, mid_id, low_id);
"Compiled: " __DATE__ " at " __TIME__ "\r\n");
shellmatta_printf(handle, "Serial: %08X-%08X-%08X", high_id, mid_id, low_id);
return SHELLMATTA_OK;
}
@@ -128,7 +154,6 @@ static shellmatta_retCode_t shell_cmd_pt1000_res(const shellmatta_handle_t han
(void)length;
float resistance;
int pt1000_status;
enum adc_pt1000_error pt1000_flags;
char display_status[100];
float temp;
int temp_conv_status;
@@ -141,13 +166,7 @@ static shellmatta_retCode_t shell_cmd_pt1000_res(const shellmatta_handle_t han
if (pt1000_status == 2) {
strcat(display_status, " UNSTABLE ");
} else if (pt1000_status) {
pt1000_flags = adc_pt1000_check_error();
if (pt1000_flags & ADC_PT1000_INACTIVE)
strcat(display_status, " DEACTIVATED ");
else if (pt1000_flags & ADC_PT1000_WATCHDOG_ERROR)
strcat(display_status, " WATCHDOG ");
else if (pt1000_flags & ADC_PT1000_OVERFLOW)
strcat(display_status, " OVERFLOW ");
strcpy(display_status, "ERROR");
} else {
strcpy(display_status, "VALID");
}
@@ -171,17 +190,6 @@ static shellmatta_retCode_t shell_cmd_pt1000_res(const shellmatta_handle_t han
return SHELLMATTA_OK;
}
static shellmatta_retCode_t shell_cmd_clear_error_status(const shellmatta_handle_t handle, const char *arguments,
uint32_t length)
{
(void)arguments;
(void)length;
(void)handle;
adc_pt1000_clear_error();
return SHELLMATTA_OK;
}
static shellmatta_retCode_t shell_cmd_uptime(const shellmatta_handle_t handle,
const char *arguments,
@@ -197,10 +205,10 @@ static shellmatta_retCode_t shell_cmd_uptime(const shellmatta_handle_t handle,
systick_get_uptime_from_tick(&days, &hours, &mins, &secs);
shellmatta_printf(handle, "Uptime: %u day%s %02u:%02u:%02u",
days, (days == 1 ? "" : "s"),
hours,
mins,
secs);
days, (days == 1 ? "" : "s"),
hours,
mins,
secs);
return SHELLMATTA_OK;
}
@@ -247,7 +255,6 @@ static shellmatta_retCode_t shell_cmd_rot(const shellmatta_handle_t handle,
{
(void)arguments;
(void)length;
uint32_t rot_val;
rot_val = rotary_encoder_get_abs_val();
@@ -313,6 +320,8 @@ static shellmatta_retCode_t shell_cmd_reset(const shellmatta_handle_t handle, co
static shellmatta_retCode_t shell_cmd_cat(const shellmatta_handle_t handle, const char *arguments,
uint32_t length)
{
#ifdef IMPLEMENT_SHELL_CAT
FIL file;
char path_buff[256];
const char *path;
@@ -349,35 +358,296 @@ static shellmatta_retCode_t shell_cmd_cat(const shellmatta_handle_t handle, cons
f_close(&file);
return SHELLMATTA_OK;
}
static shellmatta_retCode_t shell_cmd_safety_adc(const shellmatta_handle_t handle, const char *arguments,
uint32_t length)
{
#else
(void)length;
(void)arguments;
shellmatta_printf(handle, "VREF:\t%8.2f\tmV\r\n", safety_adc_get_vref());
shellmatta_printf(handle, "TEMP:\t%8.2f\tdeg. Celsius\r\n", safety_adc_get_temp());
shellmatta_printf(handle, "Errors:\t%X", safety_adc_get_errors());
shellmatta_printf(handle, "cat not implemented!\r\n");
#endif
return SHELLMATTA_OK;
}
static shellmatta_retCode_t shell_cmd_safety_adc_clear_error(const shellmatta_handle_t handle, const char *arguments,
uint32_t length)
static shellmatta_retCode_t shell_cmd_read_flags(const shellmatta_handle_t handle, const char *arguments,
uint32_t length)
{
(void)length;
(void)arguments;
uint32_t count;
uint32_t i;
char name[64];
bool flag;
bool tryack;
int status;
enum safety_flag flag_enum;
struct analog_monitor_info amon_info;
struct timing_monitor_info timing_info;
/* Check for the --ack option */
tryack = check_opt(arguments, length, "--ack");
shellmatta_printf(handle, "Error flags\r\n"
"-----------\r\n");
count = safety_controller_get_flag_count();
for (i = 0; i < count; i++) {
status = safety_controller_get_flag_name_by_index(i, name, sizeof(name));
if (status) {
shellmatta_printf(handle, "Error getting flag name %lu\r\n", i);
continue;
}
status = safety_controller_get_flag_by_index(i, &flag, &flag_enum);
if (status) {
shellmatta_printf(handle, "Error getting flag value %lu\r\n", i);
continue;
}
if (tryack)
safety_controller_ack_flag(flag_enum);
shellmatta_printf(handle, "\t%2lu) %-20s\t[%s]\r\n", i+1, name, (flag ? "\e[1;31mERR\e[m" : "\e[32mOK\e[m"));
}
shellmatta_printf(handle, "\r\nAnalog Monitors\r\n"
"---------------\r\n");
count = safety_controller_get_analog_monitor_count();
for (i = 0; i < count; i++) {
status = safety_controller_get_analog_mon_name_by_index(i, name, sizeof(name));
if (status) {
shellmatta_printf(handle, "Error getting analog value monitor %lu name\r\n", i);
continue;
}
status = safety_controller_get_analog_mon_by_index(i, &amon_info);
if (status) {
shellmatta_printf(handle, "Error reading analog monitor status %lu\r\n", i);
continue;
}
shellmatta_printf(handle, "\t%2lu) %-20s\t[%s%8.2f%s]", i+1, name,
amon_info.status == ANALOG_MONITOR_OK ? "\e[32m" : "\e[1;31m",
amon_info.value,
"\e[m");
if (amon_info.status == ANALOG_MONITOR_INACTIVE) {
shellmatta_printf(handle, "Inactive\r\n");
} else {
shellmatta_printf(handle, " valid from %-8.2f to %-8.2f", amon_info.min, amon_info.max);
shellmatta_printf(handle, "\tchecked %llu ms ago\r\n",
systick_get_global_tick() - amon_info.timestamp);
}
}
shellmatta_printf(handle, "\r\nTiming Monitors\r\n"
"--------------\r\n");
count = safety_controller_get_timing_monitor_count();
for (i = 0; i < count; i++) {
(void)safety_controller_get_timing_mon_by_index(i, &timing_info);
(void)safety_controller_get_timing_mon_name_by_index(i, name, sizeof(name));
shellmatta_printf(handle, "\t%2lu) %-20s\t", i+1, name);
if (timing_info.enabled)
shellmatta_printf(handle, "last tick: %lu ms\r\n", (unsigned long int)timing_info.delta);
else
shellmatta_printf(handle, "\e[1;31mDISABLED\e[m\r\n");
}
return SHELLMATTA_OK;
}
static shellmatta_retCode_t shell_cmd_save_cal(const shellmatta_handle_t handle, const char *arguments,
uint32_t length)
{
(void)length;
(void)arguments;
int res;
float offset, sens_dev;
bool active;
adc_pt1000_get_resistance_calibration(&offset, &sens_dev, &active);
res = settings_save_calibration(sens_dev, offset, active);
if (res) {
shellmatta_printf(handle, "Error saving %d\r\n", res);
} else {
shellmatta_printf(handle, "Saved!\r\n");
}
return SHELLMATTA_OK;
}
static shellmatta_retCode_t shell_cmd_hang(const shellmatta_handle_t handle, const char *arguments,
uint32_t length)
{
(void)handle;
(void)arguments;
(void)length;
safety_adc_clear_errors();
while (1337);
return SHELLMATTA_OK;
}
static shellmatta_retCode_t shell_cmd_ui_emulation(const shellmatta_handle_t handle, const char *arguments,
uint32_t length)
{
(void)length;
(void)arguments;
uint32_t i;
uint32_t len;
char *buff;
shellmatta_read(handle, &buff, &len);
for (i = 0; i < len; i++) {
switch (buff[i]) {
case 'W':
case 'w':
rotary_encoder_override_delta(4);
break;
case 's':
case 'S':
rotary_encoder_override_delta(-4);
break;
case '\r':
button_override_event(BUTTON_SHORT_RELEASED);
break;
case 'l':
case 'L':
button_override_event(BUTTON_LONG);
break;
case 'k':
case 'K':
button_override_event(BUTTON_SHORT);
break;
case 'r':
case 'R':
button_override_event(BUTTON_LONG_RELEASED);
break;
}
}
return SHELLMATTA_CONTINUE;
}
static shellmatta_retCode_t shell_cmd_panic(const shellmatta_handle_t handle, const char *arguments,
uint32_t length)
{
(void)handle;
(void)arguments;
(void)length;
panic_mode();
return SHELLMATTA_OK;
}
static char *get_safety_mem_dump(size_t *used_bytes)
{
char *buffer;
int res;
buffer = (char *)malloc(5470);
res = safety_memory_dump_base64(buffer, 5470UL, used_bytes);
if (res) {
if (buffer)
free(buffer);
buffer = NULL;
}
return buffer;
}
static shellmatta_retCode_t shell_cmd_dump_safety_mem(const shellmatta_handle_t handle, const char *arguments,
uint32_t length)
{
static char *buffer;
static const char *ptr;
size_t used_bytes;
static size_t full_lines = 0;
static size_t current_line;
size_t remainder;
static const char *hline = "----------------------------------------------------------------";
char string[200];
const char *token;
const char * const token_delim = "\t ";
FRESULT fres;
FIL file;
UINT bw;
/* Check if the dump shall be stored to disk */
strncpy(string, arguments, MIN(sizeof(string), length+1));
string[sizeof(string) - 1] = '\0';
token = strtok(string, token_delim);
token = strtok(NULL, token_delim);
if (token) {
buffer = get_safety_mem_dump(&used_bytes);
if (!buffer) {
shellmatta_printf(handle, "Error generating dump");
return SHELLMATTA_OK;
}
fres = f_open(&file, token, FA_CREATE_NEW | FA_WRITE);
if (fres == FR_EXIST) {
free(buffer);
shellmatta_printf(handle, "File already esists!\r\n");
return SHELLMATTA_OK;
} else if (fres != FR_OK) {
free(buffer);
shellmatta_printf(handle, "Error opening file %s\r\n", token);
return SHELLMATTA_OK;
}
fres = f_write(&file, buffer, used_bytes - 1, &bw);
if (fres != FR_OK) {
shellmatta_printf(handle, "Error writing to file %s\r\n", token);
}
free(buffer);
f_close(&file);
return SHELLMATTA_OK;
}
if (full_lines == 0) {
shellmatta_printf(handle, "Safety memory content\r\n%s\r\n", hline);
buffer = get_safety_mem_dump(&used_bytes);
if (!buffer) {
shellmatta_printf(handle, "Error dumping memory!\r\n");
return SHELLMATTA_OK;
}
full_lines = (used_bytes - 1) / 64;
remainder = (used_bytes - 1) % 64;
if (remainder)
full_lines++;
ptr = buffer;
current_line = 0;
return SHELLMATTA_BUSY;
} else {
if (current_line < full_lines) {
shellmatta_printf(handle, "%.64s\r\n", ptr);
ptr += 64;
current_line++;
} else {
shellmatta_printf(handle, "%s\r\n", hline);
full_lines = 0;
if (buffer)
free(buffer);
buffer = NULL;
return SHELLMATTA_OK;
}
}
return SHELLMATTA_BUSY;
}
//typedef struct shellmatta_cmd
//{
// char *cmd; /**< command name */
@@ -387,8 +657,7 @@ static shellmatta_retCode_t shell_cmd_safety_adc_clear_error(const shellmatta_ha
// shellmatta_cmdFct_t cmdFct; /**< pointer to the cmd callack function */
// struct shellmatta_cmd *next; /**< pointer to next command or NULL */
//} shellmatta_cmd_t;
static shellmatta_cmd_t cmd[15] = {
static shellmatta_cmd_t cmd[18] = {
{
.cmd = "version",
.cmdAlias = "ver",
@@ -413,21 +682,13 @@ static shellmatta_cmd_t cmd[15] = {
.cmdFct = shell_cmd_pt1000_res_loop,
.next = &cmd[3],
},
{
.cmd = "pt1000-clear-error",
.cmdAlias = "pt-clear",
.helpText = "Clear error status of PT1000 reading",
.usageText = NULL,
.cmdFct = shell_cmd_clear_error_status,
.next = &cmd[4],
},
{
.cmd = "digio-get",
.cmdAlias = "dg",
.helpText = "Read all digital input/output ports",
.usageText = NULL,
.cmdFct = shell_cmd_digio_get,
.next = &cmd[5],
.next = &cmd[4],
},
{
.cmd = "digio-set",
@@ -435,7 +696,7 @@ static shellmatta_cmd_t cmd[15] = {
.helpText = "Set DIGIO Port",
.usageText = "digio-set <num> <state>",
.cmdFct = shell_cmd_digio_set,
.next = &cmd[6],
.next = &cmd[5],
},
{
.cmd = "uptime",
@@ -443,7 +704,7 @@ static shellmatta_cmd_t cmd[15] = {
.helpText = "Get uptime in seconds",
.usageText = "",
.cmdFct = shell_cmd_uptime,
.next = &cmd[7],
.next = &cmd[6],
},
{
.cmd = "calibrate",
@@ -451,7 +712,7 @@ static shellmatta_cmd_t cmd[15] = {
.helpText = "Calibrate resistance measurement",
.usageText = "",
.cmdFct = shell_cmd_cal,
.next = &cmd[8],
.next = &cmd[7],
},
{
.cmd = "meminfo",
@@ -459,7 +720,7 @@ static shellmatta_cmd_t cmd[15] = {
.helpText = "Get information about memory usage",
.usageText = "",
.cmdFct = shell_meminfo,
.next = &cmd[9],
.next = &cmd[8],
},
{
.cmd = "rotary-encoder",
@@ -467,7 +728,7 @@ static shellmatta_cmd_t cmd[15] = {
.helpText = "Get current rotary encoder value",
.usageText = "",
.cmdFct = shell_cmd_rot,
.next = &cmd[10],
.next = &cmd[9],
},
{
.cmd = "ls",
@@ -475,7 +736,7 @@ static shellmatta_cmd_t cmd[15] = {
.helpText = "List filesystem contents",
.usageText = "",
.cmdFct = shell_cmd_ls,
.next = &cmd[11],
.next = &cmd[10],
},
{
.cmd = "reset",
@@ -483,7 +744,7 @@ static shellmatta_cmd_t cmd[15] = {
.helpText = "Reset controller",
.usageText = "reset",
.cmdFct = shell_cmd_reset,
.next = &cmd[12],
.next = &cmd[11],
},
{
.cmd = "cat",
@@ -491,25 +752,56 @@ static shellmatta_cmd_t cmd[15] = {
.helpText = "Print file contents",
.usageText = "cat <path>",
.cmdFct = shell_cmd_cat,
.next = &cmd[12],
},
{
.cmd = "safety-flags",
.cmdAlias = "flags",
.helpText = "Reads and may clear safety flags",
.usageText = "flags [--ack]",
.cmdFct = shell_cmd_read_flags,
.next = &cmd[13],
},
{
.cmd = "safety-adc",
.cmdAlias = NULL,
.cmd = "save-calibration",
.cmdAlias = "save-cal",
.helpText = "",
.usageText = "",
.cmdFct = shell_cmd_safety_adc,
.cmdFct = shell_cmd_save_cal,
.next = &cmd[14],
},
{
.cmd = "safety-adc-clear-error",
.cmd = "hang",
.cmdAlias = NULL,
.helpText = "",
.usageText = "",
.cmdFct = shell_cmd_safety_adc_clear_error,
.cmdFct = shell_cmd_hang,
.next = &cmd[15],
},
{
.cmd = "ui-emulate",
.cmdAlias = NULL,
.helpText = "",
.usageText = "",
.cmdFct = shell_cmd_ui_emulation,
.next = &cmd[16],
},
{
.cmd = "panic",
.cmdAlias = NULL,
.helpText = "Panic Mode!",
.usageText = "",
.cmdFct = shell_cmd_panic,
.next = &cmd[17],
},
{
.cmd = "safety-mem-dump",
.cmdAlias = NULL,
.helpText = "",
.usageText = "safety-mem-dump [output-file]",
.cmdFct = shell_cmd_dump_safety_mem,
.next = NULL,
},
};
shellmatta_handle_t shell_init(shellmatta_write_t write_func)
@@ -529,7 +821,7 @@ void shell_print_motd(shellmatta_handle_t shell)
{
/* Clear display and set cursor to home position */
shellmatta_printf(shell, "\e[2J\e[H");
shellmatta_printf(shell, "Shimatta 仕舞った Reflow Controller ready\r\n\r\n");
shellmatta_printf(shell, "Shimatta Reflow Controller ready\r\n\r\n");
shell_cmd_ver(shell, NULL, 0UL);
shell_handle_input(shell, "\r\n", 2UL);
}

View File

@@ -1,47 +0,0 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <reflow-controller/stack-check.h>
#include <stdint.h>
extern char _estack;
extern char heap_top;
int32_t stack_check_get_usage()
{
uint32_t stack_top;
uint32_t stack_ptr;
stack_ptr = read_stack_pointer();
stack_top = (uint32_t)&_estack;
return stack_top - stack_ptr;
}
int32_t stack_check_get_free()
{
uint32_t upper_heap_boundary;
uint32_t stack_ptr;
stack_ptr = read_stack_pointer();
upper_heap_boundary = (uint32_t)&heap_top;
return stack_ptr - upper_heap_boundary;
}

View File

@@ -0,0 +1,114 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stm-periph/backup-ram.h>
#include <stm-periph/clock-enable-manager.h>
#include <stm32/stm32f4xx.h>
#include <helper-macros/helper-macros.h>
#define BACKUP_RAM_BASE BKPSRAM_BASE
#define BACKUP_RAM_SIZE 4096U
#define BACKUP_RAM_SIZE_WORDS (BACKUP_RAM_SIZE / 4U)
#define BACKUP_RAM_END_ADDR (BACKUP_RAM_BASE + BACKUP_RAM_SIZE - 1U)
#define backup_ram ((volatile uint32_t *)BACKUP_RAM_BASE)
#if !is_power_of_two(BACKUP_RAM_SIZE)
#error "Backup RAM size ahs to be a power of two!"
#endif
void backup_ram_init(bool use_backup_regulator)
{
rcc_manager_enable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(RCC_APB1ENR_PWREN));
/* Enable access to backup RAM register set */
PWR->CR |= PWR_CR_DBP;
if (use_backup_regulator) {
/* Enable the backup regulator */
PWR->CSR |= PWR_CSR_BRE;
/* Wait until regulator is ready */
while (!(PWR->CSR & PWR_CSR_BRR));
}
/* Enable clock for backup ram interface */
rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(RCC_AHB1ENR_BKPSRAMEN));
}
void backup_ram_disable(void)
{
/* Disable access to backup RAM register set */
PWR->CR &= ~PWR_CR_DBP;
rcc_manager_disable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(RCC_APB1ENR_PWREN));
rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(RCC_AHB1ENR_BKPSRAMEN));
}
void backup_ram_wipe(void)
{
uint32_t i;
for (i = 0; i < BACKUP_RAM_SIZE_WORDS; i++)
backup_ram[i] = 0UL;
}
int backup_ram_get_data(uint32_t addr, uint32_t *data, uint32_t count)
{
volatile uint32_t *ptr;
if (!data)
return -1002;
if (addr >= BACKUP_RAM_SIZE_WORDS)
return -1001;
ptr = &backup_ram[addr];
for (; count > 0; count--)
*(data++) = *(ptr++);
return 0;
}
int backup_ram_write_data(uint32_t addr, const uint32_t *data, uint32_t count)
{
volatile uint32_t *ptr;
if (!data)
return -1002;
if (addr >= BACKUP_RAM_SIZE_WORDS)
return -1001;
ptr = &backup_ram[addr];
for (; count > 0; count--)
*(ptr++) = *(data++);
return 0;
}
uint32_t backup_ram_get_size_in_words(void)
{
return (uint32_t)BACKUP_RAM_SIZE_WORDS;
}
volatile void *backup_ram_get_base_ptr(void)
{
return backup_ram;
}

View File

@@ -0,0 +1,59 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* GDSII-Converter 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stm-periph/crc-unit.h>
#include <stm-periph/clock-enable-manager.h>
#include <stm32/stm32f4xx.h>
void crc_unit_init(void)
{
rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(RCC_AHB1ENR_CRCEN));
crc_unit_reset();
}
void crc_unit_deinit(void)
{
rcc_manager_disable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(RCC_AHB1ENR_CRCEN));
}
void crc_unit_reset(void)
{
CRC->CR = CRC_CR_RESET;
}
uint32_t crc_unit_get_crc(void)
{
return CRC->DR;
}
void crc_unit_input(uint32_t data)
{
CRC->DR = data;
}
void crc_unit_input_array(const uint32_t *data, uint32_t len)
{
uint32_t i;
if (!data)
return;
for (i = 0; i < len; i++)
crc_unit_input(data[i]);
}

View File

@@ -0,0 +1,70 @@
/* Reflow Oven Controller
*
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware 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 the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stm-periph/rng.h>
#include <stm-periph/clock-enable-manager.h>
#include <stm32/stm32f4xx.h>
void random_number_gen_init(bool int_enable)
{
rcc_manager_enable_clock(&RCC->AHB2ENR, BITMASK_TO_BITNO(RCC_AHB2ENR_RNGEN));
__DSB();
random_number_gen_reset(int_enable);
}
void random_number_gen_deinit()
{
RNG->CR = 0;
__DSB();
rcc_manager_disable_clock(&RCC->AHB2ENR, BITMASK_TO_BITNO(RCC_AHB2ENR_RNGEN));
}
void random_number_gen_reset(bool int_en)
{
RNG->CR = 0;
__DSB();
RNG->CR = RNG_CR_RNGEN | (int_en ? RNG_CR_IE : 0U);
}
enum random_number_error random_number_gen_get_number(uint32_t *random_number, bool wait_for_valid_value)
{
bool value_ready;
if (!(RNG->CR & RNG_CR_RNGEN))
return RNG_ERROR_INACT;
if (RNG->SR & RNG_SR_SEIS || RNG->SR & RNG_SR_CEIS) {
/* Error detected */
return RNG_ERROR_INTERNAL_ERROR;
}
/* Check if the value is ready. Wait if wait_for_valid_value is true */
do {
value_ready = !!(RNG->SR & RNG_SR_DRDY);
} while (!value_ready && wait_for_valid_value);
/* If the value is valid, return it */
if (value_ready && random_number)
*random_number = RNG->DR;
/* Return from function with proper status */
return (value_ready ? RNG_ERROR_OK : RNG_ERROR_NOT_READY);
}

View File

@@ -1,189 +1,164 @@
/*
*****************************************************************************
**
** File : stm32_flash.ld
**
** Abstract : Linker script for STM32F407VG Device with
** 1024KByte FLASH, 192KByte RAM
**
** Set heap size, stack size and stack location according
** to application requirements.
**
** Set memory bank area and size if external memory is used.
**
** Target : STMicroelectronics STM32
**
** Environment : Atollic TrueSTUDIO(R)
**
** Distribution: The file is distributed <20>as is,<2C> without any warranty
** of any kind.
**
** (c)Copyright Atollic AB.
** You may use this file as-is or modify it according to the needs of your
** project. Distribution of this file (unmodified or modified) is not
** permitted. Atollic AB permit registered Atollic TrueSTUDIO(R) users the
** rights to distribute the assembled, compiled & linked contents of this
** file as part of an application binary file, provided that it is built
** using the Atollic TrueSTUDIO(R) toolchain.
**
*****************************************************************************
*/
* STM32F407VE Linkerscript for FLASH normal flash code execution
* Copyright (C) 2017 Mario H<>ttel <mario.huettel@gmx.net>
*
* This file is part of 'STM32F407 code template'.
*
* It 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, version 2 of the License.
*
* This code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this template. If not, see <http://www.gnu.org/licenses/>.
* --------------------------------------------------------------------
* FLASH: 512K
* RAM: 128K
* CCM RAM: 64L
* FPU: fpv4-sp-d16
*
/* Entry Point */
/* USER PARAMETERS */
__ld_stack_size = 0x3000;
__ld_heap_size = 0x4200;
__stack_corruption_area_size = 128;
/* END OF USER PARAMETERS */
ENTRY(Reset_Handler)
__ld_top_of_stack = 0x20020000; /* One byte above the end of the SRAM. Stack is pre-decrewmenting, so this is okay */
/* Highest address of the user mode stack */
_estack = 0x20020000; /* end of 128K RAM on AHB bus*/
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x2100; /* required amount of heap (DEFAULT 0) */
_Min_Stack_Size = 0x3000 ; /* required amount of stack */
/* recommended min stack size for printf=0x2000, orig = 0x400 */
/* Specify the memory areas */
/* Available memory areas */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
CCM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K
FLASH (xr) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
CCM (rw) : ORIGIN = 0x10000000, LENGTH = 64K
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
.vectors :
{
. = ALIGN(4);
KEEP(*(.vectors))
. = ALIGN(4);
} >FLASH
.text :
{
. = 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)
KEEP(*(.init)) /* Constructors */
KEEP(*(.fini)) /* Destructors */
} >FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} >FLASH
.ARM :
{
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} >FLASH
/* Constructor/Destructor tables */
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} >FLASH
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} >FLASH
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(.fini_array*))
KEEP (*(SORT(.fini_array.*)))
PROVIDE_HIDDEN (__fini_array_end = .);
} >FLASH
/* The program code and other data goes into FLASH */
.text :
{
. = 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)
/* Initialized CCM data */
__ld_load_ccm_data = LOADADDR(.ccmdata);
.ccmdata :
{
. = ALIGN(4);
__ld_sdata_ccm = .;
*(.ccm.data)
*(.ccm.data*)
. = ALIGN(4);
__ld_edata_ccm = .;
} >CCM AT> FLASH
.ccmbss (NOLOAD) :
{
. = ALIGN(4);
__ld_sbss_ccm = .;
*(.ccm.bss)
*(.ccm.bss*)
. = ALIGN(4);
__ld_ebss_ccm = .;
} >CCM
KEEP (*(.init))
KEEP (*(.fini))
/* Initialized Data */
__ld_load_data = LOADADDR(.data);
.data :
{
. = ALIGN(4);
__ld_sdata = .;
*(.data)
*(.data*)
. = ALIGN(4);
__ld_edata = .;
} >RAM AT> FLASH
/* Uninitialized static data */
.bss (NOLOAD) :
{
. = ALIGN(4);
__ld_sbss = .;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
__ld_ebss = .;
} >RAM
. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
_exit = .;
} >FLASH
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
.ARM : {
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} >FLASH
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} >FLASH
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} >FLASH
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(.fini_array*))
KEEP (*(SORT(.fini_array.*)))
PROVIDE_HIDDEN (__fini_array_end = .);
} >FLASH
_siccmram = LOADADDR(.ccmram);
/* CCM-RAM section */
.ccmram (NOLOAD):
{
. = ALIGN(4);
_sccmram = .; /* create a global symbol at ccmram start */
*(.ccmram)
*(.ccmram*)
. = ALIGN(4);
_eccmram = .; /* create a global symbol at ccmram end */
} >CCM
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = 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 AT> FLASH
/* Uninitialized data section */
. = ALIGN(4);
.bss (NOLOAD):
{
/* 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)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >RAM
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack (NOLOAD):
{
. = ALIGN(8);
PROVIDE (heap_low = .); /* for _sbrk */
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
PROVIDE (heap_top = .); /* for _sbrk */
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM
/* MEMORY_bank1 section, code must be located here explicitly */
/* Example: extern int foo(void) __attribute__ ((section (".mb1text"))); */
.memory_b1_text :
{
*(.mb1text) /* .mb1text sections (code) */
*(.mb1text*) /* .mb1text* sections (code) */
*(.mb1rodata) /* read-only data (constants) */
*(.mb1rodata*)
} >MEMORY_B1
/* Remove information from the standard libraries */
/*/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}*/
.ARM.attributes 0 : { *(.ARM.attributes) }
.heap_stack (NOLOAD) :
{
. = ALIGN(4);
__ld_sheap = .;
. = . + __ld_heap_size;
__ld_eheap = .;
. = ALIGN(4);
__ld_start_stack_corruption_detect_area = .;
. = . + __stack_corruption_area_size;
. = ALIGN(4);
__ld_end_stack_corruption_detect_area = .;
__ld_end_stack = .;
. = . + __ld_stack_size;
. = ALIGN(4);
} >RAM
}

View File

@@ -27,19 +27,19 @@ extern struct stm_uart shell_uart;
char* _sbrk(int incr)
{
extern char heap_low; // Defined by the linker
extern char heap_top;
extern char __ld_sheap; // Defined by the linker
extern char __ld_eheap;
static char *heap_end;
char *prev_heap_end;
if (heap_end == 0) {
heap_end = &heap_low;
heap_end = &__ld_sheap;
}
prev_heap_end = heap_end;
if (heap_end + incr > &heap_top) {
if (heap_end + incr > &__ld_eheap) {
errno = ENOMEM;
return (char *)-1;
}
@@ -110,3 +110,10 @@ int _kill(int pid)
return -1;
}
void _exit(int pid)
{
(void)pid;
while(1);
}

View File

@@ -23,12 +23,13 @@
*/
#include <reflow-controller/systick.h>
#include <helper-macros/helper-macros.h>
#include <stm32/stm32f4xx.h>
#include <cmsis/core_cm4.h>
volatile uint32_t wait_tick_ms = 0UL;
volatile uint64_t global_tick_ms = 0ULL;
volatile uint32_t lcd_tick_100us = 0UL;
volatile uint32_t IN_SECTION(.ccm.bss) wait_tick_ms = 0UL;
volatile uint64_t IN_SECTION(.ccm.bss) global_tick_ms = 0ULL;
volatile uint32_t IN_SECTION(.ccm.bss) lcd_tick_100us = 0UL;
void systick_setup(void)
{
@@ -111,7 +112,7 @@ bool __attribute__((optimize("O3"))) systick_ticks_have_passed(uint64_t start_ti
*/
void __attribute__((optimize("O3"))) SysTick_Handler()
{
static uint32_t pre_tick = 0UL;
static uint32_t IN_SECTION(.ccm.bss) pre_tick = 0UL;
pre_tick++;
if (pre_tick == 10) {
@@ -120,5 +121,6 @@ void __attribute__((optimize("O3"))) SysTick_Handler()
wait_tick_ms++;
global_tick_ms++;
}
lcd_tick_100us++;
}

Some files were not shown because too many files have changed in this diff Show More