Compare commits
120 Commits
Author | SHA1 | Date | |
---|---|---|---|
4efa5ae797 | |||
5e90cd39fa | |||
015028bde2 | |||
4e6e5db2c3 | |||
a2bcd37800 | |||
ed936cf9f9 | |||
e595e398d4 | |||
86446e1b22 | |||
b26233d5e3 | |||
0ab13edced | |||
2bc2c0c670 | |||
aad9be9905 | |||
6f2b6e0839 | |||
45145c1f36 | |||
1722ba7e5d | |||
7a44a383ff | |||
7a2c77a83e | |||
c8e3bf5de2 | |||
400c77f11c | |||
7be6047e04 | |||
2ab7257fe6 | |||
376d9c7aaf | |||
2a6a98deac | |||
20b460cd5a | |||
ae60c30919 | |||
c6fcd3154f | |||
a7394ef170 | |||
01abb3e085 | |||
d63761d016 | |||
d468e52dea | |||
5a1deeb9ee | |||
809c86a618 | |||
88a4bb97a5 | |||
4a98d41623 | |||
836c7af163 | |||
e3670434ca | |||
a802b5c1b5 | |||
ffb544e21d | |||
e4ebf9ec3f | |||
6e89c0e098 | |||
fdb1c6e893 | |||
e753504ae3 | |||
2fa12cda60 | |||
17609a2491 | |||
1e870972e3 | |||
864c3fa0f2 | |||
9c872f6746 | |||
0bf587b8bb | |||
8a9bd0df6e | |||
baf6b67658 | |||
41fbb83b46 | |||
f4fcaf1e3a | |||
67932bf465 | |||
632973cdac | |||
9558d4bb45 | |||
90c6165278 | |||
8c0cfa4993 | |||
5f91659d04 | |||
fdb3ceb3e9 | |||
7e3d2d1d0b | |||
ef2cd6acfe | |||
d9bd434dc6 | |||
4ed2203b35 | |||
0b1ce8b614 | |||
bc5e4c14df | |||
43abca4c51 | |||
00c796c58f | |||
4d0f963585 | |||
708fdea058 | |||
ebb1383957 | |||
3345004213 | |||
afadd539c8 | |||
71315b7c92 | |||
410a5d4dd1 | |||
528db7a581 | |||
384e127085 | |||
fe0bde5c32 | |||
2beaccbe32 | |||
b6760ff426 | |||
1b2dac21f2 | |||
97f154d3b9 | |||
ee5dda4a33 | |||
afb8e93b13 | |||
9bd0dd194b | |||
6322c3728b | |||
174bf4220e | |||
566436201e | |||
61e3b58992 | |||
01b445a0fb | |||
5437a323c3 | |||
28e42d3306 | |||
08606689b4 | |||
5776feee85 | |||
6273c68821 | |||
3381840bba | |||
cf35ba735f | |||
3f31acfada | |||
77251cc1bc | |||
f2972903d5 | |||
31b17dfd8d | |||
9f1a791be2 | |||
54416a6350 | |||
9c94428144 | |||
81155887de | |||
8309cef5ec | |||
1a76a69b6d | |||
e50e3f0ace | |||
5fb1612773 | |||
72735915ee | |||
08ec458e8f | |||
d962110823 | |||
bfdc3d3246 | |||
dca839ce2e | |||
eea0826c7b | |||
6e5627fde2 | |||
0f239dc39d | |||
08eee66d30 | |||
7c9d296e34 | |||
533656ca28 | |||
d146b10569 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -10,3 +10,6 @@
|
||||
path = stm-firmware/base64-lib
|
||||
url = https://git.shimatta.de/mhu/base64-lib.git
|
||||
branch = master
|
||||
[submodule "stm-firmware/linklist-lib"]
|
||||
path = stm-firmware/linklist-lib
|
||||
url = https://git.shimatta.de/mhu/linklist-lib.git
|
||||
|
361
GPLv2.md
Normal file
361
GPLv2.md
Normal 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.
|
35
Jenkinsfile
vendored
Normal file
35
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
pipeline {
|
||||
agent any
|
||||
|
||||
stages {
|
||||
stage('Configure') {
|
||||
steps {
|
||||
echo 'Configuring Cmake...'
|
||||
sh '''
|
||||
mkdir "build" &&
|
||||
cd "build" &&
|
||||
cmake "../stm-firmware"
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh '''
|
||||
cd "build" &&
|
||||
make
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage('Test') {
|
||||
steps {
|
||||
echo 'Testing..'
|
||||
}
|
||||
}
|
||||
stage('Deploy') {
|
||||
when { tag "*" }
|
||||
steps {
|
||||
echo 'Deploying tag...'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
367
LICENSE.md
367
LICENSE.md
@@ -1,361 +1,10 @@
|
||||
### GNU GENERAL PUBLIC LICENSE
|
||||
# License of the Relow Oven Control Firmware
|
||||
|
||||
Version 2, June 1991
|
||||
This software is licsensed according to the GPLv2. See GPLv2.md for details.
|
||||
However, this software includes various third party libraries for which different licenses apply:
|
||||
|
||||
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.
|
||||
- Shellmatta Shell implementation by Stefan Strobel: 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 etc. by ST Microelectronics icensed under a custom license. See the sphinx documentation (section License) for details.
|
||||
- FatFs: Custom License (See source files of FatFS)
|
||||
- SHA256 Library <https://github.com/amosnier/sha-2> licensed under the ``Unilicense``
|
||||
|
Submodule c-style-checker updated: 3a58e3dd1c...cb937262aa
3
doc/.vscode/settings.json
vendored
Normal file
3
doc/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"restructuredtext.confPath": "${workspaceFolder}/source"
|
||||
}
|
3
doc/source/.vscode/settings.json
vendored
Normal file
3
doc/source/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"restructuredtext.confPath": "${workspaceFolder}"
|
||||
}
|
1
doc/source/firmware/calibration.drawio
Normal file
1
doc/source/firmware/calibration.drawio
Normal file
@@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2021-05-22T15:48:58.897Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.1.8 Chrome/87.0.4280.88 Electron/11.1.1 Safari/537.36" etag="60nNVEjkDLNBMWFG8AC7" version="14.1.8" type="device"><diagram id="3R1YF1CA5z1RRVBQNkpO" name="Page-1">7VlLc9o6FP41LGFkCduwbCBtF+1cbtOZpkthy7Z6ZURk8eqvr2TLLwSBFIgnmZtFxjqSjh7n+85D9NAk3X4SeJl85SFhPQjCbQ9NexB6vq/+a8GuEKCRVwhiQcNC5NSCB/qbGCEw0hUNSdYaKDlnki7bwoAvFiSQLRkWgm/awyLO2qsucUwswUOAmS39QUOZFNIR9Gv5Z0LjpFzZ8cZFT4rLweYkWYJDvmmI0H0PTQTnsvhKtxPC9N2V91LM+3ikt9qYIAt5zgRvNpa/npLHmH16+jdwvo+m7rzvmr3JXXlgEqrzmyYXMuExX2B2X0vvBF8tQqK1AtWqx3zhfKmEjhL+IlLujDHxSnIlSmTKTK/asNg9mvl546duDNyyOd02O6c70yr2qjd49ApKiGARE/nMuceVARRwCU+JWkfNE4RhSddt/dhAKK7GVVNnnKqVITBor1BrwO74oK2i2JeZ1bTVnqJ9PZ7b1pPxlQiIpUd9NI5Ti3IkvAAVhqJrzFbmKr7hjRLMvjsAgPyeMppJvAjIQfx8wXPlBVo2x4zGC/UdKFMRoQRrIiRVPPtgOlIahgW8lO7feJ7r01Zf6lPm53bveu60woFWQLa9A07ATK6p10TIcRrYcDDa+2DgAH/cMknfLHkhYhwwGLug8YcOrlIq5FGUkZuY3OnGE2ypfGx8N/yAatVuQDdKL/C33iNvzYig6qo0Al/oUgrKnXIpZ7geZ3ih72mZ/qV2HlvU7kGPqf3ezdVHLPM7KQQRz5FaI8J7WvGyo5/lNv2gBiC03NadtZZKkdpooautX4kba+6DjzEV4jXINgmV5GGJ88vfqCRjz7FkyyLuR3SroXgXUcYmnHGRK0KhS0bhUMkzKfh/pNEzgnPkec/5EwsGR10ELPlTeuyhcT2bOmlwjShp5AsuuJGdHce+0vfF5ytStwy3p7kLb5M3oHa8h8Pz8oarOX9geYWJCthzgaVCAwT/1GGnCag2XE7wdI+WURTBIDhEy9Cbe+6VaOnv3atrs9KBB2jp3YyW6O3Rsmbiz2YC/wq0hGfS8tJs/jKTQos8fdv5SkHxImZnBDTBpdo11zlxfwxs5hBHhTT/EHPGno/wtQIabAe0qiRpUAcdYM6+57raNUP4ZpijGjfMMy/OHw/HIA+cqDlvHIMgej4GVbXnt3dWe8IjOUVdfCIXOC3bXKn4RM7gcFH7CuXm0LK2Zc3SSrlFZzyjxivOuZQ81Q6wfFHTpgpxllRMtyzeQMO+8aV2BVrXUq+bbmP9jjkgTBlT6HEDPFeeFgdykK70uyOjucZOygw03CNp12UGfINlRnf5jHdmPuP4nSY09vNf0xOb1wJRlu4PZKGpuaZSb3FK1rRIX958tYBg59XCqBN2/SVTbv3A5vjn0qfTF7Zym/8/sd029lXlR2exD9hX+h5+PDtNxLOfuruNY/Zbt52tXBKVXqkSHx95HDxRiaOX414169+ji1S//lEf3f8B</diagram></mxfile>
|
@@ -9,6 +9,3 @@ Safety Controller
|
||||
safety-adc
|
||||
|
||||
|
||||
.. doxygengroup:: safety-controller
|
||||
:project: Reflow Controller Firmware
|
||||
|
||||
|
31
doc/source/firmware/hw-version-detect.rst
Normal file
31
doc/source/firmware/hw-version-detect.rst
Normal file
@@ -0,0 +1,31 @@
|
||||
.. _hw_version_detect:
|
||||
|
||||
Automatic Hardware Version detection
|
||||
====================================
|
||||
|
||||
Functional Description
|
||||
----------------------
|
||||
|
||||
In order to automatically select the available hardware features, the firmware checks the PCB's version
|
||||
using vertain pins on the microcontroller.
|
||||
|
||||
The hardware revision is represented by the enum type :c:enum:`hw_revision`. A call to :c:func:`get_pcb_hardware_version` retrieves the revision.
|
||||
When the function is called for the first time, it activates pull-up resistors on pin numbers :c:macro:`HW_REV_DETECT_PIN_LOW` to :c:macro:`HW_REV_DETECT_PIN_HIGH`
|
||||
on port :c:macro:`HW_REV_DETECT_GPIO` and reads in the binary number.
|
||||
|
||||
The revision is set by externally connecting the pins to ground. The bit pattern read from the port is inverted and then checked internally to derive the hardware version.
|
||||
After the version is read for the first time, it is stored internally and subsequent calls to :c:func:`get_pcb_hardware_version` only retrieve the internally stored value to reduce the overhead of the function.
|
||||
|
||||
Hardware Version Dependent Features
|
||||
-----------------------------------
|
||||
|
||||
- The settings module searches for an external EEPROM on the SPI interface to store some settings, like the calibration. This is enabled for all versions higher or equal to ``v1.3``.
|
||||
- The safety controller enables the acknowledging of the external watchdog for all versions higher or equal to ``v1.3``.
|
||||
- The oven driver has a separate safety enable for the solid state relay which it enables on all versions higher or equal to ``v1.3``.
|
||||
|
||||
|
||||
API Documentation
|
||||
-----------------
|
||||
|
||||
.. doxygengroup:: hw-version-detect
|
||||
:project: Reflow Controller Firmware
|
@@ -11,6 +11,8 @@ mechanisms and the behavior. For a detailed code documentation see the doxygen o
|
||||
:maxdepth: 2
|
||||
|
||||
pt1000-processing
|
||||
pid-controller
|
||||
safety/index
|
||||
code/index
|
||||
hw-version-detect
|
||||
|
||||
|
78
doc/source/firmware/pid-controller.rst
Normal file
78
doc/source/firmware/pid-controller.rst
Normal file
@@ -0,0 +1,78 @@
|
||||
.. _pid_controller:
|
||||
|
||||
PID Controller
|
||||
==============
|
||||
|
||||
The PID controller is the main element of the oven. It controls the output of the solid state relais in order to achieve the desired temperature.
|
||||
|
||||
The PID controller is implemented in the ``pid-controller.c`` file. See the :ref:`pid_code_api` for details.
|
||||
|
||||
Functional Description
|
||||
----------------------
|
||||
|
||||
The following figure shows the PID controller's structure.
|
||||
|
||||
.. drawio-image:: pid.drawio
|
||||
|
||||
The controller is composed of 3 paths. The proportional, the derivate, and the integrator path.
|
||||
Compared to a textbook PID controller, this version contains a few additional features.
|
||||
|
||||
The integrator has a configurable maximum limit of :c:var:`pid_controller.integral_max`. Once this value is reached (plus or minus), the integrator freezes.
|
||||
The integrator is also frozen in case the output value is in saturation. This serves as an anti-windup protection.
|
||||
|
||||
The output value saturates at configurable high and low limits (:c:var:`pid_controller.output_sat_max` and :c:var:`pid_controller.output_sat_min`). The controller instance used for the
|
||||
reflow oven's solid state relais output is saturated by software to a limit of 0 to 100.
|
||||
|
||||
In addition to the above features, the derivate term contains an additional first order low pass filter in order to prevent coupling of high frequency noise amplified by the derivate term.
|
||||
The low pass filter is charcterized by its time constant :math:`k_{d\tau}`.
|
||||
|
||||
Time Continuous Transfer Function
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The time continous transfer function of the PID controller is
|
||||
|
||||
.. math:: H_c(s) = \frac{Y_c(s)}{X_c(s)} = k_p + \frac{k_i}{s} + \frac{k_ds}{1+sk_{d\tau}}
|
||||
|
||||
|
||||
Time Discrete Transfer Function
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The time descrete transfer function which is implemented in the code is derived by converting the time continuous transfer function
|
||||
with the bilinear transformation:
|
||||
|
||||
.. math:: s = \frac{2}{T_s}\cdot \frac{z-1}{z+1}
|
||||
|
||||
The frequency warping of the bilinear transform can be considered negligible because the PID controller is targetted for low frequency temperature signal data with a maximum far below the nyquist freqency.
|
||||
In this area, the mapping of the continuous frequencies to the time descrete can be considered linear.
|
||||
|
||||
The time discrete transfer function after inserting the bilinear transform is:
|
||||
|
||||
.. math:: H_d(z) = H_c(s)\bigg|_{s=\frac{2}{T_s}\cdot \frac{z-1}{z+1}} = k_p + \frac{k_i T_s (z+1)}{2(z-1)} + \frac{\frac{2}{T_s}k_d(z-1)}{\left(1+\frac{2k_{d\tau}}{T_s}\right)z+\left(1-\frac{2k_{d\tau}}{T_s}\right)}.
|
||||
|
||||
Converted to an implementable form:
|
||||
|
||||
.. math:: H_d(z) = k_p + \frac{k_iT_s(1+ z^{-1})}{2(1-z^{-1})} + \frac{\frac{2}{T_s}k_d(1-z^{-1})}{(1+2k_{d\tau}T_s^{-1})+(1-2k_{d\tau}T_s^{-1})z^{-1}}
|
||||
|
||||
This function can be splitted in the three individual parts of the PID controller:
|
||||
|
||||
.. math:: H_{d1}(z) = k_p
|
||||
.. math:: H_{d2}(z) = \frac{k_iT_s(1+ z^{-1})}{2(1-z^{-1})}
|
||||
.. math:: H_{d3}(z) = \frac{\frac{2}{T_s}k_d(1-z^{-1})}{(1+2k_{d\tau}T_s^{-1})+(1-2k_{d\tau}T_s^{-1})z^{-1}}
|
||||
|
||||
The individual time domain difference equations :math:`y_i[n] = \mathcal{Z}^{-1}\left\lbrace H_{di} * X(z)\right\rbrace` that are implemented in software are:
|
||||
|
||||
.. math:: y_1[n] = k_p x[n]
|
||||
.. math:: y_2[n] = \underbrace{\frac{k_iT_s}{2}}_{k_{i_t}} \left( x[n] + x[n-1]\right) + y_2[n-1]
|
||||
.. math:: y_3[n] = \underbrace{\frac{2k_{d}}{2k_{d\tau} + T_s}}_{k_{d_t}}\left( x[n] - x[n-1] \right) + \underbrace{\frac{2k_{d\tau} - T_s}{2k_{d\tau} + T_s}}_{\overline{k_{d_t}}} y_3[n-1]
|
||||
|
||||
The final output value is the sum of all three terms:
|
||||
|
||||
.. math:: y[n] = \sum_{i=1}^{3} y_i[n]
|
||||
|
||||
.. _pid_code_api:
|
||||
|
||||
PID Controller Code API
|
||||
-----------------------
|
||||
|
||||
.. doxygengroup:: pid-controller
|
||||
:project: Reflow Controller Firmware
|
1
doc/source/firmware/pid.drawio
Normal file
1
doc/source/firmware/pid.drawio
Normal file
@@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2021-06-14T21:01:35.428Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.1.8 Chrome/87.0.4280.88 Electron/11.1.1 Safari/537.36" etag="ORruDkFRflEPSgIrI-Cx" version="14.1.8" type="device"><diagram name="Page-1" id="b520641d-4fe3-3701-9064-5fc419738815">3Vttb6M4EP41+dgIMObl4/bl7lba1VXqSbt3XyonOMRXiDnjtGR//dnYQHhLySaEdFFV2ePx2Hjm8YzHZAbu4ux3hpL1VxrgaGYZQTYD9zPL8j1T/JeEnSJA6CtCyEigSGZFeCI/sCYamrolAU5rjJzSiJOkTlzSzQYveY2GGKNvdbYVjeqjJijELcLTEkVt6jcS8LWmmoZRNfyBSbjWQ3tQNyzQ8iVkdLvR480ssMof1RyjUpYipGsU0DdFykWAhxm4Y5RyVYqzOxzJpS2WTY3zW09rOW+GN3xIBy/JHl5eQ/vrX8//QJh8C+iPpxutq5TvivXAgVgeXaWMr2lINyh6qKi3+TtjKdUQtYrnC6WJIJqC+C/mfKd1jbacCtKax5FuxRnh3/fKf0tRc6hr95mWnFd2RWXD2e57xSirf++3Vd3yWq3fI2YkxhwzTVQvLd+0dy0LpdEtW+IDC1iYLGIh5of4zFLlAkmYiumwnejIcIQ4ea1PBGmbDku+susjJWKKlqHh50BLrEfeSePPstyCUohRk9M9KwsRhb2pVKTcbo6wISlyAiMqDaJmDpV19BjEcN2/r9NRVFrujIVCHWOQOtuC7HcEKeNuCfrEGNrtsSWSIT0wYbd7nN55NfjNOr8oqBmc1Ui1Gl5RtMXFfu1EQru3C1EIZeHzhuOQIU5Z0SRGKltLdlZRbPH38kxUoQWCysSlvb6tCcdPCcp3kzfhSuvmvCJRdEcjyvK+IIDYC2xBTzmjL3ivxbMWwHFKQ37FjOPssCm3TVR3ALChCe1CjLeaJ1S09b4TbFjSvlXXtpej1QQ+ikM6ow8Bo+wjtt+AJfTnHnRN21L//Ys6CfA+/u6Fl35Vb3wU/oJfBn/W9PgzPgr++gLCMjaYKCCEVxcQ+talA8JpNvHpA0I4TkBo1HcJAH7xgBBcIiCEHQ5J+ZOkx59wRtAmzI31Kp2JU8c96HAm3iV9ifO+y39kNBFQJxLo3U6/qQOc8fpao4iEG1FeimWS+/mtXESyRNEn3RCTIFA7DE7JD7TIRUngazMWcuHtDN5LWWJTSdX+MqEa62CwDdhSo9+hRmssNYI2Eq41JBCVMVM9I3ns93blkb212XU0bii8wNQXtMDRo8CIhKxoWlDOaSwhUSQ35ZoHKF2Xum/hc0/JTahyaRxSViLHjbNQZpznOMJLsfcKvjlaCOyhJZ+n2zhGchLPOYqnAitsgFVE9C2wwg6wwtHi92nA+pOh1+jJWWtg4HYytE9TmtUbi8zc22AG7zjaztz7nrjkWsAZ0ZAsn0PEcSrKbwlK02eBSiV4Eni2UiDAHQRP2xlL0+608Dzu7uScSHSGInFSIPaHrCua++xKcc5/W1o03Kig8ZNgMK0kqxqLOPYryki8jUUVxRJFm0WaHMhpCcQnsZysxH+Msj3slzGymlBPmHxS6gthb7Xsgqaz9PBiNQ403WGHldEcpzVJ4uuMCANDkxTjZJubO+3A1MLZ0ssDglghhiTpgOM7ShP1pcGKZFK7TYQY+XMeHFjNCBK2gWBd9LjXsXDXA4TzJ9csf+7bP2W7bVmwLUuQnEY0MfZNS/uq5fMm2cp5BviV5KemThXn8eN4yZVesOgve3TnWWna+8o/YKq90Lox5o4NzZo+dO1Eo7kBcG47RvXUBzHmvufvPZ5dH4CuVikeRff2JOCd+vp0nLS7Z7Rc2hyYBrBtz3Kh54BBqD42d+56x+XO3+EfJ3duX1FK0Bp8TWhNdfgBg+8ERzHklokMyy8ebbnNWx8bHrbcBj88bOjAdQ+wj2Tn7fPgn1uufGmK+JZpZ2qsMA7k96C/hl9V8O71q2Jr9626Mm7O41c9Z97cVsfzlaB9A1gq92lPuec8Wl8k69W6UADtI4XZdaZoxr1nO1PYkxyuT/yqxJ3MXXgD3cXJgc9pB0WvHz8x2ah814fLSzmNr2+9NnbcDujYo0HHbC3i9UNnMuT4HwM5/gHkFJniD48c054aOu1ESLnMitZc5It+XXKGJW+F3B3Xz11Z9J9IHopq9VMlFYJVPwcDD/8D</diagram></mxfile>
|
@@ -120,19 +120,7 @@ are used to set the resistance calibration internally. For a guide on how to cal
|
||||
|
||||
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 -> OFFSET -> SENS -> OUT
|
||||
}
|
||||
.. drawio-image:: calibration.drawio
|
||||
|
||||
The final calibrated PT1000 resistance is calculated as:
|
||||
|
||||
@@ -141,11 +129,11 @@ The final calibrated PT1000 resistance is calculated as:
|
||||
|
||||
The default values, if no calibration is loaded / executed, are:
|
||||
|
||||
============== =========
|
||||
:math:`\sigma` :math:`O`
|
||||
============== =========
|
||||
0 0
|
||||
============== =========
|
||||
======== =====================
|
||||
Offset Sensitivity Deviation
|
||||
======== =====================
|
||||
0 0
|
||||
======== =====================
|
||||
|
||||
Get Calibration Corrected Value
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@@ -21,4 +21,5 @@ which are used to retain boot information across resets, for example to communic
|
||||
flags
|
||||
backup-ram
|
||||
error-handling
|
||||
startup-tests
|
||||
stack-checking
|
||||
|
23
doc/source/firmware/safety/startup-tests.rst
Normal file
23
doc/source/firmware/safety/startup-tests.rst
Normal file
@@ -0,0 +1,23 @@
|
||||
.. _safety_startup_tests:
|
||||
|
||||
Startup Tests
|
||||
=============
|
||||
|
||||
The following tests will be performed after each reset / power cycle of the controller.
|
||||
|
||||
Internal Memory Test
|
||||
--------------------
|
||||
Directly after startup and after setting up the system clocks, the controller performs memory checks on the internal SRAM and the core coupled memory.
|
||||
|
||||
RAM checking is performed by testing the following sequences:
|
||||
|
||||
- Alternating 0x55AA55AA / 0xAA55AA55 pattern
|
||||
- Alternating 0xAA55AA55 / 0x55AA55AA pattern
|
||||
- 0xFFFFFFFF constant value
|
||||
- 0x00000000 constant value
|
||||
|
||||
The following functions implement the RAM checking:
|
||||
|
||||
.. doxygenfunction:: startup_test_perform_ccm_ram_check
|
||||
|
||||
.. doxygenfunction:: startup_test_perform_system_ram_check
|
27
doc/source/hardware/hardware-watchdog.rst
Normal file
27
doc/source/hardware/hardware-watchdog.rst
Normal file
@@ -0,0 +1,27 @@
|
||||
.. _hw_watchdog:
|
||||
|
||||
Hardware Watchdog
|
||||
=================
|
||||
|
||||
the PCB (> v1.3) is equipped with an external power supply monitor and hardware watchdog IC ``STM6822S``.
|
||||
The watchdog is periodically reset by the firmware.
|
||||
|
||||
.. note:: The external watchdog is only activated in Release mode. When building the application in Debug mode, it is deactivated because setting breakpoints or other events that distrb the program flow would trigger a the external watchdog and force a software reset.
|
||||
|
||||
|
||||
During startup of the power supply, it keeps the controller in reset until the power supply is stable.
|
||||
The following picture shows the 3.3 Volt supply rail on channel 2 and the low active reset signal of the STM32F407 on channel 1.
|
||||
|
||||
.. image:: hw_watchdog_delay.png
|
||||
:alt: Image not found
|
||||
|
||||
The total startup delay is larger than 200 ms.
|
||||
|
||||
The follwoing figure shows the startup of the voltage rgulator and the reset signal.
|
||||
|
||||
.. image:: hw_watchdog_startup.png
|
||||
:alt: Image not found
|
||||
|
||||
The power supply is gradually ramped up over approximately 500 us. The external watchdog does not work fpr voltages below 0.9 Volt. Therefore the reset signal rises simultaneously.
|
||||
Once the supply voltage exceeds 0.9 Volts, the reset signal is pulled low. This ensures that the controller is not able to start until the > 200 ms startup time have passed.
|
||||
|
@@ -3,7 +3,7 @@
|
||||
Hardware
|
||||
========
|
||||
|
||||
This guide on the reflow controller's hardware is based on the ``reflow-oven-control-pcb`` -- Version ``v1.2``
|
||||
This guide on the reflow controller's hardware is based on the ``reflow-oven-control-pcb`` -- Version ``v1.3``
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
@@ -1,7 +1,7 @@
|
||||
.. _hardware_modifications:
|
||||
.. _hardware_modifications_v1_2:
|
||||
|
||||
Hardware Modifications
|
||||
======================
|
||||
Hardware Modifications for PCB v1.2
|
||||
===================================
|
||||
|
||||
Analog Frontend
|
||||
---------------
|
||||
@@ -13,3 +13,11 @@ 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.
|
||||
|
||||
.. _hardware_modifications_v1_3:
|
||||
|
||||
Hardware Modifications for PCB v1.3
|
||||
===================================
|
||||
|
||||
.. note::
|
||||
solder jumpers to bridge GND and Earth together must be soldered for the SD card slot to work properly. The card detect's switch terminal is on Earth potential.
|
@@ -10,7 +10,8 @@ Quick Links
|
||||
===========
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`hardware_modifications`
|
||||
* :ref:`hardware_modifications_v1_2`
|
||||
* :ref:`hardware_modifications_v1_3`
|
||||
|
||||
.. 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.
|
||||
|
@@ -9,8 +9,36 @@ 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_>`_
|
||||
- ``SHA-256 Library`` by ``Alain Mosnier`` <https://github.com/amosnier/sha-2>, licensed under ``Zero Clause BSD License`` or the ``Unlicense``
|
||||
- ``FatFS`` Library by ``ChaN`` licensed under a `custom license <FatFS License_>`_
|
||||
|
||||
|
||||
FatFS License
|
||||
-------------
|
||||
|
||||
FatFs has being developped as a personal project of the author, ChaN. It is free from the code anyone else wrote at current release. Following code block shows a copy of the FatFs license document that heading the source files.::
|
||||
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT Filesystem Module Rx.xx /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 20xx, ChaN, all right reserved.
|
||||
/
|
||||
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||
/ source and binary forms, with or without modification, are permitted provided
|
||||
/ that the following condition is met:
|
||||
/
|
||||
/ 1. Redistributions of source code must retain the above copyright notice,
|
||||
/ this condition and the following disclaimer.
|
||||
/
|
||||
/ This software is provided by the copyright holder and contributors "AS IS"
|
||||
/ and any warranties related to this software are DISCLAIMED.
|
||||
/ The copyright owner or contributors be NOT LIABLE for any damages caused
|
||||
/ by use of this software.
|
||||
/----------------------------------------------------------------------------*/
|
||||
|
||||
Therefore FatFs license is one of the BSD-style licenses, but there is a significant feature. FatFs is mainly intended for embedded systems. In order to extend the usability for commercial products, the redistributions of FatFs in binary form, such as embedded code, binary library and any forms without source code, do not need to include about FatFs in the documentations. This is equivalent to the 1-clause BSD license. Of course FatFs is compatible with the most of open source software licenses include GNU GPL. When you redistribute the FatFs source code with changes or create a fork, the license can also be changed to GNU GPL, BSD-style license or any open source software license that not conflict with FatFs license.
|
||||
|
||||
ST License for Used Header Files
|
||||
--------------------------------
|
||||
|
||||
|
15
doc/source/reflow-profiles/reflow-245.tpr
Normal file
15
doc/source/reflow-profiles/reflow-245.tpr
Normal file
@@ -0,0 +1,15 @@
|
||||
# This is a comment
|
||||
pid_conf 10 0.3 5 60 2.5 0.5
|
||||
temp_set 50
|
||||
wait_temp 45
|
||||
temp_set 45
|
||||
temp_ramp 150 120
|
||||
temp_ramp 190 90
|
||||
pid_conf 9 0.5 6 60 2.5 0.5
|
||||
temp_set 250
|
||||
wait_temp 245
|
||||
temp_off
|
||||
beep 1
|
||||
wait_time 1
|
||||
beep 0
|
||||
|
@@ -21,6 +21,7 @@ struct safety_memory_header {
|
||||
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 firmware_update_filename; /**< @brief Filename of the firmware update. This string is at maximum 256 bytes long including the 0 terminator */
|
||||
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 */
|
||||
|
@@ -1,12 +1,11 @@
|
||||
#include <error-mem-viewer/crc.h>
|
||||
|
||||
uint32_t do_crc(uint32_t init, uint32_t data)
|
||||
static uint32_t do_crc(uint32_t init, uint32_t data)
|
||||
{
|
||||
uint32_t crc = init;
|
||||
uint32_t cnt;
|
||||
|
||||
for(cnt=0; cnt < 32; cnt++)
|
||||
{
|
||||
for (cnt=0; cnt < 32; cnt++) {
|
||||
crc = ((int32_t)(crc ^ data))<0 ? (crc << 1) ^ 0x04C11DB7 : crc << 1;
|
||||
data <<=1;
|
||||
}
|
||||
@@ -18,7 +17,7 @@ uint32_t calculate_stm_crc(uint32_t *data, size_t len)
|
||||
{
|
||||
uint32_t crc = ~0U;
|
||||
|
||||
while(len--) {
|
||||
while (len--) {
|
||||
crc = do_crc(crc, *data++);
|
||||
}
|
||||
|
||||
|
@@ -214,16 +214,21 @@ static void show_error_memory(GtkTreeView *tree_view, const unsigned char *memor
|
||||
valid = true;
|
||||
break;
|
||||
case 4:
|
||||
header.firmware_update_filename = dat;
|
||||
interpret = new_string_printf("Offset of FW update filename: %u", dat);
|
||||
valid = true;
|
||||
break;
|
||||
case 5:
|
||||
header.err_memory_offset = dat;
|
||||
interpret = new_string_printf("Error memory offset addr: %u", dat);
|
||||
valid = true;
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
header.err_memory_end = dat;
|
||||
interpret = new_string_printf("Error memory end ptr: %u", dat);
|
||||
valid = true;
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
header.crc = dat;
|
||||
valid = check_err_mem_header(&header, &expected_value);
|
||||
if (valid) {
|
||||
@@ -235,7 +240,7 @@ static void show_error_memory(GtkTreeView *tree_view, const unsigned char *memor
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (i <= 6) {
|
||||
if (i <= 7) {
|
||||
state = ENTRY_STATE_INVALID;
|
||||
goto print;
|
||||
}
|
||||
|
@@ -422,6 +422,13 @@
|
||||
"print(calc_temp(2000))\n",
|
||||
"print(calc_temp(2000+adc_min_res))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
@@ -440,7 +447,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.1"
|
||||
"version": "3.9.6"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
475
measurement-data/FilterAnalysis.ipynb
Normal file
475
measurement-data/FilterAnalysis.ipynb
Normal file
@@ -0,0 +1,475 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "7c270395",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import numpy as np\n",
|
||||
"from scipy import signal\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"import pandas as pd\n",
|
||||
"from scipy.fft import fft, fftfreq"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "b09956cf",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "348b4663",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Filter comparison"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "eef8fc32",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"alpha = 0.01\n",
|
||||
"mavg_b = [alpha]\n",
|
||||
"mavg_a = [1, -(1-alpha)]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "06cc8d91",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Sinc filter\n",
|
||||
"b_sinc = [\n",
|
||||
" 0.013166773445594984,\n",
|
||||
" 0.015510026576574206,\n",
|
||||
" 0.017943762856303655,\n",
|
||||
" 0.020436419999452039,\n",
|
||||
" 0.022953654956480787,\n",
|
||||
" 0.025459023223972144,\n",
|
||||
" 0.027914730737546672,\n",
|
||||
" 0.030282439536854874,\n",
|
||||
" 0.032524106530629059,\n",
|
||||
" 0.034602833419516990,\n",
|
||||
" 0.036483705201686277,\n",
|
||||
" 0.038134594717421900,\n",
|
||||
" 0.039526911389630152,\n",
|
||||
" 0.040636273671486346,\n",
|
||||
" 0.041443086683647982,\n",
|
||||
" 0.041933009054862178,\n",
|
||||
" 0.042097295996679322,\n",
|
||||
" 0.041933009054862178,\n",
|
||||
" 0.041443086683647982,\n",
|
||||
" 0.040636273671486346,\n",
|
||||
" 0.039526911389630152,\n",
|
||||
" 0.038134594717421900,\n",
|
||||
" 0.036483705201686277,\n",
|
||||
" 0.034602833419516990,\n",
|
||||
" 0.032524106530629059,\n",
|
||||
" 0.030282439536854874,\n",
|
||||
" 0.027914730737546672,\n",
|
||||
" 0.025459023223972144,\n",
|
||||
" 0.022953654956480787,\n",
|
||||
" 0.020436419999452039,\n",
|
||||
" 0.017943762856303655,\n",
|
||||
" 0.015510026576574206,\n",
|
||||
" 0.013166773445594984,\n",
|
||||
"]\n",
|
||||
"b_sinc = [\n",
|
||||
" -0.000005301919181359,\n",
|
||||
" -0.000020372384569462,\n",
|
||||
" -0.000043393375246200,\n",
|
||||
" -0.000071643508460196,\n",
|
||||
" -0.000101272447699964,\n",
|
||||
" -0.000127045709202358,\n",
|
||||
" -0.000142084479366378,\n",
|
||||
" -0.000137630058679112,\n",
|
||||
" -0.000102865422361326,\n",
|
||||
" -0.000024826753571648,\n",
|
||||
" 0.000111564545505229,\n",
|
||||
" 0.000323323938002668,\n",
|
||||
" 0.000629062120588115,\n",
|
||||
" 0.001048471626315150,\n",
|
||||
" 0.001601644955843253,\n",
|
||||
" 0.002308239744151386,\n",
|
||||
" 0.003186518744650963,\n",
|
||||
" 0.004252303894058680,\n",
|
||||
" 0.005517893567100900,\n",
|
||||
" 0.006990999568786174,\n",
|
||||
" 0.008673764799041549,\n",
|
||||
" 0.010561923390649710,\n",
|
||||
" 0.012644162210467481,\n",
|
||||
" 0.014901735907231652,\n",
|
||||
" 0.017308377414832196,\n",
|
||||
" 0.019830532444994709,\n",
|
||||
" 0.022427930710902887,\n",
|
||||
" 0.025054489273884574,\n",
|
||||
" 0.027659525485854094,\n",
|
||||
" 0.030189239563895277,\n",
|
||||
" 0.032588410933658420,\n",
|
||||
" 0.034802239101869442,\n",
|
||||
" 0.036778249821351520,\n",
|
||||
" 0.038468181363671021,\n",
|
||||
" 0.039829764251821352,\n",
|
||||
" 0.040828311002284692,\n",
|
||||
" 0.041438040179150003,\n",
|
||||
" 0.041643070995549647,\n",
|
||||
" 0.041438040179150003,\n",
|
||||
" 0.040828311002284692,\n",
|
||||
" 0.039829764251821366,\n",
|
||||
" 0.038468181363671021,\n",
|
||||
" 0.036778249821351520,\n",
|
||||
" 0.034802239101869456,\n",
|
||||
" 0.032588410933658420,\n",
|
||||
" 0.030189239563895291,\n",
|
||||
" 0.027659525485854094,\n",
|
||||
" 0.025054489273884584,\n",
|
||||
" 0.022427930710902898,\n",
|
||||
" 0.019830532444994709,\n",
|
||||
" 0.017308377414832207,\n",
|
||||
" 0.014901735907231647,\n",
|
||||
" 0.012644162210467488,\n",
|
||||
" 0.010561923390649715,\n",
|
||||
" 0.008673764799041547,\n",
|
||||
" 0.006990999568786178,\n",
|
||||
" 0.005517893567100902,\n",
|
||||
" 0.004252303894058680,\n",
|
||||
" 0.003186518744650965,\n",
|
||||
" 0.002308239744151388,\n",
|
||||
" 0.001601644955843254,\n",
|
||||
" 0.001048471626315152,\n",
|
||||
" 0.000629062120588114,\n",
|
||||
" 0.000323323938002668,\n",
|
||||
" 0.000111564545505229,\n",
|
||||
" -0.000024826753571648,\n",
|
||||
" -0.000102865422361327,\n",
|
||||
" -0.000137630058679112,\n",
|
||||
" -0.000142084479366378,\n",
|
||||
" -0.000127045709202359,\n",
|
||||
" -0.000101272447699963,\n",
|
||||
" -0.000071643508460196,\n",
|
||||
" -0.000043393375246200,\n",
|
||||
" -0.000020372384569461,\n",
|
||||
" -0.000005301919181359,\n",
|
||||
"]\n",
|
||||
"b_sinc = np.around([k * 56536 for k in b_sinc])/65536\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def combined_sinc_mavg(alpha = 0.4):\n",
|
||||
" b1 = [alpha]\n",
|
||||
" b2 = b_sinc\n",
|
||||
" a1 = [1, -(1-alpha)]\n",
|
||||
" a2 = [1]\n",
|
||||
" \n",
|
||||
" b = np.convolve(b1, b2)\n",
|
||||
" a = np.convolve(a1, a2)\n",
|
||||
" return b,a\n",
|
||||
"\n",
|
||||
"def iir_notch(freq, r, fsa):\n",
|
||||
" b = [1, -2*np.cos(freq/fsa*2*np.pi), 1]\n",
|
||||
" a = [1, -2*r*np.cos(freq/fsa*2*np.pi), r*r]\n",
|
||||
" return b,a"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "299fc59e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "0af3fa7b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def plot_transfer_func(b, a, fsa = 1):\n",
|
||||
" omega, vals = signal.freqz(b, a, worN = 1024)\n",
|
||||
" plt.plot(omega/(2*np.pi)*fsa, abs(vals))\n",
|
||||
" \n",
|
||||
"sample_rate = 1e3/6\n",
|
||||
"plt.figure(figsize=(16,8))\n",
|
||||
"plt.yscale('log')\n",
|
||||
"plot_transfer_func(mavg_b, mavg_a, sample_rate)\n",
|
||||
"plt.ylabel('|H(w)| [dB]')\n",
|
||||
"plt.xlabel('Frequency [Hz]')\n",
|
||||
"\n",
|
||||
"plot_transfer_func(b_sinc, [1], sample_rate)\n",
|
||||
"\n",
|
||||
"# Combined filter\n",
|
||||
"b,a = combined_sinc_mavg(alpha = 1)\n",
|
||||
"b_notch, a_notch = iir_notch(50, 0.875, sample_rate)\n",
|
||||
"b = np.convolve(b, b_notch)\n",
|
||||
"a = np.convolve(a, a_notch)\n",
|
||||
"plot_transfer_func(b,a, sample_rate)\n",
|
||||
"plt.grid()\n",
|
||||
"\n",
|
||||
"plt.legend(['MAVG a = 0.01', 'SINC', 'SINC+MAVG'])\n",
|
||||
"plt.xlim(0, 70)\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c9b87b82",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Notch filters (Not used for implementation)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "c360b127",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"plt.figure(figsize=(16,8))\n",
|
||||
"plt.yscale('log')\n",
|
||||
"a = [1]\n",
|
||||
"b = [1, -2*np.cos(50/sample_rate*2*np.pi), 1]\n",
|
||||
"plot_transfer_func(b,a, sample_rate)\n",
|
||||
"\n",
|
||||
"b = [1, -2*np.cos(60/sample_rate*2*np.pi), 1]\n",
|
||||
"plot_transfer_func(b,a, sample_rate)\n",
|
||||
"\n",
|
||||
"r = 0.875\n",
|
||||
"b,a = iir_notch(50, r, sample_rate)\n",
|
||||
"plot_transfer_func(b,a, sample_rate)\n",
|
||||
"\n",
|
||||
"b,a = iir_notch(60, r, sample_rate)\n",
|
||||
"plot_transfer_func(b,a, sample_rate)\n",
|
||||
"plt.xlim(40,70)\n",
|
||||
"plt.legend(['FIR 50 Hz Notch', 'FIR 60 Hz', 'IIR 50Hz', 'IIR 60Hz'])\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "891bf909",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Singal testing"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "29277520",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"N = 30000\n",
|
||||
"time = np.linspace(0, 1/sample_rate*N, N, endpoint=False)\n",
|
||||
"sig = 10*signal.sawtooth(2*np.pi*0.2*time)+10 #+ 0.5 * np.sin(2*np.pi*50*time)+0.8 * np.sin(2*np.pi*80*time)\n",
|
||||
"\n",
|
||||
"plt.figure(figsize=(16,8))\n",
|
||||
"\n",
|
||||
"spur_len = 3\n",
|
||||
"\n",
|
||||
"sig[1000:1000+spur_len] = sig[1000]+5\n",
|
||||
"sig[2000:2000+spur_len] = sig[2000]-6\n",
|
||||
"sig[3000:3000+spur_len] = sig[3000]+2\n",
|
||||
"sig[4000:4000+spur_len] = sig[4000]+8\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"plt.plot(time, sig)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Apply the combined filter:\n",
|
||||
"b,a = combined_sinc_mavg(alpha = 1)\n",
|
||||
"\n",
|
||||
"bn,an = iir_notch(50, 0.875, sample_rate)\n",
|
||||
"a = np.convolve(a, an)\n",
|
||||
"b = np.convolve(b, bn)\n",
|
||||
"\n",
|
||||
"#b,a = mavg_b, mavg_a\n",
|
||||
"\n",
|
||||
"w,h = signal.freqz(b,a)\n",
|
||||
"gain_corr = round(1/abs(h[0])*65536)/65536\n",
|
||||
"\n",
|
||||
"sig_f = signal.lfilter(b, a, sig) * gain_corr\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"print('Gain correction:', gain_corr)\n",
|
||||
"\n",
|
||||
"plt.plot(time, sig_f)\n",
|
||||
"plt.xlim(5,10)\n",
|
||||
"plt.show()\n",
|
||||
"\n",
|
||||
"plt.figure(figsize=(16,8))\n",
|
||||
"plt.plot(time, abs(sig-sig_f))\n",
|
||||
"plt.xlim(5,10)\n",
|
||||
"plt.ylim(0,2)\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "13758640",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"\n",
|
||||
"window = signal.windows.hamming(N)\n",
|
||||
"\n",
|
||||
"h_sig = fft(sig*window)\n",
|
||||
"f_sig = fftfreq(N, 1/sample_rate)[:N//2]\n",
|
||||
"\n",
|
||||
"h_sig_f = fft(sig_f*window)\n",
|
||||
"f_sig_f = fftfreq(N, 1/sample_rate)[:N//2]\n",
|
||||
"\n",
|
||||
"plt.figure(figsize=(16,10))\n",
|
||||
"plt.yscale('log')\n",
|
||||
"plt.plot(f_sig, 2.0/N*abs(h_sig[0:N//2]))\n",
|
||||
"plt.plot(f_sig_f, 2.0/N*abs(h_sig_f[0:N//2]))\n",
|
||||
"plt.show();"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "f4b81600",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# PT1000 HF Filtering"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "a87370ee",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"raw_data = pd.read_csv(r'pt1000_hf_2kOhm_v1.3.dat')\n",
|
||||
"raw_data = pd.read_csv(r'pt1000_hf_changing.dat')\n",
|
||||
"time = np.linspace(0, 2000*6e-3, 2000, endpoint=False)\n",
|
||||
"\n",
|
||||
"plt.figure(figsize=(22,12))\n",
|
||||
"plt.plot(time, raw_data['hf_value']*2500/4096)\n",
|
||||
"\n",
|
||||
"alpha = 0.005\n",
|
||||
"\n",
|
||||
"mavg_b = [alpha]\n",
|
||||
"mavg_a = [1, -(1-alpha)]\n",
|
||||
"\n",
|
||||
"zi = signal.lfilter_zi(mavg_b, mavg_a)\n",
|
||||
"filtered, _ = signal.lfilter(mavg_b, mavg_a, raw_data['hf_value'], zi=zi*raw_data['hf_value'][0])\n",
|
||||
"plt.plot(time, filtered*2500/4096)\n",
|
||||
"filtered_avg_low = filtered\n",
|
||||
"\n",
|
||||
"alpha = 0.01\n",
|
||||
"mavg_b = [alpha]\n",
|
||||
"mavg_a = [1, -(1-alpha)]\n",
|
||||
"\n",
|
||||
"zi = signal.lfilter_zi(mavg_b, mavg_a)\n",
|
||||
"filtered, _ = signal.lfilter(mavg_b, mavg_a, raw_data['hf_value'], zi=zi*raw_data['hf_value'][0])\n",
|
||||
"plt.plot(time, filtered*2500/4096)\n",
|
||||
"filtered_avg = filtered\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Apply the combined filter:\n",
|
||||
"b,a = combined_sinc_mavg(alpha = 0.08)\n",
|
||||
"\n",
|
||||
"bn,an = iir_notch(4, 0.75, sample_rate)\n",
|
||||
"a = np.convolve(a, an)\n",
|
||||
"b = np.convolve(b, bn)\n",
|
||||
"\n",
|
||||
"zi = signal.lfilter_zi(b, a)\n",
|
||||
"filtered, _ = signal.lfilter(b, a, raw_data['hf_value'], zi=zi*raw_data['hf_value'][0])\n",
|
||||
"\n",
|
||||
"w,h = signal.freqz(b,a)\n",
|
||||
"gain_corr = round(1/abs(h[0])*65536)/65536\n",
|
||||
"\n",
|
||||
"plt.plot(time, filtered*gain_corr*2500/4096)\n",
|
||||
"plt.savefig('expl.pdf', format='pdf', dpi=1200)\n",
|
||||
"plt.show()\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "3eae0e71",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Spectrum"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "8d082b99",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"window =signal.windows.hamming(len(raw_data['hf_value']))\n",
|
||||
"raw_fft = fft(np.array(raw_data['hf_value'].to_list())*window)\n",
|
||||
"f_raw = fftfreq(len(raw_data), 1/sample_rate)[:len(raw_data)//2]\n",
|
||||
"plt.figure(figsize=(16,10));\n",
|
||||
"plt.yscale('log')\n",
|
||||
"plt.plot(f_raw, abs(raw_fft[:len(raw_data)//2]))\n",
|
||||
"\n",
|
||||
"avg_fft = fft(np.array(filtered_avg)*window)\n",
|
||||
"plt.plot(f_raw, abs(avg_fft[:len(raw_data)//2]))\n",
|
||||
"\n",
|
||||
"avg_fft = fft(np.array(filtered_avg_low)*window)\n",
|
||||
"plt.plot(f_raw, abs(avg_fft[:len(raw_data)//2]))\n",
|
||||
"\n",
|
||||
"sinc_fft = fft(np.array(filtered)*window)\n",
|
||||
"plt.plot(f_raw, abs(sinc_fft[:len(raw_data)//2]))\n",
|
||||
"plt.xscale('log')\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "e09d63f9",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(np.std(filtered_avg_low/4096*2500))\n",
|
||||
"print(np.std(filtered_avg/4096*2500))\n",
|
||||
"print(np.std(filtered/4096*2500))\n",
|
||||
"\n",
|
||||
"print('Min',min(filtered_avg/4096*2500), 'Max', max(filtered_avg)/4096*2500)\n",
|
||||
"print('Min',min(filtered)/4096*2500*gain_corr, 'Max', max(filtered)/4096*2500*gain_corr)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.6"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
2001
measurement-data/pt1000_hf_2kOhm_v1.3.dat
Normal file
2001
measurement-data/pt1000_hf_2kOhm_v1.3.dat
Normal file
File diff suppressed because it is too large
Load Diff
2001
measurement-data/pt1000_hf_changing.dat
Normal file
2001
measurement-data/pt1000_hf_changing.dat
Normal file
File diff suppressed because it is too large
Load Diff
@@ -27,11 +27,47 @@ if(NOT WIN32)
|
||||
set(BoldWhite "${Esc}[1;37m")
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND bash -c "echo -n $(git describe --always --tags --dirty)"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_DESCRIBE)
|
||||
find_package(Git)
|
||||
if (GIT_FOUND)
|
||||
message("Git found")
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} describe --always --tags --dirty
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_DESCRIBE
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
message("${BoldGreen}Git based version number: ${GIT_DESCRIBE}${ColorReset}")
|
||||
else (GIT_FOUND)
|
||||
set(GIT_DESCRIBE "v0.0.0-unknown")
|
||||
message("${BoldRed}No git installation found. It is highly recommended using git to generate the version number")
|
||||
message("Version is set to: ${GIT_DESCRIBE}${ColorReset}")
|
||||
endif (GIT_FOUND)
|
||||
|
||||
find_program(VIRTUALENV virtualenv)
|
||||
if (VIRTUALENV)
|
||||
message("Python virtual environment found")
|
||||
execute_process(
|
||||
COMMAND ${VIRTUALENV} venv
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
OUTPUT_QUIET
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
set(VENV_BIN "${CMAKE_CURRENT_BINARY_DIR}/venv/bin")
|
||||
execute_process(
|
||||
COMMAND ./pip install -r "${CMAKE_CURRENT_SOURCE_DIR}/crc-patcher/requirements.txt"
|
||||
WORKING_DIRECTORY ${VENV_BIN}
|
||||
OUTPUT_QUIET
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
message("${BoldGreen}python virtual environment set up!${ColorReset}")
|
||||
else(VIRTUALENV)
|
||||
message(FATAL_ERROR "${BoldRed}Python virtual environment not set up: virtualenv: command not found!${ColorReset}")
|
||||
endif (VIRTUALENV)
|
||||
|
||||
|
||||
set(ELFFILE ${PROJECT_NAME}.elf)
|
||||
set(HEXFILE ${PROJECT_NAME}.hex)
|
||||
set(MAPFILE ${PROJECT_NAME}.map)
|
||||
set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/stm32f407vet6_flash.ld)
|
||||
|
||||
add_compile_options(-Wall -Wextra -Wold-style-declaration -Wuninitialized -Wmaybe-uninitialized -Wunused-parameter)
|
||||
@@ -41,23 +77,29 @@ add_compile_options(-mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -m
|
||||
set(GIT_DESCRIBE "${GIT_DESCRIBE}")
|
||||
add_definitions(-DBASE64_LOOKUP_TABLE_SECTION=\".ccm.bss\" -DSHELLMATTA_HELP_ALIAS=\"?\" -DGIT_VER=${GIT_DESCRIBE} -DHSE_VALUE=8000000UL -DSTM32F407xx -DSTM32F4XX -DARM_MATH_CM4)
|
||||
|
||||
add_subdirectory(doxygen)
|
||||
add_subdirectory(updater/ram-code)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
message("${BoldGreen}Version: ${GIT_DESCRIBE}${ColorReset}")
|
||||
|
||||
IF(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
add_definitions(-DDEBUGBUILD)
|
||||
add_compile_options(-O0 -g)
|
||||
add_definitions(-DDEBUGBUILD)
|
||||
add_compile_options(-O0 -g)
|
||||
add_link_options(-Wl,-Map=${MAPFILE})
|
||||
ELSE()
|
||||
add_definitions(-DDEBUGBUILD)
|
||||
add_compile_options(-O3 -g)
|
||||
add_link_options(-Wl,--gc-sections)
|
||||
add_compile_options(-O3 -g)
|
||||
add_link_options(-Wl,--gc-sections)
|
||||
ENDIF(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
|
||||
add_subdirectory(base64-lib)
|
||||
|
||||
if (UART_ON_DEBUG_HEADER)
|
||||
add_definitions(-DUART_ON_DEBUG_HEADER)
|
||||
message("${BoldRed}UART forced to debug header${ColorReset}")
|
||||
endif (UART_ON_DEBUG_HEADER)
|
||||
|
||||
add_subdirectory(base64-lib)
|
||||
add_subdirectory(linklist-lib)
|
||||
|
||||
aux_source_directory("." MAIN_SOURCES)
|
||||
aux_source_directory("config-parser" CFG_PARSER_SRCS)
|
||||
@@ -71,16 +113,38 @@ aux_source_directory("settings" SETTINGS_SRCS)
|
||||
aux_source_directory("safety" SAFETY_SRCS)
|
||||
aux_source_directory("shellmatta/src" SHELLMATTA_SRCS)
|
||||
aux_source_directory("updater" UPDATER_SRCS)
|
||||
aux_source_directory("temp-profile" PROFILE_SRCS)
|
||||
|
||||
add_executable(${ELFFILE} ${MAIN_SOURCES} ${CFG_PARSER_SRCS} ${UI_SRCS}
|
||||
${FAT_SRCS} ${SDIO_SRCS} ${BOOT_SRCS} ${SETUP_SRCS}
|
||||
${STM_PERIPH_SRCS} ${SETTINGS_SRCS} ${SAFETY_SRCS} ${SHELLMATTA_SRCS} ${UPDATER_SRCS}
|
||||
${STM_PERIPH_SRCS} ${SETTINGS_SRCS} ${SAFETY_SRCS}
|
||||
${SHELLMATTA_SRCS} ${UPDATER_SRCS} ${PROFILE_SRCS}
|
||||
)
|
||||
|
||||
add_dependencies(${ELFFILE} updater-ram-code-header-blob)
|
||||
|
||||
target_include_directories(${ELFFILE} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/shellmatta/api ${CMAKE_CURRENT_SOURCE_DIR}/config-parser/include)
|
||||
target_link_options(${ELFFILE} PRIVATE -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -mfloat-abi=hard -mfpu=fpv4-sp-d16 --disable-newlib-supplied-syscalls -nostartfiles -T${LINKER_SCRIPT} -Wl,--print-memory-usage)
|
||||
target_link_libraries(${ELFFILE} base64-lib)
|
||||
target_link_libraries(${ELFFILE} base64-lib linklist-lib)
|
||||
target_include_directories(${ELFFILE} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/updater/ram-code/include/")
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${ELFFILE}
|
||||
POST_BUILD
|
||||
COMMAND ./python "${CMAKE_CURRENT_SOURCE_DIR}/crc-patcher/crc-patch-elf.py" "${CMAKE_CURRENT_BINARY_DIR}/${ELFFILE}"
|
||||
WORKING_DIRECTORY ${VENV_BIN}
|
||||
COMMENT "Running Flash CRC Patcher"
|
||||
)
|
||||
|
||||
set(HEX_PATH "${CMAKE_CURRENT_BINARY_DIR}/${HEXFILE}")
|
||||
add_custom_target(update-image ALL DEPENDS ${HEX_PATH} "${HEX_PATH}.sha")
|
||||
add_custom_command(
|
||||
DEPENDS ${ELFFILE}
|
||||
OUTPUT ${HEX_PATH}
|
||||
COMMAND ${CMAKE_OBJCOPY} -O ihex ${ELFFILE} ${HEX_PATH}
|
||||
)
|
||||
add_custom_command(
|
||||
DEPENDS ${HEX_PATH}
|
||||
OUTPUT "${HEX_PATH}.sha"
|
||||
COMMAND sha256sum "${HEX_PATH}" | cut -d " " -f 1 > "${HEX_PATH}.sha"
|
||||
)
|
||||
|
@@ -82,6 +82,8 @@ static inline void adc_pt1000_disable_adc(void)
|
||||
ADC_PT1000_PERIPH->CR2 &= ~ADC_CR2_ADON;
|
||||
DMA2_Stream0->CR = 0;
|
||||
|
||||
safety_controller_set_crc_monitor(ERR_CRC_MON_MEAS_ADC, SAFETY_CRC_MON_MEAS_ADC_PW);
|
||||
|
||||
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));
|
||||
@@ -181,6 +183,8 @@ void adc_pt1000_setup_meas(void)
|
||||
streaming_flag_ptr = NULL;
|
||||
adc_watchdog_counter = 0UL;
|
||||
stream_buffer = NULL;
|
||||
|
||||
safety_controller_set_crc_monitor(ERR_CRC_MON_MEAS_ADC, SAFETY_CRC_MON_MEAS_ADC_PW);
|
||||
}
|
||||
|
||||
void adc_pt1000_set_moving_average_filter_param(float alpha)
|
||||
@@ -311,7 +315,8 @@ static inline __attribute__((optimize("O3"))) void adc_pt1000_filter(float adc_p
|
||||
stable_sample_counter = 0;
|
||||
alpha = ADC_PT1000_FILTER_WEIGHT_FAST;
|
||||
if (old_state != 1) {
|
||||
safety_controller_report_error_with_key(ERR_FLAG_MEAS_ADC_UNSTABLE, MEAS_ADC_SAFETY_FLAG_KEY);
|
||||
safety_controller_report_error_with_key(ERR_FLAG_MEAS_ADC_UNSTABLE,
|
||||
MEAS_ADC_SAFETY_FLAG_KEY);
|
||||
old_state = 1;
|
||||
}
|
||||
} else {
|
||||
@@ -319,7 +324,8 @@ static inline __attribute__((optimize("O3"))) void adc_pt1000_filter(float adc_p
|
||||
if (old_state != 2) {
|
||||
stable_sample_counter++;
|
||||
if (stable_sample_counter >= ADC_PT1000_FILTER_STABLE_SAMPLE_COUNT) {
|
||||
safety_controller_ack_flag_with_key(ERR_FLAG_MEAS_ADC_UNSTABLE, MEAS_ADC_SAFETY_FLAG_KEY);
|
||||
safety_controller_ack_flag_with_key(ERR_FLAG_MEAS_ADC_UNSTABLE,
|
||||
MEAS_ADC_SAFETY_FLAG_KEY);
|
||||
old_state = 2;
|
||||
}
|
||||
}
|
||||
|
126
stm-firmware/boot/startup-tests.c
Normal file
126
stm-firmware/boot/startup-tests.c
Normal file
@@ -0,0 +1,126 @@
|
||||
#include "startup-tests.h"
|
||||
|
||||
uint32_t startup_test_perform_ccm_ram_check(void)
|
||||
{
|
||||
const void *ccmram_base = (void *)0x10000000UL;
|
||||
const uint32_t ccmram_size = 64U * 1024UL;
|
||||
volatile uint32_t *word_ptr;
|
||||
uint32_t target_val;
|
||||
uint32_t idx;
|
||||
uint32_t ret = 0UL;
|
||||
|
||||
|
||||
/* Perform inversion test with 0x55 and 0xAA, Part 1 */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
word_ptr[idx] = idx & 1 ? 0x55AA55AAUL : 0xAA55AA55UL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
target_val = idx & 1 ? 0x55AA55AAUL : 0xAA55AA55UL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform inversion test with 0x55 and 0xAA, Part 2 */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
word_ptr[idx] = idx & 1 ? 0xAA55AA55UL : 0x55AA55AAUL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
target_val = idx & 1 ? 0xAA55AA55UL : 0x55AA55AAUL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform static test with 0xFF */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
word_ptr[idx] = 0xFFFFFFFFUL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
target_val = 0xFFFFFFFFUL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform static test with 0x00 */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
word_ptr[idx] = 0x0UL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ccmram_base; idx < ccmram_size / 4U; idx++) {
|
||||
target_val = 0x0UL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
exit_ret_address:
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t startup_test_perform_system_ram_check()
|
||||
{
|
||||
const void *ram_base = (void *)0x20000000UL;
|
||||
const uint32_t ram_size = 128U * 1024UL;
|
||||
volatile uint32_t *word_ptr;
|
||||
uint32_t target_val;
|
||||
uint32_t idx;
|
||||
uint32_t ret = 0UL;
|
||||
|
||||
|
||||
/* Perform inversion test with 0x55 and 0xAA, Part 1 */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
word_ptr[idx] = idx & 1 ? 0x55AA55AAUL : 0xAA55AA55UL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
target_val = idx & 1 ? 0x55AA55AAUL : 0xAA55AA55UL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform inversion test with 0x55 and 0xAA, Part 2 */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
word_ptr[idx] = idx & 1 ? 0xAA55AA55UL : 0x55AA55AAUL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
target_val = idx & 1 ? 0xAA55AA55UL : 0x55AA55AAUL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform static test with 0xFF */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
word_ptr[idx] = 0xFFFFFFFFUL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
target_val = 0xFFFFFFFFUL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform static test with 0x00 */
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
word_ptr[idx] = 0x0UL;
|
||||
}
|
||||
for (idx = 0, word_ptr = (volatile uint32_t *)ram_base; idx < ram_size / 4U; idx++) {
|
||||
target_val = 0x0UL;
|
||||
if (target_val != word_ptr[idx]) {
|
||||
ret = (uint32_t)&word_ptr[idx];
|
||||
goto exit_ret_address;
|
||||
}
|
||||
}
|
||||
|
||||
exit_ret_address:
|
||||
return ret;
|
||||
}
|
25
stm-firmware/boot/startup-tests.h
Normal file
25
stm-firmware/boot/startup-tests.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef _STARTUP_TESTS_H_
|
||||
#define _STARTUP_TESTS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Do a RAM check of the CCM RAM.
|
||||
*
|
||||
* Loop over the whole CCM memory area and check it.
|
||||
*
|
||||
* @return 0 if successful. Else the defect address is returned.
|
||||
* @warning This will completely corrupt this memory!
|
||||
* You have to ensure to set it to sane values afterwards!
|
||||
*/
|
||||
uint32_t startup_test_perform_ccm_ram_check(void);
|
||||
|
||||
/**
|
||||
* @brief Do a RAM check of the stnadard SRAM regions
|
||||
* @return 0 if successful. If an error is found, the faulty address is returned
|
||||
* @warning This completely destroys all content in the memory!
|
||||
* @warning Ensure that the stack pointer is moved to a different memory reagion (CCM RAM)!
|
||||
*/
|
||||
uint32_t startup_test_perform_system_ram_check(void);
|
||||
|
||||
#endif /* _STARTUP_TESTS_H_ */
|
@@ -19,6 +19,7 @@
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "startup-tests.h"
|
||||
|
||||
/* C++ library init */
|
||||
# if defined(__cplusplus)
|
||||
@@ -288,7 +289,7 @@ extern unsigned int __ld_eheap;
|
||||
|
||||
#define CPACR (*((volatile uint32_t *)0xE000ED88))
|
||||
|
||||
void Reset_Handler(void) {
|
||||
void __attribute__((noreturn)) Reset_Handler(void) {
|
||||
/* Stack is already initialized by hardware */
|
||||
|
||||
/* The first thing we do here, is to initialize the FPU
|
||||
@@ -297,6 +298,9 @@ void Reset_Handler(void) {
|
||||
*/
|
||||
CPACR |= (0xF << 20);
|
||||
|
||||
/**
|
||||
* Prepare RAM etc for the System Init function. This ensures, the RAM tests execute at max speed.
|
||||
*/
|
||||
|
||||
/* Copy .data section */
|
||||
__init_section(&__ld_load_data, &__ld_sdata, &__ld_edata);
|
||||
@@ -311,6 +315,75 @@ void Reset_Handler(void) {
|
||||
|
||||
/* Set clocks, waitstates, ART operation etc. */
|
||||
SystemInit();
|
||||
|
||||
if (startup_test_perform_ccm_ram_check()) {
|
||||
/* Hang forever in case of an error. We cannot handle this (yet?) */
|
||||
while (1);
|
||||
}
|
||||
|
||||
/* Move the stack and the stack pointer to CCMRAM
|
||||
* This allows us to perform a RAM test on the main RAM.
|
||||
*/
|
||||
/* R2 holds the amount of bytes / words on the stack. */
|
||||
__asm__ __volatile__ (
|
||||
"mov r2, sp\n" /* Move stack pointer to register 2 */
|
||||
"sub r2, %[stacktop], r2\n" /* Subtract stackpointer from top of ram => byte usage */
|
||||
"mov r3, sp\n" /* Init r3 with first word address to copy (stack pointer) */
|
||||
"sub r4, %[ccmtop], r2\n" /* Init r4 with first address to copy to! This will be the new stack pointer! */
|
||||
"mov r5, r4\n" /* R5 will be the new stackpointer after we are finished copying */
|
||||
"copyloop:\n"
|
||||
"cmp r3, %[stacktop]\n" /* Check if we still have word to copy. If not => finish */
|
||||
"beq finish\n"
|
||||
"ldr.w r6, [r3, #0]\n" /* Load word from [r3] and store into [r4] */
|
||||
"str.w r6, [r4, #0]\n"
|
||||
"add r3, #4\n" /* Increment pointers */
|
||||
"add r4, #4\n"
|
||||
"b copyloop\n" /* go back to loop head */
|
||||
"finish:\n"
|
||||
"mov sp, r5\n" /* Set the new stack pointer to the beginning of the copied area */
|
||||
:
|
||||
: [stacktop]"r"(&__ld_top_of_stack), [ccmtop]"r"(0x10000000UL + (64U * 1024UL))
|
||||
: "memory", "r2", "r3", "r4", "r5", "r6");
|
||||
|
||||
if (startup_test_perform_system_ram_check()) {
|
||||
while (1);
|
||||
}
|
||||
|
||||
/* Move the stack back to system ram */
|
||||
__asm__ __volatile__ (
|
||||
"mov r2, sp\n" /* Move stack pointer to register 2 */
|
||||
"sub r2, %[ccmtop], r2\n" /* Subtract stackpointer from top of ccmram => byte usage */
|
||||
"mov r3, sp\n" /* Init r3 with first word address to copy (stack pointer) */
|
||||
"sub r4, %[stacktop], r2\n" /* Init r4 with first address to copy to! This will be the new stack pointer! */
|
||||
"mov r5, r4\n" /* R5 will be the new stackpointer after we are finished copying */
|
||||
"copyloop_2:\n"
|
||||
"cmp r3, %[ccmtop]\n" /* Check if we still have word to copy. If not => finish */
|
||||
"beq finish_2\n"
|
||||
"ldr.w r6, [r3, #0]\n" /* Load word from [r3] and store into [r4] */
|
||||
"str.w r6, [r4, #0]\n"
|
||||
"add r3, #4\n" /* Increment pointers */
|
||||
"add r4, #4\n"
|
||||
"b copyloop_2\n" /* go back to loop head */
|
||||
"finish_2:\n"
|
||||
"mov sp, r5\n" /* Set the new stack pointer to the beginning of the copied area */
|
||||
:
|
||||
: [stacktop]"r"(&__ld_top_of_stack), [ccmtop]"r"(0x10000000UL + (64U * 1024UL))
|
||||
: "memory", "r2", "r3", "r4", "r5", "r6");
|
||||
|
||||
/**
|
||||
* RAM tests destroyed our values. So we have to copy them again...
|
||||
*/
|
||||
|
||||
/* 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);
|
||||
|
||||
/* C++ init function */
|
||||
#if defined(__cplusplus)
|
||||
|
113
stm-firmware/crc-patcher/crc-patch-elf.py
Executable file
113
stm-firmware/crc-patcher/crc-patch-elf.py
Executable file
@@ -0,0 +1,113 @@
|
||||
#!/bin/python
|
||||
|
||||
"""
|
||||
This script patches the CRC checksums into an existing ELF file.
|
||||
|
||||
For this, it searches the follwoing sections:
|
||||
1) .text
|
||||
2) .data
|
||||
3) .ccmdata
|
||||
4) .vectors
|
||||
|
||||
All sections MUST be a multiple of 4 bytes long because the CRC calculation relies on whole 32 bit words.
|
||||
The sections are excrated and the CRC is calculated for each section.
|
||||
|
||||
In the section .flashcrc, the script expects a single struct with the prototype:
|
||||
struct flash_crcs {
|
||||
uint32_t start_magic;
|
||||
uint32_t crc_section_text;
|
||||
uint32_t crc_section_data;
|
||||
uint32_t crc_section_ccm_data;
|
||||
uint32_t crc_section_vectors;
|
||||
uint32_t end_magic;
|
||||
};
|
||||
|
||||
It checks, if the start magic and end magic are set to the appropriate values and then patches in the CRC values of the sections.
|
||||
The magic values checked for are: 0xA8BE53F9 and 0xFFA582FF
|
||||
"""
|
||||
from elftools.elf.elffile import ELFFile
|
||||
from elftools.elf.segments import Segment
|
||||
import elftools.elf.constants as elf_const
|
||||
import sys
|
||||
import crcmod
|
||||
import crcmod.predefined
|
||||
import struct
|
||||
|
||||
crc_calc = crcmod.predefined.mkCrcFun('crc-32-mpeg')
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage:", sys.argv[0], ' <elf file>')
|
||||
sys.exit(-1)
|
||||
|
||||
filename=sys.argv[1]
|
||||
|
||||
def section_calculate_crc(section):
|
||||
data = bytearray(section.data())
|
||||
be_data = bytearray([0 for k in range(0, len(data))])
|
||||
# Rearrange data, because the STM controller sees it as 32 bit little endian words
|
||||
for i in range(0, int(len(data)/4)):
|
||||
be_data[i*4+0] = data[i*4+3]
|
||||
be_data[i*4+1] = data[i*4+2]
|
||||
be_data[i*4+2] = data[i*4+1]
|
||||
be_data[i*4+3] = data[i*4+0]
|
||||
|
||||
return crc_calc(be_data)
|
||||
|
||||
with open(filename, 'r+b') as f:
|
||||
elf = ELFFile(f)
|
||||
sections = {}
|
||||
sections['.text'] = elf.get_section_by_name('.text')
|
||||
sections['.data'] = elf.get_section_by_name('.data')
|
||||
sections['.ccmdata'] = elf.get_section_by_name('.ccmdata')
|
||||
sections['.vectors'] = elf.get_section_by_name('.vectors')
|
||||
|
||||
for key, sec in sections.items():
|
||||
if sec is None:
|
||||
print("Error! Section", key, "not found in ELF file!")
|
||||
sys.exit(-1)
|
||||
print('Found section', key, 'Size:',
|
||||
sec.data_size, 'Type:', sec['sh_type'])
|
||||
if sec['sh_type'] != 'SHT_PROGBITS':
|
||||
print('Error! Section must be of type SHT_PROGBITS')
|
||||
sys.exit(-1)
|
||||
if (sec.data_size % 4 != 0):
|
||||
print("Section", key, "has wrong size. Must be a multiple of 4 bytes!")
|
||||
sys.exit(-1)
|
||||
|
||||
text_crc = section_calculate_crc(sections['.text'])
|
||||
print('CRC of .text section:', hex(text_crc))
|
||||
data_crc = section_calculate_crc(sections['.data'])
|
||||
print('CRC of .data section:', hex(data_crc))
|
||||
ccmdata_crc = section_calculate_crc(sections['.ccmdata'])
|
||||
print('CRC of .ccmdata section:', hex(ccmdata_crc))
|
||||
vextors_crc = section_calculate_crc(sections['.vectors'])
|
||||
print('CRC of .vectors section:', hex(vextors_crc))
|
||||
|
||||
# Check the flashcrc section
|
||||
flashcrc_sec = elf.get_section_by_name('.flashcrc')
|
||||
if flashcrc_sec is None:
|
||||
print('Section for flash CRC missing!')
|
||||
sys.exit(-1)
|
||||
if flashcrc_sec.data_size != 6*4:
|
||||
print("Warning!!! .flashcrc section has wrong size:",flashcrc_sec.data_size)
|
||||
|
||||
crc_sec_data = bytearray(flashcrc_sec.data())
|
||||
magic1 = struct.unpack('<I'*1, bytes(crc_sec_data[0:4]))[0]
|
||||
magic2 = struct.unpack('<I'*1, bytes(crc_sec_data[-4:]))[0]
|
||||
print("CRC section magic values:", hex(magic1), hex(magic2))
|
||||
if magic1 != 0xA8BE53F9 or magic2 != 0xFFA582FF:
|
||||
print("Wrong magics in CRC section. Data misalignment?")
|
||||
sys.exit(-2)
|
||||
|
||||
crc_sec_offset = flashcrc_sec['sh_offset']
|
||||
print('CRC section ELF file offset:', hex(crc_sec_offset))
|
||||
|
||||
crc_sec_data[4:8] = struct.pack('<I',text_crc)
|
||||
crc_sec_data[8:12] = struct.pack('<I',data_crc)
|
||||
crc_sec_data[12:16] = struct.pack('<I',ccmdata_crc)
|
||||
crc_sec_data[16:20] = struct.pack('<I',vextors_crc)
|
||||
f.seek(crc_sec_offset)
|
||||
f.write(crc_sec_data)
|
||||
print('CRCs patched successfully')
|
||||
|
||||
|
2
stm-firmware/crc-patcher/requirements.txt
Normal file
2
stm-firmware/crc-patcher/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
crcmod==1.7
|
||||
pyelftools==0.27
|
@@ -172,11 +172,10 @@ void loudspeaker_set(uint16_t val)
|
||||
{
|
||||
loudspeaker_val = val;
|
||||
|
||||
if (!val) {
|
||||
if (!val)
|
||||
loudspeaker_stop_beep();
|
||||
} else {
|
||||
else
|
||||
loudspeaker_start_beep(val);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t loudspeaker_get(void)
|
||||
|
40
stm-firmware/doxygen/CMakeLists.txt
Normal file
40
stm-firmware/doxygen/CMakeLists.txt
Normal file
@@ -0,0 +1,40 @@
|
||||
find_package(Doxygen)
|
||||
|
||||
if(NOT WIN32)
|
||||
string(ASCII 27 Esc)
|
||||
set(ColorReset "${Esc}[m")
|
||||
set(ColorBold "${Esc}[1m")
|
||||
set(Red "${Esc}[31m")
|
||||
set(Green "${Esc}[32m")
|
||||
set(Yellow "${Esc}[33m")
|
||||
set(Blue "${Esc}[34m")
|
||||
set(Magenta "${Esc}[35m")
|
||||
set(Cyan "${Esc}[36m")
|
||||
set(White "${Esc}[37m")
|
||||
set(BoldRed "${Esc}[1;31m")
|
||||
set(BoldGreen "${Esc}[1;32m")
|
||||
set(BoldYellow "${Esc}[1;33m")
|
||||
set(BoldBlue "${Esc}[1;34m")
|
||||
set(BoldMagenta "${Esc}[1;35m")
|
||||
set(BoldCyan "${Esc}[1;36m")
|
||||
set(BoldWhite "${Esc}[1;37m")
|
||||
endif()
|
||||
|
||||
if (DOXYGEN_FOUND)
|
||||
add_custom_target(documentation DEPENDS doxygen doxygen-pdf)
|
||||
|
||||
add_custom_target(doxygen
|
||||
COMMAND ./build-doxygen.sh "${PROJECT_BINARY_DIR}"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMENT "Generating documentation with Doxygen")
|
||||
|
||||
add_custom_target(
|
||||
doxygen-pdf
|
||||
COMMAND make
|
||||
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/latex"
|
||||
DEPENDS doxygen
|
||||
)
|
||||
else (DOXYGEN_FOUND)
|
||||
message("${BoldMagenta}Doxygen needs to be installed to generate the doxygen documentation${ColorReset}")
|
||||
message("${BoldMagenta}doxygen, documentation, doxygen-latex targets will not be available${ColorReset}")
|
||||
endif (DOXYGEN_FOUND)
|
@@ -1,4 +1,4 @@
|
||||
# Doxyfile 1.8.20
|
||||
# Doxyfile 1.9.2
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project.
|
||||
@@ -93,14 +93,6 @@ ALLOW_UNICODE_NAMES = NO
|
||||
|
||||
OUTPUT_LANGUAGE = English
|
||||
|
||||
# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
|
||||
# documentation generated by doxygen is written. Doxygen will use this
|
||||
# information to generate all generated output in the proper direction.
|
||||
# Possible values are: None, LTR, RTL and Context.
|
||||
# The default value is: None.
|
||||
|
||||
OUTPUT_TEXT_DIRECTION = None
|
||||
|
||||
# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
|
||||
# descriptions after the members that are listed in the file and class
|
||||
# documentation (similar to Javadoc). Set to NO to disable this.
|
||||
@@ -258,16 +250,16 @@ TAB_SIZE = 8
|
||||
# the documentation. An alias has the form:
|
||||
# name=value
|
||||
# For example adding
|
||||
# "sideeffect=@par Side Effects:\n"
|
||||
# "sideeffect=@par Side Effects:^^"
|
||||
# will allow you to put the command \sideeffect (or @sideeffect) in the
|
||||
# documentation, which will result in a user-defined paragraph with heading
|
||||
# "Side Effects:". You can put \n's in the value part of an alias to insert
|
||||
# newlines (in the resulting output). You can put ^^ in the value part of an
|
||||
# alias to insert a newline as if a physical newline was in the original file.
|
||||
# When you need a literal { or } or , in the value part of an alias you have to
|
||||
# escape them by means of a backslash (\), this can lead to conflicts with the
|
||||
# commands \{ and \} for these it is advised to use the version @{ and @} or use
|
||||
# a double escape (\\{ and \\})
|
||||
# "Side Effects:". Note that you cannot put \n's in the value part of an alias
|
||||
# to insert newlines (in the resulting output). You can put ^^ in the value part
|
||||
# of an alias to insert a newline as if a physical newline was in the original
|
||||
# file. When you need a literal { or } or , in the value part of an alias you
|
||||
# have to escape them by means of a backslash (\), this can lead to conflicts
|
||||
# with the commands \{ and \} for these it is advised to use the version @{ and
|
||||
# @} or use a double escape (\\{ and \\})
|
||||
|
||||
ALIASES =
|
||||
|
||||
@@ -312,8 +304,8 @@ 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, VHDL,
|
||||
# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
|
||||
# Csharp (C#), C, C++, Lex, 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). For instance to make doxygen treat .inc files
|
||||
@@ -323,7 +315,10 @@ OPTIMIZE_OUTPUT_SLICE = NO
|
||||
# Note: For files without extension you can use no_extension as a placeholder.
|
||||
#
|
||||
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
|
||||
# the files are not read by doxygen.
|
||||
# the files are not read by doxygen. When specifying no_extension you should add
|
||||
# * to the FILE_PATTERNS.
|
||||
#
|
||||
# Note see also the list of default file extension mappings.
|
||||
|
||||
EXTENSION_MAPPING =
|
||||
|
||||
@@ -463,7 +458,7 @@ LOOKUP_CACHE_SIZE = 0
|
||||
# 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
|
||||
# which effectively 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.
|
||||
@@ -533,6 +528,13 @@ EXTRACT_LOCAL_METHODS = NO
|
||||
|
||||
EXTRACT_ANON_NSPACES = NO
|
||||
|
||||
# If this flag is set to YES, the name of an unnamed parameter in a declaration
|
||||
# will be determined by the corresponding definition. By default unnamed
|
||||
# parameters remain unnamed in the output.
|
||||
# The default value is: YES.
|
||||
|
||||
RESOLVE_UNNAMED_PARAMS = YES
|
||||
|
||||
# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
|
||||
# undocumented members inside documented classes or files. If set to NO these
|
||||
# members will be included in the various overviews, but no documentation
|
||||
@@ -570,11 +572,18 @@ HIDE_IN_BODY_DOCS = NO
|
||||
|
||||
INTERNAL_DOCS = NO
|
||||
|
||||
# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
|
||||
# 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) and Mac users are advised to set this option to NO.
|
||||
# With the correct setting of option CASE_SENSE_NAMES doxygen will better be
|
||||
# able to match the capabilities of the underlying filesystem. In case the
|
||||
# filesystem is case sensitive (i.e. it supports files in the same directory
|
||||
# whose names only differ in casing), the option must be set to YES to properly
|
||||
# deal with such files in case they appear in the input. For filesystems that
|
||||
# are not case sensitive the option should be be set to NO to properly deal with
|
||||
# output files written for symbols that only differ in casing, such as for two
|
||||
# classes, one named CLASS and the other named Class, and to also support
|
||||
# references to files without having to specify the exact matching casing. On
|
||||
# Windows (including Cygwin) and MacOS, users should typically set this option
|
||||
# to NO, whereas on Linux or other Unix flavors it should typically be set to
|
||||
# YES.
|
||||
# The default value is: system dependent.
|
||||
|
||||
CASE_SENSE_NAMES = NO
|
||||
@@ -593,6 +602,12 @@ HIDE_SCOPE_NAMES = YES
|
||||
|
||||
HIDE_COMPOUND_REFERENCE= NO
|
||||
|
||||
# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class
|
||||
# will show which file needs to be included to use the class.
|
||||
# The default value is: YES.
|
||||
|
||||
SHOW_HEADERFILE = YES
|
||||
|
||||
# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
|
||||
# the files that are included by a file in the documentation of that file.
|
||||
# The default value is: YES.
|
||||
@@ -750,7 +765,8 @@ FILE_VERSION_FILTER =
|
||||
# output files in an output format independent way. To create the layout file
|
||||
# that represents doxygen's defaults, run doxygen with the -l option. You can
|
||||
# optionally specify a file name after the option, if omitted DoxygenLayout.xml
|
||||
# will be used as the name of the layout file.
|
||||
# will be used as the name of the layout file. See also section "Changing the
|
||||
# layout of pages" for information.
|
||||
#
|
||||
# Note that if you run doxygen from a directory containing a file called
|
||||
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
|
||||
@@ -796,24 +812,35 @@ WARNINGS = YES
|
||||
WARN_IF_UNDOCUMENTED = YES
|
||||
|
||||
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
|
||||
# potential errors in the documentation, such as not documenting some parameters
|
||||
# in a documented function, or documenting parameters that don't exist or using
|
||||
# markup commands wrongly.
|
||||
# potential errors in the documentation, such as documenting some parameters in
|
||||
# a documented function twice, or documenting parameters that don't exist or
|
||||
# using markup commands wrongly.
|
||||
# The default value is: YES.
|
||||
|
||||
WARN_IF_DOC_ERROR = YES
|
||||
|
||||
# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete
|
||||
# function parameter documentation. If set to NO, doxygen will accept that some
|
||||
# parameters have no documentation without warning.
|
||||
# The default value is: YES.
|
||||
|
||||
WARN_IF_INCOMPLETE_DOC = YES
|
||||
|
||||
# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
|
||||
# are documented, but have no documentation for their parameters or return
|
||||
# value. If set to NO, doxygen will only warn about wrong or incomplete
|
||||
# parameter documentation, but not about the absence of documentation. If
|
||||
# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
|
||||
# value. If set to NO, doxygen will only warn about wrong parameter
|
||||
# documentation, but not about the absence of documentation. If EXTRACT_ALL is
|
||||
# set to YES then this flag will automatically be disabled. See also
|
||||
# WARN_IF_INCOMPLETE_DOC
|
||||
# The default value is: NO.
|
||||
|
||||
WARN_NO_PARAMDOC = NO
|
||||
|
||||
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
|
||||
# a warning is encountered.
|
||||
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
|
||||
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
|
||||
# at the end of the doxygen process doxygen will return with a non-zero status.
|
||||
# Possible values are: NO, YES and FAIL_ON_WARNINGS.
|
||||
# The default value is: NO.
|
||||
|
||||
WARN_AS_ERROR = NO
|
||||
@@ -849,8 +876,8 @@ INPUT = ../
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
|
||||
# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
|
||||
# possible encodings.
|
||||
# documentation (see:
|
||||
# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
|
||||
# The default value is: UTF-8.
|
||||
|
||||
INPUT_ENCODING = UTF-8
|
||||
@@ -863,12 +890,14 @@ INPUT_ENCODING = UTF-8
|
||||
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
|
||||
# read by doxygen.
|
||||
#
|
||||
# Note the list of default checked file patterns might differ from the list of
|
||||
# default file extension mappings.
|
||||
#
|
||||
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
|
||||
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
|
||||
# *.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, *.f18, *.f, *.for, *.vhd,
|
||||
# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
|
||||
# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
|
||||
# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
|
||||
# *.vhdl, *.ucf, *.qsf and *.ice.
|
||||
|
||||
FILE_PATTERNS = *.c \
|
||||
@@ -934,8 +963,12 @@ RECURSIVE = YES
|
||||
|
||||
EXCLUDE = ../include/stm32 \
|
||||
../include/arm_math.h \
|
||||
../include/cmsis \
|
||||
../shellmatta/test
|
||||
../include/cmsis/ \
|
||||
../shellmatta/test/ \
|
||||
../linklist-lib/test \
|
||||
../base64-lib/test \
|
||||
../shellmatta/doc/main.dox \
|
||||
../updater/ram-code
|
||||
|
||||
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
|
||||
# directories that are symbolic links (a Unix file system feature) are excluded
|
||||
@@ -1143,13 +1176,6 @@ VERBATIM_HEADERS = YES
|
||||
|
||||
ALPHABETICAL_INDEX = YES
|
||||
|
||||
# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
|
||||
# which the alphabetical index list will be split.
|
||||
# Minimum value: 1, maximum value: 20, default value: 5.
|
||||
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
|
||||
|
||||
COLS_IN_ALPHA_INDEX = 5
|
||||
|
||||
# In case all classes in a project start with a common prefix, all classes will
|
||||
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
|
||||
# can be used to specify a prefix (or a list of prefixes) that should be ignored
|
||||
@@ -1249,7 +1275,7 @@ HTML_EXTRA_FILES =
|
||||
|
||||
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
|
||||
# will adjust the colors in the style sheet and background images according to
|
||||
# this color. Hue is specified as an angle on a colorwheel, see
|
||||
# this color. Hue is specified as an angle on a color-wheel, see
|
||||
# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
|
||||
# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
|
||||
# purple, and 360 is red again.
|
||||
@@ -1259,7 +1285,7 @@ HTML_EXTRA_FILES =
|
||||
HTML_COLORSTYLE_HUE = 220
|
||||
|
||||
# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
|
||||
# in the HTML output. For a value of 0 the output will use grayscales only. A
|
||||
# in the HTML output. For a value of 0 the output will use gray-scales only. A
|
||||
# value of 255 will produce the most vivid colors.
|
||||
# Minimum value: 0, maximum value: 255, default value: 100.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
@@ -1320,10 +1346,11 @@ HTML_INDEX_NUM_ENTRIES = 100
|
||||
|
||||
# If the GENERATE_DOCSET tag is set to YES, additional index files will be
|
||||
# generated that can be used as input for Apple's Xcode 3 integrated development
|
||||
# environment (see: https://developer.apple.com/xcode/), introduced with OSX
|
||||
# 10.5 (Leopard). To create a documentation set, doxygen will generate a
|
||||
# Makefile in the HTML output directory. Running make will produce the docset in
|
||||
# that directory and running make install will install the docset in
|
||||
# environment (see:
|
||||
# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To
|
||||
# create a documentation set, doxygen will generate a Makefile in the HTML
|
||||
# output directory. Running make will produce the docset in that directory and
|
||||
# running make install will install the docset in
|
||||
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
|
||||
# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
|
||||
# genXcode/_index.html for more information.
|
||||
@@ -1365,8 +1392,12 @@ DOCSET_PUBLISHER_NAME = Publisher
|
||||
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
|
||||
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
|
||||
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
|
||||
# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
|
||||
# Windows.
|
||||
# on Windows. In the beginning of 2021 Microsoft took the original page, with
|
||||
# a.o. the download links, offline the HTML help workshop was already many years
|
||||
# in maintenance mode). You can download the HTML help workshop from the web
|
||||
# archives at Installation executable (see:
|
||||
# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo
|
||||
# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).
|
||||
#
|
||||
# The HTML Help Workshop contains a compiler that can convert all HTML output
|
||||
# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
|
||||
@@ -1441,7 +1472,8 @@ QCH_FILE =
|
||||
|
||||
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
|
||||
# Project output. For more information please see Qt Help Project / Namespace
|
||||
# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
|
||||
# (see:
|
||||
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
|
||||
# The default value is: org.doxygen.Project.
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
@@ -1449,8 +1481,8 @@ QHP_NAMESPACE = org.doxygen.Project
|
||||
|
||||
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
|
||||
# Help Project output. For more information please see Qt Help Project / Virtual
|
||||
# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
|
||||
# folders).
|
||||
# Folders (see:
|
||||
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).
|
||||
# The default value is: doc.
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
@@ -1458,16 +1490,16 @@ QHP_VIRTUAL_FOLDER = doc
|
||||
|
||||
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
|
||||
# filter to add. For more information please see Qt Help Project / Custom
|
||||
# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
|
||||
# filters).
|
||||
# Filters (see:
|
||||
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_CUST_FILTER_NAME =
|
||||
|
||||
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
|
||||
# custom filter to add. For more information please see Qt Help Project / Custom
|
||||
# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
|
||||
# filters).
|
||||
# Filters (see:
|
||||
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_CUST_FILTER_ATTRS =
|
||||
@@ -1479,9 +1511,9 @@ QHP_CUST_FILTER_ATTRS =
|
||||
|
||||
QHP_SECT_FILTER_ATTRS =
|
||||
|
||||
# The QHG_LOCATION tag can be used to specify the location of Qt's
|
||||
# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
|
||||
# generated .qhp file.
|
||||
# The QHG_LOCATION tag can be used to specify the location (absolute path
|
||||
# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to
|
||||
# run qhelpgenerator on the generated .qhp file.
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHG_LOCATION =
|
||||
@@ -1524,16 +1556,28 @@ DISABLE_INDEX = NO
|
||||
# to work a browser that supports JavaScript, DHTML, CSS and frames is required
|
||||
# (i.e. any modern browser). Windows users are probably better off using the
|
||||
# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
|
||||
# further fine-tune the look of the index. As an example, the default style
|
||||
# sheet generated by doxygen has an example that shows how to put an image at
|
||||
# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
|
||||
# the same information as the tab index, you could consider setting
|
||||
# DISABLE_INDEX to YES when enabling this option.
|
||||
# further fine tune the look of the index (see "Fine-tuning the output"). As an
|
||||
# example, the default style sheet generated by doxygen has an example that
|
||||
# shows how to put an image at the root of the tree instead of the PROJECT_NAME.
|
||||
# Since the tree basically has the same information as the tab index, you could
|
||||
# consider setting DISABLE_INDEX to YES when enabling this option.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
GENERATE_TREEVIEW = NO
|
||||
|
||||
# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the
|
||||
# FULL_SIDEBAR option determines if the side bar is limited to only the treeview
|
||||
# area (value NO) or if it should extend to the full height of the window (value
|
||||
# YES). Setting this to YES gives a layout similar to
|
||||
# https://docs.readthedocs.io with more room for contents, but less room for the
|
||||
# project logo, title, and description. If either GENERATOR_TREEVIEW or
|
||||
# DISABLE_INDEX is set to NO, this option has no effect.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
FULL_SIDEBAR = NO
|
||||
|
||||
# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
|
||||
# doxygen will group on one line in the generated HTML documentation.
|
||||
#
|
||||
@@ -1606,11 +1650,29 @@ FORMULA_MACROFILE =
|
||||
|
||||
USE_MATHJAX = YES
|
||||
|
||||
# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.
|
||||
# Note that the different versions of MathJax have different requirements with
|
||||
# regards to the different settings, so it is possible that also other MathJax
|
||||
# settings have to be changed when switching between the different MathJax
|
||||
# versions.
|
||||
# Possible values are: MathJax_2 and MathJax_3.
|
||||
# The default value is: MathJax_2.
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
MATHJAX_VERSION = MathJax_2
|
||||
|
||||
# When MathJax is enabled you can set the default output format to be used for
|
||||
# the MathJax output. See the MathJax site (see:
|
||||
# http://docs.mathjax.org/en/latest/output.html) for more details.
|
||||
# the MathJax output. For more details about the output format see MathJax
|
||||
# version 2 (see:
|
||||
# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3
|
||||
# (see:
|
||||
# http://docs.mathjax.org/en/latest/web/components/output.html).
|
||||
# Possible values are: HTML-CSS (which is slower, but has the best
|
||||
# compatibility), NativeMML (i.e. MathML) and SVG.
|
||||
# compatibility. This is the name for Mathjax version 2, for MathJax version 3
|
||||
# this will be translated into chtml), NativeMML (i.e. MathML. Only supported
|
||||
# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This
|
||||
# is the name for Mathjax version 3, for MathJax version 2 this will be
|
||||
# translated into HTML-CSS) and SVG.
|
||||
# The default value is: HTML-CSS.
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
@@ -1623,22 +1685,29 @@ MATHJAX_FORMAT = HTML-CSS
|
||||
# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
|
||||
# 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://cdn.jsdelivr.net/npm/mathjax@2.
|
||||
# MathJax from https://www.mathjax.org before deployment. The default value is:
|
||||
# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2
|
||||
# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/
|
||||
|
||||
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
|
||||
# extension names that should be enabled during MathJax rendering. For example
|
||||
# for MathJax version 2 (see
|
||||
# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):
|
||||
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
|
||||
# For example for MathJax version 3 (see
|
||||
# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
|
||||
# MATHJAX_EXTENSIONS = ams
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
MATHJAX_EXTENSIONS =
|
||||
|
||||
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
|
||||
# of code that will be used on startup of the MathJax code. See the MathJax site
|
||||
# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
|
||||
# (see:
|
||||
# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an
|
||||
# example see the documentation.
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
@@ -1685,7 +1754,8 @@ SERVER_BASED_SEARCH = NO
|
||||
#
|
||||
# Doxygen ships with an example indexer (doxyindexer) and search engine
|
||||
# (doxysearch.cgi) which are based on the open source search engine library
|
||||
# Xapian (see: https://xapian.org/).
|
||||
# Xapian (see:
|
||||
# https://xapian.org/).
|
||||
#
|
||||
# See the section "External Indexing and Searching" for details.
|
||||
# The default value is: NO.
|
||||
@@ -1698,8 +1768,9 @@ EXTERNAL_SEARCH = NO
|
||||
#
|
||||
# Doxygen ships with an example indexer (doxyindexer) and search engine
|
||||
# (doxysearch.cgi) which are based on the open source search engine library
|
||||
# Xapian (see: https://xapian.org/). See the section "External Indexing and
|
||||
# Searching" for details.
|
||||
# Xapian (see:
|
||||
# https://xapian.org/). See the section "External Indexing and Searching" for
|
||||
# details.
|
||||
# This tag requires that the tag SEARCHENGINE is set to YES.
|
||||
|
||||
SEARCHENGINE_URL =
|
||||
@@ -1737,7 +1808,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 = NO
|
||||
GENERATE_LATEX = YES
|
||||
|
||||
# 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
|
||||
@@ -1808,29 +1879,31 @@ PAPER_TYPE = a4
|
||||
|
||||
EXTRA_PACKAGES = amsmath
|
||||
|
||||
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
|
||||
# generated LaTeX document. The header should contain everything until the first
|
||||
# chapter. If it is left blank doxygen will generate a standard header. See
|
||||
# section "Doxygen usage" for information on how to let doxygen write the
|
||||
# default header to a separate file.
|
||||
# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for
|
||||
# the generated LaTeX document. The header should contain everything until the
|
||||
# first chapter. If it is left blank doxygen will generate a standard header. It
|
||||
# is highly recommended to start with a default header using
|
||||
# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty
|
||||
# and then modify the file new_header.tex. See also section "Doxygen usage" for
|
||||
# information on how to generate the default header that doxygen normally uses.
|
||||
#
|
||||
# Note: Only use a user-defined header if you know what you are doing! The
|
||||
# following commands have a special meaning inside the header: $title,
|
||||
# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
|
||||
# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
|
||||
# string, for the replacement values of the other commands the user is referred
|
||||
# to HTML_HEADER.
|
||||
# Note: Only use a user-defined header if you know what you are doing!
|
||||
# Note: The header is subject to change so you typically have to regenerate the
|
||||
# default header when upgrading to a newer version of doxygen. The following
|
||||
# commands have a special meaning inside the header (and footer): For a
|
||||
# description of the possible markers and block names see the documentation.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_HEADER =
|
||||
|
||||
# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
|
||||
# generated LaTeX document. The footer should contain everything after the last
|
||||
# chapter. If it is left blank doxygen will generate a standard footer. See
|
||||
# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for
|
||||
# the generated LaTeX document. The footer should contain everything after the
|
||||
# last chapter. If it is left blank doxygen will generate a standard footer. See
|
||||
# LATEX_HEADER for more information on how to generate a default footer and what
|
||||
# special commands can be used inside the footer.
|
||||
#
|
||||
# Note: Only use a user-defined footer if you know what you are doing!
|
||||
# special commands can be used inside the footer. See also section "Doxygen
|
||||
# usage" for information on how to generate the default footer that doxygen
|
||||
# normally uses. Note: Only use a user-defined footer if you know what you are
|
||||
# doing!
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_FOOTER =
|
||||
@@ -1875,8 +1948,7 @@ USE_PDFLATEX = YES
|
||||
|
||||
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
|
||||
# command to the generated LaTeX files. This will instruct LaTeX to keep running
|
||||
# if errors occur, instead of asking the user for help. This option is also used
|
||||
# when generating formulas in HTML.
|
||||
# if errors occur, instead of asking the user for help.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
@@ -1889,16 +1961,6 @@ LATEX_BATCHMODE = NO
|
||||
|
||||
LATEX_HIDE_INDICES = NO
|
||||
|
||||
# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
|
||||
# code with syntax highlighting in the LaTeX output.
|
||||
#
|
||||
# Note that which sources are shown also depends on other settings such as
|
||||
# SOURCE_BROWSER.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_SOURCE_CODE = YES
|
||||
|
||||
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
|
||||
# bibliography, e.g. plainnat, or ieeetr. See
|
||||
# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
|
||||
@@ -1979,16 +2041,6 @@ RTF_STYLESHEET_FILE =
|
||||
|
||||
RTF_EXTENSIONS_FILE =
|
||||
|
||||
# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
|
||||
# with syntax highlighting in the RTF output.
|
||||
#
|
||||
# Note that which sources are shown also depends on other settings such as
|
||||
# SOURCE_BROWSER.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
RTF_SOURCE_CODE = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the man page output
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -2085,15 +2137,6 @@ GENERATE_DOCBOOK = NO
|
||||
|
||||
DOCBOOK_OUTPUT = docbook
|
||||
|
||||
# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
|
||||
# program listings (including syntax highlighting and cross-referencing
|
||||
# information) to the DOCBOOK output. Note that enabling this will significantly
|
||||
# increase the size of the DOCBOOK output.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
|
||||
|
||||
DOCBOOK_PROGRAMLISTING = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options for the AutoGen Definitions output
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -2206,7 +2249,7 @@ INCLUDE_FILE_PATTERNS =
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
PREDEFINED = __attribute__(x)= \
|
||||
IN_SECTION(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
|
||||
@@ -2383,10 +2426,32 @@ UML_LOOK = NO
|
||||
# but if the number exceeds 15, the total amount of fields shown is limited to
|
||||
# 10.
|
||||
# Minimum value: 0, maximum value: 100, default value: 10.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
# This tag requires that the tag UML_LOOK is set to YES.
|
||||
|
||||
UML_LIMIT_NUM_FIELDS = 10
|
||||
|
||||
# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and
|
||||
# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS
|
||||
# tag is set to YES, doxygen will add type and arguments for attributes and
|
||||
# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen
|
||||
# will not generate fields with class member information in the UML graphs. The
|
||||
# class diagrams will look similar to the default class diagrams but using UML
|
||||
# notation for the relationships.
|
||||
# Possible values are: NO, YES and NONE.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag UML_LOOK is set to YES.
|
||||
|
||||
DOT_UML_DETAILS = NO
|
||||
|
||||
# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
|
||||
# to display on a single line. If the actual line length exceeds this threshold
|
||||
# significantly it will wrapped across multiple lines. Some heuristics are apply
|
||||
# to avoid ugly line breaks.
|
||||
# Minimum value: 0, maximum value: 1000, default value: 17.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_WRAP_THRESHOLD = 17
|
||||
|
||||
# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
|
||||
# collaboration graphs will show the relations between templates and their
|
||||
# instances.
|
||||
@@ -2576,9 +2641,11 @@ DOT_MULTI_TARGETS = NO
|
||||
|
||||
GENERATE_LEGEND = YES
|
||||
|
||||
# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
|
||||
# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
|
||||
# files that are used to generate the various graphs.
|
||||
#
|
||||
# Note: This setting is not only used for dot files but also for msc temporary
|
||||
# files.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_CLEANUP = YES
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2019 */
|
||||
/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* If a working storage control module is available, it should be */
|
||||
/* attached to the FatFs via a glue function rather than modifying it. */
|
||||
@@ -12,14 +12,8 @@
|
||||
#include "shimatta_sdio_driver/shimatta_sdio.h"
|
||||
|
||||
/* Definitions of physical drive number for each drive */
|
||||
#define DEV_SD 0 /* Example: Map MMC/SD card to physical drive 0*/
|
||||
/*
|
||||
DSTATUS SDIO_status();
|
||||
DSTATUS SDIO_initialize();
|
||||
DRESULT SDIO_disk_read(BYTE *buff, DWORD sector, UINT count);
|
||||
DRESULT SDIO_disk_write(const BYTE *buff, DWORD sector, UINT count);
|
||||
DRESULT SDIO_disk_ioctl(BYTE cmd, void* buff);
|
||||
*/
|
||||
#define DEV_SD 0
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Get Drive Status */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
@@ -116,4 +110,3 @@ DRESULT disk_ioctl (
|
||||
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,8 @@
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT Filesystem Module R0.14 /
|
||||
/ FatFs - Generic FAT Filesystem Module R0.14a /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 2019, ChaN, all right reserved.
|
||||
/ Copyright (C) 2020, ChaN, all right reserved.
|
||||
/
|
||||
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||
/ source and binary forms, with or without modification, are permitted provided
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
#if FF_DEFINED != 86606 /* Revision ID */
|
||||
#if FF_DEFINED != 80196 /* Revision ID */
|
||||
#error Wrong include file (ff.h).
|
||||
#endif
|
||||
|
||||
@@ -1134,13 +1134,12 @@ static FRESULT sync_fs ( /* Returns FR_OK or FR_DISK_ERR */
|
||||
if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { /* FAT32: Update FSInfo sector if needed */
|
||||
/* Create FSInfo structure */
|
||||
mem_set(fs->win, 0, sizeof fs->win);
|
||||
st_word(fs->win + BS_55AA, 0xAA55);
|
||||
st_dword(fs->win + FSI_LeadSig, 0x41615252);
|
||||
st_dword(fs->win + FSI_StrucSig, 0x61417272);
|
||||
st_dword(fs->win + FSI_Free_Count, fs->free_clst);
|
||||
st_dword(fs->win + FSI_Nxt_Free, fs->last_clst);
|
||||
/* Write it into the FSInfo sector */
|
||||
fs->winsect = fs->volbase + 1;
|
||||
st_word(fs->win + BS_55AA, 0xAA55); /* Boot signature */
|
||||
st_dword(fs->win + FSI_LeadSig, 0x41615252); /* Leading signature */
|
||||
st_dword(fs->win + FSI_StrucSig, 0x61417272); /* Structure signature */
|
||||
st_dword(fs->win + FSI_Free_Count, fs->free_clst); /* Number of free clusters */
|
||||
st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); /* Last allocated culuster */
|
||||
fs->winsect = fs->volbase + 1; /* Write it into the FSInfo sector (Next to VBR) */
|
||||
disk_write(fs->pdrv, fs->win, fs->winsect, 1);
|
||||
fs->fsi_flag = 0;
|
||||
}
|
||||
@@ -1235,7 +1234,8 @@ static DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFF
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* go to default */
|
||||
val = 1; /* Internal error */
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
val = 1; /* Internal error */
|
||||
@@ -1266,7 +1266,7 @@ static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */
|
||||
|
||||
if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid range */
|
||||
switch (fs->fs_type) {
|
||||
case FS_FAT12 :
|
||||
case FS_FAT12:
|
||||
bc = (UINT)clst; bc += bc / 2; /* bc: byte offset of the entry */
|
||||
res = move_window(fs, fs->fatbase + (bc / SS(fs)));
|
||||
if (res != FR_OK) break;
|
||||
@@ -1280,16 +1280,16 @@ static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */
|
||||
fs->wflag = 1;
|
||||
break;
|
||||
|
||||
case FS_FAT16 :
|
||||
case FS_FAT16:
|
||||
res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)));
|
||||
if (res != FR_OK) break;
|
||||
st_word(fs->win + clst * 2 % SS(fs), (WORD)val); /* Simple WORD array */
|
||||
fs->wflag = 1;
|
||||
break;
|
||||
|
||||
case FS_FAT32 :
|
||||
case FS_FAT32:
|
||||
#if FF_FS_EXFAT
|
||||
case FS_EXFAT :
|
||||
case FS_EXFAT:
|
||||
#endif
|
||||
res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)));
|
||||
if (res != FR_OK) break;
|
||||
@@ -1821,7 +1821,7 @@ static FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DEN
|
||||
|
||||
static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */
|
||||
DIR* dp, /* Pointer to the directory object */
|
||||
UINT nent /* Number of contiguous entries to allocate */
|
||||
UINT n_ent /* Number of contiguous entries to allocate */
|
||||
)
|
||||
{
|
||||
FRESULT res;
|
||||
@@ -1836,16 +1836,16 @@ static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */
|
||||
res = move_window(fs, dp->sect);
|
||||
if (res != FR_OK) break;
|
||||
#if FF_FS_EXFAT
|
||||
if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) {
|
||||
if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) { /* Is the entry free? */
|
||||
#else
|
||||
if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) {
|
||||
if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) { /* Is the entry free? */
|
||||
#endif
|
||||
if (++n == nent) break; /* A block of contiguous free entries is found */
|
||||
if (++n == n_ent) break; /* Is a block of contiguous free entries found? */
|
||||
} else {
|
||||
n = 0; /* Not a blank entry. Restart to search */
|
||||
n = 0; /* Not a free entry, restart to search */
|
||||
}
|
||||
res = dir_next(dp, 1);
|
||||
} while (res == FR_OK); /* Next entry with table stretch enabled */
|
||||
res = dir_next(dp, 1); /* Next entry with table stretch enabled */
|
||||
} while (res == FR_OK);
|
||||
}
|
||||
|
||||
if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */
|
||||
@@ -2527,19 +2527,19 @@ static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too
|
||||
FRESULT res;
|
||||
FATFS *fs = dp->obj.fs;
|
||||
#if FF_USE_LFN /* LFN configuration */
|
||||
UINT n, nlen, nent;
|
||||
UINT n, len, n_ent;
|
||||
BYTE sn[12], sum;
|
||||
|
||||
|
||||
if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME; /* Check name validity */
|
||||
for (nlen = 0; fs->lfnbuf[nlen]; nlen++) ; /* Get lfn length */
|
||||
for (len = 0; fs->lfnbuf[len]; len++) ; /* Get lfn length */
|
||||
|
||||
#if FF_FS_EXFAT
|
||||
if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
|
||||
nent = (nlen + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */
|
||||
res = dir_alloc(dp, nent); /* Allocate directory entries */
|
||||
n_ent = (len + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */
|
||||
res = dir_alloc(dp, n_ent); /* Allocate directory entries */
|
||||
if (res != FR_OK) return res;
|
||||
dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1); /* Set the allocated entry block offset */
|
||||
dp->blk_ofs = dp->dptr - SZDIRE * (n_ent - 1); /* Set the allocated entry block offset */
|
||||
|
||||
if (dp->obj.stat & 4) { /* Has the directory been stretched by new allocation? */
|
||||
dp->obj.stat &= ~4;
|
||||
@@ -2580,19 +2580,19 @@ static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too
|
||||
}
|
||||
|
||||
/* Create an SFN with/without LFNs. */
|
||||
nent = (sn[NSFLAG] & NS_LFN) ? (nlen + 12) / 13 + 1 : 1; /* Number of entries to allocate */
|
||||
res = dir_alloc(dp, nent); /* Allocate entries */
|
||||
if (res == FR_OK && --nent) { /* Set LFN entry if needed */
|
||||
res = dir_sdi(dp, dp->dptr - nent * SZDIRE);
|
||||
n_ent = (sn[NSFLAG] & NS_LFN) ? (len + 12) / 13 + 1 : 1; /* Number of entries to allocate */
|
||||
res = dir_alloc(dp, n_ent); /* Allocate entries */
|
||||
if (res == FR_OK && --n_ent) { /* Set LFN entry if needed */
|
||||
res = dir_sdi(dp, dp->dptr - n_ent * SZDIRE);
|
||||
if (res == FR_OK) {
|
||||
sum = sum_sfn(dp->fn); /* Checksum value of the SFN tied to the LFN */
|
||||
do { /* Store LFN entries in bottom first */
|
||||
res = move_window(fs, dp->sect);
|
||||
if (res != FR_OK) break;
|
||||
put_lfn(fs->lfnbuf, dp->dir, (BYTE)nent, sum);
|
||||
put_lfn(fs->lfnbuf, dp->dir, (BYTE)n_ent, sum);
|
||||
fs->wflag = 1;
|
||||
res = dir_next(dp, 0); /* Next entry */
|
||||
} while (res == FR_OK && --nent);
|
||||
} while (res == FR_OK && --n_ent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2778,7 +2778,10 @@ static void get_fileinfo (
|
||||
/* Pattern matching */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
static DWORD get_achar ( /* Get a character and advances ptr */
|
||||
#define FIND_RECURS 4 /* Maximum number of wildcard terms in the pattern to limit recursion */
|
||||
|
||||
|
||||
static DWORD get_achar ( /* Get a character and advance ptr */
|
||||
const TCHAR** ptr /* Pointer to pointer to the ANSI/OEM or Unicode string */
|
||||
)
|
||||
{
|
||||
@@ -2809,41 +2812,43 @@ static DWORD get_achar ( /* Get a character and advances ptr */
|
||||
}
|
||||
|
||||
|
||||
static int pattern_matching ( /* 0:not matched, 1:matched */
|
||||
static int pattern_match ( /* 0:mismatched, 1:matched */
|
||||
const TCHAR* pat, /* Matching pattern */
|
||||
const TCHAR* nam, /* String to be tested */
|
||||
int skip, /* Number of pre-skip chars (number of ?s) */
|
||||
int inf /* Infinite search (* specified) */
|
||||
UINT skip, /* Number of pre-skip chars (number of ?s, b8:infinite (* specified)) */
|
||||
UINT recur /* Recursion count */
|
||||
)
|
||||
{
|
||||
const TCHAR *pp, *np;
|
||||
DWORD pc, nc;
|
||||
int nm, nx;
|
||||
const TCHAR *pptr, *nptr;
|
||||
DWORD pchr, nchr;
|
||||
UINT sk;
|
||||
|
||||
|
||||
while (skip--) { /* Pre-skip name chars */
|
||||
while ((skip & 0xFF) != 0) { /* Pre-skip name chars */
|
||||
if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */
|
||||
skip--;
|
||||
}
|
||||
if (*pat == 0 && inf) return 1; /* (short circuit) */
|
||||
if (*pat == 0 && skip) return 1; /* Matched? (short circuit) */
|
||||
|
||||
do {
|
||||
pp = pat; np = nam; /* Top of pattern and name to match */
|
||||
pptr = pat; nptr = nam; /* Top of pattern and name to match */
|
||||
for (;;) {
|
||||
if (*pp == '?' || *pp == '*') { /* Wildcard? */
|
||||
nm = nx = 0;
|
||||
do { /* Analyze the wildcard block */
|
||||
if (*pp++ == '?') nm++; else nx = 1;
|
||||
} while (*pp == '?' || *pp == '*');
|
||||
if (pattern_matching(pp, np, nm, nx)) return 1; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */
|
||||
nc = *np; break; /* Branch mismatched */
|
||||
if (*pptr == '?' || *pptr == '*') { /* Wildcard term? */
|
||||
if (recur == 0) return 0; /* Too many wildcard terms? */
|
||||
sk = 0;
|
||||
do { /* Analyze the wildcard term */
|
||||
if (*pptr++ == '?') sk++; else sk |= 0x100;
|
||||
} while (*pptr == '?' || *pptr == '*');
|
||||
if (pattern_match(pptr, nptr, sk, recur - 1)) return 1; /* Test new branch (recursive call) */
|
||||
nchr = *nptr; break; /* Branch mismatched */
|
||||
}
|
||||
pc = get_achar(&pp); /* Get a pattern char */
|
||||
nc = get_achar(&np); /* Get a name char */
|
||||
if (pc != nc) break; /* Branch mismatched? */
|
||||
if (pc == 0) return 1; /* Branch matched? (matched at end of both strings) */
|
||||
pchr = get_achar(&pptr); /* Get a pattern char */
|
||||
nchr = get_achar(&nptr); /* Get a name char */
|
||||
if (pchr != nchr) break; /* Branch mismatched? */
|
||||
if (pchr == 0) return 1; /* Branch matched? (matched at end of both strings) */
|
||||
}
|
||||
get_achar(&nam); /* nam++ */
|
||||
} while (inf && nc); /* Retry until end of name if infinite search is specified */
|
||||
} while (skip && nchr); /* Retry until end of name if infinite search is specified */
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -3290,23 +3295,37 @@ static DWORD make_rand (
|
||||
|
||||
/* Check what the sector is */
|
||||
|
||||
static UINT check_fs ( /* 0:FAT VBR, 1:exFAT VBR, 2:Valid BS but not FAT, 3:Invalid BS, 4:Disk error */
|
||||
static UINT check_fs ( /* 0:FAT VBR, 1:exFAT VBR, 2:Not FAT and valid BS, 3:Not FAT and invalid BS, 4:Disk error */
|
||||
FATFS* fs, /* Filesystem object */
|
||||
LBA_t sect /* Sector to load and check if it is an FAT-VBR or not */
|
||||
)
|
||||
{
|
||||
WORD w, sign;
|
||||
BYTE b;
|
||||
|
||||
|
||||
fs->wflag = 0; fs->winsect = (LBA_t)0 - 1; /* Invaidate window */
|
||||
if (move_window(fs, sect) != FR_OK) return 4; /* Load the boot sector */
|
||||
|
||||
if (ld_word(fs->win + BS_55AA) != 0xAA55) return 3; /* Check boot signature (always here regardless of the sector size) */
|
||||
|
||||
if (FF_FS_EXFAT && !mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* Check if exFAT VBR */
|
||||
|
||||
if (fs->win[BS_JmpBoot] == 0xE9 || fs->win[BS_JmpBoot] == 0xEB || fs->win[BS_JmpBoot] == 0xE8) { /* Valid JumpBoot code? */
|
||||
if (!mem_cmp(fs->win + BS_FilSysType, "FAT", 3)) return 0; /* Is it an FAT VBR? */
|
||||
if (!mem_cmp(fs->win + BS_FilSysType32, "FAT32", 5)) return 0; /* Is it an FAT32 VBR? */
|
||||
sign = ld_word(fs->win + BS_55AA);
|
||||
#if FF_FS_EXFAT
|
||||
if (sign == 0xAA55 && !mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* It is an exFAT VBR */
|
||||
#endif
|
||||
b = fs->win[BS_JmpBoot];
|
||||
if (b == 0xEB || b == 0xE9 || b == 0xE8) { /* Valid JumpBoot code? (short jump, near jump or near call) */
|
||||
if (sign == 0xAA55 && !mem_cmp(fs->win + BS_FilSysType32, "FAT32 ", 8)) return 0; /* It is an FAT32 VBR */
|
||||
/* FAT volumes formatted with early MS-DOS lack boot signature and FAT string, so that we need to identify the FAT VBR without them. */
|
||||
w = ld_word(fs->win + BPB_BytsPerSec);
|
||||
if ((w & (w - 1)) == 0 && w >= FF_MIN_SS && w <= FF_MAX_SS) { /* Properness of sector size */
|
||||
b = fs->win[BPB_SecPerClus];
|
||||
if (b != 0 && (b & (b - 1)) == 0 /* Properness of cluster size */
|
||||
&& (fs->win[BPB_NumFATs] == 1 || fs->win[BPB_NumFATs] == 2) /* Properness of number of FATs */
|
||||
&& ld_word(fs->win + BPB_RootEntCnt) != 0 /* Properness of root entry count */
|
||||
&& ld_word(fs->win + BPB_FATSz16) != 0) { /* Properness of FAT size */
|
||||
return 0; /* Sector can be presumed an FAT VBR */
|
||||
}
|
||||
}
|
||||
}
|
||||
return 2; /* Valid BS but not FAT */
|
||||
return sign == 0xAA55 ? 2 : 3; /* Not an FAT VBR (valid or invalid BS) */
|
||||
}
|
||||
|
||||
|
||||
@@ -3698,7 +3717,7 @@ FRESULT f_open (
|
||||
DIR dj;
|
||||
FATFS *fs;
|
||||
#if !FF_FS_READONLY
|
||||
DWORD cl, bcs, clst;
|
||||
DWORD cl, bcs, clst, tm;
|
||||
LBA_t sc;
|
||||
FSIZE_t ofs;
|
||||
#endif
|
||||
@@ -3765,8 +3784,10 @@ FRESULT f_open (
|
||||
#endif
|
||||
{
|
||||
/* Set directory entry initial state */
|
||||
tm = GET_FATTIME(); /* Set created time */
|
||||
st_dword(dj.dir + DIR_CrtTime, tm);
|
||||
st_dword(dj.dir + DIR_ModTime, tm);
|
||||
cl = ld_clust(fs, dj.dir); /* Get current cluster chain */
|
||||
st_dword(dj.dir + DIR_CrtTime, GET_FATTIME()); /* Set created time */
|
||||
dj.dir[DIR_Attr] = AM_ARC; /* Reset attribute */
|
||||
st_clust(fs, dj.dir, 0); /* Reset file allocation info */
|
||||
st_dword(dj.dir + DIR_FileSize, 0);
|
||||
@@ -4705,9 +4726,9 @@ FRESULT f_findnext (
|
||||
for (;;) {
|
||||
res = f_readdir(dp, fno); /* Get a directory item */
|
||||
if (res != FR_OK || !fno || !fno->fname[0]) break; /* Terminate if any error or end of directory */
|
||||
if (pattern_matching(dp->pat, fno->fname, 0, 0)) break; /* Test for the file name */
|
||||
if (pattern_match(dp->pat, fno->fname, 0, FIND_RECURS)) break; /* Test for the file name */
|
||||
#if FF_USE_LFN && FF_USE_FIND == 2
|
||||
if (pattern_matching(dp->pat, fno->altname, 0, 0)) break; /* Test for alternative name if exist */
|
||||
if (pattern_match(dp->pat, fno->altname, 0, FIND_RECURS)) break; /* Test for alternative name if exist */
|
||||
#endif
|
||||
}
|
||||
return res;
|
||||
@@ -5376,10 +5397,12 @@ FRESULT f_getlabel (
|
||||
if (res == FR_OK) {
|
||||
switch (fs->fs_type) {
|
||||
case FS_EXFAT:
|
||||
di = BPB_VolIDEx; break;
|
||||
di = BPB_VolIDEx;
|
||||
break;
|
||||
|
||||
case FS_FAT32:
|
||||
di = BS_VolID32; break;
|
||||
di = BS_VolID32;
|
||||
break;
|
||||
|
||||
default:
|
||||
di = BS_VolID;
|
||||
@@ -5677,7 +5700,7 @@ FRESULT f_forward (
|
||||
|
||||
#if !FF_FS_READONLY && FF_USE_MKFS
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Create an FAT/exFAT volume */
|
||||
/* Create FAT/exFAT volume */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#define N_SEC_TRACK 63 /* Sectors per track for determination of drive CHS */
|
||||
@@ -5685,12 +5708,12 @@ FRESULT f_forward (
|
||||
#define GPT_ITEMS 128 /* Number of GPT table size (>=128, sector aligned) */
|
||||
|
||||
|
||||
/* Create partitions on the physical drive */
|
||||
/* Create partitions on the physical drive in format of MBR or GPT */
|
||||
|
||||
static FRESULT create_partition (
|
||||
BYTE drv, /* Physical drive number */
|
||||
const LBA_t plst[], /* Partition list */
|
||||
UINT sys, /* System ID (for only MBR, temp setting) and bit8:GPT */
|
||||
BYTE sys, /* System ID (for only MBR, temp setting) */
|
||||
BYTE* buf /* Working buffer for a sector */
|
||||
)
|
||||
{
|
||||
@@ -5801,7 +5824,7 @@ static FRESULT create_partition (
|
||||
|
||||
st_dword(pte + PTE_StLba, s_lba32); /* Start LBA */
|
||||
st_dword(pte + PTE_SizLba, n_lba32); /* Number of sectors */
|
||||
pte[PTE_System] = (BYTE)sys; /* System type */
|
||||
pte[PTE_System] = sys; /* System type */
|
||||
|
||||
cy = (UINT)(s_lba32 / n_sc / n_hd); /* Start cylinder */
|
||||
hd = (BYTE)(s_lba32 / n_sc % n_hd); /* Start head */
|
||||
@@ -5966,10 +5989,9 @@ FRESULT f_mkfs (
|
||||
|
||||
#if FF_FS_EXFAT
|
||||
if (fsty == FS_EXFAT) { /* Create an exFAT volume */
|
||||
DWORD szb_bit, szb_case, sum, nb, cl, tbl[3];
|
||||
DWORD szb_bit, szb_case, sum, nbit, clu, clen[3];
|
||||
WCHAR ch, si;
|
||||
UINT j, st;
|
||||
BYTE b;
|
||||
|
||||
if (sz_vol < 0x1000) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume for exFAT? */
|
||||
#if FF_USE_TRIM
|
||||
@@ -5990,12 +6012,12 @@ FRESULT f_mkfs (
|
||||
if (n_clst <16) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too few clusters? */
|
||||
if (n_clst > MAX_EXFAT) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too many clusters? */
|
||||
|
||||
szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */
|
||||
tbl[0] = (szb_bit + sz_au * ss - 1) / (sz_au * ss); /* Number of allocation bitmap clusters */
|
||||
szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */
|
||||
clen[0] = (szb_bit + sz_au * ss - 1) / (sz_au * ss); /* Number of allocation bitmap clusters */
|
||||
|
||||
/* Create a compressed up-case table */
|
||||
sect = b_data + sz_au * tbl[0]; /* Table start sector */
|
||||
sum = 0; /* Table checksum to be stored in the 82 entry */
|
||||
sect = b_data + sz_au * clen[0]; /* Table start sector */
|
||||
sum = 0; /* Table checksum to be stored in the 82 entry */
|
||||
st = 0; si = 0; i = 0; j = 0; szb_case = 0;
|
||||
do {
|
||||
switch (st) {
|
||||
@@ -6006,10 +6028,10 @@ FRESULT f_mkfs (
|
||||
}
|
||||
for (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == ff_wtoupper((WCHAR)(si + j)); j++) ; /* Get run length of no-case block */
|
||||
if (j >= 128) {
|
||||
ch = 0xFFFF; st = 2; break; /* Compress the no-case block if run is >= 128 */
|
||||
ch = 0xFFFF; st = 2; break; /* Compress the no-case block if run is >= 128 chars */
|
||||
}
|
||||
st = 1; /* Do not compress short run */
|
||||
/* go to next case */
|
||||
/* FALLTHROUGH */
|
||||
case 1:
|
||||
ch = si++; /* Fill the short run */
|
||||
if (--j == 0) st = 0;
|
||||
@@ -6028,16 +6050,15 @@ FRESULT f_mkfs (
|
||||
sect += n; i = 0;
|
||||
}
|
||||
} while (si);
|
||||
tbl[1] = (szb_case + sz_au * ss - 1) / (sz_au * ss); /* Number of up-case table clusters */
|
||||
tbl[2] = 1; /* Number of root dir clusters */
|
||||
clen[1] = (szb_case + sz_au * ss - 1) / (sz_au * ss); /* Number of up-case table clusters */
|
||||
clen[2] = 1; /* Number of root dir clusters */
|
||||
|
||||
/* Initialize the allocation bitmap */
|
||||
sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of sectors */
|
||||
nb = tbl[0] + tbl[1] + tbl[2]; /* Number of clusters in-use by system */
|
||||
sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of bitmap sectors */
|
||||
nbit = clen[0] + clen[1] + clen[2]; /* Number of clusters in-use by system (bitmap, up-case and root-dir) */
|
||||
do {
|
||||
mem_set(buf, 0, sz_buf * ss);
|
||||
for (i = 0; nb >= 8 && i < sz_buf * ss; buf[i++] = 0xFF, nb -= 8) ;
|
||||
for (b = 1; nb != 0 && i < sz_buf * ss; buf[i] |= b, b <<= 1, nb--) ;
|
||||
mem_set(buf, 0, sz_buf * ss); /* Initialize bitmap buffer */
|
||||
for (i = 0; nbit != 0 && i / 8 < sz_buf * ss; buf[i / 8] |= 1 << (i % 8), i++, nbit--) ; /* Mark used clusters */
|
||||
n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */
|
||||
if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
|
||||
sect += n; nsect -= n;
|
||||
@@ -6045,20 +6066,20 @@ FRESULT f_mkfs (
|
||||
|
||||
/* Initialize the FAT */
|
||||
sect = b_fat; nsect = sz_fat; /* Start of FAT and number of FAT sectors */
|
||||
j = nb = cl = 0;
|
||||
j = nbit = clu = 0;
|
||||
do {
|
||||
mem_set(buf, 0, sz_buf * ss); i = 0; /* Clear work area and reset write index */
|
||||
if (cl == 0) { /* Set FAT [0] and FAT[1] */
|
||||
st_dword(buf + i, 0xFFFFFFF8); i += 4; cl++;
|
||||
st_dword(buf + i, 0xFFFFFFFF); i += 4; cl++;
|
||||
mem_set(buf, 0, sz_buf * ss); i = 0; /* Clear work area and reset write offset */
|
||||
if (clu == 0) { /* Initialize FAT [0] and FAT[1] */
|
||||
st_dword(buf + i, 0xFFFFFFF8); i += 4; clu++;
|
||||
st_dword(buf + i, 0xFFFFFFFF); i += 4; clu++;
|
||||
}
|
||||
do { /* Create chains of bitmap, up-case and root dir */
|
||||
while (nb != 0 && i < sz_buf * ss) { /* Create a chain */
|
||||
st_dword(buf + i, (nb > 1) ? cl + 1 : 0xFFFFFFFF);
|
||||
i += 4; cl++; nb--;
|
||||
while (nbit != 0 && i < sz_buf * ss) { /* Create a chain */
|
||||
st_dword(buf + i, (nbit > 1) ? clu + 1 : 0xFFFFFFFF);
|
||||
i += 4; clu++; nbit--;
|
||||
}
|
||||
if (nb == 0 && j < 3) nb = tbl[j++]; /* Next chain */
|
||||
} while (nb != 0 && i < sz_buf * ss);
|
||||
if (nbit == 0 && j < 3) nbit = clen[j++]; /* Get next chain length */
|
||||
} while (nbit != 0 && i < sz_buf * ss);
|
||||
n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */
|
||||
if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
|
||||
sect += n; nsect -= n;
|
||||
@@ -6072,13 +6093,13 @@ FRESULT f_mkfs (
|
||||
st_dword(buf + SZDIRE * 1 + 24, szb_bit); /* size */
|
||||
buf[SZDIRE * 2 + 0] = ET_UPCASE; /* Up-case table entry */
|
||||
st_dword(buf + SZDIRE * 2 + 4, sum); /* sum */
|
||||
st_dword(buf + SZDIRE * 2 + 20, 2 + tbl[0]); /* cluster */
|
||||
st_dword(buf + SZDIRE * 2 + 20, 2 + clen[0]); /* cluster */
|
||||
st_dword(buf + SZDIRE * 2 + 24, szb_case); /* size */
|
||||
sect = b_data + sz_au * (tbl[0] + tbl[1]); nsect = sz_au; /* Start of the root directory and number of sectors */
|
||||
sect = b_data + sz_au * (clen[0] + clen[1]); nsect = sz_au; /* Start of the root directory and number of sectors */
|
||||
do { /* Fill root directory sectors */
|
||||
n = (nsect > sz_buf) ? sz_buf : nsect;
|
||||
if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
|
||||
mem_set(buf, 0, ss);
|
||||
mem_set(buf, 0, ss); /* Rest of entries are filled with zero */
|
||||
sect += n; nsect -= n;
|
||||
} while (nsect);
|
||||
|
||||
@@ -6094,7 +6115,7 @@ FRESULT f_mkfs (
|
||||
st_dword(buf + BPB_FatSzEx, sz_fat); /* FAT size [sector] */
|
||||
st_dword(buf + BPB_DataOfsEx, (DWORD)(b_data - b_vol)); /* Data offset [sector] */
|
||||
st_dword(buf + BPB_NumClusEx, n_clst); /* Number of clusters */
|
||||
st_dword(buf + BPB_RootClusEx, 2 + tbl[0] + tbl[1]); /* Root dir cluster # */
|
||||
st_dword(buf + BPB_RootClusEx, 2 + clen[0] + clen[1]); /* Root dir cluster # */
|
||||
st_dword(buf + BPB_VolIDEx, GET_FATTIME()); /* VSN */
|
||||
st_word(buf + BPB_FSVerEx, 0x100); /* Filesystem version (1.00) */
|
||||
for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */
|
||||
@@ -6520,7 +6541,7 @@ static void putc_bfd (putbuff* pb, TCHAR c)
|
||||
WCHAR hs, wc;
|
||||
#if FF_LFN_UNICODE == 2
|
||||
DWORD dc;
|
||||
TCHAR *tp;
|
||||
const TCHAR *tp;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -6562,7 +6583,7 @@ static void putc_bfd (putbuff* pb, TCHAR c)
|
||||
return;
|
||||
}
|
||||
}
|
||||
tp = (TCHAR*)pb->bs;
|
||||
tp = (const TCHAR*)pb->bs;
|
||||
dc = tchar2uni(&tp); /* UTF-8 ==> UTF-16 */
|
||||
if (dc == 0xFFFFFFFF) return; /* Wrong code? */
|
||||
wc = (WCHAR)dc;
|
||||
@@ -6650,7 +6671,7 @@ static int putc_flush (putbuff* pb)
|
||||
if ( pb->idx >= 0 /* Flush buffered characters to the file */
|
||||
&& f_write(pb->fp, pb->buf, (UINT)pb->idx, &nw) == FR_OK
|
||||
&& (UINT)pb->idx == nw) return pb->nchr;
|
||||
return EOF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -6754,7 +6775,7 @@ int f_printf (
|
||||
d = c;
|
||||
if (IsLower(d)) d -= 0x20;
|
||||
switch (d) { /* Atgument type is... */
|
||||
case 'S' : /* String */
|
||||
case 'S': /* String */
|
||||
p = va_arg(arp, TCHAR*);
|
||||
for (j = 0; p[j]; j++) ;
|
||||
if (!(f & 2)) { /* Right padded */
|
||||
@@ -6764,21 +6785,26 @@ int f_printf (
|
||||
while (j++ < w) putc_bfd(&pb, ' ') ; /* Left padded */
|
||||
continue;
|
||||
|
||||
case 'C' : /* Character */
|
||||
putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue;
|
||||
case 'C': /* Character */
|
||||
putc_bfd(&pb, (TCHAR)va_arg(arp, int));
|
||||
continue;
|
||||
|
||||
case 'B' : /* Unsigned binary */
|
||||
r = 2; break;
|
||||
case 'B': /* Unsigned binary */
|
||||
r = 2;
|
||||
break;
|
||||
|
||||
case 'O' : /* Unsigned octal */
|
||||
r = 8; break;
|
||||
case 'O': /* Unsigned octal */
|
||||
r = 8;
|
||||
break;
|
||||
|
||||
case 'D' : /* Signed decimal */
|
||||
case 'U' : /* Unsigned decimal */
|
||||
r = 10; break;
|
||||
case 'D': /* Signed decimal */
|
||||
case 'U': /* Unsigned decimal */
|
||||
r = 10;
|
||||
break;
|
||||
|
||||
case 'X' : /* Unsigned hexdecimal */
|
||||
r = 16; break;
|
||||
case 'X': /* Unsigned hexdecimal */
|
||||
r = 16;
|
||||
break;
|
||||
|
||||
default: /* Unknown type (pass-through) */
|
||||
putc_bfd(&pb, c); continue;
|
||||
|
@@ -1,12 +1,32 @@
|
||||
/* Reflow Oven Controller
|
||||
*
|
||||
* Copyright (C) 2021 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 hw-version-detect
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <reflow-controller/hw-version-detect.h>
|
||||
#include <stm-periph/rcc-manager.h>
|
||||
#include <stm32/stm32f4xx.h>
|
||||
|
||||
#define HW_REV_DETECT_GPIO GPIOE
|
||||
#define HW_REV_DETECT_RCC_FIELD RCC_AHB1ENR_GPIOEEN
|
||||
#define HW_REV_DETECT_PIN_LOW (8U)
|
||||
#define HW_REV_DETECT_PIN_HIGH (15U)
|
||||
|
||||
enum hw_revision get_pcb_hardware_version(void)
|
||||
{
|
||||
uint8_t current_pin;
|
||||
@@ -46,3 +66,5 @@ enum hw_revision get_pcb_hardware_version(void)
|
||||
|
||||
return revision;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
@@ -1,8 +1,8 @@
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT Filesystem module R0.14 /
|
||||
/ FatFs - Generic FAT Filesystem module R0.14a /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 2019, ChaN, all right reserved.
|
||||
/ Copyright (C) 2020, ChaN, all right reserved.
|
||||
/
|
||||
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||
/ source and binary forms, with or without modification, are permitted provided
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
|
||||
#ifndef FF_DEFINED
|
||||
#define FF_DEFINED 86606 /* Revision ID */
|
||||
#define FF_DEFINED 80196 /* Revision ID */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -345,10 +345,6 @@ TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the fil
|
||||
#define f_rmdir(path) f_unlink(path)
|
||||
#define f_unmount(path) f_mount(0, path, 0)
|
||||
|
||||
#ifndef EOF
|
||||
#define EOF (-1)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
/ FatFs Functional Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FFCONF_DEF 86606 /* Revision ID */
|
||||
#define FFCONF_DEF 80196 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Function Configurations
|
||||
@@ -205,8 +205,8 @@
|
||||
/ To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */
|
||||
|
||||
|
||||
#define FF_MIN_GPT 0x100000000
|
||||
/* Minimum number of sectors to switch GPT format to create partition in f_mkfs and
|
||||
#define FF_MIN_GPT 0x10000000
|
||||
/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and
|
||||
/ f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */
|
||||
|
||||
|
||||
@@ -237,7 +237,7 @@
|
||||
#define FF_FS_NORTC 0
|
||||
#define FF_NORTC_MON 1
|
||||
#define FF_NORTC_MDAY 1
|
||||
#define FF_NORTC_YEAR 2019
|
||||
#define FF_NORTC_YEAR 2020
|
||||
/* The option FF_FS_NORTC switches timestamp functiton. If the system does not have
|
||||
/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
|
||||
/ the timestamp function. Every object modified by FatFs will have a fixed timestamp
|
||||
|
@@ -36,7 +36,7 @@
|
||||
/**
|
||||
* @brief Moving average filter coefficient for PT1000 measurement
|
||||
*/
|
||||
#define ADC_PT1000_FILTER_WEIGHT 0.005f
|
||||
#define ADC_PT1000_FILTER_WEIGHT 0.01f
|
||||
|
||||
/**
|
||||
* @brief Moving average filter weight used for fast regaulation. This is used when the measured resistance
|
||||
|
@@ -1,57 +0,0 @@
|
||||
/* Reflow Oven Controller
|
||||
*
|
||||
* Copyright (C) 2021 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 __CONFIG_PARSER_TEMP_PROFILE_PARSER_H__
|
||||
#define __CONFIG_PARSER_TEMP_PROFILE_PARSER_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum pl_command_type {
|
||||
PL_PID_CONF = 0,
|
||||
PL_SET_TEMP,
|
||||
PL_SET_RAMP,
|
||||
PL_WAIT_FOR_TEMP,
|
||||
PL_WAIT_FOR_TIME,
|
||||
PL_LOUDSPEAKER_SET,
|
||||
PL_OFF,
|
||||
_PL_NUM_CMDS,
|
||||
};
|
||||
|
||||
enum pl_ret_val {
|
||||
PL_RET_SUCCESS = 0,
|
||||
PL_RET_DISK_ERR,
|
||||
PL_RET_PARAM_ERR,
|
||||
PL_RET_LIST_FULL,
|
||||
PL_RET_SCRIPT_ERR,
|
||||
};
|
||||
|
||||
#define PROFILE_LANG_MAX_NUM_ARGS (8)
|
||||
|
||||
struct pl_command {
|
||||
enum pl_command_type cmd;
|
||||
float params[PROFILE_LANG_MAX_NUM_ARGS];
|
||||
};
|
||||
|
||||
enum pl_ret_val temp_profile_parse_from_file(const char *filename,
|
||||
struct pl_command *cmd_list,
|
||||
uint32_t cmd_list_length,
|
||||
uint32_t *cmds_parsed);
|
||||
|
||||
#endif /* __CONFIG_PARSER_TEMP_PROFILE_PARSER_H__ */
|
@@ -18,6 +18,11 @@
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup digio
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef __DIGIO_H__
|
||||
#define __DIGIO_H__
|
||||
|
||||
@@ -25,10 +30,22 @@
|
||||
#include <stdint.h>
|
||||
#include <helper-macros/helper-macros.h>
|
||||
|
||||
/**
|
||||
* @brief Port the digital I/Os are located in
|
||||
*/
|
||||
#define DIGIO_PORT GPIOB
|
||||
|
||||
/**
|
||||
* @brief Reset and clock control bit mask to enable clock of @ref DIGIO_PORT
|
||||
*/
|
||||
#define DIGIO_RCC_MASK RCC_AHB1ENR_GPIOBEN
|
||||
|
||||
/**
|
||||
* @brief List of the pins used by the digio module
|
||||
*/
|
||||
#define DIGIO_PINS 4,5,6,7
|
||||
|
||||
|
||||
#if defined(DEBUGBUILD) || defined(UART_ON_DEBUG_HEADER)
|
||||
#define DIGIO_INOUT_DEFAULT 0,0,0,0
|
||||
#define DIGIO_ALTFUNC_DEFAULT 0,0,0,0
|
||||
@@ -37,33 +54,124 @@
|
||||
#define DIGIO_ALTFUNC_DEFAULT 0,0,7,7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief The port, the loudspeaker is connected to
|
||||
*/
|
||||
#define BEEPER_PORT GPIOB
|
||||
|
||||
/**
|
||||
* @brief The RCC mask for @ref BEEPER_PORT
|
||||
*/
|
||||
#define BEEPER_RCC_MASK RCC_AHB1ENR_GPIOBEN
|
||||
|
||||
|
||||
/**
|
||||
* @brief Enable all clocks and setup pins in default setting
|
||||
* @warning This function uses @ref rcc_manager_enable_clock to enable the clocks. Therefore, it must not be called
|
||||
* multiple times.
|
||||
*/
|
||||
void digio_setup_default_all(void);
|
||||
|
||||
/**
|
||||
* @brief Set up a DIGIO pin.
|
||||
*
|
||||
* Set up a digital IO pin config. For the index association see the @ref DIGIO_PINS table.
|
||||
*
|
||||
* @param num DIGIO number starting with 0
|
||||
* @param in_out In: 0, Out 1, alternate function: 2
|
||||
* @param alt_func Alternate function to set. Consult datasheet of STM32F407
|
||||
*/
|
||||
void digio_setup_pin(uint8_t num, uint8_t in_out, uint8_t alt_func);
|
||||
|
||||
/**
|
||||
* @brief Set a digio output port
|
||||
* @param num Digio channel number
|
||||
* @param val Value: 0 or 1.
|
||||
*/
|
||||
void digio_set(uint8_t num, int val);
|
||||
|
||||
/**
|
||||
* @brief Read the state of a digio channel
|
||||
* @param num Channel number
|
||||
* @return digital pin level.
|
||||
*/
|
||||
int digio_get(uint8_t num);
|
||||
|
||||
/**
|
||||
* @brief Port the LEDs are connected to
|
||||
*/
|
||||
#define LED_PORT GPIOB
|
||||
|
||||
/**
|
||||
* @brief RCC mask to enable clock for @ref LED_PORT
|
||||
*/
|
||||
#define LED_RCC_MASK RCC_AHB1ENR_GPIOBEN
|
||||
|
||||
/**
|
||||
* @brief Pin list on @ref LED_PORT that are connected to LEDs
|
||||
*/
|
||||
#define LED_PINS 2,3
|
||||
|
||||
/**
|
||||
* @brief Set up the LED Port
|
||||
*/
|
||||
void led_setup(void);
|
||||
|
||||
/**
|
||||
* @brief Set the LED status
|
||||
* @param num LED number
|
||||
* @param val Value: 1 on, 0 off.
|
||||
*/
|
||||
void led_set(uint8_t num, int val);
|
||||
|
||||
/**
|
||||
* @brief Get the state of a LED
|
||||
* @param num LED number
|
||||
* @return Stateof the LED: 0 off, 1 on; -1 in case of an error
|
||||
*/
|
||||
int led_get(uint8_t num);
|
||||
|
||||
/**
|
||||
* @brief Port to use for the loudpseaker
|
||||
*/
|
||||
#define LOUDSPEAKER_PORT GPIOB
|
||||
|
||||
/**
|
||||
* @brief RCC mask for @ref LOUDSPEAKER_PORT
|
||||
*/
|
||||
#define LOUDSPEAKER_RCC_MASK RCC_AHB1ENR_GPIOBEN
|
||||
|
||||
/**
|
||||
* @brief Pin on @ref LOUDSPEAKER_PORT the speaker is cnnected to
|
||||
*/
|
||||
#define LOUDSPEAKER_PIN 1
|
||||
|
||||
/**
|
||||
* @brief The loudpseaker requires a frequncy signal instead of a simple on/off signal.
|
||||
*/
|
||||
#define LOUDSPEAKER_MULTIFREQ 1
|
||||
|
||||
/**
|
||||
* @brief Defautl timer relaod for the frequency of the speaker. Only relevant if @ref LOUDSPEAKER_MULTIFREQ is 1.
|
||||
*/
|
||||
#define LOUDSPEAKER_MULTIFREQ_DEFAULT 9
|
||||
|
||||
/**
|
||||
* @brief Setup the loudspeaker
|
||||
*/
|
||||
void loudspeaker_setup(void);
|
||||
|
||||
/**
|
||||
* @brief Set the loudspeaker value
|
||||
* @param val Value
|
||||
*/
|
||||
void loudspeaker_set(uint16_t val);
|
||||
|
||||
/**
|
||||
* @brief Get current value of the loadspeaker
|
||||
* @return Value
|
||||
*/
|
||||
uint16_t loudspeaker_get(void);
|
||||
|
||||
|
||||
#endif /* __DIGIO_H__ */
|
||||
|
||||
/** @} */
|
||||
|
@@ -1,8 +1,54 @@
|
||||
/* Reflow Oven Controller
|
||||
*
|
||||
* Copyright (C) 2021 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 hw-version-detect
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _HW_VERSION_DETECT_H_
|
||||
#define _HW_VERSION_DETECT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief GPIO Port used for hardware version detection
|
||||
*/
|
||||
#define HW_REV_DETECT_GPIO GPIOE
|
||||
|
||||
/**
|
||||
* @brief RCC Clock register mask used to enable @ref HW_REV_DETECT_GPIO GPIO Port
|
||||
*/
|
||||
#define HW_REV_DETECT_RCC_FIELD RCC_AHB1ENR_GPIOEEN
|
||||
|
||||
/**
|
||||
* @brief Lowest pin on @ref HW_REV_DETECT_GPIO GPIO Port that shall be used to read in the hardware version
|
||||
*/
|
||||
#define HW_REV_DETECT_PIN_LOW (8U)
|
||||
|
||||
/**
|
||||
* @brief Highest pin on @ref HW_REV_DETECT_GPIO GPIO Port that shall be used to read in the hardware version
|
||||
*/
|
||||
#define HW_REV_DETECT_PIN_HIGH (15U)
|
||||
|
||||
|
||||
/**
|
||||
* @brief PCB/Hardware Revision Type
|
||||
*/
|
||||
@@ -32,3 +78,5 @@ enum hw_revision {
|
||||
enum hw_revision get_pcb_hardware_version(void);
|
||||
|
||||
#endif /* _HW_VERSION_DETECT_H_ */
|
||||
|
||||
/** @} */
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* Reflow Oven Controller
|
||||
*
|
||||
* Copyright (C) 2021 Mario Hüttel <mario.huettel@gmx.net>
|
||||
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
|
||||
*
|
||||
* This file is part of the Reflow Oven Controller Project.
|
||||
*
|
||||
@@ -18,35 +18,15 @@
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __TEMP_PROFILE_EXECUTER_H__
|
||||
#define __TEMP_PROFILE_EXECUTER_H__
|
||||
#ifndef __MAIN_CYCLE_COUNTER_H__
|
||||
#define __MAIN_CYCLE_COUNTER_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <reflow-controller/config-parser/temp-profile-parser.h>
|
||||
|
||||
#define MAX_PROFILE_LENGTH 50
|
||||
void main_cycle_counter_init(void);
|
||||
|
||||
enum tpe_status {
|
||||
TPE_OFF,
|
||||
TPE_RUNNING,
|
||||
TPE_ABORT,
|
||||
};
|
||||
void main_cycle_counter_inc(void);
|
||||
|
||||
struct tpe_current_state {
|
||||
enum tpe_status status;
|
||||
float setpoint;
|
||||
uint64_t start_timestamp;
|
||||
uint32_t step;
|
||||
uint32_t profile_steps;
|
||||
enum pl_command_type current_command;
|
||||
};
|
||||
uint64_t main_cycle_counter_get(void);
|
||||
|
||||
enum pl_ret_val temp_profile_executer_start(const char *filename);
|
||||
|
||||
int temp_profile_executer_handle(void);
|
||||
|
||||
const struct tpe_current_state *temp_profile_executer_status(void);
|
||||
|
||||
int temp_profile_executer_stop(void);
|
||||
|
||||
#endif /* __TEMP_PROFILE_EXECUTER_H__ */
|
||||
#endif /* __MAIN_CYCLE_COUNTER_H__ */
|
@@ -18,35 +18,150 @@
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @addtogroup pid-controller
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef __PID_CONTROLLER_H__
|
||||
#define __PID_CONTROLLER_H__
|
||||
|
||||
/**
|
||||
* @brief Representation of a PID controller
|
||||
*/
|
||||
struct pid_controller {
|
||||
/**
|
||||
* @brief Derivate term @f$k_d@f$
|
||||
*/
|
||||
float k_deriv;
|
||||
|
||||
/**
|
||||
* @brief Integral term @f$k_i@f$
|
||||
*/
|
||||
float k_int;
|
||||
|
||||
/**
|
||||
* @brief Proportional term @f$k_p@f$
|
||||
*/
|
||||
float k_p;
|
||||
|
||||
/**
|
||||
* @brief Internally precalculated auxiliary value. @f$k_{i_t} = \frac{1}{2}k_{i} \cdot T_s@f$
|
||||
*
|
||||
* This value is caluclated during the @ref pid_init function to reduce calculations during the continous
|
||||
* PID sampling.
|
||||
*/
|
||||
float k_int_t;
|
||||
|
||||
/**
|
||||
* @brief Internally precalculated auxiliary value. @f$k_{d_t} = 2\frac{k_{d}}{T_s + 2 k_{d\tau}}@f$
|
||||
*
|
||||
* This value is caluclated during the @ref pid_init function to reduce calculations during the continous
|
||||
* PID sampling.
|
||||
*/
|
||||
float k_deriv_t;
|
||||
|
||||
/**
|
||||
* @brief Internally precalculated auxiliary value. @f$\overline{k_{d_t}} = \frac{2 k_{d\tau} - T_s}{ 2k_{d\tau} + T_s}@f$
|
||||
*
|
||||
* This value is caluclated during the @ref pid_init function to reduce calculations during the continous
|
||||
* PID sampling.
|
||||
*/
|
||||
float k_inv_deriv_t;
|
||||
|
||||
/**
|
||||
* @brief Saturation of output value
|
||||
* @note This value is set to 100 (corresponding to 100%) for the temperature controller
|
||||
*/
|
||||
float output_sat_max;
|
||||
|
||||
/**
|
||||
* @brief Minumum saturation of PID output value.
|
||||
* @note This value is set to 0 for the temperature controller as the oven driver is not able to cool
|
||||
*/
|
||||
float output_sat_min;
|
||||
|
||||
/**
|
||||
* @brief Maximum value @f$i_{max}@f$ the inegrator in the PID controller can reach.
|
||||
*
|
||||
* The output of the integral term of the PID controller is limited to @f$\pm i_{max}@f$
|
||||
*
|
||||
*/
|
||||
float integral_max;
|
||||
|
||||
/**
|
||||
* @brief Sampling period @f$T_s@f$ of the PID controller in seconds
|
||||
*/
|
||||
float sample_period;
|
||||
|
||||
/**
|
||||
* @brief Output value of the PID controller
|
||||
*/
|
||||
volatile float control_output;
|
||||
|
||||
/**
|
||||
* @brief Internal state variable holding the last input value.
|
||||
*/
|
||||
volatile float last_in;
|
||||
|
||||
/**
|
||||
* @brief integral term's value
|
||||
*/
|
||||
volatile float integral;
|
||||
|
||||
/**
|
||||
* @brief derivate term's value
|
||||
*/
|
||||
volatile float derivate;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Initialize a @ref pid_controller struct
|
||||
* @param pid Struct to initilaize
|
||||
* @param k_deriv Derivate term
|
||||
* @param k_int integral term
|
||||
* @param k_p Proportional term
|
||||
* @param output_sat_min Minimum output saturation
|
||||
* @param output_sat_max Maximum output saturation
|
||||
* @param integral_max Maximum integral term
|
||||
* @param kd_tau Time constant of derivate term low pass filter
|
||||
* @param sample_period Sample time in seconds
|
||||
*/
|
||||
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 kd_tau, float sample_period);
|
||||
|
||||
/**
|
||||
* @brief Reset a PID controller.
|
||||
*
|
||||
* This sets all internal states to 0.
|
||||
*
|
||||
* @param pid Controller
|
||||
*/
|
||||
void pid_zero(struct pid_controller *pid);
|
||||
|
||||
/**
|
||||
* @brief Execute a tiome step of the PID controller. This function must be periodically called in an pid_controller::sample_period interval
|
||||
* @note This function does not check its calling interval. It must be ensured by the caller.
|
||||
* @param pid Pid controller to execute
|
||||
* @param deviation Input deviation
|
||||
* @return Current controller output after the calculated time step
|
||||
*/
|
||||
float pid_sample(struct pid_controller *pid, float deviation);
|
||||
|
||||
/**
|
||||
* @brief Retrieve the controller's current output
|
||||
* @param pid Pid controller
|
||||
* @return Output
|
||||
*/
|
||||
float pid_get_control_output(const struct pid_controller *pid);
|
||||
|
||||
/**
|
||||
* @brief Duplicate a PID controller
|
||||
* @param dest destination controller
|
||||
* @param src Source controller
|
||||
* @return 0 if successful
|
||||
*/
|
||||
int pid_copy(struct pid_controller *dest, const struct pid_controller *src);
|
||||
|
||||
#endif /* __PID_CONTROLLER_H__ */
|
||||
|
||||
/** @} */
|
||||
|
@@ -67,7 +67,7 @@ int safety_adc_poll_result(void);
|
||||
* After that, it is safe to use the output values until a new conversion is triggered using #safety_adc_trigger_meas
|
||||
*
|
||||
* @warning This function return a constant buffer, that is directly written on by the DMA! Check #safety_adc_poll_result to prevent race conditions.
|
||||
* @return Array of raw ADC readings with lenght of #SAFETY_ADC_NUM_OF_CHANNELS
|
||||
* @return Array of raw ADC readings with length of #SAFETY_ADC_NUM_OF_CHANNELS
|
||||
*/
|
||||
const uint16_t *safety_adc_get_values(void);
|
||||
|
||||
|
@@ -56,6 +56,10 @@ enum safety_flag {
|
||||
ERR_FLAG_SAFETY_TAB_CORRUPT = (1<<16),
|
||||
ERR_FLAG_AMON_SUPPLY_VOLT = (1<<17),
|
||||
ERR_FLAG_OVERTEMP = (1<<18),
|
||||
ERR_FLAG_FLASH_CRC_CODE = (1<<19),
|
||||
ERR_FLAG_FLASH_CRC_DATA = (1<<20),
|
||||
ERR_FLAG_CFG_CRC_MEAS_ADC = (1<<21),
|
||||
ERR_FLAG_CFG_CRC_SAFETY_ADC = (1<<22),
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -71,6 +75,12 @@ enum timing_monitor {
|
||||
ERR_TIMING_MAIN_LOOP = (1<<3),
|
||||
};
|
||||
|
||||
enum crc_monitor {
|
||||
ERR_CRC_MON_MEAS_ADC = 0,
|
||||
ERR_CRC_MON_SAFETY_ADC,
|
||||
N_ERR_CRC_MON
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Enum Type representing analog value monitors.
|
||||
*
|
||||
@@ -144,6 +154,19 @@ enum analog_value_monitor {
|
||||
*/
|
||||
#define SAFETY_CONTROLLER_ADC_DELAY_MS 250
|
||||
|
||||
/**
|
||||
* @brief Password for resetting ERR_CRC_MON_MEAS_ADC
|
||||
*/
|
||||
#define SAFETY_CRC_MON_MEAS_ADC_PW 0xD96254A2
|
||||
|
||||
/**
|
||||
* @brief Password for resetting ERR_CRC_MON_SAFETY_ADC
|
||||
*/
|
||||
#define SAFETY_CRC_MON_SAFETY_ADC_PW 0xA8DF2368
|
||||
|
||||
/**
|
||||
* @brief Default persistence of safety flags. These values are loaded into the safety tables on startup
|
||||
*/
|
||||
#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), \
|
||||
@@ -163,7 +186,13 @@ enum analog_value_monitor {
|
||||
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_SAFETY_TAB_CORRUPT, true), \
|
||||
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_AMON_SUPPLY_VOLT, false), \
|
||||
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_OVERTEMP, false), \
|
||||
|
||||
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_FLASH_CRC_CODE, true), \
|
||||
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_FLASH_CRC_DATA, true), \
|
||||
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_CFG_CRC_MEAS_ADC, true), \
|
||||
ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_CFG_CRC_SAFETY_ADC, true), \
|
||||
/**
|
||||
* @brief Default config weights of safety flags. These values are loaded into the safety tables on startup
|
||||
*/
|
||||
#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), \
|
||||
@@ -175,9 +204,7 @@ enum analog_value_monitor {
|
||||
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 periodically, 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_WTCHDG_FIRED, SAFETY_FLAG_CONFIG_WEIGHT_PID), \
|
||||
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), \
|
||||
@@ -185,5 +212,9 @@ enum analog_value_monitor {
|
||||
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_SAFETY_TAB_CORRUPT, SAFETY_FLAG_CONFIG_WEIGHT_PANIC), \
|
||||
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_AMON_SUPPLY_VOLT, SAFETY_FLAG_CONFIG_WEIGHT_PID), \
|
||||
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_OVERTEMP, SAFETY_FLAG_CONFIG_WEIGHT_PID), \
|
||||
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_FLASH_CRC_CODE, SAFETY_FLAG_CONFIG_WEIGHT_NONE), \
|
||||
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_FLASH_CRC_DATA, SAFETY_FLAG_CONFIG_WEIGHT_NONE), \
|
||||
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_CFG_CRC_MEAS_ADC, SAFETY_FLAG_CONFIG_WEIGHT_NONE), \
|
||||
ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_CFG_CRC_SAFETY_ADC, SAFETY_FLAG_CONFIG_WEIGHT_NONE), \
|
||||
|
||||
#endif /* __SAFETY_CONFIG_H__ */
|
||||
|
@@ -267,6 +267,21 @@ int safety_controller_set_overtemp_limit(float over_temperature);
|
||||
*/
|
||||
float safety_controller_get_overtemp_limit(void);
|
||||
|
||||
/**
|
||||
* @brief Perform a CRC check of the flash memory and set appropriate flags
|
||||
* @return negative if internal error occured. Otherwise (independent from CRC check result) 0.
|
||||
* @note This function requires the safety controller to be set up before!
|
||||
*/
|
||||
int safety_controller_trigger_flash_crc_check(void);
|
||||
|
||||
/**
|
||||
* @brief Recalculate the CRC of a given CRC Monitor. This has to be done once the supervised registers update
|
||||
* @param mon Monitor to recalculate
|
||||
* @param password Password
|
||||
* @return 0 if successful
|
||||
*/
|
||||
int safety_controller_set_crc_monitor(enum crc_monitor mon, uint32_t password);
|
||||
|
||||
#endif /* __SAFETY_CONTROLLER_H__ */
|
||||
|
||||
/** @} */
|
||||
|
@@ -35,9 +35,14 @@
|
||||
#define SAFETY_MEMORY_MAGIC 0x12AA5CB7
|
||||
|
||||
/**
|
||||
* @brief Error memory NOP entry
|
||||
* @brief Error memory NOP entry word written to the memory
|
||||
*/
|
||||
#define SAFETY_MEMORY_NOP_ENTRY 0xC1AA1222
|
||||
#define SAFETY_MEMORY_NOP_ENTRY_WORD 0xC1AA1222UL
|
||||
|
||||
/**
|
||||
* @brief Low Byte (byte 0) of error memory entry
|
||||
*/
|
||||
#define SAFETY_MEMORY_ERROR_ENTRY_MARKER 0x51U
|
||||
|
||||
/**
|
||||
* @brief Offset address for the safety_memory_header.
|
||||
@@ -47,6 +52,8 @@
|
||||
|
||||
#define SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT 32UL
|
||||
|
||||
#define SAFETY_MEMORY_UPDATE_FILENAME_MAXSIZE 256U
|
||||
|
||||
/**
|
||||
* @brief Safety memory header
|
||||
*/
|
||||
@@ -55,6 +62,7 @@ struct safety_memory_header {
|
||||
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 firmware_update_filename; /**< @brief Filename of the firmware update. This string is at maximum 256 bytes long including the 0 terminator */
|
||||
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 */
|
||||
@@ -240,13 +248,35 @@ int safety_memory_insert_config_override(struct config_override *config_override
|
||||
int safety_memory_get_config_override_count(uint32_t *count);
|
||||
|
||||
/**
|
||||
* @brief Get a config ovveide entry
|
||||
* @brief Get a config 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 Read the set update filename from the safety backup memory
|
||||
*
|
||||
* \p filename has to be large enough to hold the filename (255 chars) plus the '\0'-terminator.
|
||||
*
|
||||
* @param[out] filename Array to fill in file name. May be NULL to only read the length.
|
||||
* @param[out] outlen String length of the filename. May be NULL to only read the file name.
|
||||
* @note \p filename may be NULL. In this case this function can be used to read the currently set filename's length.
|
||||
* @warning Function will fail if both parameters are NULL
|
||||
* @return 0 if successful
|
||||
*/
|
||||
int safety_memory_get_update_filename(char *filename, size_t *outlen);
|
||||
|
||||
/**
|
||||
* @brief Set the filename of the update file
|
||||
* @param[in] filename Filename to set. Must be 255 chars at max (256 including null terminator)
|
||||
* @return 0 if successful
|
||||
*/
|
||||
int safety_memory_set_update_filename(const char *filename);
|
||||
|
||||
#ifndef SAFETY_MEMORY_STRIPOUT_DUMP
|
||||
|
||||
/**
|
||||
* @brief Get a base64 dump of the whole safety memory.
|
||||
* @param[out] buffer Buffer to write the base 64 dump into.
|
||||
@@ -256,6 +286,8 @@ int safety_memory_get_config_override(uint32_t idx, struct config_override *conf
|
||||
*/
|
||||
int safety_memory_dump_base64(char *buffer, size_t buffsize, size_t *used_size);
|
||||
|
||||
#endif /* SAFETY_MEMORY_STRIPOUT_DUMP */
|
||||
|
||||
#endif /* __SAFETY_MEMORY_H__ */
|
||||
|
||||
/** @} */
|
||||
|
@@ -24,6 +24,13 @@
|
||||
#include <stdbool.h>
|
||||
#include <reflow-controller/settings/settings.h>
|
||||
|
||||
/**
|
||||
* @brief Save the calibration to the SD card
|
||||
* @param sens_deviation Sensitivity deviation from nomila sensitivity (1)
|
||||
* @param offset Offset
|
||||
* @param active Calibration is active. If false, the calibration will be deleted from SD card
|
||||
* @return 0 if successful
|
||||
*/
|
||||
int sd_card_settings_save_calibration(float sens_deviation, float offset, bool active);
|
||||
|
||||
/**
|
||||
@@ -34,6 +41,13 @@ int sd_card_settings_save_calibration(float sens_deviation, float offset, bool a
|
||||
*/
|
||||
int sd_card_settings_try_load_calibration(float *sens_deviation, float *offset);
|
||||
|
||||
/**
|
||||
* @brief Load the PID parameters from @ref SETTINGS_PID_PARAMETER_FILE
|
||||
* @param settings The PID settings
|
||||
* @return Load result.
|
||||
* @note This function is currently not used for the temperature profiles. They implement the PID parameters directy
|
||||
* inside the profile
|
||||
*/
|
||||
enum settings_load_result sd_card_settings_load_pid_oven_parameters(struct oven_pid_settings *settings);
|
||||
|
||||
#endif /* __SETTINGS_SETTINGS_SD_CARD_H__ */
|
||||
|
@@ -18,26 +18,31 @@
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __SETTINGS_SETTINGS_H__
|
||||
#define __SETTINGS_SETTINGS_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* @brief Settings for the PID controller that are stored in the config file
|
||||
*/
|
||||
struct oven_pid_settings {
|
||||
float kd;
|
||||
float kp;
|
||||
float ki;
|
||||
float kd_tau;
|
||||
float t_sample;
|
||||
float max_integral;
|
||||
float kd; /**< @brief Derivate term */
|
||||
float kp; /**< @brief Proportional term */
|
||||
float ki; /**< @brief Integral term */
|
||||
float kd_tau; /**< @brief Time constant of the derivate term's low pass filter */
|
||||
float t_sample; /**< @brief Sampling time in seconds. @warning The loading function expects the file to hold a ms value */
|
||||
float max_integral; /**< @brief Maximum value of the intgral term */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Results of a setting loading
|
||||
*/
|
||||
enum settings_load_result {
|
||||
SETT_LOAD_SUCCESS = 0,
|
||||
SETT_LOAD_FILE_NOT_FOUND,
|
||||
SETT_LOAD_ERR,
|
||||
SETT_LOAD_DISK_ERR
|
||||
SETT_LOAD_SUCCESS = 0, /**< @brief Setting loaded successfully */
|
||||
SETT_LOAD_FILE_NOT_FOUND, /**< @brief File not found. This is only used by Settings on the SD card */
|
||||
SETT_LOAD_ERR, /**< @brief Generic loading error */
|
||||
SETT_LOAD_DISK_ERR, /**< @brief Disk access failure during loading */
|
||||
};
|
||||
|
||||
#define SETTINGS_PID_PARAMETER_FILE "pid.conf"
|
||||
@@ -50,14 +55,49 @@ enum settings_load_result {
|
||||
*/
|
||||
int settings_save_calibration(float sens_deviation, float offset, bool active);
|
||||
|
||||
/**
|
||||
* @brief Load the calibration
|
||||
*
|
||||
* If an EEPROM is present, it is first tried to be retrieved from EEPROM.
|
||||
* If there is no EEPROM or there is no valid data inside, it is tried to be loaded from SD card.
|
||||
*
|
||||
* @param sens_dev Sensiotivity deviation
|
||||
* @param offset Offset
|
||||
* @return 0 if successful and calibration valid
|
||||
*/
|
||||
int settings_load_calibration(float *sens_dev, float *offset);
|
||||
|
||||
/**
|
||||
* @brief Load PID overn parameters from file on SD card. This function is not implemented for EEPROM.
|
||||
* @param settings settings
|
||||
* @return Load result
|
||||
*/
|
||||
enum settings_load_result settings_load_pid_oven_parameters(struct oven_pid_settings *settings);
|
||||
|
||||
/**
|
||||
* @brief read the configured overtemperature limit
|
||||
* @param[out] over_temp_limit Overtemperature limit in degrees Celsius
|
||||
* @return Load result
|
||||
*/
|
||||
enum settings_load_result settings_load_overtemp_limit(float *over_temp_limit);
|
||||
|
||||
/**
|
||||
* @brief Save the overtemperature limit
|
||||
* @param over_temp_limit Limit in degrees Celsius
|
||||
* @param active Overtemperature limit active. If false: The config is delted and the controller uses its default limit
|
||||
* @return 0 if successful
|
||||
*/
|
||||
int settings_save_overtemp_limit(float over_temp_limit, bool active);
|
||||
|
||||
/**
|
||||
* @brief Setup the settings module
|
||||
*
|
||||
* This function has to be called before performing any settings operations.
|
||||
* It checks if an EEPTROM is connected and sets the appropriate settings storage in this case.
|
||||
*
|
||||
* EEPROM storage will only be available for HW versions > 1.3. Some functions require an EEPROM because the counterpart
|
||||
* on the SD card is not defined. These functions will fail without an EEPROM.
|
||||
*/
|
||||
void settings_setup(void);
|
||||
|
||||
#endif /* __SETTINGS_SETTINGS_H__ */
|
||||
|
@@ -24,19 +24,59 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize the SPI for the eeprom.
|
||||
* @return 0 if succesful
|
||||
*/
|
||||
int spi_eeprom_init();
|
||||
|
||||
/**
|
||||
* @brief Uninitialize the SPI EEPROM
|
||||
*/
|
||||
void spi_eeprom_deinit();
|
||||
|
||||
/**
|
||||
* @brief Read from SPI EEPROM
|
||||
* @param addr address to read from
|
||||
* @param rx_buff buffer to write data to
|
||||
* @param count Amount of bytes to read
|
||||
* @return 0 if successful
|
||||
*/
|
||||
int spi_eeprom_read(uint32_t addr, uint8_t *rx_buff, uint32_t count);
|
||||
|
||||
/**
|
||||
* @brief Check if the EEPROM is currently performing a write and therefore cannot serve other requests
|
||||
* @return true: Write in Progress, false: No write active
|
||||
*/
|
||||
bool spi_eeprom_write_in_progress(void);
|
||||
|
||||
/**
|
||||
* @brief Write data to the EEPROM
|
||||
* @param addr Address to write to
|
||||
* @param data Data to write
|
||||
* @param count Amount of bytes to write
|
||||
* @return 0 if successful
|
||||
* @note The page handling of the EEPROM is done internally. When using this function there is no need to worry about
|
||||
* the 16 byte page boundaries of the EEPROM
|
||||
*/
|
||||
int spi_eeprom_write(uint32_t addr, const uint8_t *data, uint32_t count);
|
||||
|
||||
/**
|
||||
* @brief Read the status register of the EEPROM
|
||||
* @return status register
|
||||
*/
|
||||
uint8_t spi_eeprom_read_status_reg(void);
|
||||
|
||||
/**
|
||||
* @brief Check if an EEPROM is connected to the SPI.
|
||||
*
|
||||
* This is done by trying to set the write enable latch in the status register and reading it back.
|
||||
* After it has been set, it is immediately reset.
|
||||
*
|
||||
* If it can't be read back, no EEPROM is connected
|
||||
*
|
||||
* @return true: EEPROM is connected, false: No (compatible) EEPROM found
|
||||
*/
|
||||
bool spi_eeprom_check_connected(void);
|
||||
|
||||
#endif /* __SETTINGS_SPI_EEPROM_H__ */
|
||||
|
@@ -69,10 +69,28 @@ void systick_setup(void);
|
||||
*/
|
||||
void systick_wait_ms(uint32_t ms);
|
||||
|
||||
uint64_t systick_get_global_tick();
|
||||
/**
|
||||
* @brief Get the gloabl millisecond tick. This can be used to implement all sorts of time based procedures / waits.
|
||||
* @warning Use this with care in interrupts. It may lead to race conditions. It is generally safe for use in standard program flow though.
|
||||
* @return Global millisecond tick.
|
||||
*/
|
||||
uint64_t systick_get_global_tick(void);
|
||||
|
||||
/**
|
||||
* @brief Calculate the uptime from the current millisecond tick.
|
||||
* @param[out] days Days. May be NULL.
|
||||
* @param[out] hours Hours. May be NULL.
|
||||
* @param[out] minutes Minutes. May be NULL.
|
||||
* @param[out] seconds Seconds. May be NULL.
|
||||
*/
|
||||
void systick_get_uptime_from_tick(uint32_t *days, uint32_t *hours, uint32_t *minutes, uint32_t *seconds);
|
||||
|
||||
/**
|
||||
* @brief Check if \p ticks (milliseconds) have passed since \p start_timestamp
|
||||
* @param start_timestamp Start timestamp
|
||||
* @param ticks tick count
|
||||
* @return true: Time has passed
|
||||
*/
|
||||
bool systick_ticks_have_passed(uint64_t start_timestamp, uint64_t ticks);
|
||||
|
||||
#endif /* __SYSTICK_H__ */
|
||||
|
@@ -36,6 +36,6 @@ int temp_converter_convert_resistance_to_temp(float resistance, float *temp_out)
|
||||
* @return 0 if ok, -1 if tmeperature is below the lookup table, 1 if value is above the lookup table, -1000 in case of a pointer error,
|
||||
* -100 if an internal error occured. This should never happen, if the lookup table is correct
|
||||
*/
|
||||
int temp_convertet_convert_temp_to_resistance(float temp, float *resistance_out);
|
||||
int temp_converter_convert_temp_to_resistance(float temp, float *resistance_out);
|
||||
|
||||
#endif /* __TEMP_CONVERTER_H__ */
|
||||
|
@@ -0,0 +1,94 @@
|
||||
/* Reflow Oven Controller
|
||||
*
|
||||
* Copyright (C) 2021 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 temp-profile
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef __TEMP_PROFILE_EXECUTER_H__
|
||||
#define __TEMP_PROFILE_EXECUTER_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <reflow-controller/temp-profile/temp-profile-parser.h>
|
||||
|
||||
/**
|
||||
* @brief Maximum command count of a temperature profile the parser will handle
|
||||
*/
|
||||
#define MAX_PROFILE_LENGTH 64
|
||||
|
||||
/**
|
||||
* @brief Status of the temperature profile execution
|
||||
*/
|
||||
enum tpe_status {
|
||||
TPE_OFF, /**< @brief No profile execution */
|
||||
TPE_RUNNING, /**< @brief Profile currently running */
|
||||
TPE_ABORT, /**< @brief Profile execution aborted due to event / error */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The current execution state of the temperature profile
|
||||
*/
|
||||
struct tpe_current_state {
|
||||
enum tpe_status status; /**< @brief Execution status */
|
||||
float setpoint; /**< @brief Temperature setpoint in degrees Celsius */
|
||||
uint64_t start_timestamp; /**< @brief The millisicend tick timestamp, the profile execution was started */
|
||||
uint32_t step; /**< @brief Current execution step. Starts at 0 */
|
||||
uint32_t profile_steps; /**< @brief Number of steps (commands) in profile */
|
||||
enum pl_command_type current_command; /**< @brief The currently executed command */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Start a temperature profile execution from a file on SD card.
|
||||
* @param filename Filename of the temperature profile
|
||||
* @return Status. PL_RET_SUCCESS id profile was parsed and started successfully
|
||||
*/
|
||||
enum pl_ret_val temp_profile_executer_start(const char *filename);
|
||||
|
||||
/**
|
||||
* @brief Handle the profile execution
|
||||
*
|
||||
* The function checks if since the last time it ran, 100 ms have passed. If yes,
|
||||
* it proceeds with the command execution.
|
||||
*
|
||||
* @note This must be periodically called.
|
||||
* @return 0 if successul, negative if profile not running or aborted or temperature set and PID not configured.
|
||||
*/
|
||||
int temp_profile_executer_handle(void);
|
||||
|
||||
/**
|
||||
* @brief Get the curretn execution state of the temperature profile executer
|
||||
* @warning The returned state structure is static and used internally. You must not modify it.
|
||||
* @return Execution state
|
||||
*/
|
||||
const struct tpe_current_state *temp_profile_executer_status(void);
|
||||
|
||||
/**
|
||||
* @brief Stop the temperature profile execution.
|
||||
*
|
||||
* The profile execution is stopped and the beeper is deactivated.
|
||||
*
|
||||
* @return always 0. This function cannot fail.
|
||||
*/
|
||||
int temp_profile_executer_stop(void);
|
||||
|
||||
#endif /* __TEMP_PROFILE_EXECUTER_H__ */
|
||||
|
||||
/** @} */
|
@@ -0,0 +1,102 @@
|
||||
/* Reflow Oven Controller
|
||||
*
|
||||
* Copyright (C) 2021 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 temp-profile
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef __CONFIG_PARSER_TEMP_PROFILE_PARSER_H__
|
||||
#define __CONFIG_PARSER_TEMP_PROFILE_PARSER_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <linklist-lib/singly-linked-list.h>
|
||||
|
||||
/**
|
||||
* @brief Command types the temperature profile language supports
|
||||
*/
|
||||
enum pl_command_type {
|
||||
PL_PID_CONF = 0, /**< @brief Configure the PID parameters and start PID controller */
|
||||
PL_SET_TEMP, /**< @brief Set the target temperature of the PID controller */
|
||||
PL_SET_RAMP, /**< @brief Perform a temperature ramp */
|
||||
PL_WAIT_FOR_TEMP, /**< @brief Wait until a temperature is reached */
|
||||
PL_WAIT_FOR_TIME, /**< @brief Wait for a specific amount of time */
|
||||
PL_LOUDSPEAKER_SET, /**< @brief Set the loudspeaker/beeper */
|
||||
PL_OFF, /**< @brief Disable the temperature output and shutdown the PID controller */
|
||||
PL_CLEAR_FLAGS, /**< @brief Try clear all flags */
|
||||
_PL_NUM_CMDS, /**< @brief Sentinel to determine the total amount of commands */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Profile language parser return value
|
||||
*/
|
||||
enum pl_ret_val {
|
||||
PL_RET_SUCCESS = 0, /**< @brief Profile parsed successfully */
|
||||
PL_RET_DISK_ERR, /**< @brief Disk I/O error */
|
||||
PL_RET_PARAM_ERR, /**< @brief Function parameter error */
|
||||
PL_RET_LIST_FULL, /**< @brief Command list is full. Temperature profile is longer than the specified maximum */
|
||||
PL_RET_SCRIPT_ERR, /**< @brief Syntax error in temperature profile */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Maximum parameter count of a command
|
||||
*/
|
||||
#define PROFILE_LANG_MAX_NUM_ARGS (8)
|
||||
|
||||
/**
|
||||
* @brief Structure representing a command in a temperature profile.
|
||||
*/
|
||||
struct pl_command {
|
||||
enum pl_command_type cmd; /**< @brief Command */
|
||||
float params[PROFILE_LANG_MAX_NUM_ARGS]; /**< @brief Parameters */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Parse a temperature profile from file and generate the command list
|
||||
*
|
||||
* Commands are parsed into the list given by \p command_list. If \p max_len is reached,
|
||||
* the function returns with @ref PL_RET_LIST_FULL
|
||||
*
|
||||
* In any case, the command list not cleared afterwards. The user has to clear the command list manually
|
||||
* using @ref temp_profile_free_command_list.
|
||||
*
|
||||
* @param filename File to parse
|
||||
* @param[in, out] command_list Command list to output @ref pl_command elements in.
|
||||
* @param max_len maximum number of commands.
|
||||
* @param cmds_parsed Number of parsed commands
|
||||
* @return
|
||||
*/
|
||||
enum pl_ret_val temp_profile_parse_from_file(const char *filename,
|
||||
SlList **command_list,
|
||||
uint32_t max_len,
|
||||
uint32_t *cmds_parsed);
|
||||
|
||||
/**
|
||||
* @brief Fully free a comamnd list including hte sotred command structures.
|
||||
*
|
||||
* \p list's destination is set to NULL to indicate the empty list.
|
||||
*
|
||||
* @param[in, out] list Pointer to list.
|
||||
*/
|
||||
void temp_profile_free_command_list(SlList **list);
|
||||
|
||||
#endif /* __CONFIG_PARSER_TEMP_PROFILE_PARSER_H__ */
|
||||
|
||||
/** @} */
|
@@ -21,6 +21,8 @@
|
||||
#ifndef _GUI_H_
|
||||
#define _GUI_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Handle the reflow controller's LCD Menu
|
||||
* @return 0 if no delay is requested, 1 if delay is requested
|
||||
@@ -29,4 +31,8 @@ int gui_handle(void);
|
||||
|
||||
void gui_init(void);
|
||||
|
||||
void gui_root_menu_message_set(const char *heading, const char *text);
|
||||
|
||||
void gui_lcd_write_direct_blocking(uint8_t line, const char *text);
|
||||
|
||||
#endif /* _GUI_H_ */
|
||||
|
@@ -21,6 +21,8 @@
|
||||
#ifndef __LCD_H__
|
||||
#define __LCD_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define LCD_DPORT (GPIOD)
|
||||
#define LCD_RCC_MASK RCC_AHB1ENR_GPIODEN
|
||||
#define LCD_DATA_BIT_OFFSET (8)
|
||||
@@ -48,6 +50,8 @@ void lcd_init(void);
|
||||
|
||||
void lcd_string(const char *data);
|
||||
|
||||
void lcd_setcursor(uint8_t x, uint8_t y);
|
||||
|
||||
void lcd_home(void);
|
||||
|
||||
enum lcd_fsm_ret lcd_fsm_write_buffer(const char (*display_buffer)[21]);
|
||||
|
@@ -22,7 +22,7 @@
|
||||
#define __MENU_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <reflow-controller/button.h>
|
||||
#include <reflow-controller/ui/button.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct lcd_menu;
|
||||
|
@@ -25,7 +25,14 @@
|
||||
|
||||
/**
|
||||
* @brief Start the RAM Code of the updater. This function will never return!
|
||||
*
|
||||
* This function is called at startup when the controller detects, that an update should
|
||||
* be performed.
|
||||
*
|
||||
* @note You prabably want to call @ref start_updater function to update.
|
||||
*/
|
||||
void __attribute__((noreturn)) start_updater(void);
|
||||
void __attribute__((noreturn)) start_updater_ram_code(void);
|
||||
|
||||
void __attribute__((noreturn)) updater_update_from_file(const char *filename);
|
||||
|
||||
#endif /* __UPDATER_UPDATER_H__ */
|
||||
|
1
stm-firmware/linklist-lib
Submodule
1
stm-firmware/linklist-lib
Submodule
Submodule stm-firmware/linklist-lib added at 18b3ab377a
39
stm-firmware/main-cycle-counter.c
Normal file
39
stm-firmware/main-cycle-counter.c
Normal file
@@ -0,0 +1,39 @@
|
||||
/* 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/main-cycle-counter.h>
|
||||
#include <helper-macros/helper-macros.h>
|
||||
|
||||
static uint64_t IN_SECTION(.ccm.bss) main_cycle_counter;
|
||||
|
||||
void main_cycle_counter_init(void)
|
||||
{
|
||||
main_cycle_counter = 0ULL;
|
||||
}
|
||||
|
||||
void main_cycle_counter_inc(void)
|
||||
{
|
||||
main_cycle_counter++;
|
||||
}
|
||||
|
||||
uint64_t main_cycle_counter_get(void)
|
||||
{
|
||||
return main_cycle_counter;
|
||||
}
|
@@ -37,7 +37,7 @@
|
||||
#include <stm-periph/stm32-gpio-macros.h>
|
||||
#include <stm-periph/rcc-manager.h>
|
||||
#include <stm-periph/uart.h>
|
||||
#include <reflow-controller/shell-uart-config.h>
|
||||
#include <reflow-controller/periph-config/shell-uart-config.h>
|
||||
#include <reflow-controller/oven-driver.h>
|
||||
#include <fatfs/ff.h>
|
||||
#include <reflow-controller/ui/gui.h>
|
||||
@@ -46,8 +46,9 @@
|
||||
#include <reflow-controller/safety/safety-memory.h>
|
||||
#include <reflow-controller/safety/fault.h>
|
||||
#include <reflow-controller/updater/updater.h>
|
||||
#include <reflow-controller/temp-profile-executer.h>
|
||||
#include <reflow-controller/temp-profile/temp-profile-executer.h>
|
||||
#include <reflow-controller/settings/spi-eeprom.h>
|
||||
#include <reflow-controller/main-cycle-counter.h>
|
||||
|
||||
static void setup_nvic_priorities(void)
|
||||
{
|
||||
@@ -139,9 +140,9 @@ static bool mount_sd_card_if_avail(bool mounted)
|
||||
if (res == FR_OK) {
|
||||
led_set(1, 1);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return mounted;
|
||||
@@ -155,6 +156,12 @@ static inline void handle_boot_status(void)
|
||||
res = safety_memory_get_boot_status(&status);
|
||||
if (res != 0)
|
||||
panic_mode();
|
||||
|
||||
if (status.reset_from_panic) {
|
||||
/* We've seen a panic */
|
||||
gui_root_menu_message_set("!! PANIC !!", "Check error me- mory!");
|
||||
}
|
||||
|
||||
if (status.reboot_to_bootloader) {
|
||||
status.reboot_to_bootloader = 0UL;
|
||||
safety_memory_set_boot_status(&status);
|
||||
@@ -162,7 +169,17 @@ static inline void handle_boot_status(void)
|
||||
led_set(0, 1);
|
||||
led_set(1, 1);
|
||||
|
||||
start_updater();
|
||||
gui_lcd_write_direct_blocking(0, "Updating...");
|
||||
|
||||
start_updater_ram_code();
|
||||
}
|
||||
|
||||
if (status.code_updated) {
|
||||
status.code_updated = 0x0UL;
|
||||
safety_memory_set_boot_status(&status);
|
||||
|
||||
/* Display notification on GUI */
|
||||
gui_root_menu_message_set("Firmware updated", "[Press Key]");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,9 +202,8 @@ static inline void setup_system(void)
|
||||
settings_setup();
|
||||
|
||||
/* Load the overtemperature limit from eeprom if available. Otherwise the default value will be used */
|
||||
if (settings_load_overtemp_limit(&tmp) == SETT_LOAD_SUCCESS) {
|
||||
if (settings_load_overtemp_limit(&tmp) == SETT_LOAD_SUCCESS)
|
||||
safety_controller_set_overtemp_limit(tmp);
|
||||
}
|
||||
|
||||
handle_boot_status();
|
||||
|
||||
@@ -218,19 +234,20 @@ int main(void)
|
||||
shellmatta_handle_t shell_handle;
|
||||
int menu_wait_request;
|
||||
uint64_t quarter_sec_timestamp = 0ULL;
|
||||
static uint64_t IN_SECTION(.ccm.bss) main_loop_iter_count;
|
||||
|
||||
/* Setup all the peripherals and external componets like LCD, EEPROM etc. and the safety controller */
|
||||
setup_system();
|
||||
|
||||
/* Try load the calibration. This will only succeed if there's an EEPROM */
|
||||
status = settings_load_calibration(&sens, &offset);
|
||||
if (!status) {
|
||||
if (!status)
|
||||
adc_pt1000_set_resistance_calibration(offset, sens, true);
|
||||
}
|
||||
|
||||
shell_handle = shell_init(write_shell_callback);
|
||||
shell_print_motd(shell_handle);
|
||||
|
||||
main_cycle_counter_init();
|
||||
|
||||
while (1) {
|
||||
|
||||
if (systick_ticks_have_passed(quarter_sec_timestamp, 250)) {
|
||||
@@ -242,9 +259,8 @@ int main(void)
|
||||
adc_pt1000_get_resistance_calibration(NULL, NULL, &cal_active);
|
||||
if (!cal_active) {
|
||||
status = settings_load_calibration(&sens, &offset);
|
||||
if (!status) {
|
||||
if (!status)
|
||||
adc_pt1000_set_resistance_calibration(offset, sens, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,9 +274,9 @@ int main(void)
|
||||
temp_profile_executer_handle();
|
||||
|
||||
safety_controller_handle();
|
||||
if (oven_pid_get_status() == OVEN_PID_RUNNING) {
|
||||
if (oven_pid_get_status() == OVEN_PID_RUNNING)
|
||||
oven_pid_handle();
|
||||
}
|
||||
|
||||
oven_driver_apply_power_level();
|
||||
safety_controller_report_timing(ERR_TIMING_MAIN_LOOP);
|
||||
|
||||
@@ -268,7 +284,7 @@ int main(void)
|
||||
__WFI();
|
||||
else
|
||||
__NOP();
|
||||
main_loop_iter_count++;
|
||||
main_cycle_counter_inc();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@@ -18,6 +18,10 @@
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @addtogroup pid-controller
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <reflow-controller/pid-controller.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -85,7 +89,8 @@ float pid_sample(struct pid_controller *pid, float deviation)
|
||||
}
|
||||
|
||||
/* Calculate derivative part */
|
||||
pid->derivate = pid->k_deriv_t * (deviation - pid->last_in) - pid->k_inv_deriv_t * pid->derivate;
|
||||
pid->derivate = pid->k_deriv_t * (deviation - pid->last_in) +
|
||||
pid->k_inv_deriv_t * pid->derivate;
|
||||
|
||||
output += pid->derivate;
|
||||
output += pid->integral;
|
||||
@@ -109,3 +114,5 @@ float pid_get_control_output(const struct pid_controller *pid)
|
||||
|
||||
return pid->control_output;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
@@ -27,7 +27,6 @@
|
||||
void HardFault_Handler(void)
|
||||
{
|
||||
/* This is a non recoverable fault. Stop the oven */
|
||||
|
||||
oven_driver_set_power(0);
|
||||
oven_driver_apply_power_level();
|
||||
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#include <helper-macros/helper-macros.h>
|
||||
#include <stm-periph/rcc-manager.h>
|
||||
#include <reflow-controller/hw-version-detect.h>
|
||||
#include <reflow-controller/safety/safety-controller.h>
|
||||
|
||||
static const uint8_t safety_adc_channels[SAFETY_ADC_NUM_OF_CHANNELS] = {SAFETY_ADC_CHANNELS};
|
||||
static volatile uint8_t safety_adc_conversion_complete;
|
||||
@@ -86,7 +87,9 @@ void safety_adc_init(void)
|
||||
NVIC_EnableIRQ(DMA2_Stream4_IRQn);
|
||||
|
||||
/* Enable ADC */
|
||||
SAFETY_ADC_ADC_PERIPHERAL->CR1 |= ADC_CR1_SCAN;
|
||||
SAFETY_ADC_ADC_PERIPHERAL->CR2 = ADC_CR2_ADON | ADC_CR2_DMA | ADC_CR2_DDS;
|
||||
safety_controller_set_crc_monitor(ERR_CRC_MON_SAFETY_ADC, SAFETY_CRC_MON_SAFETY_ADC_PW);
|
||||
}
|
||||
|
||||
|
||||
@@ -95,9 +98,11 @@ void safety_adc_deinit(void)
|
||||
SAFETY_ADC_ADC_PERIPHERAL->CR1 = 0UL;
|
||||
SAFETY_ADC_ADC_PERIPHERAL->CR2 = 0UL;
|
||||
SAFETY_ADC_ADC_PERIPHERAL->SMPR1 = 0UL;
|
||||
|
||||
rcc_manager_disable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(RCC_APB2ENR_ADC2EN));
|
||||
DMA2_Stream4->CR = 0;
|
||||
rcc_manager_disable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(RCC_AHB1ENR_DMA2EN));
|
||||
safety_controller_set_crc_monitor(ERR_CRC_MON_SAFETY_ADC, SAFETY_CRC_MON_SAFETY_ADC_PW);
|
||||
}
|
||||
|
||||
float safety_adc_convert_channel(enum safety_adc_meas_channel channel, uint16_t analog_value)
|
||||
|
@@ -43,6 +43,7 @@
|
||||
#include <stm-periph/rcc-manager.h>
|
||||
#include <reflow-controller/temp-converter.h>
|
||||
#include <reflow-controller/adc-meas.h>
|
||||
#include <reflow-controller/periph-config/safety-adc-hwcfg.h>
|
||||
|
||||
/**
|
||||
* @brief Macro that checks if a given @ref error_flag is persistent
|
||||
@@ -153,6 +154,37 @@ struct overtemp_config {
|
||||
uint32_t crc;
|
||||
};
|
||||
|
||||
struct flash_crcs {
|
||||
uint32_t start_magic;
|
||||
uint32_t crc_section_text;
|
||||
uint32_t crc_section_data;
|
||||
uint32_t crc_section_ccm_data;
|
||||
uint32_t crc_section_vectors;
|
||||
uint32_t end_magic;
|
||||
};
|
||||
|
||||
struct crc_monitor_register {
|
||||
const volatile void *reg_addr;
|
||||
uint32_t mask;
|
||||
uint8_t size;
|
||||
};
|
||||
|
||||
#define CRC_MON_REGISTER_ENTRY(_addr, _mask, _size) {.reg_addr = &(_addr), .mask = (_mask), .size = (_size)}
|
||||
|
||||
struct crc_mon {
|
||||
/**
|
||||
* @brief Array of registers to monitor. Terminated by NULL sentinel!
|
||||
*/
|
||||
const struct crc_monitor_register *registers;
|
||||
const enum crc_monitor monitor;
|
||||
const uint32_t pw;
|
||||
const enum safety_flag flag_to_set;
|
||||
uint32_t expected_crc;
|
||||
uint32_t expected_crc_inv;
|
||||
uint32_t last_crc;
|
||||
bool active;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief All safety error flags.
|
||||
*/
|
||||
@@ -176,6 +208,10 @@ static volatile struct error_flag IN_SECTION(.ccm.data) flags[] = {
|
||||
ERR_FLAG_ENTRY(ERR_FLAG_SAFETY_TAB_CORRUPT),
|
||||
ERR_FLAG_ENTRY(ERR_FLAG_AMON_SUPPLY_VOLT),
|
||||
ERR_FLAG_ENTRY(ERR_FLAG_OVERTEMP),
|
||||
ERR_FLAG_ENTRY(ERR_FLAG_FLASH_CRC_CODE),
|
||||
ERR_FLAG_ENTRY(ERR_FLAG_FLASH_CRC_DATA),
|
||||
ERR_FLAG_ENTRY(ERR_FLAG_CFG_CRC_MEAS_ADC),
|
||||
ERR_FLAG_ENTRY(ERR_FLAG_CFG_CRC_SAFETY_ADC),
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -248,6 +284,64 @@ static uint32_t IN_SECTION(.ccm.bss) flag_weight_crc;
|
||||
*/
|
||||
static struct overtemp_config IN_SECTION(.ccm.bss) safety_controller_overtemp_config;
|
||||
|
||||
static const struct crc_monitor_register meas_adc_crc_regs[] = {
|
||||
CRC_MON_REGISTER_ENTRY(ADC_PT1000_PERIPH->CR1, 0xFFFFFFFF, 4),
|
||||
CRC_MON_REGISTER_ENTRY(ADC_PT1000_PERIPH->CR2, ADC_CR2_ADON | ADC_CR2_CONT | ADC_CR2_ALIGN |
|
||||
ADC_CR2_DMA | ADC_CR2_DDS | ADC_CR2_EOCS | ADC_CR2_EXTEN | ADC_CR2_EXTSEL, 4),
|
||||
CRC_MON_REGISTER_ENTRY(ADC_PT1000_PERIPH->SMPR1, 0xFFFFFFFF, 4),
|
||||
CRC_MON_REGISTER_ENTRY(ADC_PT1000_PERIPH->SMPR2, 0xFFFFFFFF, 4),
|
||||
CRC_MON_REGISTER_ENTRY(ADC_PT1000_PERIPH->HTR, ADC_HTR_HT, 4),
|
||||
CRC_MON_REGISTER_ENTRY(ADC_PT1000_PERIPH->LTR, ADC_LTR_LT, 4),
|
||||
CRC_MON_REGISTER_ENTRY(ADC_PT1000_PERIPH->SQR1, ADC_SQR1_L |
|
||||
ADC_SQR1_SQ16 | ADC_SQR1_SQ15 | ADC_SQR1_SQ14 | ADC_SQR1_SQ13, 4),
|
||||
CRC_MON_REGISTER_ENTRY(ADC_PT1000_PERIPH->SQR2,
|
||||
ADC_SQR2_SQ12 | ADC_SQR2_SQ11 | ADC_SQR2_SQ10 | ADC_SQR2_SQ9 |
|
||||
ADC_SQR2_SQ8 | ADC_SQR2_SQ7, 4),
|
||||
CRC_MON_REGISTER_ENTRY(ADC_PT1000_PERIPH->SQR3, ADC_SQR3_SQ6 | ADC_SQR3_SQ5 | ADC_SQR3_SQ4 |
|
||||
ADC_SQR3_SQ3| ADC_SQR3_SQ2 | ADC_SQR3_SQ1, 4),
|
||||
{NULL, 0, 0}
|
||||
};
|
||||
|
||||
static const struct crc_monitor_register safety_adc_crc_regs[] = {
|
||||
CRC_MON_REGISTER_ENTRY(SAFETY_ADC_ADC_PERIPHERAL->CR1, 0xFFFFFFFF, 4),
|
||||
CRC_MON_REGISTER_ENTRY(SAFETY_ADC_ADC_PERIPHERAL->CR2, ADC_CR2_ADON | ADC_CR2_CONT | ADC_CR2_ALIGN |
|
||||
ADC_CR2_DMA | ADC_CR2_DDS | ADC_CR2_EOCS | ADC_CR2_EXTEN | ADC_CR2_EXTSEL, 4),
|
||||
CRC_MON_REGISTER_ENTRY(SAFETY_ADC_ADC_PERIPHERAL->SMPR1, 0xFFFFFFFF, 4),
|
||||
CRC_MON_REGISTER_ENTRY(SAFETY_ADC_ADC_PERIPHERAL->SMPR2, 0xFFFFFFFF, 4),
|
||||
CRC_MON_REGISTER_ENTRY(SAFETY_ADC_ADC_PERIPHERAL->SQR1, ADC_SQR1_L |
|
||||
ADC_SQR1_SQ16 | ADC_SQR1_SQ15 | ADC_SQR1_SQ14 | ADC_SQR1_SQ13, 4),
|
||||
CRC_MON_REGISTER_ENTRY(SAFETY_ADC_ADC_PERIPHERAL->SQR2,
|
||||
ADC_SQR2_SQ12 | ADC_SQR2_SQ11 | ADC_SQR2_SQ10 | ADC_SQR2_SQ9 |
|
||||
ADC_SQR2_SQ8 | ADC_SQR2_SQ7, 4),
|
||||
CRC_MON_REGISTER_ENTRY(SAFETY_ADC_ADC_PERIPHERAL->SQR3, ADC_SQR3_SQ6 | ADC_SQR3_SQ5 | ADC_SQR3_SQ4 |
|
||||
ADC_SQR3_SQ3| ADC_SQR3_SQ2 | ADC_SQR3_SQ1, 4),
|
||||
{NULL, 0, 0}
|
||||
};
|
||||
|
||||
static struct crc_mon IN_SECTION(.ccm.data) crc_monitors[] =
|
||||
{
|
||||
{
|
||||
.registers = meas_adc_crc_regs,
|
||||
.monitor = ERR_CRC_MON_MEAS_ADC,
|
||||
.pw = SAFETY_CRC_MON_MEAS_ADC_PW,
|
||||
.flag_to_set = ERR_FLAG_CFG_CRC_MEAS_ADC,
|
||||
.expected_crc = 0UL,
|
||||
.expected_crc_inv = ~0UL,
|
||||
.last_crc = 0UL,
|
||||
.active = false,
|
||||
},
|
||||
{
|
||||
.registers = safety_adc_crc_regs,
|
||||
.monitor = ERR_CRC_MON_SAFETY_ADC,
|
||||
.pw = SAFETY_CRC_MON_SAFETY_ADC_PW,
|
||||
.flag_to_set = ERR_FLAG_CFG_CRC_SAFETY_ADC,
|
||||
.expected_crc = 0UL,
|
||||
.expected_crc_inv = ~0UL,
|
||||
.last_crc = 0UL,
|
||||
.active = false,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Configure the overtemperature flag's settings
|
||||
* @param over_temperature Temperature to set the limit to.
|
||||
@@ -257,7 +351,7 @@ static void set_overtemp_config(float over_temperature)
|
||||
int result;
|
||||
float resistance;
|
||||
|
||||
result = temp_convertet_convert_temp_to_resistance(over_temperature, &resistance);
|
||||
result = temp_converter_convert_temp_to_resistance(over_temperature, &resistance);
|
||||
/* An error in this function is really bad... */
|
||||
if (result < -1)
|
||||
panic_mode();
|
||||
@@ -378,6 +472,62 @@ static volatile struct error_flag *find_error_flag(enum safety_flag flag)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int crc_monitor_calculate_crc(const struct crc_monitor_register *registers, uint32_t *crc_out)
|
||||
{
|
||||
const struct crc_monitor_register *reg;
|
||||
uint32_t i;
|
||||
uint32_t reg_val;
|
||||
|
||||
if (!registers || !crc_out)
|
||||
return -1000;
|
||||
|
||||
crc_unit_reset();
|
||||
for (i = 0; registers[i].reg_addr != NULL; i++) {
|
||||
reg = ®isters[i];
|
||||
switch (reg->size) {
|
||||
case 1:
|
||||
reg_val = *((volatile uint8_t *)reg->reg_addr);
|
||||
break;
|
||||
case 2:
|
||||
reg_val = *((volatile uint16_t *)reg->reg_addr);
|
||||
break;
|
||||
case 4: /* FALLTHRU */
|
||||
default:
|
||||
reg_val = *((volatile uint32_t *)reg->reg_addr);
|
||||
break;
|
||||
}
|
||||
|
||||
reg_val &= reg->mask;
|
||||
crc_unit_input(reg_val);
|
||||
}
|
||||
|
||||
*crc_out = crc_unit_get_crc();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int safety_controller_check_crc_monitors(void)
|
||||
{
|
||||
uint32_t i;
|
||||
struct crc_mon *mon;
|
||||
uint32_t crc;
|
||||
|
||||
for (i = 0; i < COUNT_OF(crc_monitors); i++) {
|
||||
mon = &crc_monitors[i];
|
||||
if (!mon->active)
|
||||
continue;
|
||||
|
||||
if (crc_monitor_calculate_crc(mon->registers, &crc))
|
||||
return -1;
|
||||
if (mon->expected_crc != crc || ~mon->expected_crc_inv != crc) {
|
||||
safety_controller_report_error(mon->flag_to_set);
|
||||
}
|
||||
mon->last_crc = crc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function copies the safety weigths from flash ro RAM and computes the CRC
|
||||
*/
|
||||
@@ -597,6 +747,8 @@ static void safety_controller_process_monitor_checks(void)
|
||||
safety_controller_report_error(ERR_FLAG_OVERTEMP);
|
||||
}
|
||||
|
||||
(void)safety_controller_check_crc_monitors();
|
||||
|
||||
safety_controller_process_active_timing_mons();
|
||||
}
|
||||
|
||||
@@ -764,6 +916,7 @@ void safety_controller_init()
|
||||
/* This is usually done by the safety memory already. But, since this module also uses the CRC... */
|
||||
crc_unit_init();
|
||||
|
||||
safety_controller_trigger_flash_crc_check();
|
||||
stack_check_init_corruption_detect_area();
|
||||
|
||||
hw_rev = get_pcb_hardware_version();
|
||||
@@ -1296,4 +1449,125 @@ float safety_controller_get_overtemp_limit(void)
|
||||
return safety_controller_overtemp_config.overtemp_deg_celsius;
|
||||
}
|
||||
|
||||
extern const uint32_t __ld_vectors_start;
|
||||
extern const uint32_t __ld_vectors_end;
|
||||
extern const uint32_t __ld_text_start;
|
||||
extern const uint32_t __ld_text_end;
|
||||
|
||||
extern const uint32_t __ld_sdata_ccm;
|
||||
extern const uint32_t __ld_edata_ccm;
|
||||
extern const uint32_t __ld_load_ccm_data;
|
||||
|
||||
extern const uint32_t __ld_sdata;
|
||||
extern const uint32_t __ld_edata;
|
||||
extern const uint32_t __ld_load_data;
|
||||
|
||||
int safety_controller_trigger_flash_crc_check()
|
||||
{
|
||||
/* This structs needs to be volatile!!
|
||||
* This prevents the compiler form optimizing out the reads to the crcs which will be patched in later by
|
||||
* a separate python script!
|
||||
*/
|
||||
static volatile const struct flash_crcs IN_SECTION(.flashcrc) crcs_in_flash =
|
||||
{
|
||||
.start_magic = 0xA8BE53F9UL,
|
||||
.crc_section_ccm_data = 0UL,
|
||||
.crc_section_text = 0UL,
|
||||
.crc_section_data = 0UL,
|
||||
.crc_section_vectors = 0UL,
|
||||
.end_magic = 0xFFA582FFUL,
|
||||
};
|
||||
|
||||
int ret = -1;
|
||||
uint32_t len;
|
||||
uint32_t crc;
|
||||
|
||||
/* Perform CRC check over vector table */
|
||||
len = (uint32_t)((void *)&__ld_vectors_end - (void *)&__ld_vectors_start);
|
||||
if (len % 4) {
|
||||
safety_controller_report_error(ERR_FLAG_FLASH_CRC_CODE);
|
||||
} else {
|
||||
len /= 4;
|
||||
crc_unit_reset();
|
||||
crc_unit_input_array(&__ld_vectors_start, len);
|
||||
crc = crc_unit_get_crc();
|
||||
if (crc != crcs_in_flash.crc_section_vectors) {
|
||||
safety_controller_report_error(ERR_FLAG_FLASH_CRC_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform CRC check over text section */
|
||||
len = (uint32_t)((void *)&__ld_text_end - (void *)&__ld_text_start);
|
||||
if (len % 4) {
|
||||
safety_controller_report_error(ERR_FLAG_FLASH_CRC_CODE);
|
||||
} else {
|
||||
len /= 4;
|
||||
crc_unit_reset();
|
||||
crc_unit_input_array(&__ld_text_start, len);
|
||||
crc = crc_unit_get_crc();
|
||||
if (crc != crcs_in_flash.crc_section_text) {
|
||||
safety_controller_report_error(ERR_FLAG_FLASH_CRC_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform CRC check over data section */
|
||||
len = (uint32_t)((void *)&__ld_edata - (void *)&__ld_sdata);
|
||||
if (len % 4) {
|
||||
safety_controller_report_error(ERR_FLAG_FLASH_CRC_DATA);
|
||||
} else {
|
||||
len /= 4;
|
||||
crc_unit_reset();
|
||||
crc_unit_input_array(&__ld_load_data, len);
|
||||
crc = crc_unit_get_crc();
|
||||
if (crc != crcs_in_flash.crc_section_data) {
|
||||
safety_controller_report_error(ERR_FLAG_FLASH_CRC_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform CRC check over ccm data section */
|
||||
len = (uint32_t)((void *)&__ld_edata_ccm - (void *)&__ld_sdata_ccm);
|
||||
if (len % 4) {
|
||||
safety_controller_report_error(ERR_FLAG_FLASH_CRC_DATA);
|
||||
} else {
|
||||
len /= 4;
|
||||
crc_unit_reset();
|
||||
crc_unit_input_array(&__ld_load_ccm_data, len);
|
||||
crc = crc_unit_get_crc();
|
||||
if (crc != crcs_in_flash.crc_section_ccm_data) {
|
||||
safety_controller_report_error(ERR_FLAG_FLASH_CRC_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int safety_controller_set_crc_monitor(enum crc_monitor mon, uint32_t password)
|
||||
{
|
||||
uint32_t i;
|
||||
struct crc_mon *monitor;
|
||||
uint32_t crc;
|
||||
|
||||
for (i = 0; i < COUNT_OF(crc_monitors); i++) {
|
||||
monitor = &crc_monitors[i];
|
||||
if (monitor->monitor != mon)
|
||||
continue;
|
||||
|
||||
monitor->active = true;
|
||||
|
||||
if (password != monitor->pw)
|
||||
return -1002;
|
||||
|
||||
crc = 0;
|
||||
(void)crc_monitor_calculate_crc(monitor->registers, &crc);
|
||||
monitor->expected_crc = crc;
|
||||
monitor->expected_crc_inv = ~crc;
|
||||
monitor->last_crc = crc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1001;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
@@ -22,7 +22,11 @@
|
||||
#include <helper-macros/helper-macros.h>
|
||||
#include <stm-periph/crc-unit.h>
|
||||
#include <stm-periph/backup-ram.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef SAFETY_MEMORY_STRIPOUT_DUMP
|
||||
#include <base64-lib/base64-lib.h>
|
||||
#endif /* SAFETY_MEMORY_STRIPOUT_DUMP */
|
||||
|
||||
static int word_to_error_memory_entry(uint32_t entry_data, struct error_memory_entry *out)
|
||||
{
|
||||
@@ -31,11 +35,11 @@ static int word_to_error_memory_entry(uint32_t entry_data, struct error_memory_e
|
||||
if (!out)
|
||||
return -1002;
|
||||
|
||||
if (entry_data == SAFETY_MEMORY_ERR_ENTRY_NOP) {
|
||||
if (entry_data == SAFETY_MEMORY_NOP_ENTRY_WORD) {
|
||||
out->flag_num = 0U;
|
||||
out->type = SAFETY_MEMORY_ERR_ENTRY_NOP;
|
||||
out->counter = 0U;
|
||||
} else if ((entry_data & 0xFFU) == 0x51U) {
|
||||
} else if ((entry_data & 0xFFU) == SAFETY_MEMORY_ERROR_ENTRY_MARKER) {
|
||||
out->flag_num = (uint8_t)((entry_data >> 8U) & 0xFFU);
|
||||
out->type = SAFETY_MEMORY_ERR_ENTRY_FLAG;
|
||||
out->counter = (uint16_t)((entry_data >> 16U) & 0xFFFF);
|
||||
@@ -53,10 +57,10 @@ static uint32_t error_memory_entry_to_word(const struct error_memory_entry *entr
|
||||
|
||||
switch (entry->type) {
|
||||
case SAFETY_MEMORY_ERR_ENTRY_NOP:
|
||||
word = SAFETY_MEMORY_NOP_ENTRY;
|
||||
word = SAFETY_MEMORY_NOP_ENTRY_WORD;
|
||||
break;
|
||||
case SAFETY_MEMORY_ERR_ENTRY_FLAG:
|
||||
word = 0x51UL | ((uint32_t)entry->flag_num << 8U) |
|
||||
word = (uint32_t)SAFETY_MEMORY_ERROR_ENTRY_MARKER | ((uint32_t)entry->flag_num << 8U) |
|
||||
((uint32_t)entry->counter << 16U);
|
||||
break;
|
||||
}
|
||||
@@ -99,7 +103,9 @@ static enum safety_memory_state safety_memory_get_header(struct safety_memory_he
|
||||
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)
|
||||
if (header->firmware_update_filename < header->config_overrides_offset + header->config_overrides_len)
|
||||
res++;
|
||||
if (header->err_memory_offset < header->firmware_update_filename + (SAFETY_MEMORY_UPDATE_FILENAME_MAXSIZE / 4))
|
||||
res++;
|
||||
if (header->err_memory_end >= backup_ram_get_size_in_words() || header->err_memory_end < header->err_memory_offset)
|
||||
res++;
|
||||
@@ -133,7 +139,8 @@ static void safety_memory_write_new_header(void)
|
||||
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.firmware_update_filename = header.config_overrides_offset + SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT;
|
||||
header.err_memory_offset = header.firmware_update_filename + (SAFETY_MEMORY_UPDATE_FILENAME_MAXSIZE / 4);
|
||||
header.err_memory_end = header.err_memory_offset;
|
||||
header.magic = SAFETY_MEMORY_MAGIC;
|
||||
|
||||
@@ -315,9 +322,9 @@ static int safety_memory_check_error_entries()
|
||||
return -100;
|
||||
|
||||
/* Valid flag entry */
|
||||
if ((data & 0xFF) == 0x51)
|
||||
if ((data & 0xFF) == SAFETY_MEMORY_ERROR_ENTRY_MARKER)
|
||||
continue;
|
||||
if (data == SAFETY_MEMORY_NOP_ENTRY)
|
||||
if (data == SAFETY_MEMORY_NOP_ENTRY_WORD)
|
||||
continue;
|
||||
|
||||
ret--;
|
||||
@@ -608,6 +615,8 @@ int safety_memory_get_config_override(uint32_t idx, struct config_override *conf
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef SAFETY_MEMORY_STRIPOUT_DUMP
|
||||
|
||||
int safety_memory_dump_base64(char *buffer, size_t buffsize, size_t *used_size)
|
||||
{
|
||||
uint32_t safety_mem_size;
|
||||
@@ -633,3 +642,66 @@ int safety_memory_dump_base64(char *buffer, size_t buffsize, size_t *used_size)
|
||||
*used_size = output_size + 1u;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* SAFETY_MEMORY_STRIPOUT_DUMP */
|
||||
|
||||
int safety_memory_get_update_filename(char *filename, size_t *outlen)
|
||||
{
|
||||
struct safety_memory_header header;
|
||||
unsigned int i;
|
||||
volatile char *ptr;
|
||||
size_t len = 0u;
|
||||
|
||||
/* If filename and outlen are both NULL, we don't do anything */
|
||||
if (!filename && !outlen)
|
||||
return -1;
|
||||
|
||||
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY)
|
||||
return -2000;
|
||||
|
||||
/* Get the filename */
|
||||
ptr = (volatile char *)backup_ram_get_base_ptr();
|
||||
ptr += (unsigned int)(header.firmware_update_filename * 4);
|
||||
for (i = 0; i < SAFETY_MEMORY_UPDATE_FILENAME_MAXSIZE; i++) {
|
||||
if (filename)
|
||||
filename[i] = *ptr;
|
||||
if (*ptr)
|
||||
len++;
|
||||
else
|
||||
break;
|
||||
ptr++;
|
||||
}
|
||||
|
||||
if (outlen)
|
||||
*outlen = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int safety_memory_set_update_filename(const char *filename)
|
||||
{
|
||||
int ret = 0;
|
||||
size_t len;
|
||||
unsigned int i;
|
||||
struct safety_memory_header header;
|
||||
volatile char *ram_ptr;
|
||||
|
||||
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY)
|
||||
return -2000;
|
||||
|
||||
if (!filename)
|
||||
return -1001;
|
||||
|
||||
len = strnlen(filename, SAFETY_MEMORY_UPDATE_FILENAME_MAXSIZE - 1);
|
||||
ram_ptr = backup_ram_get_base_ptr();
|
||||
ram_ptr += header.firmware_update_filename * 4;
|
||||
|
||||
for (i = 0u; i < len; i++) {
|
||||
ram_ptr[i] = filename[i];
|
||||
}
|
||||
ram_ptr[i] = 0;
|
||||
|
||||
ret = safety_memory_gen_crc();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@
|
||||
#include <reflow-controller/digio.h>
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
#include <inttypes.h>
|
||||
#include <helper-macros/helper-macros.h>
|
||||
#include <reflow-controller/systick.h>
|
||||
#include <stm-periph/unique-id.h>
|
||||
@@ -34,46 +35,26 @@
|
||||
#include <reflow-controller/temp-converter.h>
|
||||
#include <fatfs/ff.h>
|
||||
#include <reflow-controller/safety/stack-check.h>
|
||||
#include <reflow-controller/rotary-encoder.h>
|
||||
#include <reflow-controller/ui/rotary-encoder.h>
|
||||
#include <reflow-controller/safety/safety-controller.h>
|
||||
#include <reflow-controller/settings/settings.h>
|
||||
#include <reflow-controller/button.h>
|
||||
#include <reflow-controller/ui/button.h>
|
||||
#include <reflow-controller/safety/fault.h>
|
||||
#include <reflow-controller/safety/safety-memory.h>
|
||||
#include <reflow-controller/hw-version-detect.h>
|
||||
#include <reflow-controller/temp-profile-executer.h>
|
||||
#include <reflow-controller/temp-profile/temp-profile-executer.h>
|
||||
#include <reflow-controller/updater/updater.h>
|
||||
#include <reflow-controller/main-cycle-counter.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef GIT_VER
|
||||
#define GIT_VER "VERSION NOT SET"
|
||||
#endif
|
||||
|
||||
extern struct stm_uart shell_uart;
|
||||
static shellmatta_instance_t shell;
|
||||
static char shell_buffer[512];
|
||||
static char IN_SECTION(.ccm.bss) history_buffer[512];
|
||||
|
||||
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,
|
||||
uint32_t length)
|
||||
@@ -89,7 +70,7 @@ 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");
|
||||
"Compiled: " __DATE__ " at " __TIME__ "\r\n");
|
||||
shellmatta_printf(handle, "Serial: %08X-%08X-%08X\r\n", high_id, mid_id, low_id);
|
||||
|
||||
pcb_rev = get_pcb_hardware_version();
|
||||
@@ -181,13 +162,12 @@ static shellmatta_retCode_t shell_cmd_pt1000_res(const shellmatta_handle_t han
|
||||
|
||||
pt1000_status = adc_pt1000_get_current_resistance(&resistance);
|
||||
|
||||
if (pt1000_status == 2) {
|
||||
strcat(display_status, " UNSTABLE ");
|
||||
} else if (pt1000_status) {
|
||||
if (pt1000_status == 2)
|
||||
strcat(display_status, "UNSTABLE");
|
||||
else if (pt1000_status)
|
||||
strcpy(display_status, "ERROR");
|
||||
} else {
|
||||
else
|
||||
strcpy(display_status, "VALID");
|
||||
}
|
||||
|
||||
temp_conv_status = temp_converter_convert_resistance_to_temp(resistance, &temp);
|
||||
switch (temp_conv_status) {
|
||||
@@ -204,7 +184,8 @@ static shellmatta_retCode_t shell_cmd_pt1000_res(const shellmatta_handle_t han
|
||||
break;
|
||||
}
|
||||
|
||||
shellmatta_printf(handle, "PT1000 resistance: %.2f Ohm (%s%.1f °C) [%s]\r\n", resistance, temp_prefix,temp, display_status);
|
||||
shellmatta_printf(handle, "PT1000 resistance: %.2f Ohm (%s%.1f °C) [%s]\r\n",
|
||||
resistance, temp_prefix, temp, display_status);
|
||||
|
||||
return SHELLMATTA_OK;
|
||||
}
|
||||
@@ -344,14 +325,35 @@ static shellmatta_retCode_t shell_cmd_read_flags(const shellmatta_handle_t handl
|
||||
uint32_t i;
|
||||
char name[64];
|
||||
bool flag;
|
||||
bool tryack;
|
||||
bool tryack = false;
|
||||
int status;
|
||||
enum safety_flag flag_enum;
|
||||
struct analog_monitor_info amon_info;
|
||||
struct timing_monitor_info timing_info;
|
||||
char *argument;
|
||||
uint32_t len;
|
||||
char option;
|
||||
shellmatta_retCode_t opt_ret;
|
||||
static const shellmatta_opt_long_t options[] = {
|
||||
{"ack", 'a', SHELLMATTA_OPT_ARG_NONE},
|
||||
{NULL, '\0', SHELLMATTA_OPT_ARG_NONE}
|
||||
};
|
||||
|
||||
do {
|
||||
opt_ret = shellmatta_opt_long(handle, options, &option, &argument, &len);
|
||||
if (opt_ret != SHELLMATTA_OK)
|
||||
break;
|
||||
|
||||
switch (option) {
|
||||
case 'a':
|
||||
tryack = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
|
||||
/* Check for the --ack option */
|
||||
tryack = check_opt(arguments, length, "--ack");
|
||||
|
||||
shellmatta_printf(handle, "Error Flags\r\n"
|
||||
"-----------\r\n");
|
||||
@@ -373,7 +375,11 @@ static shellmatta_retCode_t shell_cmd_read_flags(const shellmatta_handle_t handl
|
||||
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,
|
||||
"\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"
|
||||
@@ -417,7 +423,7 @@ static shellmatta_retCode_t shell_cmd_read_flags(const shellmatta_handle_t handl
|
||||
|
||||
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);
|
||||
shellmatta_printf(handle, "last tick: %lu ms\r\n", (unsigned long)timing_info.delta);
|
||||
else
|
||||
shellmatta_printf(handle, "\e[1;31mDISABLED\e[m\r\n");
|
||||
}
|
||||
@@ -438,11 +444,10 @@ static shellmatta_retCode_t shell_cmd_save_cal(const shellmatta_handle_t handle,
|
||||
adc_pt1000_get_resistance_calibration(&offset, &sens_dev, &active);
|
||||
res = settings_save_calibration(sens_dev, offset, active);
|
||||
|
||||
if (res) {
|
||||
if (res)
|
||||
shellmatta_printf(handle, "Error saving %d\r\n", res);
|
||||
} else {
|
||||
else
|
||||
shellmatta_printf(handle, "Saved!\r\n");
|
||||
}
|
||||
|
||||
return SHELLMATTA_OK;
|
||||
}
|
||||
@@ -454,7 +459,8 @@ static shellmatta_retCode_t shell_cmd_hang(const shellmatta_handle_t handle, con
|
||||
(void)arguments;
|
||||
(void)length;
|
||||
|
||||
while (1337);
|
||||
while (1337)
|
||||
;
|
||||
|
||||
return SHELLMATTA_OK;
|
||||
}
|
||||
@@ -573,9 +579,9 @@ static shellmatta_retCode_t shell_cmd_dump_safety_mem(const shellmatta_handle_t
|
||||
}
|
||||
|
||||
fres = f_write(&file, buffer, used_bytes - 1, &bw);
|
||||
if (fres != FR_OK) {
|
||||
if (fres != FR_OK)
|
||||
shellmatta_printf(handle, "Error writing to file %s\r\n", token);
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
f_close(&file);
|
||||
return SHELLMATTA_OK;
|
||||
@@ -631,13 +637,35 @@ shellmatta_retCode_t shell_cmd_update(const shellmatta_handle_t handle, const ch
|
||||
(void)handle;
|
||||
(void)arguments;
|
||||
(void)length;
|
||||
struct safety_memory_boot_status status;
|
||||
shellmatta_retCode_t opt_stat;
|
||||
char option;
|
||||
char *argument;
|
||||
uint32_t arg_len;
|
||||
const char *update_file = NULL;
|
||||
|
||||
safety_memory_get_boot_status(&status);
|
||||
status.reboot_to_bootloader = 0xFFFFFFFFUL;
|
||||
safety_memory_set_boot_status(&status);
|
||||
const shellmatta_opt_long_t options[] = {
|
||||
{NULL, '\0', SHELLMATTA_OPT_ARG_NONE},
|
||||
};
|
||||
|
||||
NVIC_SystemReset();
|
||||
while (1) {
|
||||
opt_stat = shellmatta_opt_long(handle, options, &option, &argument, &arg_len);
|
||||
if (opt_stat != SHELLMATTA_OK)
|
||||
break;
|
||||
switch (option) {
|
||||
case '\0':
|
||||
update_file = argument;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!update_file || !strlen(update_file)) {
|
||||
shellmatta_printf(handle, "Please specify a valid update file!\r\n");
|
||||
return SHELLMATTA_ERROR;
|
||||
}
|
||||
|
||||
updater_update_from_file(update_file);
|
||||
|
||||
return SHELLMATTA_OK;
|
||||
}
|
||||
@@ -751,9 +779,9 @@ shellmatta_retCode_t shell_cmd_execute(const shellmatta_handle_t handle, const c
|
||||
if (state->status != TPE_RUNNING) {
|
||||
shellmatta_printf(handle, "Profile executed.\r\n");
|
||||
running = false;
|
||||
if(state->status == TPE_ABORT) {
|
||||
if (state->status == TPE_ABORT)
|
||||
shellmatta_printf(handle, "Profile execution aborted!\r\n");
|
||||
}
|
||||
|
||||
return SHELLMATTA_OK;
|
||||
}
|
||||
|
||||
@@ -767,6 +795,88 @@ shellmatta_retCode_t shell_cmd_execute(const shellmatta_handle_t handle, const c
|
||||
return SHELLMATTA_CONTINUE;
|
||||
}
|
||||
|
||||
shellmatta_retCode_t shell_cmd_cycle_count(const shellmatta_handle_t handle, const char *args, uint32_t len)
|
||||
{
|
||||
uint64_t counter;
|
||||
(void)args;
|
||||
(void)len;
|
||||
char option;
|
||||
char *argument;
|
||||
uint32_t arg_len;
|
||||
int opt_stat;
|
||||
bool clear = false;
|
||||
const shellmatta_opt_long_t options[] = {
|
||||
{"clear", 'c', SHELLMATTA_OPT_ARG_NONE},
|
||||
{NULL, '\0', SHELLMATTA_OPT_ARG_NONE},
|
||||
};
|
||||
|
||||
while (1) {
|
||||
opt_stat = shellmatta_opt_long(handle, options, &option, &argument, &arg_len);
|
||||
if (opt_stat != SHELLMATTA_OK)
|
||||
break;
|
||||
switch (option) {
|
||||
case 'c':
|
||||
clear = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
counter = main_cycle_counter_get();
|
||||
shellmatta_printf(handle, "%"PRIu64"\r\n", counter);
|
||||
if (clear)
|
||||
main_cycle_counter_init();
|
||||
|
||||
return SHELLMATTA_OK;
|
||||
}
|
||||
|
||||
shellmatta_retCode_t shell_cmd_filter_alpha(const shellmatta_handle_t handle, const char *args, uint32_t len)
|
||||
{
|
||||
shellmatta_retCode_t opt_stat;
|
||||
char option;
|
||||
char *argument;
|
||||
uint32_t arg_len;
|
||||
char *alpha_string = NULL;
|
||||
float alpha;
|
||||
(void)len;
|
||||
(void)args;
|
||||
|
||||
const shellmatta_opt_long_t options[] = {
|
||||
{NULL, '\0', SHELLMATTA_OPT_ARG_NONE},
|
||||
};
|
||||
|
||||
while (1) {
|
||||
opt_stat = shellmatta_opt_long(handle, options, &option, &argument, &arg_len);
|
||||
if (opt_stat != SHELLMATTA_OK)
|
||||
break;
|
||||
switch (option) {
|
||||
case '\0':
|
||||
alpha_string = argument;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!alpha_string) {
|
||||
shellmatta_printf(handle, "Specify filter value!\r\n");
|
||||
return SHELLMATTA_OK;
|
||||
}
|
||||
|
||||
alpha = strtof(alpha_string, NULL);
|
||||
if (alpha < 0.0f || alpha == 0.0f || alpha > 0.9f) {
|
||||
shellmatta_printf(handle, "Filter param outside of valid range!\r\n");
|
||||
return SHELLMATTA_OK;
|
||||
}
|
||||
|
||||
adc_pt1000_set_moving_average_filter_param(alpha);
|
||||
shellmatta_printf(handle, "Filter param set to %f\r\n", alpha);
|
||||
|
||||
return SHELLMATTA_OK;
|
||||
}
|
||||
|
||||
//typedef struct shellmatta_cmd
|
||||
//{
|
||||
// char *cmd; /**< command name */
|
||||
@@ -776,7 +886,7 @@ shellmatta_retCode_t shell_cmd_execute(const shellmatta_handle_t handle, const c
|
||||
// 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[21] = {
|
||||
static shellmatta_cmd_t cmd[24] = {
|
||||
{
|
||||
.cmd = "version",
|
||||
.cmdAlias = "ver",
|
||||
@@ -876,7 +986,7 @@ static shellmatta_cmd_t cmd[21] = {
|
||||
{
|
||||
.cmd = "save-calibration",
|
||||
.cmdAlias = "save-cal",
|
||||
.helpText = "",
|
||||
.helpText = "Permanently save the calibration to EEPROM",
|
||||
.usageText = "",
|
||||
.cmdFct = shell_cmd_save_cal,
|
||||
.next = &cmd[13],
|
||||
@@ -924,8 +1034,8 @@ static shellmatta_cmd_t cmd[21] = {
|
||||
{
|
||||
.cmd = "update",
|
||||
.cmdAlias = NULL,
|
||||
.helpText = "Update Firmware",
|
||||
.usageText = "",
|
||||
.helpText = "Update Firmware from HEX file",
|
||||
.usageText = "update </path/to/update.hex>",
|
||||
.cmdFct = shell_cmd_update,
|
||||
.next = &cmd[19],
|
||||
|
||||
@@ -944,6 +1054,22 @@ static shellmatta_cmd_t cmd[21] = {
|
||||
.helpText = "Execute Temp Profile",
|
||||
.usageText = "execute /path/to/script.tpr",
|
||||
.cmdFct = shell_cmd_execute,
|
||||
.next = &cmd[21],
|
||||
},
|
||||
{
|
||||
.cmd = "cyclecount",
|
||||
.cmdAlias = "cc",
|
||||
.helpText = "Print out the cycle counter of the main loop",
|
||||
.usageText = "cyclecount [--clear]",
|
||||
.cmdFct = shell_cmd_cycle_count,
|
||||
.next = &cmd[22],
|
||||
},
|
||||
{
|
||||
.cmd = "filter-alpha",
|
||||
.cmdAlias = "alpha",
|
||||
.helpText = "Sets the filter constant",
|
||||
.usageText = "filter-alpha <alpha>",
|
||||
.cmdFct = shell_cmd_filter_alpha,
|
||||
.next = NULL,
|
||||
}
|
||||
};
|
||||
@@ -953,8 +1079,15 @@ shellmatta_handle_t shell_init(shellmatta_write_t write_func)
|
||||
shellmatta_handle_t handle;
|
||||
shellmatta_retCode_t ret;
|
||||
|
||||
ret = shellmatta_doInit(&shell, &handle, shell_buffer, sizeof(shell_buffer), history_buffer, sizeof(history_buffer),
|
||||
"\e[1;32mReflow Controller>\e[m ", cmd, write_func);
|
||||
ret = shellmatta_doInit(&shell,
|
||||
&handle,
|
||||
shell_buffer,
|
||||
sizeof(shell_buffer),
|
||||
history_buffer,
|
||||
sizeof(history_buffer),
|
||||
"\e[1;32mReflow Controller>\e[m ",
|
||||
cmd,
|
||||
write_func);
|
||||
if (ret != SHELLMATTA_OK)
|
||||
handle = NULL;
|
||||
|
||||
|
@@ -18,13 +18,13 @@
|
||||
* --------------------------------------------------------------------
|
||||
* FLASH: 512K
|
||||
* RAM: 128K
|
||||
* CCM RAM: 64L
|
||||
* CCM RAM: 64K
|
||||
* FPU: fpv4-sp-d16
|
||||
*/
|
||||
|
||||
/* USER PARAMETERS */
|
||||
__ld_stack_size = 0x3000;
|
||||
__ld_heap_size = 0x4200;
|
||||
__ld_stack_size = 0x3500;
|
||||
__ld_heap_size = 0x5000;
|
||||
__stack_corruption_area_size = 128;
|
||||
|
||||
/* END OF USER PARAMETERS */
|
||||
@@ -42,16 +42,17 @@ MEMORY
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.vectors :
|
||||
.vectors : ALIGN(4)
|
||||
{
|
||||
. = ALIGN(4);
|
||||
__ld_vectors_start = .;
|
||||
KEEP(*(.vectors))
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
__ld_vectors_end = .;
|
||||
} >FLASH =0xFF
|
||||
|
||||
.text :
|
||||
.text : ALIGN(4)
|
||||
{
|
||||
. = ALIGN(4);
|
||||
__ld_text_start = .;
|
||||
*(.text) /* .text sections (code) */
|
||||
*(.text*) /* .text* sections (code) */
|
||||
*(.rodata) /* .rodata sections (constants, strings, etc.) */
|
||||
@@ -61,49 +62,59 @@ SECTIONS
|
||||
*(.eh_frame)
|
||||
KEEP(*(.init)) /* Constructors */
|
||||
KEEP(*(.fini)) /* Destructors */
|
||||
} >FLASH
|
||||
. = ALIGN(4);
|
||||
__ld_text_end = .;
|
||||
} >FLASH =0xFF
|
||||
|
||||
.ARM.extab :
|
||||
.ARM.extab : ALIGN(4)
|
||||
{
|
||||
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||
} >FLASH
|
||||
} >FLASH =0xFF
|
||||
|
||||
.ARM :
|
||||
.ARM : ALIGN(4)
|
||||
{
|
||||
__exidx_start = .;
|
||||
*(.ARM.exidx*)
|
||||
__exidx_end = .;
|
||||
} >FLASH
|
||||
} >FLASH =0xFF
|
||||
|
||||
/* Constructor/Destructor tables */
|
||||
.preinit_array :
|
||||
.preinit_array : ALIGN(4)
|
||||
{
|
||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array*))
|
||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||
} >FLASH
|
||||
} >FLASH =0xFF
|
||||
|
||||
.init_array :
|
||||
.init_array : ALIGN(4)
|
||||
{
|
||||
PROVIDE_HIDDEN (__init_array_start = .);
|
||||
KEEP (*(SORT(.init_array.*)))
|
||||
KEEP (*(.init_array*))
|
||||
PROVIDE_HIDDEN (__init_array_end = .);
|
||||
} >FLASH
|
||||
} >FLASH =0xFF
|
||||
|
||||
.fini_array :
|
||||
.fini_array : ALIGN(4)
|
||||
{
|
||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||
KEEP (*(.fini_array*))
|
||||
KEEP (*(SORT(.fini_array.*)))
|
||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||
} >FLASH
|
||||
/* Ensure LMA of ram data is at 32 bit word address in Flash */
|
||||
. = ALIGN(4);
|
||||
} >FLASH =0xFF
|
||||
|
||||
.flashcrc : ALIGN(4)
|
||||
{
|
||||
KEEP(*(.flashcrc))
|
||||
KEEP(*(.flashcrc.*))
|
||||
. = ALIGN(4);
|
||||
} >FLASH =0xFF
|
||||
|
||||
/* Initialized CCM data */
|
||||
__ld_load_ccm_data = LOADADDR(.ccmdata);
|
||||
.ccmdata :
|
||||
.ccmdata : ALIGN(4)
|
||||
{
|
||||
. = ALIGN(4);
|
||||
__ld_sdata_ccm = .;
|
||||
*(.ccm.data)
|
||||
*(.ccm.data*)
|
||||
@@ -111,9 +122,8 @@ SECTIONS
|
||||
__ld_edata_ccm = .;
|
||||
} >CCM AT> FLASH
|
||||
|
||||
.ccmbss (NOLOAD) :
|
||||
.ccmbss (NOLOAD) : ALIGN(4)
|
||||
{
|
||||
. = ALIGN(4);
|
||||
__ld_sbss_ccm = .;
|
||||
*(.ccm.bss)
|
||||
*(.ccm.bss*)
|
||||
@@ -123,9 +133,8 @@ SECTIONS
|
||||
|
||||
/* Initialized Data */
|
||||
__ld_load_data = LOADADDR(.data);
|
||||
.data :
|
||||
.data : ALIGN(4)
|
||||
{
|
||||
. = ALIGN(4);
|
||||
__ld_sdata = .;
|
||||
*(.data)
|
||||
*(.data*)
|
||||
@@ -134,9 +143,8 @@ SECTIONS
|
||||
} >RAM AT> FLASH
|
||||
|
||||
/* Uninitialized static data */
|
||||
.bss (NOLOAD) :
|
||||
.bss (NOLOAD) : ALIGN(4)
|
||||
{
|
||||
. = ALIGN(4);
|
||||
__ld_sbss = .;
|
||||
*(.bss)
|
||||
*(.bss*)
|
||||
@@ -145,9 +153,8 @@ SECTIONS
|
||||
__ld_ebss = .;
|
||||
} >RAM
|
||||
|
||||
.heap_stack (NOLOAD) :
|
||||
.heap_stack (NOLOAD) : ALIGN(4)
|
||||
{
|
||||
. = ALIGN(4);
|
||||
__ld_sheap = .;
|
||||
. = . + __ld_heap_size;
|
||||
__ld_eheap = .;
|
||||
|
@@ -40,10 +40,11 @@ void systick_setup(void)
|
||||
void systick_wait_ms(uint32_t ms)
|
||||
{
|
||||
wait_tick_ms = 0UL;
|
||||
while (wait_tick_ms < ms);
|
||||
while (wait_tick_ms < ms)
|
||||
;
|
||||
}
|
||||
|
||||
uint64_t systick_get_global_tick()
|
||||
uint64_t systick_get_global_tick(void)
|
||||
{
|
||||
uint64_t temp;
|
||||
|
||||
@@ -62,7 +63,6 @@ void systick_get_uptime_from_tick(uint32_t *days, uint32_t *hours, uint32_t *min
|
||||
uint32_t hs;
|
||||
uint32_t ds;
|
||||
|
||||
|
||||
tick_secs = systick_get_global_tick() / 1000;
|
||||
secs = tick_secs % 60;
|
||||
tick_secs /= 60;
|
||||
@@ -106,11 +106,12 @@ bool __attribute__((optimize("O3"))) systick_ticks_have_passed(uint64_t start_ti
|
||||
/**
|
||||
* @brief Interrupt Handler for SysTick
|
||||
*
|
||||
* This handler is called every millisecond
|
||||
* This handler is called every 100 us.
|
||||
* It is important to keep this function simple as it is called that often and may prevent program flow.
|
||||
*
|
||||
* @warning For calling cyclic functions use separate timers/flags and don't spoil this function
|
||||
*/
|
||||
void __attribute__((optimize("O3"))) SysTick_Handler()
|
||||
void __attribute__((optimize("O3"))) SysTick_Handler(void)
|
||||
{
|
||||
static uint32_t IN_SECTION(.ccm.bss) pre_tick = 0UL;
|
||||
|
||||
|
@@ -75,7 +75,7 @@ return_ret_val:
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
int temp_convertet_convert_temp_to_resistance(float temp, float *resistance_out)
|
||||
int temp_converter_convert_temp_to_resistance(float temp, float *resistance_out)
|
||||
{
|
||||
int retcode = 0;
|
||||
unsigned int i;
|
||||
|
@@ -18,7 +18,12 @@
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <reflow-controller/temp-profile-executer.h>
|
||||
/**
|
||||
* @addtogroup temp-profile
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <reflow-controller/temp-profile/temp-profile-executer.h>
|
||||
#include <reflow-controller/systick.h>
|
||||
#include <helper-macros/helper-macros.h>
|
||||
#include <reflow-controller/oven-driver.h>
|
||||
@@ -26,6 +31,8 @@
|
||||
#include <reflow-controller/adc-meas.h>
|
||||
#include <reflow-controller/digio.h>
|
||||
|
||||
#include <reflow-controller/safety/safety-controller.h>
|
||||
|
||||
static struct tpe_current_state IN_SECTION(.ccm.data) state = {
|
||||
.status = TPE_OFF,
|
||||
.start_timestamp = 0,
|
||||
@@ -34,7 +41,7 @@ static bool IN_SECTION(.ccm.bss) pid_should_run;
|
||||
struct pid_controller IN_SECTION(.ccm.bss) pid;
|
||||
bool IN_SECTION(.ccm.bss) cmd_continue;
|
||||
|
||||
static struct pl_command IN_SECTION(.ccm.bss) cmd_list[MAX_PROFILE_LENGTH];
|
||||
static SlList *command_list = NULL;
|
||||
|
||||
static void tpe_abort(void)
|
||||
{
|
||||
@@ -59,11 +66,19 @@ enum pl_ret_val temp_profile_executer_start(const char *filename)
|
||||
state.profile_steps = 0;
|
||||
cmd_continue = false;
|
||||
|
||||
res = temp_profile_parse_from_file(filename, cmd_list, MAX_PROFILE_LENGTH, &parsed_count);
|
||||
/* This should never happen... But who knows */
|
||||
if (command_list) {
|
||||
temp_profile_free_command_list(&command_list);
|
||||
}
|
||||
|
||||
res = temp_profile_parse_from_file(filename, &command_list, MAX_PROFILE_LENGTH, &parsed_count);
|
||||
if (res == PL_RET_SUCCESS) {
|
||||
state.profile_steps = parsed_count;
|
||||
state.status = TPE_RUNNING;
|
||||
state.start_timestamp = systick_get_global_tick();
|
||||
} else {
|
||||
if (command_list)
|
||||
temp_profile_free_command_list(&command_list);
|
||||
}
|
||||
|
||||
return res;
|
||||
@@ -113,10 +128,8 @@ static void reactivate_pid_if_suspended(void)
|
||||
pid_should_run = true;
|
||||
}
|
||||
|
||||
static bool cmd_set_temp(struct pl_command *cmd, bool cmd_continue)
|
||||
static bool cmd_set_temp(struct pl_command *cmd)
|
||||
{
|
||||
(void)cmd_continue;
|
||||
|
||||
reactivate_pid_if_suspended();
|
||||
oven_pid_set_target_temperature(cmd->params[0]);
|
||||
state.setpoint = cmd->params[0];
|
||||
@@ -153,6 +166,25 @@ static bool cmd_ramp(struct pl_command *cmd, bool cmd_continue)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Try to acknowledge all set flags.
|
||||
*/
|
||||
static void cmd_ack_flags(void)
|
||||
{
|
||||
uint32_t flag_cnt;
|
||||
uint32_t i;
|
||||
enum safety_flag flag_enum;
|
||||
bool status;
|
||||
|
||||
flag_cnt = safety_controller_get_flag_count();
|
||||
for (i = 0; i < flag_cnt; i++) {
|
||||
safety_controller_get_flag_by_index(i, &status, &flag_enum);
|
||||
if (status)
|
||||
(void)safety_controller_ack_flag(flag_enum);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int temp_profile_executer_handle(void)
|
||||
{
|
||||
struct pl_command *current_cmd;
|
||||
@@ -177,7 +209,7 @@ int temp_profile_executer_handle(void)
|
||||
if (!systick_ticks_have_passed(last_tick, 100))
|
||||
return 0;
|
||||
|
||||
current_cmd = &cmd_list[state.step];
|
||||
current_cmd = (struct pl_command *)sl_list_nth(command_list, state.step)->data;
|
||||
next_step = state.step;
|
||||
|
||||
switch (current_cmd->cmd) {
|
||||
@@ -188,7 +220,7 @@ int temp_profile_executer_handle(void)
|
||||
advance = cmd_wait_temp(current_cmd, cmd_continue);
|
||||
break;
|
||||
case PL_SET_TEMP:
|
||||
advance = cmd_set_temp(current_cmd, cmd_continue);
|
||||
advance = cmd_set_temp(current_cmd);
|
||||
break;
|
||||
case PL_LOUDSPEAKER_SET:
|
||||
loudspeaker_set((uint16_t)current_cmd->params[0]);
|
||||
@@ -214,6 +246,10 @@ int temp_profile_executer_handle(void)
|
||||
case PL_SET_RAMP:
|
||||
advance = cmd_ramp(current_cmd, cmd_continue);
|
||||
break;
|
||||
case PL_CLEAR_FLAGS:
|
||||
cmd_ack_flags();
|
||||
advance = true;
|
||||
break;
|
||||
default:
|
||||
tpe_abort();
|
||||
advance = true;
|
||||
@@ -250,7 +286,13 @@ int temp_profile_executer_stop(void)
|
||||
oven_pid_stop();
|
||||
}
|
||||
|
||||
/* Free the command list */
|
||||
if (command_list)
|
||||
temp_profile_free_command_list(&command_list);
|
||||
|
||||
loudspeaker_set(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @} */
|
@@ -18,11 +18,17 @@
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <reflow-controller/config-parser/temp-profile-parser.h>
|
||||
/**
|
||||
* @addtogroup temp-profile
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <reflow-controller/temp-profile/temp-profile-parser.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <fatfs/ff.h>
|
||||
#include <linklist-lib/singly-linked-list.h>
|
||||
|
||||
struct pl_command_list_map {
|
||||
enum pl_command_type command;
|
||||
@@ -40,7 +46,8 @@ static const struct pl_command_list_map cmd_list_map[_PL_NUM_CMDS] = {
|
||||
{PL_WAIT_FOR_TIME, "wait_time", 1u},
|
||||
{PL_SET_RAMP, "temp_ramp", 2u},
|
||||
{PL_LOUDSPEAKER_SET, "beep", 1u},
|
||||
{PL_OFF, "temp_off", 0u}
|
||||
{PL_OFF, "temp_off", 0u},
|
||||
{PL_CLEAR_FLAGS, "clear_flags", 0u},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -106,7 +113,7 @@ static int parse_line(char *line, struct pl_command *cmd)
|
||||
uint8_t token_idx = 0;
|
||||
char *token;
|
||||
const char * const delim = " \t";
|
||||
const struct pl_command_list_map *map;
|
||||
const struct pl_command_list_map *map = NULL;
|
||||
char *endptr;
|
||||
struct pl_command c;
|
||||
|
||||
@@ -146,7 +153,7 @@ static int parse_line(char *line, struct pl_command *cmd)
|
||||
token_idx++;
|
||||
}
|
||||
|
||||
if (token_idx - 1 < map->expected_param_count) {
|
||||
if (!map || (token_idx - 1 < map->expected_param_count)) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
@@ -155,9 +162,20 @@ static int parse_line(char *line, struct pl_command *cmd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SlList *copy_and_append_command_to_list(SlList *list, const struct pl_command *cmd)
|
||||
{
|
||||
struct pl_command *alloced_cmd;
|
||||
|
||||
alloced_cmd = (struct pl_command *)malloc(sizeof(struct pl_command));
|
||||
memcpy(alloced_cmd, cmd, sizeof(struct pl_command));
|
||||
list = sl_list_append(list, alloced_cmd);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
enum pl_ret_val temp_profile_parse_from_file(const char *filename,
|
||||
struct pl_command *cmd_list,
|
||||
uint32_t cmd_list_length,
|
||||
SlList **command_list,
|
||||
uint32_t max_len,
|
||||
uint32_t *cmds_parsed)
|
||||
{
|
||||
FIL script_file;
|
||||
@@ -165,9 +183,10 @@ enum pl_ret_val temp_profile_parse_from_file(const char *filename,
|
||||
int res;
|
||||
enum pl_ret_val ret = PL_RET_SUCCESS;
|
||||
char workbuff[256];
|
||||
struct pl_command temp_command;
|
||||
uint32_t cmd_idx;
|
||||
|
||||
if (!filename || !cmd_list || !cmd_list_length || !cmds_parsed)
|
||||
if (!filename || !command_list || !max_len || !cmds_parsed)
|
||||
return PL_RET_PARAM_ERR;
|
||||
|
||||
|
||||
@@ -189,19 +208,22 @@ enum pl_ret_val temp_profile_parse_from_file(const char *filename,
|
||||
}
|
||||
|
||||
/* Check if list already full */
|
||||
if (cmd_idx >= cmd_list_length) {
|
||||
if (cmd_idx >= max_len) {
|
||||
ret = PL_RET_LIST_FULL;
|
||||
goto exit_close;
|
||||
}
|
||||
|
||||
/* Parse the line */
|
||||
res = parse_line(workbuff, &cmd_list[cmd_idx]);
|
||||
res = parse_line(workbuff, &temp_command);
|
||||
if (res < 0) {
|
||||
ret = PL_RET_SCRIPT_ERR;
|
||||
goto exit_close;
|
||||
} else if (res == 0) {
|
||||
cmd_idx++;
|
||||
*cmds_parsed= cmd_idx;
|
||||
*cmds_parsed = cmd_idx;
|
||||
|
||||
/* Append the temp_command to the list */
|
||||
*command_list = copy_and_append_command_to_list(*command_list, &temp_command);
|
||||
}
|
||||
|
||||
|
||||
@@ -212,3 +234,17 @@ exit_close:
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void delete_pl_command(void *cmd)
|
||||
{
|
||||
if (cmd)
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
void temp_profile_free_command_list(SlList **list)
|
||||
{
|
||||
sl_list_free_full(*list, delete_pl_command);
|
||||
*list = NULL;
|
||||
}
|
||||
|
||||
/** @} */
|
@@ -19,7 +19,7 @@
|
||||
*/
|
||||
|
||||
#include <stm32/stm32f4xx.h>
|
||||
#include <reflow-controller/button.h>
|
||||
#include <reflow-controller/ui/button.h>
|
||||
#include <stm-periph/stm32-gpio-macros.h>
|
||||
#include <stm-periph/rcc-manager.h>
|
||||
#include <stdint.h>
|
@@ -22,12 +22,13 @@
|
||||
#include <reflow-controller/ui/gui-config.h>
|
||||
#include <reflow-controller/ui/menu.h>
|
||||
#include <reflow-controller/ui/lcd.h>
|
||||
#include <reflow-controller/rotary-encoder.h>
|
||||
#include <reflow-controller/ui/rotary-encoder.h>
|
||||
#include <reflow-controller/systick.h>
|
||||
#include <reflow-controller/adc-meas.h>
|
||||
#include <reflow-controller/safety/safety-controller.h>
|
||||
#include <reflow-controller/settings/settings.h>
|
||||
#include <reflow-controller/temp-converter.h>
|
||||
#include <reflow-controller/updater/updater.h>
|
||||
#include <helper-macros/helper-macros.h>
|
||||
#include <stm-periph/unique-id.h>
|
||||
#include <stddef.h>
|
||||
@@ -37,7 +38,9 @@
|
||||
#include <inttypes.h>
|
||||
#include <reflow-controller/oven-driver.h>
|
||||
#include <fatfs/ff.h>
|
||||
#include <reflow-controller/temp-profile-executer.h>
|
||||
#include <reflow-controller/temp-profile/temp-profile-executer.h>
|
||||
#include <linklist-lib/singly-linked-list.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static char IN_SECTION(.ccm.bss) display_buffer[4][21] = {0};
|
||||
static struct lcd_menu IN_SECTION(.ccm.bss) reflow_menu;
|
||||
@@ -374,43 +377,38 @@ static void gui_menu_constant_temperature_driver(struct lcd_menu *menu, enum men
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief load_temperature_file_list_from_sdcard
|
||||
* @return -1 File error, 0 successful
|
||||
*/
|
||||
static int load_temperature_file_list_from_sdcard(char (*list)[17], uint32_t len)
|
||||
static void delete_file_list_entry(void *obj)
|
||||
{
|
||||
if (obj)
|
||||
free(obj);
|
||||
}
|
||||
|
||||
static SlList *load_file_list_from_sdcard(int *error, const char *file_pattern)
|
||||
{
|
||||
uint32_t i, j;
|
||||
DIR directory;
|
||||
FILINFO finfo;
|
||||
FRESULT fres;
|
||||
|
||||
if (!list)
|
||||
return -1001;
|
||||
|
||||
/* Zero out the list */
|
||||
for (i = 0; i < len; i++) {
|
||||
for (j = 0; j < sizeof(*list); j++) {
|
||||
list[i][j] = 0;
|
||||
}
|
||||
}
|
||||
SlList *list = NULL;
|
||||
char *name;
|
||||
|
||||
/* find the frist file */
|
||||
fres = f_findfirst(&directory, &finfo, "/", "*.tpr");
|
||||
i = 0;
|
||||
fres = f_findfirst(&directory, &finfo, "/", file_pattern);
|
||||
while (fres == FR_OK && finfo.fname[0]) {
|
||||
strncpy(list[i], finfo.fname, sizeof(*list));
|
||||
name = malloc(strlen(finfo.fname) + 1);
|
||||
strcpy(name, finfo.fname);
|
||||
list = sl_list_append(list, name);
|
||||
fres = f_findnext(&directory, &finfo);
|
||||
i++;
|
||||
if (i >= len)
|
||||
break;
|
||||
}
|
||||
|
||||
if (fres != FR_OK) {
|
||||
return -1;
|
||||
sl_list_free_full(list, delete_file_list_entry);
|
||||
list = NULL;
|
||||
if (error)
|
||||
*error = -1;
|
||||
}
|
||||
|
||||
return (int)i;
|
||||
return list;
|
||||
}
|
||||
|
||||
static void gui_menu_temp_profile_execute(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void* parent)
|
||||
@@ -428,23 +426,32 @@ static void gui_menu_temp_profile_execute(struct lcd_menu *menu, enum menu_entry
|
||||
last_tick = 0ULL;
|
||||
}
|
||||
|
||||
if (systick_ticks_have_passed(last_tick, 300)) {
|
||||
if (systick_ticks_have_passed(last_tick, 250)) {
|
||||
state = temp_profile_executer_status();
|
||||
if (state->status == TPE_RUNNING)
|
||||
menu_lcd_outputf(menu, 0, "Profile running");
|
||||
else if (state->status == TPE_OFF) {
|
||||
menu_lcd_outputf(menu, 0, "Profile finished");
|
||||
if (state->status == TPE_RUNNING) {
|
||||
menu_lcd_outputf(menu, 0, "Executing...");
|
||||
menu_lcd_outputf(menu, 1, "Step %u/%u", state->step, state->profile_steps);
|
||||
(void)adc_pt1000_get_current_resistance(&resistance);
|
||||
res = temp_converter_convert_resistance_to_temp(resistance, &temperature);
|
||||
menu_lcd_outputf(menu, 2, "Temp: %s%.1f " LCD_DEGREE_SYMBOL_STRING "C",
|
||||
(res < 0 ? "<" : (res > 0 ? ">" : "")), temperature);
|
||||
if (oven_pid_get_status() == OVEN_PID_RUNNING) {
|
||||
menu_lcd_outputf(menu, 3, "Target: %.0f " LCD_DEGREE_SYMBOL_STRING "C", state->setpoint);
|
||||
} else {
|
||||
menu_lcd_outputf(menu, 3, "Temp Off");
|
||||
}
|
||||
} else if (state->status == TPE_OFF) {
|
||||
menu_lcd_outputf(menu, 0, "Finished!");
|
||||
menu_lcd_outputf(menu, 1, "Press button");
|
||||
menu_lcd_outputf(menu, 2, "to return.");
|
||||
(void)adc_pt1000_get_current_resistance(&resistance);
|
||||
res = temp_converter_convert_resistance_to_temp(resistance, &temperature);
|
||||
menu_lcd_outputf(menu, 3, "Temp: %.1f ", LCD_DEGREE_SYMBOL_STRING "C", temperature);
|
||||
} else {
|
||||
menu_lcd_outputf(menu, 0, "Profile aborted!");
|
||||
}
|
||||
menu_lcd_outputf(menu, 1, "Step %u/%u", state->step, state->profile_steps);
|
||||
(void)adc_pt1000_get_current_resistance(&resistance);
|
||||
res = temp_converter_convert_resistance_to_temp(resistance, &temperature);
|
||||
menu_lcd_outputf(menu, 2, "Temp: %s%.0f", (res < 0 ? "<" : (res > 0 ? ">" : "")), temperature);
|
||||
if (oven_pid_get_status() == OVEN_PID_RUNNING) {
|
||||
menu_lcd_outputf(menu, 3, "Target: %.0f", state->setpoint);
|
||||
} else {
|
||||
menu_lcd_outputf(menu, 3, "Temp Off");
|
||||
menu_lcd_outputf(menu, 1, "Check flags!");
|
||||
menu_lcd_outputf(menu, 2, "");
|
||||
menu_lcd_outputf(menu, 3, "Press button");
|
||||
}
|
||||
|
||||
last_tick = systick_get_global_tick();
|
||||
@@ -461,29 +468,29 @@ static void gui_menu_temp_profile_execute(struct lcd_menu *menu, enum menu_entry
|
||||
static void gui_menu_temp_profile_select(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void *parent)
|
||||
{
|
||||
static void *my_parent;
|
||||
static char profile_list[10][17];
|
||||
static bool file_error = false;
|
||||
static SlList *file_list = NULL;
|
||||
static int file_error = 0;
|
||||
static enum pl_ret_val profile_ret_val = PL_RET_SUCCESS;
|
||||
static uint8_t currently_selected = 0U;
|
||||
static uint8_t loaded;
|
||||
int16_t delta;
|
||||
enum button_state button;
|
||||
|
||||
int res;
|
||||
|
||||
if (entry_type == MENU_ENTRY_FIRST_ENTER) {
|
||||
menu_display_clear(menu);
|
||||
my_parent = parent;
|
||||
res = load_temperature_file_list_from_sdcard(profile_list, 10);
|
||||
file_error = false;
|
||||
file_error = 0;
|
||||
loaded = 0;
|
||||
if (res < 0) {
|
||||
file_error = true;
|
||||
|
||||
if (file_list) {
|
||||
sl_list_free_full(file_list, delete_file_list_entry);
|
||||
}
|
||||
|
||||
file_list = load_file_list_from_sdcard(&file_error, "*.tpr");
|
||||
|
||||
currently_selected = 0u;
|
||||
profile_ret_val = PL_RET_SUCCESS;
|
||||
loaded = (uint32_t)res;
|
||||
loaded = sl_list_length(file_list);
|
||||
menu_lcd_outputf(menu, 0, "Select:");
|
||||
} else if (entry_type == MENU_ENTRY_DROPBACK) {
|
||||
menu_entry_dropback(menu, my_parent);
|
||||
@@ -501,14 +508,20 @@ static void gui_menu_temp_profile_select(struct lcd_menu *menu, enum menu_entry_
|
||||
if (file_error) {
|
||||
menu_lcd_outputf(menu, 0, "Disk Error");
|
||||
menu_lcd_outputf(menu, 1, "SD inserted?");
|
||||
if (button == BUTTON_SHORT_RELEASED)
|
||||
if (button == BUTTON_SHORT_RELEASED) {
|
||||
sl_list_free_full(file_list, delete_file_list_entry);
|
||||
file_list = NULL;
|
||||
menu_entry_dropback(menu, my_parent);
|
||||
}
|
||||
return;
|
||||
} else if (loaded == 0) {
|
||||
menu_lcd_outputf(menu, 0, "No profiles");
|
||||
menu_lcd_outputf(menu, 1, "found");
|
||||
if (button == BUTTON_SHORT_RELEASED)
|
||||
if (button == BUTTON_SHORT_RELEASED) {
|
||||
sl_list_free_full(file_list, delete_file_list_entry);
|
||||
file_list = NULL;
|
||||
menu_entry_dropback(menu, my_parent);
|
||||
}
|
||||
return;
|
||||
} else if (profile_ret_val != PL_RET_SUCCESS) {
|
||||
menu_lcd_outputf(menu, 0, "ERROR");
|
||||
@@ -528,17 +541,22 @@ static void gui_menu_temp_profile_select(struct lcd_menu *menu, enum menu_entry_
|
||||
break;
|
||||
}
|
||||
|
||||
if (button == BUTTON_SHORT_RELEASED)
|
||||
if (button == BUTTON_SHORT_RELEASED) {
|
||||
sl_list_free_full(file_list, delete_file_list_entry);
|
||||
file_list = NULL;
|
||||
menu_entry_dropback(menu, my_parent);
|
||||
}
|
||||
return;
|
||||
} else if (currently_selected < loaded) {
|
||||
/* Show currently selected profile */
|
||||
menu_lcd_outputf(menu, 1, "%s", &profile_list[currently_selected][0]);
|
||||
menu_lcd_outputf(menu, 1, "%s", sl_list_nth(file_list, currently_selected)->data);
|
||||
|
||||
if (button == BUTTON_SHORT_RELEASED) {
|
||||
/* Execute selected profile */
|
||||
profile_ret_val = temp_profile_executer_start(&profile_list[currently_selected][0]);
|
||||
profile_ret_val = temp_profile_executer_start(sl_list_nth(file_list, currently_selected)->data);
|
||||
if (profile_ret_val == PL_RET_SUCCESS) {
|
||||
sl_list_free_full(file_list, delete_file_list_entry);
|
||||
file_list = NULL;
|
||||
menu_entry_enter(menu, gui_menu_temp_profile_execute, true);
|
||||
return;
|
||||
}
|
||||
@@ -590,6 +608,121 @@ static void gui_menu_constant_temperature_driver_setup(struct lcd_menu *menu, en
|
||||
}
|
||||
}
|
||||
|
||||
static void gui_update_firmware(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void *parent)
|
||||
{
|
||||
static void *my_parent;
|
||||
static SlList *file_list = NULL;
|
||||
static int error;
|
||||
enum button_state button;
|
||||
static uint32_t currently_selected_file;
|
||||
uint32_t previously_selected_file;
|
||||
static uint32_t list_length;
|
||||
const char *fname;
|
||||
int16_t rotary;
|
||||
|
||||
if (entry_type == MENU_ENTRY_FIRST_ENTER) {
|
||||
my_parent = parent;
|
||||
error = 0;
|
||||
list_length = 0;
|
||||
if (file_list)
|
||||
sl_list_free_full(file_list, delete_file_list_entry);
|
||||
file_list = load_file_list_from_sdcard(&error, "*.hex");
|
||||
if (error) {
|
||||
if (file_list)
|
||||
sl_list_free_full(file_list, delete_file_list_entry);
|
||||
file_list = NULL;
|
||||
} else {
|
||||
list_length = sl_list_length(file_list);
|
||||
}
|
||||
currently_selected_file = 0;
|
||||
menu_display_clear(menu);
|
||||
}
|
||||
|
||||
if (menu_get_button_ready_state(menu)) {
|
||||
button = menu_get_button_state(menu);
|
||||
rotary = menu_get_rotary_delta(menu);
|
||||
|
||||
if (error) {
|
||||
menu_lcd_output(menu, 0, "Error reading");
|
||||
menu_lcd_output(menu, 1, "file list");
|
||||
if (button == BUTTON_LONG || button == BUTTON_SHORT_RELEASED)
|
||||
menu_entry_dropback(menu, my_parent);
|
||||
} else {
|
||||
previously_selected_file = currently_selected_file;
|
||||
|
||||
/* Display the list */
|
||||
if (rotary <= -4) {
|
||||
menu_ack_rotary_delta(menu);
|
||||
if (currently_selected_file > 0) {
|
||||
currently_selected_file--;
|
||||
}
|
||||
} else if (rotary >= 4) {
|
||||
menu_ack_rotary_delta(menu);
|
||||
if (currently_selected_file < (list_length - 1)) {
|
||||
currently_selected_file++;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry_type == MENU_ENTRY_FIRST_ENTER ||
|
||||
previously_selected_file != currently_selected_file) {
|
||||
fname = sl_list_nth(file_list, currently_selected_file)->data;
|
||||
menu_lcd_output(menu, 0, "Select File:");
|
||||
if (fname)
|
||||
menu_lcd_output(menu, 1, fname);
|
||||
}
|
||||
if (button == BUTTON_SHORT_RELEASED) {
|
||||
fname = sl_list_nth(file_list, currently_selected_file)->data;
|
||||
menu_display_clear(menu);
|
||||
file_list = NULL;
|
||||
updater_update_from_file(fname);
|
||||
/* This code is here for completeness. It will never be reached! */
|
||||
sl_list_free_full(file_list, delete_file_list_entry);
|
||||
} else if (button == BUTTON_LONG) {
|
||||
sl_list_free_full(file_list, delete_file_list_entry);
|
||||
file_list = NULL;
|
||||
menu_entry_dropback(menu, my_parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static char *overlay_heading = NULL;
|
||||
static char *overlay_text = NULL;
|
||||
|
||||
static void gui_menu_overlay_entry(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void *parent)
|
||||
{
|
||||
static void *my_parent;
|
||||
enum button_state button;
|
||||
|
||||
if (entry_type == MENU_ENTRY_FIRST_ENTER) {
|
||||
my_parent = parent;
|
||||
menu_display_clear(menu);
|
||||
if (overlay_heading)
|
||||
menu_lcd_output(menu, 0, overlay_heading);
|
||||
if (overlay_text) {
|
||||
menu_lcd_output(menu, 2, overlay_text);
|
||||
if (strlen(overlay_text) > 16) {
|
||||
menu_lcd_output(menu, 3, &overlay_text[16]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (menu_get_button_ready_state(menu)) {
|
||||
button = menu_get_button_state(menu);
|
||||
menu_ack_rotary_delta(menu);
|
||||
|
||||
if (button != BUTTON_IDLE) {
|
||||
if (overlay_heading)
|
||||
free(overlay_heading);
|
||||
if (overlay_text)
|
||||
free(overlay_text);
|
||||
overlay_heading = NULL;
|
||||
overlay_text = NULL;
|
||||
menu_entry_dropback(menu, my_parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gui_menu_root_entry(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void *parent)
|
||||
{
|
||||
(void)parent;
|
||||
@@ -601,6 +734,7 @@ static void gui_menu_root_entry(struct lcd_menu *menu, enum menu_entry_func_entr
|
||||
"Monitoring",
|
||||
"Error Flags",
|
||||
"About",
|
||||
"Update",
|
||||
NULL
|
||||
};
|
||||
static const menu_func_t root_entry_funcs[] = {
|
||||
@@ -609,6 +743,7 @@ static void gui_menu_root_entry(struct lcd_menu *menu, enum menu_entry_func_entr
|
||||
gui_menu_monitor,
|
||||
gui_menu_err_flags,
|
||||
gui_menu_about,
|
||||
gui_update_firmware,
|
||||
};
|
||||
enum button_state push_button;
|
||||
int16_t rot_delta;
|
||||
@@ -630,23 +765,30 @@ static void gui_menu_root_entry(struct lcd_menu *menu, enum menu_entry_func_entr
|
||||
push_button = menu_get_button_state(menu);
|
||||
rot_delta = menu_get_rotary_delta(menu);
|
||||
|
||||
if (menu_get_button_ready_state(menu) && push_button == BUTTON_SHORT_RELEASED) {
|
||||
/* Enter currently selected menu_entry */
|
||||
menu_list_enter_selected_entry(&list, menu);
|
||||
if (menu_get_button_ready_state(menu)) {
|
||||
if (menu_get_button_ready_state(menu) && push_button == BUTTON_SHORT_RELEASED) {
|
||||
/* Enter currently selected menu_entry */
|
||||
menu_list_enter_selected_entry(&list, menu);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (menu_changed)
|
||||
/* Display the message overlay in case it is set */
|
||||
if (overlay_heading || overlay_text) {
|
||||
menu_entry_enter(menu, gui_menu_overlay_entry, true);
|
||||
return;
|
||||
} else if (menu_changed) {
|
||||
menu_list_display(&list, 1, 3);
|
||||
}
|
||||
}
|
||||
|
||||
int gui_handle()
|
||||
@@ -677,5 +819,37 @@ void gui_init()
|
||||
button_init();
|
||||
lcd_init();
|
||||
|
||||
if (overlay_heading)
|
||||
free(overlay_heading);
|
||||
if (overlay_text)
|
||||
free(overlay_text);
|
||||
|
||||
overlay_heading = NULL;
|
||||
overlay_text = NULL;
|
||||
|
||||
menu_init(reflow_menu_ptr, gui_menu_root_entry, update_display_buffer);
|
||||
}
|
||||
|
||||
void gui_root_menu_message_set(const char *heading, const char *text)
|
||||
{
|
||||
if (heading) {
|
||||
overlay_heading = (char *)malloc(strlen(heading) + 1);
|
||||
strcpy(overlay_heading, heading);
|
||||
}
|
||||
|
||||
if (text) {
|
||||
overlay_text = (char *)malloc(strlen(text) + 1);
|
||||
strcpy(overlay_text, text);
|
||||
}
|
||||
}
|
||||
|
||||
void gui_lcd_write_direct_blocking(uint8_t line, const char *text)
|
||||
{
|
||||
if (!text)
|
||||
return;
|
||||
if (line > 3)
|
||||
return;
|
||||
|
||||
lcd_setcursor(0, line);
|
||||
lcd_string(text);
|
||||
}
|
||||
|
@@ -51,9 +51,7 @@ static void lcd_enable(void)
|
||||
__ASM("nop");
|
||||
__ASM("nop");
|
||||
__ASM("nop");
|
||||
//systick_wait_ms(10);
|
||||
LCD_DPORT->ODR &= ~LCD_E_MASK;
|
||||
//systick_wait_ms(10);
|
||||
__ASM("nop");
|
||||
__ASM("nop");
|
||||
__ASM("nop");
|
||||
|
@@ -39,7 +39,8 @@ void menu_handle(struct lcd_menu *menu, int16_t rotary_encoder_delta, enum butto
|
||||
|
||||
tmp = menu->active_entry;
|
||||
|
||||
if (menu->active_entry_type == MENU_ENTRY_FIRST_ENTER && push_button != BUTTON_IDLE) {
|
||||
if ((menu->active_entry_type == MENU_ENTRY_FIRST_ENTER || menu->active_entry_type == MENU_ENTRY_DROPBACK)
|
||||
&& push_button != BUTTON_IDLE) {
|
||||
menu->inputs.button_ready = false;
|
||||
}
|
||||
|
||||
|
@@ -18,7 +18,7 @@
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <reflow-controller/rotary-encoder.h>
|
||||
#include <reflow-controller/ui/rotary-encoder.h>
|
||||
#include <stm-periph/rcc-manager.h>
|
||||
#include <stm-periph/stm32-gpio-macros.h>
|
||||
#include <helper-macros/helper-macros.h>
|
46
stm-firmware/updater/ram-code/3rd-party/sha256/LICENSE.md
vendored
Normal file
46
stm-firmware/updater/ram-code/3rd-party/sha256/LICENSE.md
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
# Licensing Information
|
||||
|
||||
Except as otherwise noted (below and/or in individual files), this project is
|
||||
licensed under the [Unlicense](#the-unlicense)
|
||||
(https://opensource.org/licenses/unlicense) or the [Zero Clause BSD
|
||||
license](#zero-clause-bsd-license) (https://opensource.org/licenses/0bsd), at
|
||||
your option.
|
||||
|
||||
## The Unlicense
|
||||
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org>
|
||||
|
||||
## Zero Clause BSD License
|
||||
|
||||
© 2021 Alain Mosnier
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
134
stm-firmware/updater/ram-code/3rd-party/sha256/README.md
vendored
Normal file
134
stm-firmware/updater/ram-code/3rd-party/sha256/README.md
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
# sha-2 [](https://travis-ci.com/amosnier/sha-2)
|
||||
|
||||
## Disclaimer
|
||||
The SHA256 implementation used in thsi project is taken from https://github.com/amosnier/sha-2. Check the appropriate license file.
|
||||
|
||||
## Contents
|
||||
|
||||
SHA-2 algorithm implementations.
|
||||
|
||||
At the moment, only SHA-256 is implemented.
|
||||
|
||||
## NEW: streaming API
|
||||
|
||||
In response to [an enhancement
|
||||
request](https://github.com/amosnier/sha-2/issues/9), a new streaming
|
||||
API has been created.
|
||||
|
||||
The following code is a silly but complete example:
|
||||
|
||||
```C
|
||||
struct Sha_256 sha_256;
|
||||
uint8_t hash[32];
|
||||
sha_256_init(&sha_256, hash);
|
||||
sha_256_write(&sha_256, "ab", strlen("ab"));
|
||||
sha_256_write(&sha_256, "c", strlen("c"));
|
||||
sha_256_close(&sha_256);
|
||||
```
|
||||
|
||||
That is the equivalent of the (of course still supported) legacy way:
|
||||
|
||||
```C
|
||||
uint8_t hash[32];
|
||||
calc_sha_256(hash, "abc", strlen("abc"));
|
||||
```
|
||||
|
||||
See [header file](https://github.com/amosnier/sha-2/blob/master/sha-256.h)
|
||||
for more information.
|
||||
|
||||
Since the streaming API is a generalization of the non-streaming one,
|
||||
the latter has been ported to the former, without measurable performance
|
||||
impact. However, [a legacy
|
||||
branch](https://github.com/amosnier/sha-2/tree/legacy) has been created
|
||||
in order to make the legacy implementation easily available. If you can
|
||||
measure a significant difference between the two, please post an issue.
|
||||
|
||||
## Design criteria
|
||||
|
||||
- Easy to test, include in any project, compile and link.
|
||||
|
||||
- ANSI C with as little specific C99 as possible (e.g. extended
|
||||
integer types are used, but not bool).
|
||||
|
||||
- Portable. Makes no assumptions on the target system's endianess or
|
||||
word size.
|
||||
|
||||
- The SHA-256 implementation is a straightforward implementation of
|
||||
the algorithm specified on
|
||||
[Wikipedia](https://en.wikipedia.org/wiki/SHA-2).
|
||||
|
||||
## Notes
|
||||
|
||||
The Makefile is as minimal as possible. No effort was put into making
|
||||
it general. Its purpose is mainly to ease testing for the developer's
|
||||
host machine. The actual implementation is however extremely easy to
|
||||
include in any project, may it use GNU make or any other build tool.
|
||||
|
||||
## Code review
|
||||
|
||||
This code has been reviewed at [Stack Exchange CODE
|
||||
REVIEW](https://codereview.stackexchange.com/questions/182812/self-contained-sha-256-implementation-in-c),
|
||||
and the implementation has been improved accordingly.
|
||||
|
||||
## Testing
|
||||
|
||||
Testing is continuously performed on Travis CI (see above).
|
||||
|
||||
Apart from that, the implementation has been successfully tested on an x86-64 machine
|
||||
under Linux as well as on a 16-bit DSP. On the x86-64 machine, all the
|
||||
available NIST test vectors where successfully tested ([SHA-256
|
||||
examples](https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA256.pdf)
|
||||
and [SHA-2 Additional
|
||||
examples](https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA2_Additional.pdf),
|
||||
plus a few others).
|
||||
|
||||
In particular:
|
||||
|
||||
```
|
||||
Input Message: "abc"
|
||||
Message Digest is BA7816BF 8F01CFEA 414140DE 5DAE2223 B00361A3 96177A9C B410FF61 F20015AD
|
||||
```
|
||||
|
||||
```
|
||||
Input Message: "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
||||
Message Digest is 248D6A61 D20638B8 E5C02693 0C3E6039 A33CE459 64FF2167 F6ECEDD4 19DB06C1
|
||||
```
|
||||
|
||||
```
|
||||
SHA-256 Test Data
|
||||
#1) 1 byte 0xbd
|
||||
68325720 aabd7c82 f30f554b 313d0570 c95accbb 7dc4b5aa e11204c0 8ffe732b
|
||||
#2) 4 bytes 0xc98c8e55
|
||||
7abc22c0 ae5af26c e93dbb94 433a0e0b 2e119d01 4f8e7f65 bd56c61c cccd9504
|
||||
#3) 55 bytes of zeros
|
||||
02779466 cdec1638 11d07881 5c633f21 90141308 1449002f 24aa3e80 f0b88ef7
|
||||
#4) 56 bytes of zeros
|
||||
d4817aa5 497628e7 c77e6b60 6107042b bba31308 88c5f47a 375e6179 be789fbb
|
||||
#5) 57 bytes of zeros
|
||||
65a16cb7 861335d5 ace3c607 18b5052e 44660726 da4cd13b b745381b 235a1785
|
||||
#6) 64 bytes of zeros
|
||||
f5a5fd42 d16a2030 2798ef6e d309979b 43003d23 20d9f0e8 ea9831a9 2759fb4b
|
||||
#7) 1000 bytes of zeros
|
||||
541b3e9d aa09b20b f85fa273 e5cbd3e8 0185aa4e c298e765 db87742b 70138a53
|
||||
#8) 1000 bytes of 0x41 ‘A’
|
||||
c2e68682 3489ced2 017f6059 b8b23931 8b6364f6 dcd835d0 a519105a 1eadd6e4
|
||||
#9) 1005 bytes of 0x55 ‘U’
|
||||
f4d62dde c0f3dd90 ea1380fa 16a5ff8d c4c54b21 740650f2 4afc4120 903552b0
|
||||
#10) 1000000 bytes of zeros
|
||||
d29751f2 649b32ff 572b5e0a 9f541ea6 60a50f94 ff0beedf b0b692b9 24cc8025
|
||||
#11) 0x20000000 (536870912) bytes of 0x5a ‘Z’
|
||||
15a1868c 12cc5395 1e182344 277447cd 0979536b adcc512a d24c67e9 b2d4f3dd
|
||||
#12) 0x41000000 (1090519040) bytes of zeros
|
||||
461c19a9 3bd4344f 9215f5ec 64357090 342bc66b 15a14831 7d276e31 cbc20b53
|
||||
#13) 0x6000003e (1610612798) bytes of 0x42 ‘B’
|
||||
c23ce8a7 895f4b21 ec0daf37 920ac0a2 62a22004 5a03eb2d fed48ef9 b05aabea
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This repository is made available under a permissive license. See
|
||||
[LICENSE FILE](LICENSE.md).
|
||||
|
||||
## Reference implementation
|
||||
|
||||
I had missed that when I made this implementation but [RFC 6234, chapter 8](https://tools.ietf.org/html/rfc6234#section-8) actually includes a reference implementation in C that is (at least in ambition) broader in scope than this one. I have however neither compiled nor tested it.
|
226
stm-firmware/updater/ram-code/3rd-party/sha256/sha-256.c
vendored
Normal file
226
stm-firmware/updater/ram-code/3rd-party/sha256/sha-256.c
vendored
Normal file
@@ -0,0 +1,226 @@
|
||||
#include "sha-256.h"
|
||||
|
||||
#define TOTAL_LEN_LEN 8
|
||||
|
||||
/*
|
||||
* ABOUT bool: this file does not use bool in order to be as pre-C99 compatible as possible.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Comments from pseudo-code at https://en.wikipedia.org/wiki/SHA-2 are reproduced here.
|
||||
* When useful for clarification, portions of the pseudo-code are reproduced here too.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @brief Rotate a 32-bit value by a number of bits to the right.
|
||||
* @param value The value to be rotated.
|
||||
* @param count The number of bits to rotate by.
|
||||
* @return The rotated value.
|
||||
*/
|
||||
static inline uint32_t right_rot(uint32_t value, unsigned int count)
|
||||
{
|
||||
/*
|
||||
* Defined behaviour in standard C for all count where 0 < count < 32, which is what we need here.
|
||||
*/
|
||||
return value >> count | value << (32 - count);
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Update a hash value under calculation with a new chunk of data.
|
||||
* @param h Pointer to the first hash item, of a total of eight.
|
||||
* @param p Pointer to the chunk data, which has a standard length.
|
||||
*
|
||||
* @note This is the SHA-256 work horse.
|
||||
*/
|
||||
static inline void consume_chunk(uint32_t *h, const uint8_t *p)
|
||||
{
|
||||
unsigned i, j;
|
||||
uint32_t ah[8];
|
||||
|
||||
/* Initialize working variables to current hash value: */
|
||||
for (i = 0; i < 8; i++)
|
||||
ah[i] = h[i];
|
||||
|
||||
/*
|
||||
* The w-array is really w[64], but since we only need 16 of them at a time, we save stack by
|
||||
* calculating 16 at a time.
|
||||
*
|
||||
* This optimization was not there initially and the rest of the comments about w[64] are kept in their
|
||||
* initial state.
|
||||
*/
|
||||
|
||||
/*
|
||||
* create a 64-entry message schedule array w[0..63] of 32-bit words (The initial values in w[0..63]
|
||||
* don't matter, so many implementations zero them here) copy chunk into first 16 words w[0..15] of the
|
||||
* message schedule array
|
||||
*/
|
||||
uint32_t w[16];
|
||||
|
||||
/* Compression function main loop: */
|
||||
for (i = 0; i < 4; i++) {
|
||||
for (j = 0; j < 16; j++) {
|
||||
if (i == 0) {
|
||||
w[j] =
|
||||
(uint32_t)p[0] << 24 | (uint32_t)p[1] << 16 | (uint32_t)p[2] << 8 | (uint32_t)p[3];
|
||||
p += 4;
|
||||
} else {
|
||||
/* Extend the first 16 words into the remaining 48 words w[16..63] of the
|
||||
* message schedule array: */
|
||||
const uint32_t s0 = right_rot(w[(j + 1) & 0xf], 7) ^ right_rot(w[(j + 1) & 0xf], 18) ^
|
||||
(w[(j + 1) & 0xf] >> 3);
|
||||
const uint32_t s1 = right_rot(w[(j + 14) & 0xf], 17) ^
|
||||
right_rot(w[(j + 14) & 0xf], 19) ^ (w[(j + 14) & 0xf] >> 10);
|
||||
w[j] = w[j] + s0 + w[(j + 9) & 0xf] + s1;
|
||||
}
|
||||
const uint32_t s1 = right_rot(ah[4], 6) ^ right_rot(ah[4], 11) ^ right_rot(ah[4], 25);
|
||||
const uint32_t ch = (ah[4] & ah[5]) ^ (~ah[4] & ah[6]);
|
||||
|
||||
/*
|
||||
* Initialize array of round constants:
|
||||
* (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311):
|
||||
*/
|
||||
static const uint32_t k[] = {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4,
|
||||
0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe,
|
||||
0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f,
|
||||
0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
|
||||
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
|
||||
0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
|
||||
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116,
|
||||
0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,
|
||||
0xc67178f2};
|
||||
|
||||
const uint32_t temp1 = ah[7] + s1 + ch + k[i << 4 | j] + w[j];
|
||||
const uint32_t s0 = right_rot(ah[0], 2) ^ right_rot(ah[0], 13) ^ right_rot(ah[0], 22);
|
||||
const uint32_t maj = (ah[0] & ah[1]) ^ (ah[0] & ah[2]) ^ (ah[1] & ah[2]);
|
||||
const uint32_t temp2 = s0 + maj;
|
||||
|
||||
ah[7] = ah[6];
|
||||
ah[6] = ah[5];
|
||||
ah[5] = ah[4];
|
||||
ah[4] = ah[3] + temp1;
|
||||
ah[3] = ah[2];
|
||||
ah[2] = ah[1];
|
||||
ah[1] = ah[0];
|
||||
ah[0] = temp1 + temp2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add the compressed chunk to the current hash value: */
|
||||
for (i = 0; i < 8; i++)
|
||||
h[i] += ah[i];
|
||||
}
|
||||
|
||||
/*
|
||||
* Public functions. See header file for documentation.
|
||||
*/
|
||||
|
||||
void sha_256_init(struct Sha_256 *sha_256, uint8_t hash[SIZE_OF_SHA_256_HASH])
|
||||
{
|
||||
sha_256->hash = hash;
|
||||
sha_256->chunk_pos = sha_256->chunk;
|
||||
sha_256->space_left = SIZE_OF_SHA_256_CHUNK;
|
||||
sha_256->total_len = 0;
|
||||
/*
|
||||
* Initialize hash values (first 32 bits of the fractional parts of the square roots of the first 8 primes
|
||||
* 2..19):
|
||||
*/
|
||||
sha_256->h[0] = 0x6a09e667;
|
||||
sha_256->h[1] = 0xbb67ae85;
|
||||
sha_256->h[2] = 0x3c6ef372;
|
||||
sha_256->h[3] = 0xa54ff53a;
|
||||
sha_256->h[4] = 0x510e527f;
|
||||
sha_256->h[5] = 0x9b05688c;
|
||||
sha_256->h[6] = 0x1f83d9ab;
|
||||
sha_256->h[7] = 0x5be0cd19;
|
||||
}
|
||||
|
||||
void sha_256_write(struct Sha_256 *sha_256, const void *data, size_t len)
|
||||
{
|
||||
sha_256->total_len += len;
|
||||
|
||||
const uint8_t *p = data;
|
||||
|
||||
while (len > 0) {
|
||||
/*
|
||||
* If the input chunks have sizes that are multiples of the calculation chunk size, no copies are
|
||||
* necessary. We operate directly on the input data instead.
|
||||
*/
|
||||
if (sha_256->space_left == SIZE_OF_SHA_256_CHUNK && len >= SIZE_OF_SHA_256_CHUNK) {
|
||||
consume_chunk(sha_256->h, p);
|
||||
len -= SIZE_OF_SHA_256_CHUNK;
|
||||
p += SIZE_OF_SHA_256_CHUNK;
|
||||
continue;
|
||||
}
|
||||
/* General case, no particular optimization. */
|
||||
const size_t consumed_len = len < sha_256->space_left ? len : sha_256->space_left;
|
||||
memcpy(sha_256->chunk_pos, p, consumed_len);
|
||||
sha_256->space_left -= consumed_len;
|
||||
len -= consumed_len;
|
||||
p += consumed_len;
|
||||
if (sha_256->space_left == 0) {
|
||||
consume_chunk(sha_256->h, sha_256->chunk);
|
||||
sha_256->chunk_pos = sha_256->chunk;
|
||||
sha_256->space_left = SIZE_OF_SHA_256_CHUNK;
|
||||
} else {
|
||||
sha_256->chunk_pos += consumed_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t *sha_256_close(struct Sha_256 *sha_256)
|
||||
{
|
||||
uint8_t *pos = sha_256->chunk_pos;
|
||||
size_t space_left = sha_256->space_left;
|
||||
uint32_t *const h = sha_256->h;
|
||||
|
||||
/*
|
||||
* The current chunk cannot be full. Otherwise, it would already have be consumed. I.e. there is space left for
|
||||
* at least one byte. The next step in the calculation is to add a single one-bit to the data.
|
||||
*/
|
||||
*pos++ = 0x80;
|
||||
--space_left;
|
||||
|
||||
/*
|
||||
* Now, the last step is to add the total data length at the end of the last chunk, and zero padding before
|
||||
* that. But we do not necessarily have enough space left. If not, we pad the current chunk with zeroes, and add
|
||||
* an extra chunk at the end.
|
||||
*/
|
||||
if (space_left < TOTAL_LEN_LEN) {
|
||||
memset(pos, 0x00, space_left);
|
||||
consume_chunk(h, sha_256->chunk);
|
||||
pos = sha_256->chunk;
|
||||
space_left = SIZE_OF_SHA_256_CHUNK;
|
||||
}
|
||||
const size_t left = space_left - TOTAL_LEN_LEN;
|
||||
memset(pos, 0x00, left);
|
||||
pos += left;
|
||||
size_t len = sha_256->total_len;
|
||||
pos[7] = (uint8_t)(len << 3);
|
||||
len >>= 5;
|
||||
int i;
|
||||
for (i = 6; i >= 0; --i) {
|
||||
pos[i] = (uint8_t)len;
|
||||
len >>= 8;
|
||||
}
|
||||
consume_chunk(h, sha_256->chunk);
|
||||
/* Produce the final hash value (big-endian): */
|
||||
int j;
|
||||
uint8_t *const hash = sha_256->hash;
|
||||
for (i = 0, j = 0; i < 8; i++) {
|
||||
hash[j++] = (uint8_t)(h[i] >> 24);
|
||||
hash[j++] = (uint8_t)(h[i] >> 16);
|
||||
hash[j++] = (uint8_t)(h[i] >> 8);
|
||||
hash[j++] = (uint8_t)h[i];
|
||||
}
|
||||
return sha_256->hash;
|
||||
}
|
||||
|
||||
void calc_sha_256(uint8_t hash[SIZE_OF_SHA_256_HASH], const void *input, size_t len)
|
||||
{
|
||||
struct Sha_256 sha_256;
|
||||
sha_256_init(&sha_256, hash);
|
||||
sha_256_write(&sha_256, input, len);
|
||||
(void)sha_256_close(&sha_256);
|
||||
}
|
103
stm-firmware/updater/ram-code/3rd-party/sha256/sha-256.h
vendored
Normal file
103
stm-firmware/updater/ram-code/3rd-party/sha256/sha-256.h
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
#ifndef SHA_256_H
|
||||
#define SHA_256_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* @brief Size of the SHA-256 sum. This times eight is 256 bits.
|
||||
*/
|
||||
#define SIZE_OF_SHA_256_HASH 32
|
||||
|
||||
/*
|
||||
* @brief Size of the chunks used for the calculations.
|
||||
*
|
||||
* @note This should mostly be ignored by the user, although when using the streaming API, it has an impact for
|
||||
* performance. Add chunks whose size is a multiple of this, and you will avoid a lot of superfluous copying in RAM!
|
||||
*/
|
||||
#define SIZE_OF_SHA_256_CHUNK 64
|
||||
|
||||
/*
|
||||
* @brief The opaque SHA-256 type, that should be instantiated when using the streaming API.
|
||||
*
|
||||
* @note Although the details are exposed here, in order to make instantiation easy, you should refrain from directly
|
||||
* accessing the fields, as they may change in the future.
|
||||
*/
|
||||
struct Sha_256 {
|
||||
uint8_t *hash;
|
||||
uint8_t chunk[SIZE_OF_SHA_256_CHUNK];
|
||||
uint8_t *chunk_pos;
|
||||
size_t space_left;
|
||||
size_t total_len;
|
||||
uint32_t h[8];
|
||||
};
|
||||
|
||||
/*
|
||||
* @brief The simple SHA-256 calculation function.
|
||||
* @param hash Hash array, where the result is delivered.
|
||||
* @param input Pointer to the data the hash shall be calculated on.
|
||||
* @param len Length of the input data, in byte.
|
||||
*
|
||||
* @note If all of the data you are calculating the hash value on is available in a contiguous buffer in memory, this is
|
||||
* the function you should use.
|
||||
*
|
||||
* @note If either of the passed pointers is NULL, the results are unpredictable.
|
||||
*/
|
||||
void calc_sha_256(uint8_t hash[SIZE_OF_SHA_256_HASH], const void *input, size_t len);
|
||||
|
||||
/*
|
||||
* @brief Initialize a SHA-256 streaming calculation.
|
||||
* @param sha_256 A pointer to a SHA-256 structure.
|
||||
* @param hash Hash array, where the result will be delivered.
|
||||
*
|
||||
* @note If all of the data you are calculating the hash value on is not available in a contiguous buffer in memory, this is
|
||||
* where you should start. Instantiate a SHA-256 structure, for instance by simply declaring it locally, make your hash
|
||||
* buffer available, and invoke this function. Once a SHA-256 hash has been calculated (see further below) a SHA-256
|
||||
* structure can be initialized again for the next calculation.
|
||||
*
|
||||
* @note If either of the passed pointers is NULL, the results are unpredictable.
|
||||
*/
|
||||
void sha_256_init(struct Sha_256 *sha_256, uint8_t hash[SIZE_OF_SHA_256_HASH]);
|
||||
|
||||
/*
|
||||
* @brief Stream more input data for an on-going SHA-256 calculation.
|
||||
* @param sha_256 A pointer to a previously initialized SHA-256 structure.
|
||||
* @param data Pointer to the data to be added to the calculation.
|
||||
* @param len Length of the data to add, in byte.
|
||||
*
|
||||
* @note This function may be invoked an arbitrary number of times between initialization and closing, but the maximum
|
||||
* data length is limited by the SHA-256 algorithm: the total number of bits (i.e. the total number of bytes times
|
||||
* eight) must be representable by a 64-bit unsigned integer. While that is not a practical limitation, the results are
|
||||
* unpredictable if that limit is exceeded.
|
||||
*
|
||||
* @note This function may be invoked on empty data (zero length), although that obviously will not add any data.
|
||||
*
|
||||
* @note If either of the passed pointers is NULL, the results are unpredictable.
|
||||
*/
|
||||
void sha_256_write(struct Sha_256 *sha_256, const void *data, size_t len);
|
||||
|
||||
/*
|
||||
* @brief Conclude a SHA-256 streaming calculation, making the hash value available.
|
||||
* @param sha_256 A pointer to a previously initialized SHA-256 structure.
|
||||
* @return Pointer to the hash array, where the result is delivered.
|
||||
*
|
||||
* @note After this function has been invoked, the result is available in the hash buffer that initially was provided. A
|
||||
* pointer to the hash value is returned for convenience, but you should feel free to ignore it: it is simply a pointer
|
||||
* to the first byte of your initially provided hash array.
|
||||
*
|
||||
* @note If the passed pointer is NULL, the results are unpredictable.
|
||||
*
|
||||
* @note Invoking this function for a calculation with no data (the writing function has never been invoked, or it only
|
||||
* has been invoked with empty data) is legal. It will calculate the SHA-256 value of the empty string.
|
||||
*/
|
||||
uint8_t *sha_256_close(struct Sha_256 *sha_256);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@@ -6,20 +6,32 @@ cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
set(CMAKE_TOOLCHAIN_FILE "arm-none-eabi-gcc.cmake")
|
||||
|
||||
find_package(Python COMPONENTS Interpreter)
|
||||
|
||||
if (Python_FOUND)
|
||||
message("Python interpreter used for converting updater RAM code: ${Python_EXECUTABLE}")
|
||||
else (Python_FOUND)
|
||||
message(FATAL_ERROR "${BoldRed}Python interpreter not found! Please install python 3.${ColorReset}")
|
||||
endif (Python_FOUND)
|
||||
|
||||
|
||||
set(ELFFILE ${PROJECT_NAME}.elf)
|
||||
set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/ram-link.ld)
|
||||
|
||||
set(ELFFILE "${PROJECT_NAME}.elf")
|
||||
aux_source_directory("." SRCS)
|
||||
aux_source_directory("fatfs" FATFS_SRCS)
|
||||
aux_source_directory("3rd-party/sha256" SHA256_SRCS)
|
||||
aux_source_directory("fatfs/shimatta_sdio_driver" SDIO_SRCS)
|
||||
set(STM_PERIPH_SRCS "../../stm-periph/backup-ram.c" "../../stm-periph/rcc-manager.c" "../../stm-periph/crc-unit.c")
|
||||
set(SAFETY_MEMORY_SRCS "../../safety/safety-memory.c")
|
||||
|
||||
add_executable(${ELFFILE} ${SRCS} ${FATFS_SRCS} ${SDIO_SRCS})
|
||||
add_executable(${ELFFILE} ${SRCS} ${FATFS_SRCS} ${SDIO_SRCS} ${STM_PERIPH_SRCS} ${SAFETY_MEMORY_SRCS} ${SHA256_SRCS})
|
||||
|
||||
target_include_directories(${ELFFILE} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
target_include_directories(${ELFFILE} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ../../include ${CMAKE_CURRENT_SOURCE_DIR}/3rd-party)
|
||||
target_compile_options(${ELFFILE} PRIVATE -Wall -Wextra -Wold-style-declaration -Wuninitialized -Wmaybe-uninitialized -Wunused-parameter)
|
||||
target_compile_options(${ELFFILE} PRIVATE -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -mfloat-abi=hard -mfpu=fpv4-sp-d16 -nostartfiles -Wimplicit-fallthrough=3 -Wsign-compare -Os -g3)
|
||||
target_compile_definitions(${ELFFILE} PRIVATE -DBASE64_LOOKUP_TABLE_SECTION=\".ccm.bss\" -DSHELLMATTA_HELP_ALIAS=\"?\" -DGIT_VER=${GIT_DESCRIBE} -DHSE_VALUE=8000000UL -DSTM32F407xx -DSTM32F4XX -DARM_MATH_CM4)
|
||||
target_compile_definitions(${ELFFILE} PRIVATE -DGIT_VER=${GIT_DESCRIBE} -DHSE_VALUE=8000000UL -DSTM32F407xx -DSTM32F4XX -DARM_MATH_CM4 -DSAFETY_MEMORY_STRIPOUT_DUMP)
|
||||
target_link_options(${ELFFILE} PRIVATE -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -mfloat-abi=hard -mfpu=fpv4-sp-d16 --disable-newlib-supplied-syscalls -nostartfiles -T${LINKER_SCRIPT} -Wl,--print-memory-usage)
|
||||
set(GEN_HEADER_PATH "${CMAKE_CURRENT_BINARY_DIR}/include/generated")
|
||||
set(GEN_HEADER_FILE "${GEN_HEADER_PATH}/${PROJECT_NAME}.bin.h")
|
||||
@@ -42,5 +54,5 @@ add_custom_command(DEPENDS
|
||||
OUTPUT
|
||||
${GEN_HEADER_FILE}
|
||||
COMMAND
|
||||
mkdir -p ${GEN_HEADER_PATH} && python "${CMAKE_CURRENT_SOURCE_DIR}/bin2carray.py" "${GEN_HEADER_FILE}" "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.bin"
|
||||
mkdir -p ${GEN_HEADER_PATH} && ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/bin2carray.py" "${GEN_HEADER_FILE}" "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.bin"
|
||||
)
|
||||
|
134
stm-firmware/updater/ram-code/flash-writer.c
Normal file
134
stm-firmware/updater/ram-code/flash-writer.c
Normal file
@@ -0,0 +1,134 @@
|
||||
#include "flash-writer.h"
|
||||
#include <stm32/stm32f4xx.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
static bool flash_op_busy(void)
|
||||
{
|
||||
return !!(FLASH->SR & FLASH_SR_BSY);
|
||||
}
|
||||
|
||||
static void lock_flash_cr(void)
|
||||
{
|
||||
FLASH->CR |= FLASH_CR_LOCK;
|
||||
}
|
||||
|
||||
void flash_writer_enable_access(void)
|
||||
{
|
||||
const uint32_t key1 = 0x45670123UL;
|
||||
const uint32_t key2 = 0xCDEF89ABUL;
|
||||
|
||||
FLASH->KEYR = key1;
|
||||
FLASH->KEYR = key2;
|
||||
}
|
||||
|
||||
void flash_writer_perform_mass_erase(void)
|
||||
{
|
||||
flash_writer_enable_access();
|
||||
while (flash_op_busy());
|
||||
|
||||
FLASH->CR = DMA_SxCR_PSIZE_1;
|
||||
FLASH->CR |= FLASH_CR_MER;
|
||||
FLASH->CR |= FLASH_CR_STRT;
|
||||
|
||||
while(flash_op_busy());
|
||||
lock_flash_cr();
|
||||
}
|
||||
|
||||
uint32_t flash_writer_get_flash_size(void)
|
||||
{
|
||||
uint32_t flash_size;
|
||||
const uint16_t *flash_size_ptr = (const uint16_t *)0x1FFF7A22UL;
|
||||
|
||||
flash_size = (uint32_t)*flash_size_ptr;
|
||||
flash_size *= 1024;
|
||||
|
||||
return flash_size;
|
||||
}
|
||||
|
||||
int flash_writer_write_to_memory(void *dest, const void *src, uint32_t size)
|
||||
{
|
||||
uint32_t full_word_cnt;
|
||||
uint32_t byte_cnt;
|
||||
uint32_t idx;
|
||||
const uint32_t *word_src_ptr;
|
||||
uint32_t *word_dest_ptr;
|
||||
const char *char_src_ptr;
|
||||
char *char_dest_ptr;
|
||||
uint32_t pre_byte_count;
|
||||
|
||||
flash_writer_enable_access();
|
||||
while (flash_op_busy());
|
||||
|
||||
/* Number of full words to program */
|
||||
full_word_cnt = size / 4u;
|
||||
byte_cnt = size % 4;
|
||||
|
||||
word_dest_ptr = dest;
|
||||
word_src_ptr = src;
|
||||
|
||||
/* Do the first bytes, in case the destination is not word aligned */
|
||||
pre_byte_count = (4 - ((uint32_t)dest % 4u)) % 4;
|
||||
if (pre_byte_count) {
|
||||
FLASH->CR = 0u;
|
||||
FLASH->CR |= FLASH_CR_PG;
|
||||
|
||||
char_src_ptr = src;
|
||||
char_dest_ptr = dest;
|
||||
|
||||
/* Write bytes to memory until we hit the next word aligned address */
|
||||
for (idx = 0; idx < pre_byte_count; idx++) {
|
||||
*(char_dest_ptr++) = *(char_src_ptr++);
|
||||
}
|
||||
|
||||
/* Correct the word addresses set above */
|
||||
full_word_cnt = (size - pre_byte_count) / 4u;
|
||||
byte_cnt = (size - pre_byte_count) % 4;
|
||||
word_dest_ptr = (uint32_t *)char_dest_ptr;
|
||||
word_src_ptr = (uint32_t *)char_src_ptr;
|
||||
|
||||
while(flash_op_busy());
|
||||
FLASH->CR = 0u;
|
||||
}
|
||||
|
||||
/* Do the full word flash write */
|
||||
if (full_word_cnt) {
|
||||
FLASH->CR = FLASH_CR_PSIZE_1;
|
||||
FLASH->CR |= FLASH_CR_PG;
|
||||
|
||||
for (idx = 0; idx < full_word_cnt; idx++) {
|
||||
*word_dest_ptr = *word_src_ptr;
|
||||
word_dest_ptr++;
|
||||
word_src_ptr++;
|
||||
}
|
||||
|
||||
while (flash_op_busy());
|
||||
FLASH->CR = 0u;
|
||||
}
|
||||
|
||||
/* write remaining bytes */
|
||||
if (byte_cnt) {
|
||||
char_src_ptr = (char *)word_src_ptr;
|
||||
char_dest_ptr = (char *)word_dest_ptr;
|
||||
|
||||
FLASH->CR = 0u;
|
||||
FLASH->CR |= FLASH_CR_PG;
|
||||
|
||||
for (idx = 0; idx < byte_cnt; idx++) {
|
||||
*char_dest_ptr = *char_src_ptr;
|
||||
char_dest_ptr++;
|
||||
char_src_ptr++;
|
||||
}
|
||||
|
||||
while (flash_op_busy());
|
||||
FLASH->CR = 0u;
|
||||
}
|
||||
|
||||
lock_flash_cr();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t flash_writer_get_base_address(void)
|
||||
{
|
||||
return FLASH_BASE;
|
||||
}
|
16
stm-firmware/updater/ram-code/flash-writer.h
Normal file
16
stm-firmware/updater/ram-code/flash-writer.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef _FLASH_WRITER_H_
|
||||
#define _FLASH_WRITER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void flash_writer_enable_access(void);
|
||||
|
||||
void flash_writer_perform_mass_erase(void);
|
||||
|
||||
uint32_t flash_writer_get_flash_size(void);
|
||||
|
||||
uint32_t flash_writer_get_base_address(void);
|
||||
|
||||
int flash_writer_write_to_memory(void *dest, const void *src, uint32_t size);
|
||||
|
||||
#endif /* _FLASH_WRITER_H_ */
|
@@ -1,72 +1,74 @@
|
||||
#include "hex-parser.h"
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
static int convert_hex_char_to_value(char c, uint32_t *out)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t value = 0;
|
||||
int ret = 0;
|
||||
uint32_t value = 0;
|
||||
|
||||
if (!out)
|
||||
return -1002;
|
||||
if (!out)
|
||||
return -1002;
|
||||
|
||||
switch (c) {
|
||||
case '0' ... '9':
|
||||
value = (uint32_t)c - (uint32_t)'0';
|
||||
break;
|
||||
case 'a' ... 'f':
|
||||
/* Convert to upper */
|
||||
c -= 0x20;
|
||||
/* FALLTHRU */
|
||||
case 'A' ... 'F':
|
||||
value = (uint32_t)c - (uint32_t)'A' + 10UL;
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
}
|
||||
switch (c) {
|
||||
case '0' ... '9':
|
||||
value = (uint32_t)c - (uint32_t)'0';
|
||||
break;
|
||||
case 'a' ... 'f':
|
||||
/* Convert to upper */
|
||||
c -= 0x20;
|
||||
/* FALLTHRU */
|
||||
case 'A' ... 'F':
|
||||
value = (uint32_t)c - (uint32_t)'A' + 10UL;
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
*out = value;
|
||||
if (ret == 0)
|
||||
*out = value;
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int convert_big_endian_hex_string_to_val(const char *string, size_t len, uint32_t *out)
|
||||
{
|
||||
int ret_val = -1;
|
||||
uint32_t converted_value = 0UL;
|
||||
uint32_t digit;
|
||||
int res;
|
||||
unsigned int i;
|
||||
int ret_val = -1;
|
||||
uint32_t converted_value = 0UL;
|
||||
uint32_t digit;
|
||||
int res;
|
||||
unsigned int i;
|
||||
|
||||
/* Return error in case of an input error */
|
||||
if (!string || !len)
|
||||
goto exit;
|
||||
/* Return error in case of an input error */
|
||||
if (!string || !len)
|
||||
goto exit;
|
||||
|
||||
if (!out)
|
||||
return -1003;
|
||||
if (!out)
|
||||
return -1003;
|
||||
|
||||
/* we don't support strings larger than 8 chars */
|
||||
if (len > 8)
|
||||
goto exit;
|
||||
/* we don't support strings larger than 8 chars */
|
||||
if (len > 8)
|
||||
goto exit;
|
||||
|
||||
|
||||
for (i = 0; i < len && string[i] != '\0'; i++) {
|
||||
/* Convert current character to number */
|
||||
res = convert_hex_char_to_value(string[i], &digit);
|
||||
if (res) {
|
||||
/* Not a hex number */
|
||||
ret_val = -2;
|
||||
goto exit;
|
||||
}
|
||||
for (i = 0; i < len && string[i] != '\0'; i++) {
|
||||
/* Convert current character to number */
|
||||
res = convert_hex_char_to_value(string[i], &digit);
|
||||
if (res) {
|
||||
/* Not a hex number */
|
||||
ret_val = -2;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
converted_value *= 0x10;
|
||||
converted_value += digit;
|
||||
}
|
||||
converted_value *= 0x10;
|
||||
converted_value += digit;
|
||||
}
|
||||
|
||||
*out = converted_value;
|
||||
*out = converted_value;
|
||||
ret_val = 0;
|
||||
|
||||
exit:
|
||||
return ret_val;
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
enum hex_parser_ret hex_parser_open(struct hex_parser *parser, const char *file_name)
|
||||
@@ -85,7 +87,179 @@ enum hex_parser_ret hex_parser_open(struct hex_parser *parser, const char *file_
|
||||
return HEX_PARSER_OK;
|
||||
}
|
||||
|
||||
enum hex_parser_ret hex_parser_parse(struct hex_parser *parser, uint32_t *address, char *data, size_t data_len);
|
||||
static int read_line_from_file(FIL *file, char *data, int size)
|
||||
{
|
||||
char *ret_ptr;
|
||||
int length;
|
||||
|
||||
ret_ptr = f_gets(data, size, file);
|
||||
if (!ret_ptr)
|
||||
return -1;
|
||||
|
||||
/* To be sure */
|
||||
data[size - 1] = 0;
|
||||
length = strlen(ret_ptr);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
static int hex_record_check_checksum(const char *buff, int hex_byte_count)
|
||||
{
|
||||
int i;
|
||||
int res;
|
||||
uint32_t checksum = 0;
|
||||
uint32_t tmp;
|
||||
|
||||
if (!buff || !hex_byte_count)
|
||||
return -1000;
|
||||
|
||||
for (i = 0; i < hex_byte_count; i++) {
|
||||
res = convert_big_endian_hex_string_to_val(&buff[2 * i], 2, &tmp);
|
||||
if (res)
|
||||
return -1;
|
||||
checksum += tmp;
|
||||
}
|
||||
|
||||
if (checksum & 0xFF) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum hex_parser_ret hex_parser_parse(struct hex_parser *parser, uint32_t *address, char *data, size_t data_len,
|
||||
size_t *lenout)
|
||||
{
|
||||
static char workbuff[512];
|
||||
int count;
|
||||
int i;
|
||||
enum hex_parser_ret retval = HEX_PARSER_DATA_OK;
|
||||
uint32_t hex_addr;
|
||||
uint32_t byte_count;
|
||||
uint32_t record_type;
|
||||
uint32_t tmp;
|
||||
|
||||
if (!parser || !lenout || !data_len || !data || !address)
|
||||
return HEX_PARSER_ERROR;
|
||||
|
||||
/* Read a line from the file */
|
||||
count = read_line_from_file(&parser->file, workbuff, sizeof(workbuff));
|
||||
if (!count) {
|
||||
/* Check for error in case nothing is read */
|
||||
if (f_error(&parser->file)) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
} else if (f_eof(&parser->file)) {
|
||||
retval = HEX_PARSER_FILE_END;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* Strip out invalid characters at the end */
|
||||
for (i = count - 1; i >= 0; i--) {
|
||||
if (workbuff[i] == '\r' || workbuff[i] == '\n' ||
|
||||
workbuff[i] == '\t' || workbuff[i] == ' ')
|
||||
{
|
||||
workbuff[i] = 0;
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
/* We read a valid line, check for valid marker */
|
||||
if (workbuff[0] != ':') {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Line has to be longer than 11 chars in total */
|
||||
if (count < 11) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Read in the data count */
|
||||
if (convert_big_endian_hex_string_to_val(&workbuff[1], 2, &byte_count)) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Read in the address */
|
||||
if (convert_big_endian_hex_string_to_val(&workbuff[3], 4, &hex_addr)) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Read in the record type */
|
||||
if (convert_big_endian_hex_string_to_val(&workbuff[7], 2, &record_type)) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (byte_count * 2 + 9 + 2 != (unsigned int)count) {
|
||||
/* Line not the expected length */
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Check the checksum. We have bytecount + 5 bytes in a record */
|
||||
if (hex_record_check_checksum(&workbuff[1], byte_count + 5)) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Check record type */
|
||||
switch (record_type) {
|
||||
case 0x00: /* Data */
|
||||
if (byte_count > data_len) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
*lenout = 0;
|
||||
*address = hex_addr + parser->current_address_offset;
|
||||
|
||||
for (i = 0; i < (int)byte_count; i++) {
|
||||
if (convert_big_endian_hex_string_to_val(&workbuff[9 + 2*i], 2, &tmp)) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
*data = (char)(tmp & 0xFF);
|
||||
data++;
|
||||
(*lenout)++;
|
||||
}
|
||||
retval = HEX_PARSER_DATA_OK;
|
||||
break;
|
||||
case 0x01: /* End of file */
|
||||
retval = HEX_PARSER_EOF_RECORD;
|
||||
break;
|
||||
case 0x04: /* extended linear address */
|
||||
if (byte_count != 2) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Parse the upper 16 bit of the address */
|
||||
if (convert_big_endian_hex_string_to_val(&workbuff[9], 4, &tmp)) {
|
||||
retval = HEX_PARSER_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
parser->current_address_offset = tmp << 16;
|
||||
retval = HEX_PARSER_OK;
|
||||
break;
|
||||
case 0x05:
|
||||
retval = HEX_PARSER_OK;
|
||||
break;
|
||||
default:
|
||||
retval = HEX_PARSER_ERROR;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
enum hex_parser_ret hex_parser_close(struct hex_parser *parser) {
|
||||
if (!parser)
|
||||
|
@@ -6,10 +6,11 @@
|
||||
#include <stddef.h>
|
||||
|
||||
enum hex_parser_ret {
|
||||
HEX_PARSER_OK,
|
||||
HEX_PARSER_OK = 0,
|
||||
HEX_PARSER_DATA_OK,
|
||||
HEX_PARSER_ERROR,
|
||||
HEX_PARSER_FILE_END,
|
||||
HEX_PARSER_EOF_RECORD,
|
||||
};
|
||||
|
||||
struct hex_parser {
|
||||
@@ -19,7 +20,8 @@ struct hex_parser {
|
||||
|
||||
enum hex_parser_ret hex_parser_open(struct hex_parser *parser, const char *file_name);
|
||||
|
||||
enum hex_parser_ret hex_parser_parse(struct hex_parser *parser, uint32_t *address, char *data, size_t data_len);
|
||||
enum hex_parser_ret hex_parser_parse(struct hex_parser *parser, uint32_t *address, char *data, size_t data_len,
|
||||
size_t *len_out);
|
||||
|
||||
enum hex_parser_ret hex_parser_close(struct hex_parser *parser);
|
||||
|
||||
|
87
stm-firmware/updater/ram-code/itoa.c
Normal file
87
stm-firmware/updater/ram-code/itoa.c
Normal file
@@ -0,0 +1,87 @@
|
||||
#include "itoa.h"
|
||||
|
||||
/*
|
||||
* The heapless_itoa function is copied from the shellmatta project.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief itoa like function to convert int to an ascii string
|
||||
* @warning you have to provide a large enough buffer
|
||||
* @param[in] value
|
||||
* @param[in,out] buffer
|
||||
* @param[in] base
|
||||
* @return number of bytes in string
|
||||
*/
|
||||
uint32_t heapless_itoa(int32_t value, char *buffer, uint32_t base)
|
||||
{
|
||||
char tempBuffer[34u];
|
||||
uint32_t i;
|
||||
uint32_t bufferIdx = 0u;
|
||||
int8_t digitValue;
|
||||
|
||||
/** -# check the base for plausibility */
|
||||
if((base >= 2) && (base <= 16))
|
||||
{
|
||||
/** -# check for sign */
|
||||
if(value < 0)
|
||||
{
|
||||
value = value * (-1);
|
||||
buffer[0u] = '-';
|
||||
bufferIdx += 1u;
|
||||
}
|
||||
|
||||
/** -# loop through all digits in reverse order */
|
||||
i = 0u;
|
||||
do
|
||||
{
|
||||
digitValue = (int8_t) (value % base);
|
||||
tempBuffer[i] = (digitValue < 10) ? ('0' + digitValue) : ('A' + (digitValue - 10));
|
||||
value /= base;
|
||||
i ++;
|
||||
}while(value > 0);
|
||||
|
||||
/** -# store the string in the correct order onto the buffer */
|
||||
while(i > 0u)
|
||||
{
|
||||
buffer[bufferIdx] = tempBuffer[i - 1u];
|
||||
i --;
|
||||
bufferIdx ++;
|
||||
}
|
||||
}
|
||||
return bufferIdx;
|
||||
}
|
||||
|
||||
static char num_to_hex_digit(uint8_t num, bool capitalized)
|
||||
{
|
||||
char ret = 'U';
|
||||
|
||||
switch (num) {
|
||||
case 0 ... 9:
|
||||
ret = '0' + num;
|
||||
break;
|
||||
case 0xA ... 0xF:
|
||||
if (capitalized)
|
||||
ret = 'A' + num - 0xA;
|
||||
else
|
||||
ret = 'a' + num - 0xA;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t bytes_to_hex_string(uint8_t *input, uint32_t count, char *output_buffer, bool capitalized)
|
||||
{
|
||||
uint32_t idx;
|
||||
uint8_t b;
|
||||
|
||||
for (idx = 0; idx < count; idx++) {
|
||||
b = input[idx];
|
||||
output_buffer[2*idx] = num_to_hex_digit(b >> 4, capitalized);
|
||||
output_buffer[2*idx+1] = num_to_hex_digit(b & 0xF, capitalized);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
11
stm-firmware/updater/ram-code/itoa.h
Normal file
11
stm-firmware/updater/ram-code/itoa.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef _ITOA_H_
|
||||
#define _ITOA_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
uint32_t heapless_itoa(int32_t value, char *buffer, uint32_t base);
|
||||
|
||||
uint32_t bytes_to_hex_string(uint8_t *input, uint32_t count, char *output_buffer, bool capitalized);
|
||||
|
||||
#endif /* _ITOA_H_ */
|
@@ -3,7 +3,20 @@
|
||||
#include <cmsis/core_cm4.h>
|
||||
#include "hex-parser.h"
|
||||
#include <fatfs/ff.h>
|
||||
|
||||
/* This is used to get the defines for the external watchdog */
|
||||
#include <reflow-controller/safety/safety-config.h>
|
||||
#include <helper-macros/helper-macros.h>
|
||||
#include <stm-periph/stm32-gpio-macros.h>
|
||||
|
||||
#include <reflow-controller/safety/safety-memory.h>
|
||||
#include "flash-writer.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "uart.h"
|
||||
#include "itoa.h"
|
||||
#include <sha256/sha-256.h>
|
||||
|
||||
static volatile unsigned int wait_tick;
|
||||
|
||||
@@ -12,6 +25,14 @@ static void watchdog_ack(void)
|
||||
IWDG->KR = 0xAAAA;
|
||||
}
|
||||
|
||||
static void external_watchdog_disable(void)
|
||||
{
|
||||
RCC->AHB1ENR |= SAFETY_EXT_WATCHDOG_RCC_MASK;
|
||||
__DSB();
|
||||
/* Set Pin to input. This disables the external watchdog. */
|
||||
SAFETY_EXT_WATCHDOG_PORT->MODER &= MODER_DELETE(SAFETY_EXT_WATCHDOG_PIN);
|
||||
}
|
||||
|
||||
void sdio_wait_ms(unsigned int ms)
|
||||
{
|
||||
wait_tick = 0;
|
||||
@@ -23,24 +44,360 @@ static FATFS _fs;
|
||||
|
||||
static void __attribute__((noreturn)) ram_code_exit(bool updated)
|
||||
{
|
||||
(void)updated;
|
||||
struct safety_memory_boot_status boot_status;
|
||||
safety_memory_get_boot_status(&boot_status);
|
||||
boot_status.code_updated = updated ? 0xFFFFFFFFUL : 0x0UL;
|
||||
boot_status.reboot_to_bootloader = 0x0UL;
|
||||
safety_memory_set_boot_status(&boot_status);
|
||||
|
||||
uart_send_string("Rebooting in 1s...\r\n");
|
||||
sdio_wait_ms(1000);
|
||||
|
||||
NVIC_SystemReset();
|
||||
while(1);
|
||||
}
|
||||
|
||||
static int check_hex_file(const char *fname, uint32_t *update_size)
|
||||
{
|
||||
enum hex_parser_ret hex_ret;
|
||||
struct hex_parser parser;
|
||||
uint32_t addr;
|
||||
char data[128];
|
||||
size_t dlen;
|
||||
int retval = -1;
|
||||
uint32_t flash_base;
|
||||
uint32_t flash_top;
|
||||
uint32_t total_size = 0UL;
|
||||
|
||||
flash_base = flash_writer_get_base_address();
|
||||
flash_top = flash_base + flash_writer_get_flash_size();
|
||||
|
||||
hex_ret = hex_parser_open(&parser, fname);
|
||||
if (hex_ret != HEX_PARSER_OK) {
|
||||
retval = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
do {
|
||||
hex_ret = hex_parser_parse(&parser, &addr, data, sizeof(data), &dlen);
|
||||
if (hex_ret == HEX_PARSER_DATA_OK) {
|
||||
if (addr < flash_base || addr+dlen >= flash_top) {
|
||||
retval = -2;
|
||||
goto ret_close_parser;
|
||||
}
|
||||
total_size += dlen;
|
||||
}
|
||||
} while (hex_ret == HEX_PARSER_DATA_OK || hex_ret == HEX_PARSER_OK);
|
||||
|
||||
if (hex_ret == HEX_PARSER_EOF_RECORD) {
|
||||
retval = 0;
|
||||
if (update_size)
|
||||
*update_size = total_size;
|
||||
}
|
||||
|
||||
ret_close_parser:
|
||||
hex_parser_close(&parser);
|
||||
exit:
|
||||
return retval;
|
||||
}
|
||||
|
||||
int write_flash_from_buffer(const char *buffer, uint32_t len, uint32_t addr)
|
||||
{
|
||||
int res;
|
||||
uint32_t i;
|
||||
const char *verify_ptr = (const char *)addr;
|
||||
|
||||
res = flash_writer_write_to_memory((void *)addr, buffer, len);
|
||||
if (res) {
|
||||
uart_send_string("Error writing to flash!\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Verify the write */
|
||||
for (i = 0; i < len; i++, verify_ptr++) {
|
||||
if (*verify_ptr != buffer[i]) {
|
||||
uart_send_string("Error verifying written data!\r\n");
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int update_flash_from_file(const char *fname)
|
||||
{
|
||||
enum hex_parser_ret hex_ret;
|
||||
struct hex_parser parser;
|
||||
static char write_buffer[4096];
|
||||
uint32_t wbuffer_base_addr = 0;
|
||||
uint32_t wbuffer_fill_level = 0;
|
||||
uint32_t addr;
|
||||
static char tmp_buff[256];
|
||||
size_t dlen;
|
||||
int retval = 0;
|
||||
int res;
|
||||
|
||||
hex_ret = hex_parser_open(&parser, fname);
|
||||
if (hex_ret != HEX_PARSER_OK) {
|
||||
uart_send_string("Error reading hex file.\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
do {
|
||||
hex_ret = hex_parser_parse(&parser, &addr, tmp_buff, sizeof(tmp_buff), &dlen);
|
||||
if (hex_ret == HEX_PARSER_DATA_OK) {
|
||||
/* Check if tmp would fit in wbuffer */
|
||||
if (dlen + wbuffer_fill_level > sizeof(write_buffer)) {
|
||||
/* Write out the buffer and clean it if it doens't fit */
|
||||
res = write_flash_from_buffer(write_buffer, wbuffer_fill_level, wbuffer_base_addr);
|
||||
if (res) {
|
||||
retval = -4;
|
||||
goto exit_parser_close;
|
||||
}
|
||||
wbuffer_fill_level = 0;
|
||||
wbuffer_base_addr = 0;
|
||||
}
|
||||
|
||||
/* Check if parsed data can be linearily appended to buffer */
|
||||
if (wbuffer_fill_level && wbuffer_base_addr + wbuffer_fill_level != addr) {
|
||||
/* Write out the buffer and clean it if it cannot be appended */
|
||||
res = write_flash_from_buffer(write_buffer, wbuffer_fill_level, wbuffer_base_addr);
|
||||
if (res) {
|
||||
retval = -4;
|
||||
goto exit_parser_close;
|
||||
}
|
||||
wbuffer_fill_level = 0;
|
||||
wbuffer_base_addr = 0;
|
||||
}
|
||||
|
||||
/* Fill in the data into the buffer */
|
||||
if (wbuffer_fill_level == 0) {
|
||||
wbuffer_base_addr = addr;
|
||||
}
|
||||
memcpy(&write_buffer[wbuffer_fill_level], tmp_buff, dlen);
|
||||
wbuffer_fill_level += dlen;
|
||||
}
|
||||
} while (hex_ret == HEX_PARSER_DATA_OK || hex_ret == HEX_PARSER_OK);
|
||||
|
||||
if (hex_ret == HEX_PARSER_EOF_RECORD) {
|
||||
if (wbuffer_fill_level > 0) {
|
||||
res = write_flash_from_buffer(write_buffer, wbuffer_fill_level, wbuffer_base_addr);
|
||||
if (res) {
|
||||
retval = -4;
|
||||
goto exit_parser_close;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
} else {
|
||||
retval = -3;
|
||||
}
|
||||
|
||||
exit_parser_close:
|
||||
hex_parser_close(&parser);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int compute_sha256_of_file(const char *fname, uint8_t *sha256_out)
|
||||
{
|
||||
FIL _file;
|
||||
int ret = 0;
|
||||
FIL *file = &_file;
|
||||
FRESULT fres;
|
||||
static char workbuff[1024];
|
||||
UINT act_read;
|
||||
struct Sha_256 sha;
|
||||
|
||||
if (!fname || !sha256_out)
|
||||
return -1000;
|
||||
|
||||
fres = f_open(file, fname, FA_READ);
|
||||
if (fres != FR_OK) {
|
||||
ret = -1;
|
||||
goto ret_noact;
|
||||
}
|
||||
|
||||
sha_256_init(&sha, sha256_out);
|
||||
do {
|
||||
fres = f_read(file, workbuff, sizeof(workbuff), &act_read);
|
||||
if (act_read > 0) {
|
||||
sha_256_write(&sha, workbuff, (size_t)act_read);
|
||||
} else {
|
||||
if (f_eof(file)) {
|
||||
break;
|
||||
} else if (f_error(file)) {
|
||||
ret = -2;
|
||||
goto ret_close_file;
|
||||
}
|
||||
}
|
||||
} while (1);
|
||||
|
||||
(void)sha_256_close(&sha);
|
||||
ret = 0;
|
||||
|
||||
ret_close_file:
|
||||
(void)f_close(file);
|
||||
ret_noact:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int read_file_content(const char *fname, char *dest, size_t count)
|
||||
{
|
||||
FIL f;
|
||||
FRESULT fres;
|
||||
UINT act_read;
|
||||
int ret = 0;
|
||||
|
||||
fres = f_open(&f, fname, FA_READ);
|
||||
if (fres != FR_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
fres = f_read(&f, dest, (UINT)count, &act_read);
|
||||
if (fres != FR_OK) {
|
||||
ret = -2;
|
||||
goto exit_close_file;
|
||||
}
|
||||
|
||||
ret = (int)act_read;
|
||||
|
||||
exit_close_file:
|
||||
(void)f_close(&f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ram_code_main(void)
|
||||
{
|
||||
FRESULT fres;
|
||||
int res;
|
||||
enum safety_memory_state safety_mem_state;
|
||||
static char filename[256];
|
||||
static char hash_file_name[256];
|
||||
static char tmp_buff[256];
|
||||
static char sha_string[SIZE_OF_SHA_256_HASH*2+2];
|
||||
uint32_t count;
|
||||
uint32_t update_size;
|
||||
int retries = 3;
|
||||
uint8_t sha_hash[SIZE_OF_SHA_256_HASH];
|
||||
|
||||
|
||||
|
||||
SysTick_Config(168000UL);
|
||||
external_watchdog_disable();
|
||||
__enable_irq();
|
||||
|
||||
fres = f_mount(fs, "0:/", 1);
|
||||
if (fres != FR_OK) {
|
||||
|
||||
/* Init the uart module
|
||||
* Pins don't need configuration. They're already setup by the main program
|
||||
*/
|
||||
uart_init();
|
||||
|
||||
/* Clear display and set cursor to home position */
|
||||
uart_send_string("\e[2J\e[H");
|
||||
uart_send_string("Updater started.\r\n");
|
||||
|
||||
res = safety_memory_init(&safety_mem_state);
|
||||
if (res || safety_mem_state != SAFETY_MEMORY_INIT_VALID_MEMORY) {
|
||||
ram_code_exit(false);
|
||||
}
|
||||
|
||||
fres = f_mount(fs, "0:/", 1);
|
||||
if (fres != FR_OK) {
|
||||
uart_send_string("Could not mount SD card\r\n");
|
||||
ram_code_exit(false);
|
||||
}
|
||||
|
||||
res = safety_memory_get_update_filename(filename, NULL);
|
||||
if (res)
|
||||
ram_code_exit(false);
|
||||
|
||||
uart_send_string("Calculating SHA256 checksum\r\n");
|
||||
if (compute_sha256_of_file(filename, sha_hash)) {
|
||||
uart_send_string("Calculation failed!");
|
||||
ram_code_exit(false);
|
||||
}
|
||||
|
||||
uart_send_string("SHA256: ");
|
||||
bytes_to_hex_string(sha_hash, SIZE_OF_SHA_256_HASH, sha_string, false);
|
||||
sha_string[SIZE_OF_SHA_256_HASH*2] = 0;
|
||||
uart_send_string(sha_string);
|
||||
uart_send_string("\r\n");
|
||||
|
||||
|
||||
|
||||
strncpy(hash_file_name, filename, sizeof(hash_file_name));
|
||||
strcat(hash_file_name, ".sha");
|
||||
res = read_file_content(hash_file_name, tmp_buff, sizeof(tmp_buff)-1);
|
||||
if (res < 0) {
|
||||
uart_send_string("Error reading expected hash. Is the file present?\r\n");
|
||||
ram_code_exit(false);
|
||||
} else if (res >= SIZE_OF_SHA_256_HASH*2) {
|
||||
tmp_buff[res] = 0;
|
||||
uart_send_string("Expected sha: ");
|
||||
|
||||
/* Strip out the newline form the file for printing */
|
||||
if (tmp_buff[res-1] == '\n' || tmp_buff[res-1] == '\r')
|
||||
tmp_buff[res-1] = 0;
|
||||
if (tmp_buff[res-2] == '\n' || tmp_buff[res-2] == '\r')
|
||||
tmp_buff[res-2] = 0;
|
||||
|
||||
uart_send_string(tmp_buff);
|
||||
uart_send_string("\r\n");
|
||||
if (strncmp(sha_string, tmp_buff, SIZE_OF_SHA_256_HASH*2) != 0) {
|
||||
uart_send_string("SHA sums don't match!\r\n");
|
||||
ram_code_exit(false);
|
||||
}
|
||||
} else {
|
||||
uart_send_string("Expected hash has wrong length!\r\n");
|
||||
ram_code_exit(false);
|
||||
}
|
||||
|
||||
uart_send_string("Checking hex file ");
|
||||
uart_send_string(filename);
|
||||
uart_send_string("\r\n");
|
||||
if (check_hex_file(filename, &update_size)) {
|
||||
uart_send_string("Error in hex file\r\n");
|
||||
ram_code_exit(false);
|
||||
}
|
||||
|
||||
uart_send_string("File ");
|
||||
uart_send_string(filename);
|
||||
uart_send_string(" checked successfully.\r\n");
|
||||
count = heapless_itoa(update_size, tmp_buff, 10);
|
||||
if (count > 0) {
|
||||
tmp_buff[count] = 0;
|
||||
uart_send_string("Update size: ");
|
||||
uart_send_string(tmp_buff);
|
||||
uart_send_string(" bytes\r\n");
|
||||
}
|
||||
|
||||
uart_send_string("Starting updater...\r\n");
|
||||
|
||||
/* disable the ART caches */
|
||||
FLASH->ACR &= ~FLASH_ACR_DCEN;
|
||||
FLASH->ACR &= ~FLASH_ACR_ICEN;
|
||||
FLASH->ACR |= FLASH_ACR_DCRST | FLASH_ACR_ICRST;
|
||||
|
||||
do {
|
||||
uart_send_string("Erasing chip...");
|
||||
flash_writer_perform_mass_erase();
|
||||
uart_send_string(" done\r\n");
|
||||
|
||||
uart_send_string("Programming flash...\r\n");
|
||||
res = update_flash_from_file(filename);
|
||||
|
||||
if (res) {
|
||||
uart_send_string("Programming NOT successful.\r\n");
|
||||
if (retries > 0) {
|
||||
uart_send_string("Will retry...\r\n");
|
||||
}
|
||||
} else {
|
||||
uart_send_string("Programming completed successfully!\r\n");
|
||||
ram_code_exit(true);
|
||||
break;
|
||||
}
|
||||
|
||||
} while (retries > 0);
|
||||
|
||||
while(1) {
|
||||
__WFI();
|
||||
}
|
||||
|
@@ -260,29 +260,26 @@ extern unsigned int __ld_vector_start;
|
||||
extern unsigned int __ld_sbss;
|
||||
extern unsigned int __ld_ebss;
|
||||
|
||||
|
||||
#ifdef CPACR
|
||||
#undef CPACR
|
||||
#endif
|
||||
|
||||
#define CPACR (*((volatile uint32_t *)0xE000ED88))
|
||||
|
||||
void Reset_Handler(void)
|
||||
void Reset_Handler()
|
||||
{
|
||||
|
||||
|
||||
/* 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);
|
||||
/* Reset the stack pointer to top of stack. SP is not required to be inside the clobber list! */
|
||||
__asm__ __volatile__ ("mov sp, %0\n\t" :: "r"(&__ld_top_of_stack) :);
|
||||
|
||||
/* Fill bss with zero */
|
||||
__fill_zero(&__ld_sbss, &__ld_ebss);
|
||||
/* Fill Heap with zero */
|
||||
|
||||
|
||||
/* Reset the stack pointer to top of stack. SP is not required to be inside the clobber list! */
|
||||
__asm__ __volatile__ ("mov sp, %0\n\t" :: "r"(&__ld_top_of_stack) :);
|
||||
|
||||
ram_code_main();
|
||||
|
||||
/* Catch return from main() */
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user