Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d3d74f381
|
||
|
|
20639b77a6
|
||
|
|
b8909fca06
|
||
|
|
54b72a4bf1
|
||
|
|
afee9ab6a3
|
||
|
|
a1e9cf2a57
|
||
|
|
482a67c5d2
|
||
|
|
9703408307
|
||
|
|
b40825a75f
|
||
|
|
6bfa40ce9f
|
||
|
|
315da1fc82
|
||
|
|
0bce53403f
|
||
|
|
30d1762710
|
||
|
|
49c291dade
|
||
|
|
02b0ceb139
|
||
|
|
c0846f0bb7
|
||
|
|
396501564a
|
||
|
|
041e4f5b70
|
||
|
|
4ede660471
|
||
|
|
73e134d0b6
|
||
|
|
dc78c4da41
|
||
|
|
2bad5d69e6
|
||
|
|
e4ae8e23fc
|
||
|
|
2f4101a6e0
|
||
|
|
5eec46c041
|
||
|
|
9182c666ea
|
||
|
|
a66c453820
|
||
|
|
de75f2d91c
|
||
|
|
e667a2dd3a
|
||
|
|
9b1d084308
|
||
|
|
102d5f5a4c
|
||
|
|
1797f179b4
|
||
|
|
218d26fb7b
|
||
|
|
b9f82c8077
|
||
|
|
d1f8804a93
|
||
|
|
2e2dab9244
|
||
|
|
e046be30b2
|
||
|
|
f9200fcc7d
|
||
|
|
ffec192a27
|
+6
-28
@@ -90,6 +90,7 @@
|
||||
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
|
||||
#include <stdint.h>
|
||||
#include "main.h"
|
||||
#include "plat_config.h"
|
||||
#include "plat_compat.h"
|
||||
extern uint32_t SystemCoreClock;
|
||||
#endif
|
||||
@@ -134,34 +135,11 @@ to exclude the API function. */
|
||||
|
||||
///* Cortex-M specific definitions. */
|
||||
|
||||
#if defined(GEX_PLAT_F103_BLUEPILL) || defined(GEX_PLAT_F303_DISCOVERY) \
|
||||
|| defined(GEX_PLAT_F407_DISCOVERY)
|
||||
// This is for F103+
|
||||
|
||||
/* The lowest interrupt priority that can be used in a call to a "set priority"
|
||||
function. */
|
||||
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
|
||||
|
||||
#define configPRIO_BITS 4
|
||||
|
||||
/* The highest interrupt priority that can be used by any interrupt service
|
||||
routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL
|
||||
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
|
||||
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
|
||||
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
|
||||
|
||||
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
|
||||
#elif defined(STM32F072xB)
|
||||
// This is for F072
|
||||
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 3
|
||||
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 1
|
||||
|
||||
#define configPRIO_BITS 2
|
||||
|
||||
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
|
||||
#else
|
||||
#error "BAD PLATFORM!!"
|
||||
#endif
|
||||
// These are defined in plat_compat.h:
|
||||
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY PLAT_FREERTOS_LOWEST_INTERRUPT_PRIORITY
|
||||
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY PLAT_FREERTOS_MAX_SYSCALL_INTERRUPT_PRIORITY
|
||||
#define configPRIO_BITS PLAT_FREERTOS_PRIO_BITS
|
||||
#define configUSE_PORT_OPTIMISED_TASK_SELECTION PLAT_FREERTOS_USE_PORT_OPTIMISED_TASK_SELECTION
|
||||
|
||||
/* Interrupt priorities used by the kernel port layer itself. These are generic
|
||||
to all Cortex-M ports, and do not rely on any particular library functions. */
|
||||
|
||||
+373
@@ -0,0 +1,373 @@
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
@@ -0,0 +1,13 @@
|
||||
GEX core
|
||||
========
|
||||
|
||||
This is the (mostly) independent core of the GEX platform. To port GEX to a new STM32 microcontroller,
|
||||
add this as a submodule and implement platform specific configuration by following the example of another
|
||||
existing port. Some examples are in the template folder.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
- Original GEX source code is provided under the Mozilla Public License v2.0.
|
||||
- The VFS driver is adapted from the DAPLink project and remains licensed under the Apache license v2.0.
|
||||
- STM32 device drivers etc. provided by ST Microelectronics are licensed under the 3-clause BSD license.
|
||||
+20
-44
@@ -6,6 +6,12 @@
|
||||
|
||||
#include "platform.h"
|
||||
#include "task_main.h"
|
||||
#include "comm/messages.h"
|
||||
#include "comm/interfaces.h"
|
||||
#include "comm/iface_uart.h"
|
||||
#include "comm/iface_nordic.h"
|
||||
#include "comm/iface_usb.h"
|
||||
#include "framework/system_settings.h"
|
||||
|
||||
#include "USB/usbd_cdc_if.h"
|
||||
#include "USB/usb_device.h"
|
||||
@@ -16,64 +22,34 @@ extern osMutexId mutTinyFrameTxHandle;
|
||||
|
||||
void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, uint32_t len)
|
||||
{
|
||||
#if 1
|
||||
const uint32_t real_size = len;
|
||||
|
||||
// Padding to a multiple of 64 bytes - this is supposed to maximize the bulk transfer speed
|
||||
if (len&0x3F) {
|
||||
uint32_t pad = (64 - (len&0x3F));
|
||||
memset((void *) (buff + len), 0, pad);
|
||||
len += pad; // padding to a multiple of 64 (size of the endpoint)
|
||||
if (gActiveComport == COMPORT_USB) {
|
||||
iface_usb_transmit(buff, len);
|
||||
}
|
||||
|
||||
// We bypass the USBD driver library's overhead by using the HAL function directly
|
||||
assert_param(HAL_OK == HAL_PCD_EP_Transmit(hUsbDeviceFS.pData, CDC_IN_EP, (uint8_t *) buff, len));
|
||||
|
||||
// The buffer is the TF transmit buffer, we can't leave it to work asynchronously because
|
||||
// the next call could modify it before it's been transmitted (in the case of a chunked / multi-part frame)
|
||||
|
||||
// the assumption here is that all until the last chunk use the full buffer capacity
|
||||
if (real_size == TF_SENDBUF_LEN) {
|
||||
if (pdTRUE != xSemaphoreTake(semVcomTxReadyHandle, 100)) {
|
||||
TF_Error("Tx stalled in WriteImpl");
|
||||
return;
|
||||
else if (gActiveComport == COMPORT_USART) {
|
||||
iface_uart_transmit(buff, len);
|
||||
}
|
||||
#if SUPPORT_NRF
|
||||
else if (gActiveComport == COMPORT_NORDIC) {
|
||||
iface_nordic_transmit(buff, len);
|
||||
}
|
||||
#else
|
||||
(void) tf;
|
||||
#define CHUNK 64 // same as TF_SENDBUF_LEN, so we should always have only one run of the loop
|
||||
int32_t total = (int32_t) len;
|
||||
while (total > 0) {
|
||||
const int32_t mxStatus = osSemaphoreWait(semVcomTxReadyHandle, 100);
|
||||
if (mxStatus != osOK) {
|
||||
TF_Error("Tx stalled");
|
||||
return;
|
||||
#endif // SUPPORT_NRF
|
||||
else {
|
||||
// TODO other transports
|
||||
trap("not implemented.");
|
||||
}
|
||||
|
||||
const uint16_t chunksize = (uint16_t) MIN(total, CHUNK);
|
||||
|
||||
// this is an attempt to speed it up a little by removing a couple levels of indirection
|
||||
assert_param(HAL_OK == HAL_PCD_EP_Transmit(hUsbDeviceFS.pData, CDC_IN_EP, (uint8_t *) buff, chunksize));
|
||||
|
||||
// USBD_LL_Transmit(&hUsbDeviceFS, CDC_IN_EP, (uint8_t *) buff, chunksize);
|
||||
// assert_param(USBD_OK == CDC_Transmit_FS((uint8_t *) buff, chunksize));
|
||||
|
||||
buff += chunksize;
|
||||
total -= chunksize;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Claim the TX interface before composing and sending a frame */
|
||||
bool TF_ClaimTx(TinyFrame *tf)
|
||||
{
|
||||
(void) tf;
|
||||
// assert_param(!inIRQ()); // useless delay
|
||||
assert_param(!inIRQ());
|
||||
|
||||
assert_param(pdTRUE == xSemaphoreTake(mutTinyFrameTxHandle, 5000)); // trips the wd
|
||||
|
||||
// The last chunk from some previous frame may still be being transmitted,
|
||||
// wait for it to finish (the semaphore is given in the CDC tx done handler)
|
||||
if (pdTRUE != xSemaphoreTake(semVcomTxReadyHandle, 100)) {
|
||||
if (pdTRUE != xSemaphoreTake(semVcomTxReadyHandle, 200)) {
|
||||
TF_Error("Tx stalled in Claim");
|
||||
|
||||
// release the guarding mutex again
|
||||
|
||||
@@ -17,3 +17,15 @@ and processed by the message queue thread. This makes it possible to query hardw
|
||||
also makes it possible to wait on a binary semaphore when sending data back to host. The
|
||||
semaphore is set from the CDC TxComplete callback and taken by the TinyFrame write
|
||||
function, serving as a form of flow control.
|
||||
|
||||
--------------------------------------------
|
||||
|
||||
COMM API:
|
||||
|
||||
GEX supports alternate command interfaces.
|
||||
|
||||
The active interface is set in the global variable gActiveComport
|
||||
Due to special init procedures, the com_switch_transfer() function must be called to change it.
|
||||
|
||||
The TX function is defined in TF_Integration.c and any RX'd data is sent through rxQuePostMsg()
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ __ALIGN_BEGIN uint8_t USBD_MSC_CDC_CfgFSDesc[USBD_MSC_CDC_CONFIG_DESC_SIZ] __AL
|
||||
3, /* bNumInterfaces - 1 MSC + 2 composite CDC ACM */
|
||||
0x01, /* bConfigurationValue: */
|
||||
0, /* iConfiguration: - string descriptor */
|
||||
0xC0, /* bmAttributes: - self powered */
|
||||
150, /* MaxPower 300 mA */
|
||||
0x80, /* bmAttributes: - bus powered */
|
||||
250, /* MaxPower 500 mA */
|
||||
|
||||
/******************** Mass Storage interface ********************/
|
||||
/*9*/ 0x09, /* bLength: Interface Descriptor size */
|
||||
@@ -63,7 +63,7 @@ __ALIGN_BEGIN uint8_t USBD_MSC_CDC_CfgFSDesc[USBD_MSC_CDC_CONFIG_DESC_SIZ] __AL
|
||||
0x02, /* bInterfaceCount */
|
||||
0x02, /* bFunctionClass */ // #36
|
||||
0x02, /* bFunctionSubClass (ACM) */
|
||||
0x01, /* bFunctionProtocol (AT-COMMANDS) */
|
||||
0x01, /* bFunctionProtocol (AT-COMMANDS - we don't implement this but it's a good default) */
|
||||
0x05, /* iFunction: string descriptor */
|
||||
|
||||
/********************** ACM interface **********************/
|
||||
@@ -131,7 +131,7 @@ __ALIGN_BEGIN uint8_t USBD_MSC_CDC_CfgFSDesc[USBD_MSC_CDC_CONFIG_DESC_SIZ] __AL
|
||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||
CDC_OUT_EP, /* bEndpointAddress */
|
||||
0x02, /* bmAttributes: Bulk */
|
||||
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: TODO 8? */
|
||||
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize */
|
||||
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
|
||||
0x00, /* bInterval: ignore for Bulk transfer */
|
||||
|
||||
@@ -140,7 +140,7 @@ __ALIGN_BEGIN uint8_t USBD_MSC_CDC_CfgFSDesc[USBD_MSC_CDC_CONFIG_DESC_SIZ] __AL
|
||||
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
|
||||
CDC_IN_EP, /* bEndpointAddress */
|
||||
0x02, /* bmAttributes: Bulk */
|
||||
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: TODO 16? */
|
||||
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize */
|
||||
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
|
||||
0x00 /* bInterval: ignore for Bulk transfer */
|
||||
};
|
||||
|
||||
+7
-6
@@ -84,13 +84,14 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef* pcdHandle)
|
||||
__HAL_RCC_USB_CLK_ENABLE();
|
||||
|
||||
/* Peripheral interrupt init */
|
||||
#if GEX_PLAT_F103_BLUEPILL
|
||||
// TODO convert this to feature flags if possible - avoid tying to particular platforms
|
||||
#if GEX_PLAT_F103
|
||||
HAL_NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 5, 0);
|
||||
HAL_NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
|
||||
#elif STM32F072xB
|
||||
#elif GEX_PLAT_F072
|
||||
HAL_NVIC_SetPriority(USB_IRQn, 3, 0);
|
||||
HAL_NVIC_EnableIRQ(USB_IRQn);
|
||||
#elif GEX_PLAT_F303_DISCOVERY
|
||||
#elif GEX_PLAT_F303
|
||||
// Pins need to be configured here
|
||||
|
||||
/**USB GPIO Configuration
|
||||
@@ -149,11 +150,11 @@ void HAL_PCD_MspDeInit(PCD_HandleTypeDef* pcdHandle)
|
||||
__HAL_RCC_USB_CLK_DISABLE();
|
||||
|
||||
/* Peripheral interrupt Deinit*/
|
||||
#if GEX_PLAT_F103_BLUEPILL
|
||||
#if GEX_PLAT_F103
|
||||
HAL_NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
|
||||
#elif STM32F072xB
|
||||
#elif GEX_PLAT_F072
|
||||
HAL_NVIC_DisableIRQ(USB_IRQn);
|
||||
#elif GEX_PLAT_F303_DISCOVERY
|
||||
#elif GEX_PLAT_F303
|
||||
HAL_NVIC_DisableIRQ(USB_LP_CAN_RX0_IRQn);
|
||||
#else
|
||||
#error "BAD PLATFORM"
|
||||
|
||||
+3
-3
@@ -72,11 +72,11 @@
|
||||
/** @defgroup USBD_DESC_Private_Defines
|
||||
* @{
|
||||
*/
|
||||
#define USBD_VID 1155 // TODO choose a VID:PID (eg. via pid.codes - 0x1209)
|
||||
#define USBD_PID_FS 22314
|
||||
#define USBD_VID 0x1209
|
||||
#define USBD_PID_FS 0x4c60
|
||||
#define USBD_LANGID_NUM 1033
|
||||
#define USBD_MANUFACTURER_STRING "MightyPork"
|
||||
#define USBD_PRODUCT_STRING_FS "GEX"
|
||||
#define USBD_PRODUCT_STRING_FS "GEX USB module"
|
||||
#define USBD_VERSION_NUM 0x0001 // 0xMMmp
|
||||
//#define USBD_SERIALNUMBER_STRING_FS "00000000001A"
|
||||
//#define USBD_CONFIGURATION_STRING_FS "MSC Config"
|
||||
|
||||
@@ -186,7 +186,7 @@ USBD_StorageTypeDef USBD_Storage_Interface_fops_FS =
|
||||
int8_t STORAGE_Init_FS (uint8_t lun)
|
||||
{
|
||||
/* USER CODE BEGIN 2 */
|
||||
dbg("Plug In");
|
||||
dbg("MSC init request");
|
||||
vfs_mngr_fs_enable(1);
|
||||
return (USBD_OK);
|
||||
/* USER CODE END 2 */
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Created by MightyPork on 2018/01/27.
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_EVENT_REPORTS_H
|
||||
#define GEX_F072_EVENT_REPORTS_H
|
||||
#ifndef GEX_CORE_EVENT_REPORTS_H
|
||||
#define GEX_CORE_EVENT_REPORTS_H
|
||||
|
||||
#ifndef GEX_MESSAGES_H
|
||||
#error "Include messages.h instead!"
|
||||
@@ -31,4 +31,4 @@ void EventReport_Data(const uint8_t *buff, uint16_t len);
|
||||
void EventReport_PB(PayloadBuilder *pb);
|
||||
void EventReport_End(void);
|
||||
|
||||
#endif //GEX_F072_EVENT_REPORTS_H
|
||||
#endif //GEX_CORE_EVENT_REPORTS_H
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/04/06.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#if SUPPORT_NRF
|
||||
|
||||
#include "iface_nordic.h"
|
||||
#include "nrf_pins.h"
|
||||
#include "resources.h"
|
||||
#include "hw_utils.h"
|
||||
#include "nrf.h"
|
||||
#include "unit_base.h"
|
||||
#include "system_settings.h"
|
||||
#include "utils/hexdump.h"
|
||||
|
||||
|
||||
extern osSemaphoreId semVcomTxReadyHandle;
|
||||
|
||||
#define RX_PIPE_NUM 0
|
||||
|
||||
void iface_nordic_claim_resources(void)
|
||||
{
|
||||
assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, NRF_R_SPI));
|
||||
assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, NRF_R_CE));
|
||||
assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, NRF_R_NSS));
|
||||
assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, NRF_R_IRQ));
|
||||
assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, NRF_R_MISO));
|
||||
assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, NRF_R_MOSI));
|
||||
assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, NRF_R_SCK));
|
||||
|
||||
assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, R_EXTI0+NRF_EXTI_LINENUM));
|
||||
}
|
||||
|
||||
void iface_nordic_free_resources(void)
|
||||
{
|
||||
rsc_free(&UNIT_SYSTEM, NRF_R_SPI);
|
||||
rsc_free(&UNIT_SYSTEM, NRF_R_CE);
|
||||
rsc_free(&UNIT_SYSTEM, NRF_R_NSS);
|
||||
rsc_free(&UNIT_SYSTEM, NRF_R_IRQ);
|
||||
rsc_free(&UNIT_SYSTEM, NRF_R_MISO);
|
||||
rsc_free(&UNIT_SYSTEM, NRF_R_MOSI);
|
||||
rsc_free(&UNIT_SYSTEM, NRF_R_SCK);
|
||||
|
||||
rsc_free(&UNIT_SYSTEM, R_EXTI0+NRF_EXTI_LINENUM);
|
||||
}
|
||||
|
||||
static uint8_t rx_buffer[32];
|
||||
|
||||
static void NrfIrqHandler(void *arg)
|
||||
{
|
||||
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINES[NRF_EXTI_LINENUM]);
|
||||
dbg_nrf("[EXTI] ---");
|
||||
|
||||
while (NRF_IsRxPacket()) {
|
||||
uint8_t pipenum;
|
||||
uint8_t count = NRF_ReceivePacket(rx_buffer, &pipenum);
|
||||
if (count > 0) {
|
||||
dbg_nrf("NRF RX %d bytes", (int) count);
|
||||
rxQuePostMsg(rx_buffer, count);
|
||||
}
|
||||
else {
|
||||
dbg("IRQ but no Rx");
|
||||
}
|
||||
}
|
||||
|
||||
dbg_nrf("--- end [EXTI]");
|
||||
}
|
||||
|
||||
bool iface_nordic_init(void)
|
||||
{
|
||||
dbg("Setting up Nordic...");
|
||||
|
||||
hw_periph_clock_enable(NRF_SPI);
|
||||
|
||||
// SPI pins
|
||||
assert_param(E_SUCCESS == hw_configure_gpiorsc_af(NRF_R_SCK, NRF_SCK_AF));
|
||||
assert_param(E_SUCCESS == hw_configure_gpiorsc_af(NRF_R_MOSI, NRF_MOSI_AF));
|
||||
assert_param(E_SUCCESS == hw_configure_gpiorsc_af(NRF_R_MISO, NRF_MISO_AF));
|
||||
|
||||
// Manual pins
|
||||
LL_GPIO_SetPinMode(NRF_NSS_GPIO_Port, NRF_NSS_Pin, LL_GPIO_MODE_OUTPUT);
|
||||
LL_GPIO_SetPinMode(NRF_CE_GPIO_Port, NRF_CE_Pin, LL_GPIO_MODE_OUTPUT);
|
||||
LL_GPIO_SetPinMode(NRF_RST_GPIO_Port, NRF_RST_Pin, LL_GPIO_MODE_OUTPUT);
|
||||
LL_GPIO_SetPinMode(NRF_IRQ_GPIO_Port, NRF_IRQ_Pin, LL_GPIO_MODE_INPUT);
|
||||
|
||||
// set up SPI
|
||||
LL_SPI_Disable(NRF_SPI);
|
||||
{
|
||||
LL_SPI_SetBaudRatePrescaler(NRF_SPI, LL_SPI_BAUDRATEPRESCALER_DIV32);
|
||||
//LL_SPI_BAUDRATEPRESCALER_DIV8
|
||||
|
||||
LL_SPI_SetClockPolarity(NRF_SPI, LL_SPI_POLARITY_LOW);
|
||||
LL_SPI_SetClockPhase(NRF_SPI, LL_SPI_PHASE_1EDGE);
|
||||
LL_SPI_SetTransferDirection(NRF_SPI, LL_SPI_FULL_DUPLEX);
|
||||
LL_SPI_SetTransferBitOrder(NRF_SPI, LL_SPI_MSB_FIRST);
|
||||
|
||||
LL_SPI_SetNSSMode(NRF_SPI, LL_SPI_NSS_SOFT);
|
||||
LL_SPI_SetDataWidth(NRF_SPI, LL_SPI_DATAWIDTH_8BIT);
|
||||
LL_SPI_SetRxFIFOThreshold(NRF_SPI, LL_SPI_RX_FIFO_TH_QUARTER); // trigger RXNE on 1 byte
|
||||
|
||||
LL_SPI_SetMode(NRF_SPI, LL_SPI_MODE_MASTER);
|
||||
}
|
||||
LL_SPI_Enable(NRF_SPI);
|
||||
|
||||
// reset the radio / enable its power supply
|
||||
NRF_Reset(1);
|
||||
LL_mDelay(5);
|
||||
NRF_Reset(0);
|
||||
|
||||
dbg("configure nrf module");
|
||||
|
||||
// Now configure the radio
|
||||
if (!NRF_Init(NRF_SPEED_2M)) {// TODO configurable speed - also maybe better to use slower
|
||||
dbg("--- NRF not present!");
|
||||
return false;
|
||||
}
|
||||
|
||||
NRF_SetChannel(SystemSettings.nrf_channel);
|
||||
NRF_SetBaseAddress(SystemSettings.nrf_network);
|
||||
NRF_SetRxAddress(RX_PIPE_NUM, SystemSettings.nrf_address);
|
||||
NRF_EnablePipe(RX_PIPE_NUM);
|
||||
NRF_ModeRX();
|
||||
|
||||
dbg("enable exti");
|
||||
// EXTI
|
||||
LL_EXTI_EnableIT_0_31(LL_EXTI_LINES[NRF_EXTI_LINENUM]);
|
||||
LL_EXTI_EnableFallingTrig_0_31(LL_EXTI_LINES[NRF_EXTI_LINENUM]);
|
||||
LL_SYSCFG_SetEXTISource(NRF_SYSCFG_EXTI_PORT, LL_SYSCFG_EXTI_LINES[NRF_EXTI_LINENUM]);
|
||||
irqd_attach(EXTIS[NRF_EXTI_LINENUM], NrfIrqHandler, NULL);
|
||||
// TODO increase priority in NVIC?
|
||||
|
||||
dbg("nrf setup done");
|
||||
return true;
|
||||
}
|
||||
|
||||
void iface_nordic_deinit(void)
|
||||
{
|
||||
LL_EXTI_DisableIT_0_31(LL_EXTI_LINES[NRF_EXTI_LINENUM]);
|
||||
LL_EXTI_DisableFallingTrig_0_31(LL_EXTI_LINES[NRF_EXTI_LINENUM]);
|
||||
irqd_detach(EXTIS[NRF_EXTI_LINENUM], NrfIrqHandler);
|
||||
hw_periph_clock_disable(NRF_SPI);
|
||||
|
||||
hw_deinit_pin_rsc(NRF_R_SCK);
|
||||
hw_deinit_pin_rsc(NRF_R_MOSI);
|
||||
hw_deinit_pin_rsc(NRF_R_MISO);
|
||||
hw_deinit_pin_rsc(NRF_R_NSS);
|
||||
hw_deinit_pin_rsc(NRF_R_CE);
|
||||
hw_deinit_pin_rsc(NRF_R_IRQ);
|
||||
hw_deinit_pin_rsc(NRF_R_RST);
|
||||
}
|
||||
|
||||
// FIXME
|
||||
#define MAX_RETRY 5
|
||||
|
||||
void iface_nordic_transmit(const uint8_t *buff, uint32_t len)
|
||||
{
|
||||
bool suc = false;
|
||||
while (len > 0) {
|
||||
uint8_t chunk = (uint8_t) MIN(32, len);
|
||||
|
||||
uint16_t delay = 1;
|
||||
//hexDump("Tx chunk", buff, chunk);
|
||||
for (int i = 0; i < MAX_RETRY; i++) {
|
||||
suc = NRF_SendPacket(RX_PIPE_NUM, buff, chunk); // use the pipe to retrieve the address
|
||||
if (!suc) {
|
||||
vTaskDelay(delay);
|
||||
delay *= 2; // longer delay next time
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!suc) {
|
||||
break;
|
||||
}
|
||||
|
||||
buff += chunk;
|
||||
len -= chunk;
|
||||
}
|
||||
|
||||
if (suc) {
|
||||
dbg_nrf("+ NRF Tx OK!");
|
||||
} else {
|
||||
dbg("- NRF sending failed");
|
||||
}
|
||||
|
||||
// give when it's done
|
||||
xSemaphoreGive(semVcomTxReadyHandle); // similar to how it's done in USB - this is called in the Tx Done handler
|
||||
}
|
||||
|
||||
#endif // SUPPORT_NRF
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/04/06.
|
||||
//
|
||||
|
||||
#ifndef GEX_CORE_IFACE_NORDIC_H
|
||||
#define GEX_CORE_IFACE_NORDIC_H
|
||||
|
||||
#include "platform.h"
|
||||
#if SUPPORT_NRF
|
||||
|
||||
void iface_nordic_claim_resources(void);
|
||||
|
||||
void iface_nordic_free_resources(void);
|
||||
|
||||
void iface_nordic_deinit(void);
|
||||
|
||||
bool iface_nordic_init(void);
|
||||
|
||||
void iface_nordic_transmit(const uint8_t *buff, uint32_t len);
|
||||
|
||||
#endif // SUPPORT_NRF
|
||||
|
||||
#endif //GEX_CORE_IFACE_NORDIC_H
|
||||
@@ -0,0 +1,105 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/04/06.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "iface_uart.h"
|
||||
#include "resources.h"
|
||||
#include "hw_utils.h"
|
||||
#include "irq_dispatcher.h"
|
||||
#include "task_msg.h"
|
||||
#include "system_settings.h"
|
||||
|
||||
extern osSemaphoreId semVcomTxReadyHandle;
|
||||
|
||||
static uint32_t usart_rxi = 0;
|
||||
static uint8_t usart_rx_buffer[MSG_QUE_SLOT_SIZE];
|
||||
static uint32_t last_rx_time = 0;
|
||||
|
||||
void iface_uart_claim_resources(void)
|
||||
{
|
||||
assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, R_USART2));
|
||||
assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, R_PA2));
|
||||
assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, R_PA3));
|
||||
}
|
||||
|
||||
void iface_uart_free_resources(void)
|
||||
{
|
||||
rsc_free(&UNIT_SYSTEM, R_USART2);
|
||||
rsc_free(&UNIT_SYSTEM, R_PA2);
|
||||
rsc_free(&UNIT_SYSTEM, R_PA3);
|
||||
}
|
||||
|
||||
/** Handler for the USART transport */
|
||||
static void com_UsartIrqHandler(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
if (LL_USART_IsActiveFlag_RXNE(USART2)) {
|
||||
vPortEnterCritical();
|
||||
{
|
||||
usart_rx_buffer[usart_rxi++] = LL_USART_ReceiveData8(USART2);
|
||||
if (usart_rxi == MSG_QUE_SLOT_SIZE) {
|
||||
rxQuePostMsg(usart_rx_buffer, MSG_QUE_SLOT_SIZE); // avoid it happening in the irq
|
||||
usart_rxi = 0;
|
||||
}
|
||||
last_rx_time = HAL_GetTick();
|
||||
}
|
||||
vPortExitCritical();
|
||||
}
|
||||
}
|
||||
|
||||
/** this is called from the hal tick irq */
|
||||
void com_iface_flush_buffer(void)
|
||||
{
|
||||
if (usart_rxi > 0 && (HAL_GetTick()-last_rx_time)>=2) {
|
||||
vPortEnterCritical();
|
||||
{
|
||||
rxQuePostMsg(usart_rx_buffer, usart_rxi);
|
||||
usart_rxi = 0;
|
||||
}
|
||||
vPortExitCritical();
|
||||
}
|
||||
}
|
||||
|
||||
bool iface_uart_init(void)
|
||||
{
|
||||
dbg("Setting up UART transfer");
|
||||
assert_param(E_SUCCESS == hw_configure_gpiorsc_af(R_PA2, LL_GPIO_AF_1));
|
||||
assert_param(E_SUCCESS == hw_configure_gpiorsc_af(R_PA3, LL_GPIO_AF_1));
|
||||
|
||||
__HAL_RCC_USART2_CLK_ENABLE();
|
||||
__HAL_RCC_USART2_FORCE_RESET();
|
||||
__HAL_RCC_USART2_RELEASE_RESET();
|
||||
|
||||
LL_USART_Disable(USART2);
|
||||
|
||||
LL_USART_SetBaudRate(USART2, PLAT_APB1_HZ, LL_USART_OVERSAMPLING_16, SystemSettings.comm_uart_baud);
|
||||
dbg("baud = %d", (int)SystemSettings.comm_uart_baud);
|
||||
|
||||
irqd_attach(USART2, com_UsartIrqHandler, NULL);
|
||||
LL_USART_EnableIT_RXNE(USART2);
|
||||
LL_USART_SetTransferDirection(USART2, LL_USART_DIRECTION_TX_RX);
|
||||
|
||||
LL_USART_Enable(USART2);
|
||||
|
||||
return true; // always OK (TODO check voltage on Rx if it's 3V3 when idle?)
|
||||
}
|
||||
|
||||
void iface_uart_deinit(void)
|
||||
{
|
||||
// this doesn't normally happen
|
||||
hw_deinit_pin_rsc(R_PA2);
|
||||
hw_deinit_pin_rsc(R_PA3);
|
||||
__HAL_RCC_USART2_CLK_DISABLE();
|
||||
irqd_detach(USART2, com_UsartIrqHandler);
|
||||
}
|
||||
|
||||
void iface_uart_transmit(const uint8_t *buff, uint32_t len)
|
||||
{
|
||||
// TODO rewrite this to use DMA, then wait for the DMA
|
||||
for(uint32_t i=0;i<len;i++) {
|
||||
while(!LL_USART_IsActiveFlag_TXE(USART2));
|
||||
LL_USART_TransmitData8(USART2, buff[i]);
|
||||
}
|
||||
xSemaphoreGive(semVcomTxReadyHandle); // act as if we just finished it and this is perhaps the DMA irq
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/04/06.
|
||||
//
|
||||
|
||||
#ifndef GEX_CORE_IFACE_UART_H
|
||||
#define GEX_CORE_IFACE_UART_H
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
void com_iface_flush_buffer(void);
|
||||
|
||||
void iface_uart_deinit(void);
|
||||
bool iface_uart_init(void);
|
||||
|
||||
void iface_uart_free_resources(void);
|
||||
void iface_uart_claim_resources(void);
|
||||
|
||||
void iface_uart_transmit(const uint8_t *buff, uint32_t len);
|
||||
|
||||
#endif //GEX_CORE_IFACE_UART_H
|
||||
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/04/06.
|
||||
//
|
||||
|
||||
#include "iface_usb.h"
|
||||
#include "TinyFrame.h"
|
||||
#include "system_settings.h"
|
||||
#include "USB/usb_device.h"
|
||||
|
||||
extern osSemaphoreId semVcomTxReadyHandle;
|
||||
extern osMutexId mutTinyFrameTxHandle;
|
||||
|
||||
bool iface_usb_ready(void)
|
||||
{
|
||||
return 0 != (USB->DADDR & USB_DADDR_ADD);
|
||||
}
|
||||
|
||||
/**
|
||||
* USB transmit implementation
|
||||
*
|
||||
* @param buff - buffer to send (can be longer than the buffers)
|
||||
* @param len - buffer size
|
||||
*/
|
||||
void iface_usb_transmit(const uint8_t *buff, uint32_t len)
|
||||
{
|
||||
#if 1
|
||||
const uint32_t real_size = len;
|
||||
|
||||
// Padding to a multiple of 64 bytes - this is supposed to maximize the bulk transfer speed
|
||||
if ((len&0x3F) && !SystemSettings.visible_vcom) { // this corrupts VCOM on Linux for some reason
|
||||
uint32_t pad = (64 - (len&0x3F));
|
||||
memset((void *) (buff + len), 0, pad);
|
||||
len += pad; // padding to a multiple of 64 (size of the endpoint)
|
||||
}
|
||||
|
||||
// We bypass the USBD driver library's overhead by using the HAL function directly
|
||||
assert_param(HAL_OK == HAL_PCD_EP_Transmit(hUsbDeviceFS.pData, CDC_IN_EP, (uint8_t *) buff, len));
|
||||
|
||||
// The buffer is the TF transmit buffer, we can't leave it to work asynchronously because
|
||||
// the next call could modify it before it's been transmitted (in the case of a chunked / multi-part frame)
|
||||
|
||||
// If this is not the last chunk (assuming all but the last use full 512 bytes of the TF buffer), wait now for completion
|
||||
if (real_size == TF_SENDBUF_LEN) {
|
||||
// TODO this seems wrong - investigate
|
||||
if (pdTRUE != xSemaphoreTake(semVcomTxReadyHandle, 100)) {
|
||||
TF_Error("Tx stalled in WriteImpl");
|
||||
return;
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void) tf;
|
||||
#define CHUNK 64 // size of the USB packet
|
||||
int32_t total = (int32_t) len;
|
||||
while (total > 0) {
|
||||
const int32_t mxStatus = osSemaphoreWait(semVcomTxReadyHandle, 100);
|
||||
if (mxStatus != osOK) {
|
||||
TF_Error("Tx stalled");
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t chunksize = (uint16_t) MIN(total, CHUNK);
|
||||
|
||||
// this is an attempt to speed it up a little by removing a couple levels of indirection
|
||||
assert_param(HAL_OK == HAL_PCD_EP_Transmit(hUsbDeviceFS.pData, CDC_IN_EP, (uint8_t *) buff, chunksize));
|
||||
|
||||
// USBD_LL_Transmit(&hUsbDeviceFS, CDC_IN_EP, (uint8_t *) buff, chunksize);
|
||||
// assert_param(USBD_OK == CDC_Transmit_FS((uint8_t *) buff, chunksize));
|
||||
|
||||
buff += chunksize;
|
||||
total -= chunksize;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/04/06.
|
||||
//
|
||||
|
||||
#ifndef GEX_CORE_IFACE_USB_H
|
||||
#define GEX_CORE_IFACE_USB_H
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
bool iface_usb_ready(void);
|
||||
|
||||
/**
|
||||
* USB transmit implementation
|
||||
*
|
||||
* @param buff - buffer to send (can be longer than the buffers)
|
||||
* @param len - buffer size
|
||||
*/
|
||||
void iface_usb_transmit(const uint8_t *buff, uint32_t len);
|
||||
|
||||
#endif //GEX_CORE_IFACE_USB_H
|
||||
@@ -0,0 +1,156 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/03/23.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "usbd_core.h"
|
||||
#include "USB/usb_device.h"
|
||||
#include "interfaces.h"
|
||||
#include "framework/system_settings.h"
|
||||
#include "framework/unit.h"
|
||||
#include "framework/resources.h"
|
||||
#include "platform/hw_utils.h"
|
||||
#include "framework/unit_base.h"
|
||||
#include "nrf_pins.h"
|
||||
#include "iface_uart.h"
|
||||
#include "iface_usb.h"
|
||||
#include "iface_nordic.h"
|
||||
|
||||
// those correspond to the ENUM values
|
||||
const char * COMPORT_NAMES[] = {
|
||||
"NONE",
|
||||
"USB",
|
||||
"UART",
|
||||
"NRF",
|
||||
"LORA",
|
||||
};
|
||||
|
||||
enum ComportSelection gActiveComport = COMPORT_USB; // start with USB so the handlers work correctly initially
|
||||
|
||||
static uint32_t last_switch_time = 0; // started with USB
|
||||
static bool xfer_verify_done = false;
|
||||
|
||||
static bool configure_interface(enum ComportSelection iface);
|
||||
|
||||
/** Switch com transfer if the current one doesnt seem to work */
|
||||
void com_switch_transfer_if_needed(void)
|
||||
{
|
||||
if (xfer_verify_done) return;
|
||||
|
||||
const uint32_t now = HAL_GetTick();
|
||||
const uint32_t elapsed = now - last_switch_time;
|
||||
|
||||
if (gActiveComport == COMPORT_USB) {
|
||||
if (elapsed > 1000) {
|
||||
// USB may or may not work, depending on whether the module is plugged in
|
||||
// or running from a battery/external supply remotely.
|
||||
|
||||
// Check if USB is enumerated
|
||||
|
||||
if (!iface_usb_ready()) {
|
||||
dbg("Not enumerated, assuming USB is dead");
|
||||
|
||||
// Fallback to radio or bare USART
|
||||
do {
|
||||
if (SystemSettings.use_comm_nordic) {
|
||||
if (configure_interface(COMPORT_NORDIC)) break;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (SystemSettings.use_comm_lora) {
|
||||
if (configure_interface(COMPORT_LORA)) break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (SystemSettings.use_comm_uart) {
|
||||
// after nordic/lora
|
||||
if (configure_interface(COMPORT_USART)) break;
|
||||
}
|
||||
|
||||
dbg("No alternate com interface configured.");
|
||||
gActiveComport = COMPORT_NONE;
|
||||
} while (0);
|
||||
} else {
|
||||
dbg("USB got address - OK");
|
||||
}
|
||||
|
||||
xfer_verify_done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Claim resources that may be needed for alternate transfers */
|
||||
void com_claim_resources_for_alt_transfers(void)
|
||||
{
|
||||
if (SystemSettings.use_comm_uart) {
|
||||
iface_uart_claim_resources();
|
||||
}
|
||||
|
||||
#if SUPPORT_NRF
|
||||
if (SystemSettings.use_comm_nordic) {
|
||||
iface_nordic_claim_resources();
|
||||
}
|
||||
#endif // SUPPORT_NRF
|
||||
}
|
||||
|
||||
/** Release resources allocated for alternate transfers */
|
||||
void com_release_resources_for_alt_transfers(void)
|
||||
{
|
||||
if (SystemSettings.use_comm_uart) {
|
||||
iface_uart_free_resources();
|
||||
}
|
||||
|
||||
#if SUPPORT_NRF
|
||||
if (SystemSettings.use_comm_nordic) {
|
||||
iface_nordic_free_resources();
|
||||
}
|
||||
#endif // SUPPORT_NRF
|
||||
}
|
||||
|
||||
|
||||
static bool configure_interface(enum ComportSelection iface)
|
||||
{
|
||||
// Teardown
|
||||
if (gActiveComport == COMPORT_USB) {
|
||||
// simplest USB disabling (XXX needs porting)
|
||||
HAL_PCD_DeInit(&hpcd_USB_FS);
|
||||
__HAL_RCC_USB_CLK_DISABLE();
|
||||
}
|
||||
else if (gActiveComport == COMPORT_USART) {
|
||||
iface_uart_deinit();
|
||||
}
|
||||
#if SUPPORT_NRF
|
||||
else if (gActiveComport == COMPORT_NORDIC) {
|
||||
iface_nordic_deinit();
|
||||
}
|
||||
#endif // SUPPORT_NRF
|
||||
|
||||
|
||||
gActiveComport = iface;
|
||||
|
||||
// Init
|
||||
if (iface == COMPORT_USB) {
|
||||
trap("illegal"); // this never happens
|
||||
return false;
|
||||
}
|
||||
else if (iface == COMPORT_USART) {
|
||||
return iface_uart_init();
|
||||
}
|
||||
#if SUPPORT_NRF
|
||||
else if (iface == COMPORT_NORDIC) {
|
||||
return iface_nordic_init();
|
||||
}
|
||||
#endif // SUPPORT_NRF
|
||||
#if 0
|
||||
else if (iface == COMPORT_LORA) {
|
||||
// Try to configure nordic
|
||||
dbg("Setting up LoRa transfer");
|
||||
// TODO set up and check LoRa transport
|
||||
dbg("LoRa not impl!");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
trap("Bad iface %d", iface);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/03/23.
|
||||
//
|
||||
|
||||
#ifndef GEX_CORE_COM_INTERFACES_H
|
||||
#define GEX_CORE_COM_INTERFACES_H
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
enum ComportSelection {
|
||||
COMPORT_NONE = 0,
|
||||
COMPORT_USB = 1,
|
||||
COMPORT_USART = 2,
|
||||
COMPORT_NORDIC = 3,
|
||||
COMPORT_LORA = 4,
|
||||
};
|
||||
|
||||
extern const char * COMPORT_NAMES[];
|
||||
|
||||
/**
|
||||
* The currently active communication port
|
||||
*/
|
||||
extern enum ComportSelection gActiveComport;
|
||||
|
||||
/** Switch com transfer if the current one doesnt seem to work */
|
||||
void com_switch_transfer_if_needed(void);
|
||||
|
||||
/** Claim resources that may be needed for alternate transfers */
|
||||
void com_claim_resources_for_alt_transfers(void);
|
||||
|
||||
/** Release resources allocated for alternate transfers */
|
||||
void com_release_resources_for_alt_transfers(void);
|
||||
|
||||
/** Flush the rx buffer */
|
||||
void com_iface_flush_buffer(void);
|
||||
|
||||
#endif //GEX_CORE_COM_INTERFACES_H
|
||||
+35
-14
@@ -2,7 +2,6 @@
|
||||
// Created by MightyPork on 2017/11/21.
|
||||
//
|
||||
|
||||
#include <platform/status_led.h>
|
||||
#include "platform.h"
|
||||
#include "framework/settings.h"
|
||||
#include "utils/ini_parser.h"
|
||||
@@ -11,6 +10,8 @@
|
||||
#include "comm/messages.h"
|
||||
#include "framework/system_settings.h"
|
||||
#include "utils/malloc_safe.h"
|
||||
#include "platform/status_led.h"
|
||||
#include "interfaces.h"
|
||||
|
||||
static TinyFrame tf_;
|
||||
TinyFrame *comm = &tf_;
|
||||
@@ -22,8 +23,9 @@ TinyFrame *comm = &tf_;
|
||||
*/
|
||||
static TF_Result lst_ping(TinyFrame *tf, TF_Msg *msg)
|
||||
{
|
||||
dbg("Ping!");
|
||||
com_respond_snprintf(msg->frame_id, MSG_SUCCESS,
|
||||
"GEX v%s on %s", GEX_VERSION, GEX_PLATFORM);
|
||||
"GEX v%s on %s (%s)", GEX_VERSION, GEX_PLATFORM, COMPORT_NAMES[gActiveComport]);
|
||||
return TF_STAY;
|
||||
}
|
||||
|
||||
@@ -58,26 +60,33 @@ static TF_Result lst_list_units(TinyFrame *tf, TF_Msg *msg)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Callback for bulk read of the settings file */
|
||||
static void settings_bulkread_cb(BulkRead *bulk, uint32_t chunk, uint8_t *buffer)
|
||||
/** Callback for bulk read of a settings file */
|
||||
static void ini_bulkread_cb(BulkRead *bulk, uint32_t chunk, uint8_t *buffer)
|
||||
{
|
||||
// clean-up request
|
||||
if (buffer == NULL) {
|
||||
free_ck(bulk);
|
||||
iw_end();
|
||||
// dbg("INI read complete.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bulk->offset == 0) iw_begin();
|
||||
|
||||
IniWriter iw = iw_init((char *)buffer, bulk->offset, chunk);
|
||||
iw.tag = 1;
|
||||
iw.tag = 1; // indicates this is read via the API (affects some comments)
|
||||
|
||||
uint8_t filenum = (uint8_t) (int) bulk->userdata;
|
||||
|
||||
if (filenum == 0) {
|
||||
settings_build_units_ini(&iw);
|
||||
}
|
||||
else if (filenum == 1) {
|
||||
settings_build_system_ini(&iw);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener: Export INI file via TF
|
||||
* Listener: Export a file via TF
|
||||
*/
|
||||
static TF_Result lst_ini_export(TinyFrame *tf, TF_Msg *msg)
|
||||
{
|
||||
@@ -86,14 +95,29 @@ static TF_Result lst_ini_export(TinyFrame *tf, TF_Msg *msg)
|
||||
BulkRead *bulk = malloc_ck(sizeof(BulkRead));
|
||||
assert_param(bulk != NULL);
|
||||
|
||||
uint8_t filenum = 0;
|
||||
|
||||
// if any payload, the first byte defines the file to read
|
||||
// 0 - units
|
||||
// 1 - system
|
||||
// (this is optional for backwards compatibility)
|
||||
if (msg->len > 0) {
|
||||
filenum = msg->data[0];
|
||||
}
|
||||
|
||||
bulk->frame_id = msg->frame_id;
|
||||
bulk->read = ini_bulkread_cb;
|
||||
bulk->userdata = (void *) (int)filenum;
|
||||
|
||||
if (filenum == 0) {
|
||||
bulk->len = iw_measure_total(settings_build_units_ini, 1);
|
||||
bulk->read = settings_bulkread_cb;
|
||||
bulk->userdata = NULL;
|
||||
}
|
||||
else if (filenum == 1) {
|
||||
bulk->len = iw_measure_total(settings_build_system_ini, 1);
|
||||
}
|
||||
|
||||
bulkread_start(tf, bulk);
|
||||
Indicator_Effect(STATUS_DISK_BUSY_SHORT);
|
||||
|
||||
return TF_STAY;
|
||||
}
|
||||
|
||||
@@ -142,7 +166,7 @@ static TF_Result lst_ini_import(TinyFrame *tf, TF_Msg *msg)
|
||||
PayloadParser pp = pp_start(msg->data, msg->len, NULL);
|
||||
uint32_t len = pp_u32(&pp);
|
||||
if (!pp.ok) {
|
||||
com_respond_error(msg->frame_id, E_PROTOCOL_BREACH);
|
||||
com_respond_error(msg->frame_id, E_MALFORMED_COMMAND);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@@ -151,11 +175,8 @@ static TF_Result lst_ini_import(TinyFrame *tf, TF_Msg *msg)
|
||||
|
||||
settings_load_ini_begin();
|
||||
ini_parse_begin(iniparser_cb, NULL);
|
||||
|
||||
bulkwrite_start(tf, bulk);
|
||||
|
||||
Indicator_Effect(STATUS_DISK_BUSY);
|
||||
|
||||
done:
|
||||
return TF_STAY;
|
||||
}
|
||||
|
||||
+3
-3
@@ -4,8 +4,8 @@
|
||||
// Bulk read (providing data for bulk read by the PC)
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_MSG_BULKREAD_H
|
||||
#define GEX_F072_MSG_BULKREAD_H
|
||||
#ifndef GEX_CORE_MSG_BULKREAD_H
|
||||
#define GEX_CORE_MSG_BULKREAD_H
|
||||
|
||||
#ifndef GEX_MESSAGES_H
|
||||
#error "Include messages.h instead!"
|
||||
@@ -45,4 +45,4 @@ struct bulk_read {
|
||||
void bulkread_start(TinyFrame *tf, BulkRead *bulk);
|
||||
|
||||
|
||||
#endif //GEX_F072_MSG_BULKREAD_H
|
||||
#endif //GEX_CORE_MSG_BULKREAD_H
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
// Bulk write (receiving bulk write from the PC)
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_MSG_BULKWRITE_H
|
||||
#define GEX_F072_MSG_BULKWRITE_H
|
||||
#ifndef GEX_CORE_MSG_BULKWRITE_H
|
||||
#define GEX_CORE_MSG_BULKWRITE_H
|
||||
|
||||
#ifndef GEX_MESSAGES_H
|
||||
#error "Include messages.h instead!"
|
||||
@@ -44,4 +44,4 @@ struct bulk_write {
|
||||
void bulkwrite_start(TinyFrame *tf, BulkWrite *bulk);
|
||||
|
||||
|
||||
#endif //GEX_F072_MSG_BULKWRITE_H
|
||||
#endif //GEX_CORE_MSG_BULKWRITE_H
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
// Routines for sending TinyFrame responses.
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_MSG_RESPONSES_H
|
||||
#define GEX_F072_MSG_RESPONSES_H
|
||||
#ifndef GEX_CORE_MSG_RESPONSES_H
|
||||
#define GEX_CORE_MSG_RESPONSES_H
|
||||
|
||||
#ifndef GEX_MESSAGES_H
|
||||
#error "Include messages.h instead!"
|
||||
@@ -109,4 +109,4 @@ void com_respond_u16(TF_ID frame_id, uint16_t d);
|
||||
*/
|
||||
void com_respond_u32(TF_ID frame_id, uint32_t d);
|
||||
|
||||
#endif //GEX_F072_MSG_RESPONSES_H
|
||||
#endif //GEX_CORE_MSG_RESPONSES_H
|
||||
|
||||
+551
@@ -0,0 +1,551 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/04/02.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#if SUPPORT_NRF
|
||||
|
||||
#include "nrf.h"
|
||||
|
||||
/**
|
||||
* Read a register
|
||||
*
|
||||
* @param reg - register number (max 83)
|
||||
* @return register value
|
||||
*/
|
||||
static uint8_t NRF_ReadRegister(uint8_t reg);
|
||||
|
||||
/**
|
||||
* Write a register
|
||||
*
|
||||
* @param reg - register number (max 83)
|
||||
* @param value - register value
|
||||
* @return status register value
|
||||
*/
|
||||
static uint8_t NRF_WriteRegister(uint8_t reg, uint8_t value);
|
||||
|
||||
// Internal use
|
||||
static uint8_t NRF_WriteBuffer(uint8_t reg, const uint8_t *pBuf, uint8_t bytes);
|
||||
|
||||
static uint8_t NRF_ReadBuffer(uint8_t reg, uint8_t *pBuf, uint8_t bytes);
|
||||
|
||||
/**
|
||||
* Set TX address for next frame
|
||||
*
|
||||
* @param SendTo - addr
|
||||
*/
|
||||
static void NRF_SetTxAddress(uint8_t ToAddr);
|
||||
|
||||
//----------------------------------------------------------
|
||||
|
||||
/** Tight asm loop */
|
||||
#define __asm_loop(cycles) \
|
||||
do { \
|
||||
register uint32_t _count asm ("r4") = cycles; \
|
||||
asm volatile( \
|
||||
".syntax unified\n" \
|
||||
".thumb\n" \
|
||||
"0:" \
|
||||
"subs %0, #1\n" \
|
||||
"bne 0b\n" \
|
||||
: "+r" (_count)); \
|
||||
} while(0)
|
||||
|
||||
// 12 for 72 MHz
|
||||
#define _delay_us(n) __asm_loop((n)*8)
|
||||
|
||||
static inline void CE(bool level) {
|
||||
__asm_loop(1);
|
||||
if (level) LL_GPIO_SetOutputPin(NRF_CE_GPIO_Port, NRF_CE_Pin);
|
||||
else LL_GPIO_ResetOutputPin(NRF_CE_GPIO_Port, NRF_CE_Pin);
|
||||
__asm_loop(1);
|
||||
}
|
||||
|
||||
static inline void NSS(bool level) {
|
||||
__asm_loop(1);
|
||||
if (level) LL_GPIO_SetOutputPin(NRF_NSS_GPIO_Port, NRF_NSS_Pin);
|
||||
else LL_GPIO_ResetOutputPin(NRF_NSS_GPIO_Port, NRF_NSS_Pin);
|
||||
__asm_loop(1);
|
||||
}
|
||||
|
||||
static uint8_t spi(uint8_t tx) {
|
||||
while (! LL_SPI_IsActiveFlag_TXE(NRF_SPI));
|
||||
LL_SPI_TransmitData8(NRF_SPI, tx);
|
||||
while (! LL_SPI_IsActiveFlag_RXNE(NRF_SPI));
|
||||
return LL_SPI_ReceiveData8(NRF_SPI);
|
||||
}
|
||||
|
||||
void NRF_Reset(bool enable_reset)
|
||||
{
|
||||
if (enable_reset) {
|
||||
// go HIGH - this closes the PMOS
|
||||
LL_GPIO_SetOutputPin(NRF_RST_GPIO_Port, NRF_RST_Pin);
|
||||
} else {
|
||||
// LOW - open PMOS, enable power
|
||||
LL_GPIO_ResetOutputPin(NRF_RST_GPIO_Port, NRF_RST_Pin);
|
||||
}
|
||||
}
|
||||
|
||||
//-------
|
||||
|
||||
/*
|
||||
* from: http://barefootelectronics.com/NRF24L01.aspx
|
||||
*/
|
||||
|
||||
#define CMD_READ_REG 0x00 // Read command to register
|
||||
#define CMD_WRITE_REG 0x20 // Write command to register
|
||||
#define CMD_RD_RX_PLD 0x61 // Read RX payload register address
|
||||
#define CMD_WR_TX_PLD 0xA0 // Write TX payload register address
|
||||
#define CMD_FLUSH_TX 0xE1 // Flush TX register command
|
||||
#define CMD_FLUSH_RX 0xE2 // Flush RX register command
|
||||
#define CMD_REUSE_TX_PL 0xE3 // Reuse TX payload register command
|
||||
#define CMD_RD_RX_PL_WIDTH 0x60 // Read RX Payload Width
|
||||
#define CMD_WR_ACK_PLD 0xA8 // Write ACK payload for Pipe (Add pipe number 0-6)
|
||||
#define CMD_WR_TX_PLD_NK 0xB0 // Write ACK payload for not ack
|
||||
#define CMD_NOP 0xFF // No Operation, might be used to read status register
|
||||
|
||||
// SPI(nRF24L01) registers(addresses)
|
||||
#define RG_CONFIG 0x00 // 'Config' register address
|
||||
#define RG_EN_AA 0x01 // 'Enable Auto Acknowledgment' register address
|
||||
#define RG_EN_RXADDR 0x02 // 'Enabled RX addresses' register address
|
||||
#define RG_SETUP_AW 0x03 // 'Setup address width' register address
|
||||
#define RG_SETUP_RETR 0x04 // 'Setup Auto. Retrans' register address
|
||||
#define RG_RF_CH 0x05 // 'RF channel' register address
|
||||
#define RG_RF_SETUP 0x06 // 'RF setup' register address
|
||||
#define RG_STATUS 0x07 // 'Status' register address
|
||||
#define RG_OBSERVE_TX 0x08 // 'Observe TX' register address
|
||||
#define RG_CD 0x09 // 'Carrier Detect' register address
|
||||
#define RG_RX_ADDR_P0 0x0A // 'RX address pipe0' register address
|
||||
#define RG_RX_ADDR_P1 0x0B // 'RX address pipe1' register address
|
||||
#define RG_RX_ADDR_P2 0x0C // 'RX address pipe2' register address
|
||||
#define RG_RX_ADDR_P3 0x0D // 'RX address pipe3' register address
|
||||
#define RG_RX_ADDR_P4 0x0E // 'RX address pipe4' register address
|
||||
#define RG_RX_ADDR_P5 0x0F // 'RX address pipe5' register address
|
||||
#define RG_TX_ADDR 0x10 // 'TX address' register address
|
||||
#define RG_RX_PW_P0 0x11 // 'RX payload width, pipe0' register address
|
||||
#define RG_RX_PW_P1 0x12 // 'RX payload width, pipe1' register address
|
||||
#define RG_RX_PW_P2 0x13 // 'RX payload width, pipe2' register address
|
||||
#define RG_RX_PW_P3 0x14 // 'RX payload width, pipe3' register address
|
||||
#define RG_RX_PW_P4 0x15 // 'RX payload width, pipe4' register address
|
||||
#define RG_RX_PW_P5 0x16 // 'RX payload width, pipe5' register address
|
||||
#define RG_FIFO_STATUS 0x17 // 'FIFO Status Register' register address
|
||||
#define RG_DYNPD 0x1C // 'Enable dynamic payload length
|
||||
#define RG_FEATURE 0x1D // 'Feature register
|
||||
|
||||
#define RD_STATUS_TX_FULL 0x01 // tx queue full
|
||||
#define RD_STATUS_RX_PNO 0x0E // pipe number of the received payload
|
||||
#define RD_STATUS_MAX_RT 0x10 // max retransmissions
|
||||
#define RD_STATUS_TX_DS 0x20 // data sent
|
||||
#define RD_STATUS_RX_DR 0x40 // data ready
|
||||
|
||||
#define RD_CONFIG_PRIM_RX 0x01 // is primary receiver
|
||||
#define RD_CONFIG_PWR_UP 0x02 // power up
|
||||
#define RD_CONFIG_CRCO 0x04 // 2-byte CRC
|
||||
#define RD_CONFIG_EN_CRC 0x08 // enable CRC
|
||||
#define RD_CONFIG_DISABLE_IRQ_MAX_RT 0x10
|
||||
#define RD_CONFIG_DISABLE_IRQ_TX_DS 0x20
|
||||
#define RD_CONFIG_DISABLE_IRQ_RX_DR 0x40
|
||||
|
||||
#define RD_FIFO_STATUS_RX_EMPTY 0x01
|
||||
#define RD_FIFO_STATUS_RX_FULL 0x02
|
||||
#define RD_FIFO_STATUS_TX_EMPTY 0x10
|
||||
#define RD_FIFO_STATUS_TX_FULL 0x20
|
||||
#define RD_FIFO_STATUS_TX_REUSE 0x40
|
||||
|
||||
// Config register bits (excluding the bottom two that are changed dynamically)
|
||||
// enable only Rx IRQ
|
||||
#define ModeBits (RD_CONFIG_DISABLE_IRQ_MAX_RT | \
|
||||
RD_CONFIG_DISABLE_IRQ_TX_DS | \
|
||||
RD_CONFIG_EN_CRC | \
|
||||
RD_CONFIG_CRCO)
|
||||
|
||||
static inline uint8_t CS(uint8_t hl)
|
||||
{
|
||||
if (hl == 1) {
|
||||
NSS(0);
|
||||
}
|
||||
else {
|
||||
NSS(1);
|
||||
}
|
||||
return hl;
|
||||
}
|
||||
|
||||
#define CHIPSELECT for (uint8_t cs = CS(1); cs==1; cs = CS(0))
|
||||
|
||||
// Base NRF address - byte 4 may be modified before writing to a pipe register,
|
||||
// the first 4 bytes are shared in the network. Master is always code 0.
|
||||
static uint8_t nrf_base_address[5];
|
||||
|
||||
static uint8_t nrf_pipe_addr[6];
|
||||
static bool nrf_pipe_enabled[6];
|
||||
|
||||
static uint8_t NRF_WriteRegister(uint8_t reg, uint8_t value)
|
||||
{
|
||||
uint8_t status = 0;
|
||||
CHIPSELECT {
|
||||
status = spi(CMD_WRITE_REG | reg);
|
||||
spi(value);
|
||||
}
|
||||
dbg_nrf("Wr[0x%02x] := 0x%02x", (int)reg, (int) value);
|
||||
|
||||
|
||||
uint8_t reg_val = 0;
|
||||
CHIPSELECT {
|
||||
spi(CMD_READ_REG | reg);
|
||||
reg_val = spi(0);
|
||||
}
|
||||
dbg_nrf(" verify 0x%02x", (int)reg_val);
|
||||
if (reg_val != value)
|
||||
dbg_nrf(" !!!");
|
||||
else
|
||||
dbg_nrf(" OK");
|
||||
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static uint8_t NRF_ReadRegister(uint8_t reg)
|
||||
{
|
||||
uint8_t reg_val = 0;
|
||||
CHIPSELECT {
|
||||
spi(CMD_READ_REG | reg);
|
||||
reg_val = spi(0);
|
||||
}
|
||||
dbg_nrf("Rd[0x%02x] = 0x%02x", (int)reg, (int) reg_val);
|
||||
return reg_val;
|
||||
}
|
||||
|
||||
static uint8_t NRF_ReadStatus(void)
|
||||
{
|
||||
uint8_t reg_val = 0;
|
||||
CHIPSELECT {
|
||||
reg_val = spi(CMD_NOP);
|
||||
}
|
||||
return reg_val;
|
||||
}
|
||||
|
||||
static uint8_t NRF_WriteBuffer(uint8_t reg, const uint8_t *pBuf, uint8_t bytes)
|
||||
{
|
||||
uint8_t status = 0, i = 0;
|
||||
CHIPSELECT {
|
||||
status = spi(CMD_WRITE_REG | reg);
|
||||
for (i = 0; i < bytes; i++) {
|
||||
spi(*pBuf++);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static uint8_t NRF_ReadBuffer(uint8_t reg, uint8_t *pBuf, uint8_t bytes)
|
||||
{
|
||||
uint8_t status = 0, i;
|
||||
CHIPSELECT {
|
||||
status = spi(CMD_READ_REG | reg);
|
||||
for (i = 0; i < bytes; i++) {
|
||||
pBuf[i] = spi(0);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void NRF_SetBaseAddress(const uint8_t *Bytes4)
|
||||
{
|
||||
memcpy(nrf_base_address, Bytes4, 4);
|
||||
nrf_base_address[4] = 0;
|
||||
|
||||
// write it to the two full-width address registers
|
||||
NRF_WriteBuffer(RG_RX_ADDR_P0, nrf_base_address, 5);
|
||||
nrf_base_address[4] = 1;// to be different
|
||||
NRF_WriteBuffer(RG_RX_ADDR_P1, nrf_base_address, 5);
|
||||
}
|
||||
|
||||
void NRF_SetRxAddress(uint8_t pipenum, uint8_t AddrByte)
|
||||
{
|
||||
if (pipenum > 5) {
|
||||
dbg_nrf("!! bad pipe %d", pipenum);
|
||||
return;
|
||||
}
|
||||
|
||||
nrf_base_address[4] = AddrByte;
|
||||
|
||||
nrf_pipe_addr[pipenum] = AddrByte;
|
||||
|
||||
dbg_nrf("Set Rx addr (pipe %d) = 0x%02x", (int)pipenum, AddrByte);
|
||||
if (pipenum == 0) {
|
||||
dbg_nrf("W ADDR_PA0: %02X-%02X-%02X-%02X-%02X", nrf_base_address[0], nrf_base_address[1], nrf_base_address[2], nrf_base_address[3], nrf_base_address[4]);
|
||||
NRF_WriteBuffer(RG_RX_ADDR_P0, nrf_base_address, 5);
|
||||
}
|
||||
else if (pipenum == 1) {
|
||||
dbg_nrf("W ADDR_PA1: %02X-%02X-%02X-%02X-%02X", nrf_base_address[0], nrf_base_address[1], nrf_base_address[2], nrf_base_address[3], nrf_base_address[4]);
|
||||
NRF_WriteBuffer(RG_RX_ADDR_P1, nrf_base_address, 5);
|
||||
}
|
||||
else {
|
||||
NRF_WriteRegister(RG_RX_ADDR_P2 + (pipenum - 2), AddrByte);
|
||||
}
|
||||
}
|
||||
|
||||
bool NRF_AddPipe(uint8_t AddrByte, uint8_t *PipeNum)
|
||||
{
|
||||
for (uint8_t i = 0; i < 6; i++) {
|
||||
if(!nrf_pipe_enabled[i]) {
|
||||
NRF_SetRxAddress(i, AddrByte);
|
||||
*PipeNum = i;
|
||||
NRF_EnablePipe(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t NRF_PipeNum2Addr(uint8_t pipe_num)
|
||||
{
|
||||
if (pipe_num > 5) return 0; // fail
|
||||
return nrf_pipe_addr[pipe_num];
|
||||
}
|
||||
|
||||
uint8_t NRF_Addr2PipeNum(uint8_t addr)
|
||||
{
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (nrf_pipe_addr[i] == addr) return (uint8_t) i;
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
void NRF_EnablePipe(uint8_t pipenum)
|
||||
{
|
||||
dbg_nrf("Enable pipe num %d", (int)pipenum);
|
||||
uint8_t enabled = NRF_ReadRegister(RG_EN_RXADDR);
|
||||
enabled |= 1 << pipenum;
|
||||
NRF_WriteRegister(RG_EN_RXADDR, enabled);
|
||||
|
||||
nrf_pipe_enabled[pipenum] = 1;
|
||||
}
|
||||
|
||||
void NRF_DisablePipe(uint8_t pipenum)
|
||||
{
|
||||
uint8_t enabled = NRF_ReadRegister(RG_EN_RXADDR);
|
||||
enabled &= ~(1 << pipenum);
|
||||
NRF_WriteRegister(RG_EN_RXADDR, enabled);
|
||||
|
||||
nrf_pipe_enabled[pipenum] = 0;
|
||||
}
|
||||
|
||||
static void NRF_SetTxAddress(uint8_t SendTo)
|
||||
{
|
||||
nrf_base_address[4] = SendTo;
|
||||
|
||||
dbg_nrf("W Tx_ADDR + Rx0: %02X-%02X-%02X-%02X-%02X",
|
||||
nrf_base_address[0], nrf_base_address[1], nrf_base_address[2], nrf_base_address[3], nrf_base_address[4]);
|
||||
|
||||
NRF_WriteBuffer(RG_TX_ADDR, nrf_base_address, 5);
|
||||
NRF_WriteBuffer(RG_RX_ADDR_P0, nrf_base_address, 5); // the ACK will come to pipe 0
|
||||
}
|
||||
|
||||
void NRF_PowerDown(void)
|
||||
{
|
||||
dbg_nrf("PDn");
|
||||
CE(0);
|
||||
NRF_WriteRegister(RG_CONFIG, ModeBits);
|
||||
}
|
||||
|
||||
void NRF_ModeTX(void)
|
||||
{
|
||||
dbg_nrf("Tx Mode");
|
||||
|
||||
CE(0);
|
||||
uint8_t m = NRF_ReadRegister(RG_CONFIG);
|
||||
NRF_WriteRegister(RG_CONFIG, ModeBits | RD_CONFIG_PWR_UP);
|
||||
if ((m & RD_CONFIG_PWR_UP) == 0) {
|
||||
// switching on
|
||||
LL_mDelay(5);
|
||||
}
|
||||
}
|
||||
|
||||
void NRF_ModeRX(void)
|
||||
{
|
||||
dbg_nrf("Rx Mode");
|
||||
NRF_WriteRegister(RG_CONFIG, ModeBits | RD_CONFIG_PWR_UP | RD_CONFIG_PRIM_RX);
|
||||
NRF_SetRxAddress(0, nrf_pipe_addr[0]); // set the P0 address - it was changed during Rx for ACK reception
|
||||
CE(1);
|
||||
|
||||
//if ((m&2)==0) LL_mDelay()(5); You don't need to wait. Just nothing will come for 5ms or more
|
||||
}
|
||||
|
||||
uint8_t NRF_IsModePowerDown(void)
|
||||
{
|
||||
uint8_t ret = 0;
|
||||
if ((NRF_ReadRegister(RG_CONFIG) & RD_CONFIG_PWR_UP) == 0) ret = 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t NRF_IsModeTX(void)
|
||||
{
|
||||
uint8_t m = NRF_ReadRegister(RG_CONFIG);
|
||||
if ((m & RD_CONFIG_PWR_UP) == 0) return 0; // OFF
|
||||
if ((m & RD_CONFIG_PRIM_RX) == 0) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t NRF_IsModeRx(void)
|
||||
{
|
||||
uint8_t m = NRF_ReadRegister(RG_CONFIG);
|
||||
if ((m & RD_CONFIG_PWR_UP) == 0) return 0; // OFF
|
||||
if ((m & RD_CONFIG_PRIM_RX) == 0) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void NRF_SetChannel(uint8_t Ch)
|
||||
{
|
||||
NRF_WriteRegister(RG_RF_CH, Ch);
|
||||
}
|
||||
|
||||
uint8_t NRF_ReceivePacket(uint8_t *Packet, uint8_t *PipeNum)
|
||||
{
|
||||
uint8_t pw = 0, status;
|
||||
// if (!NRF_IsRxPacket()) {
|
||||
// dbg("rx queue empty");
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
// const uint8_t orig_conf = NRF_ReadRegister(RG_CONFIG);
|
||||
// CE(0); // quit Rx mode - go idle
|
||||
|
||||
CHIPSELECT {
|
||||
status = spi(CMD_RD_RX_PL_WIDTH);
|
||||
pw = spi(0);
|
||||
}
|
||||
|
||||
if (pw == 0) {
|
||||
dbg("empty pld");
|
||||
}
|
||||
|
||||
if (pw > 32) {
|
||||
CHIPSELECT {
|
||||
spi(CMD_FLUSH_RX);
|
||||
}
|
||||
pw = 0;
|
||||
dbg("over 32");
|
||||
} else {
|
||||
// Read the reception pipe number
|
||||
*PipeNum = ((status & RD_STATUS_RX_PNO) >> 1);
|
||||
CHIPSELECT {
|
||||
spi(CMD_RD_RX_PLD);
|
||||
for (uint8_t i = 0; i < pw; i++) Packet[i] = spi(0);
|
||||
}
|
||||
}
|
||||
NRF_WriteRegister(RG_STATUS, RD_STATUS_RX_DR); // Clear the RX_DR interrupt
|
||||
|
||||
// if ((orig_conf & RD_CONFIG_PWR_UP) == 0) {
|
||||
// dbg_nrf("going back PwrDn");
|
||||
// NRF_PowerDown();
|
||||
// }
|
||||
// else if ((orig_conf & RD_CONFIG_PRIM_RX) == RD_CONFIG_PRIM_RX) {
|
||||
// dbg_nrf("going back PwrUp+Rx");
|
||||
// NRF_ModeRX();
|
||||
// }
|
||||
// CE(1); // back to rx
|
||||
return pw;
|
||||
}
|
||||
|
||||
bool NRF_IsRxPacket(void)
|
||||
{
|
||||
return 0 == (NRF_ReadRegister(RG_FIFO_STATUS) & RD_FIFO_STATUS_RX_EMPTY);
|
||||
// uint8_t ret = NRF_ReadRegister(RG_STATUS) & RD_STATUS_RX_DR;
|
||||
// return 0 != ret;
|
||||
}
|
||||
|
||||
bool NRF_SendPacket(uint8_t PipeNum, const uint8_t *Packet, uint8_t Length)
|
||||
{
|
||||
if (!nrf_pipe_enabled[PipeNum]) {
|
||||
dbg_nrf("!! Pipe %d not enabled", PipeNum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dbg_nrf("Will tx to addr %02x", nrf_pipe_addr[PipeNum]);
|
||||
|
||||
const uint8_t orig_conf = NRF_ReadRegister(RG_CONFIG);
|
||||
CE(0);
|
||||
NRF_ModeTX(); // Make sure in TX mode
|
||||
NRF_SetTxAddress(nrf_pipe_addr[PipeNum]); // this sets the Tx addr and also pipe 0 addr for ACK
|
||||
|
||||
CHIPSELECT {
|
||||
spi(CMD_FLUSH_TX);
|
||||
};
|
||||
|
||||
CHIPSELECT {
|
||||
spi(CMD_WR_TX_PLD);
|
||||
for (uint8_t i = 0; i < Length; i++) spi(Packet[i]);
|
||||
};
|
||||
|
||||
// CE pulse
|
||||
CE(1);
|
||||
_delay_us(20); // At least 10 us
|
||||
CE(0);
|
||||
|
||||
uint8_t st = 0;
|
||||
while ((st & (RD_STATUS_MAX_RT|RD_STATUS_TX_DS)) == 0) {
|
||||
st = NRF_ReadStatus(); // Packet acked or timed out
|
||||
}
|
||||
|
||||
dbg_nrf("Send status: 0x%02x - MAX_RT %d, SENT %d", (int)st,
|
||||
(st&RD_STATUS_MAX_RT) != 0, (st&RD_STATUS_TX_DS) != 0);
|
||||
|
||||
NRF_WriteRegister(RG_STATUS, st & (RD_STATUS_MAX_RT|RD_STATUS_TX_DS)); // Clear the bit
|
||||
|
||||
if ((orig_conf & RD_CONFIG_PWR_UP) == 0) {
|
||||
dbg_nrf("going back PwrDn");
|
||||
NRF_PowerDown();
|
||||
}
|
||||
else if ((orig_conf & RD_CONFIG_PRIM_RX) == RD_CONFIG_PRIM_RX) {
|
||||
dbg_nrf("going back PwrUp+Rx");
|
||||
NRF_ModeRX();
|
||||
}
|
||||
|
||||
return 0 != (st & RD_STATUS_TX_DS); // success
|
||||
}
|
||||
|
||||
bool NRF_Init(uint8_t pSpeed)
|
||||
{
|
||||
// Set the required output pins
|
||||
NSS(1);
|
||||
CE(0);
|
||||
|
||||
LL_mDelay(200);
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
nrf_pipe_addr[i] = 0;
|
||||
nrf_pipe_enabled[i] = 0;
|
||||
}
|
||||
|
||||
// this is a test if there's anything present
|
||||
uint8_t awreg = NRF_ReadRegister(RG_SETUP_AW);
|
||||
if (awreg == 0 || awreg > 3) return false;
|
||||
|
||||
// clear flags etc
|
||||
NRF_PowerDown();
|
||||
CHIPSELECT { spi(CMD_FLUSH_RX); }
|
||||
CHIPSELECT { spi(CMD_FLUSH_TX); }
|
||||
NRF_WriteRegister(RG_STATUS, 0x70);
|
||||
|
||||
NRF_WriteRegister(RG_CONFIG, ModeBits);
|
||||
NRF_WriteRegister(RG_SETUP_AW, 0b11); // 5 byte addresses
|
||||
|
||||
NRF_WriteRegister(RG_EN_RXADDR, 0x01); // disable all, enable pipe 0 - this is required for shockburst, despite not being specified in the DS
|
||||
|
||||
NRF_WriteRegister(RG_SETUP_RETR, 0x18); // 8 retries
|
||||
NRF_WriteRegister(RG_RF_CH, 2); // channel 2 NO HIGHER THAN 83 in USA!
|
||||
|
||||
NRF_WriteRegister(RG_RF_SETUP, pSpeed);
|
||||
|
||||
NRF_WriteRegister(RG_DYNPD, 0b111111); // Dynamic packet length
|
||||
NRF_WriteRegister(RG_FEATURE, 0b100); // Enable dynamic payload, and no payload in the ack.
|
||||
|
||||
// for (int i = 0; i < 6; i++) {
|
||||
// NRF_WriteRegister(RG_RX_PW_P0+i, 32); // Receive 32 byte packets - XXX this is probably not needed with dynamic length
|
||||
// }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/04/02.
|
||||
//
|
||||
|
||||
#ifndef GEX_NRF_NRF_H
|
||||
#define GEX_NRF_NRF_H
|
||||
|
||||
/*
|
||||
* nordic.h
|
||||
*
|
||||
* Created:12/16/2013 3:36:04 PM
|
||||
* Author: Tom
|
||||
*
|
||||
* NRF24L01+ Library II
|
||||
*
|
||||
*/
|
||||
|
||||
#include "platform.h"
|
||||
#if SUPPORT_NRF
|
||||
|
||||
#include "resources.h"
|
||||
#include "nrf_pins.h"
|
||||
|
||||
#define dbg_nrf(...) do{}while(0)
|
||||
//#define dbg_nrf dbg
|
||||
|
||||
// Initialize SPI and the Nordic
|
||||
|
||||
/**
|
||||
* Initialize the NRF module
|
||||
*
|
||||
* @param pSpeed
|
||||
* @return success (0 if device not detected)
|
||||
*/
|
||||
bool NRF_Init(uint8_t pSpeed);
|
||||
|
||||
#define NRF_SPEED_500k 0b00100110
|
||||
#define NRF_SPEED_2M 0b00001110
|
||||
#define NRF_SPEED_1M 0b00000110
|
||||
|
||||
/**
|
||||
* Set the reset pin (this is a PMOS controlling its power)
|
||||
*
|
||||
* @param enable_reset 1 to go into reset, 0 to enter operational state (will need some time to become ready)
|
||||
*/
|
||||
void NRF_Reset(bool enable_reset);
|
||||
|
||||
/**
|
||||
* Set reception address
|
||||
* @param pipenum - pipe to set
|
||||
* @param AddrByte - byte 0
|
||||
*/
|
||||
void NRF_SetRxAddress(uint8_t pipenum, uint8_t AddrByte);
|
||||
|
||||
/**
|
||||
* Set communication channel
|
||||
*/
|
||||
void NRF_SetChannel(uint8_t Ch) ; // 0 through 83 only!
|
||||
|
||||
/**
|
||||
* Power down the transceiver
|
||||
*/
|
||||
void NRF_PowerDown(void);
|
||||
|
||||
/**
|
||||
* Selecr RX mode
|
||||
*/
|
||||
void NRF_ModeRX(void);
|
||||
|
||||
/**
|
||||
* Select TX mode
|
||||
*/
|
||||
void NRF_ModeTX(void);
|
||||
|
||||
/**
|
||||
* @return NRF is power down
|
||||
*/
|
||||
uint8_t NRF_IsModePowerDown(void);
|
||||
|
||||
/**
|
||||
* @return NRF in TX mode
|
||||
*/
|
||||
uint8_t NRF_IsModeTX(void);
|
||||
|
||||
/**
|
||||
* @return NRF in RX mode
|
||||
*/
|
||||
uint8_t NRF_IsModeRx(void);
|
||||
|
||||
/**
|
||||
* Add a pipe to the next free slot
|
||||
*
|
||||
* @param AddrByte - address byte of the peer
|
||||
* @param[out] PipeNum - pipe number is written here
|
||||
* @return success
|
||||
*/
|
||||
bool NRF_AddPipe(uint8_t AddrByte, uint8_t *PipeNum);
|
||||
|
||||
/**
|
||||
* Convert pipe number to address byte
|
||||
*
|
||||
* @param pipe_num - pipe number
|
||||
* @return address byte
|
||||
*/
|
||||
uint8_t NRF_PipeNum2Addr(uint8_t pipe_num);
|
||||
|
||||
/**
|
||||
* Convert address to a pipe number
|
||||
*
|
||||
* @param addr
|
||||
* @return
|
||||
*/
|
||||
uint8_t NRF_Addr2PipeNum(uint8_t addr);
|
||||
|
||||
/**
|
||||
* Send a packet (takes care of mode switching etc)
|
||||
*
|
||||
* @param PipeNum - pipe number
|
||||
* @param Packet - packet bytes
|
||||
* @param Length - packet length
|
||||
* @return success (ACK'd)
|
||||
*/
|
||||
bool NRF_SendPacket(uint8_t PipeNum, const uint8_t *Packet, uint8_t Length);
|
||||
|
||||
/**
|
||||
* Receive a packet
|
||||
*
|
||||
* @param[out] Packet - the receiuved packet - buffer that is written
|
||||
* @param[out] PipeNum - pipe number that was received from
|
||||
* @return packet size, 0 on failure
|
||||
*/
|
||||
uint8_t NRF_ReceivePacket(uint8_t *Packet, uint8_t *PipeNum);
|
||||
|
||||
/**
|
||||
* Set base address
|
||||
* @param Bytes4
|
||||
*/
|
||||
void NRF_SetBaseAddress(const uint8_t *Bytes4);
|
||||
|
||||
/**
|
||||
* Check if there's a packet to be read
|
||||
*
|
||||
* @return 1 if any to read
|
||||
*/
|
||||
bool NRF_IsRxPacket(void);
|
||||
|
||||
/**
|
||||
* Enable a pipe
|
||||
*
|
||||
* @param pipenum - pipe number 0-5
|
||||
*/
|
||||
void NRF_EnablePipe(uint8_t pipenum);
|
||||
|
||||
/**
|
||||
* Disable a pipe
|
||||
*
|
||||
* @param pipenum - pipe number 0-5
|
||||
*/
|
||||
void NRF_DisablePipe(uint8_t pipenum);
|
||||
|
||||
#endif // SUPPORT_NRF
|
||||
|
||||
#endif //GEX_NRF_NRF_H
|
||||
@@ -4,8 +4,8 @@
|
||||
// Enum of all defined resources
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_RSC_ENUM_H
|
||||
#define GEX_F072_RSC_ENUM_H
|
||||
#ifndef GEX_CORE_RSC_ENUM_H
|
||||
#define GEX_CORE_RSC_ENUM_H
|
||||
|
||||
// X macro: Resource name,
|
||||
#define XX_RESOURCES \
|
||||
@@ -132,4 +132,4 @@ static inline void rscmap_free(ResourceMap *rscmap, Resource rsc)
|
||||
#define RSC_CLAIM(rscmap, rsc) rscmap_claim(&rscmap, (rsc))
|
||||
#define RSC_FREE(rscmap, rsc) rscmap_free(&rscmap, (rsc))
|
||||
|
||||
#endif //GEX_F072_RSC_ENUM_H
|
||||
#endif //GEX_CORE_RSC_ENUM_H
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Created by MightyPork on 2017/11/26.
|
||||
//
|
||||
|
||||
#include <comm/interfaces.h>
|
||||
#include "platform.h"
|
||||
#include "utils/hexdump.h"
|
||||
#include "settings.h"
|
||||
@@ -9,6 +10,7 @@
|
||||
#include "system_settings.h"
|
||||
#include "utils/str_utils.h"
|
||||
#include "unit_base.h"
|
||||
#include "platform/debug_uart.h"
|
||||
#include "utils/avrlibc.h"
|
||||
|
||||
// pre-declarations
|
||||
@@ -108,7 +110,7 @@ void settings_save(void)
|
||||
erase.Banks = FLASH_BANK_1; // TODO ?????
|
||||
#endif
|
||||
|
||||
#if defined(GEX_PLAT_F407_DISCOVERY)
|
||||
#if GEX_PLAT_F407
|
||||
// specialty for F4 with too much flash
|
||||
erase.NbSectors = 1;
|
||||
erase.Sector = SETTINGS_FLASH_SECTOR;
|
||||
@@ -120,6 +122,7 @@ void settings_save(void)
|
||||
erase.PageAddress = SETTINGS_FLASH_ADDR;
|
||||
erase.TypeErase = FLASH_TYPEERASE_PAGES;
|
||||
#endif
|
||||
|
||||
uint32_t pgerror = 0;
|
||||
hst = HAL_FLASHEx_Erase(&erase, &pgerror);
|
||||
assert_param(pgerror == 0xFFFFFFFFU);
|
||||
@@ -300,7 +303,6 @@ void settings_load_ini_begin(void)
|
||||
SystemSettings.loading_inifile = 0;
|
||||
}
|
||||
|
||||
|
||||
void settings_load_ini_key(const char *restrict section, const char *restrict key, const char *restrict value)
|
||||
{
|
||||
// dbg("[%s] %s = %s", section, key, value);
|
||||
@@ -312,6 +314,7 @@ void settings_load_ini_key(const char *restrict section, const char *restrict ke
|
||||
if (streq(section, "SYSTEM")) {
|
||||
if (SystemSettings.loading_inifile == 0) {
|
||||
SystemSettings.loading_inifile = 'S';
|
||||
systemsettings_begin_load();
|
||||
systemsettings_loadDefaults();
|
||||
}
|
||||
|
||||
@@ -356,4 +359,8 @@ void settings_load_ini_end(void)
|
||||
bool suc = ureg_finalize_all_init();
|
||||
if (!suc) dbg("Some units failed to init!!");
|
||||
}
|
||||
|
||||
if (SystemSettings.loading_inifile == 'S') {
|
||||
systemsettings_finalize_load();
|
||||
}
|
||||
}
|
||||
|
||||
+223
-6
@@ -7,6 +7,15 @@
|
||||
#include "utils/str_utils.h"
|
||||
#include "platform/lock_jumper.h"
|
||||
#include "cfg_utils.h"
|
||||
#include "resources.h"
|
||||
#include "unit_base.h"
|
||||
#include "platform/debug_uart.h"
|
||||
#include "comm/interfaces.h"
|
||||
|
||||
static void systemsettings_mco_teardown(void);
|
||||
static void systemsettings_mco_init(void);
|
||||
/** Init/deinit debug uart */
|
||||
static void systemsettings_debug_uart_init_deinit(void);
|
||||
|
||||
struct system_settings SystemSettings;
|
||||
|
||||
@@ -15,6 +24,24 @@ void systemsettings_loadDefaults(void)
|
||||
{
|
||||
SystemSettings.visible_vcom = true;
|
||||
SystemSettings.ini_comments = true;
|
||||
SystemSettings.enable_mco = false;
|
||||
SystemSettings.mco_prediv = 7;
|
||||
|
||||
SystemSettings.use_comm_uart = false; // TODO configure those based on compile flags for a particular platform
|
||||
SystemSettings.use_comm_lora = false;
|
||||
SystemSettings.use_comm_nordic = false;
|
||||
SystemSettings.comm_uart_baud = 115200; // TODO
|
||||
|
||||
// just a demo, user must change this
|
||||
SystemSettings.nrf_network[0] = 0x12;
|
||||
SystemSettings.nrf_network[2] = 0x09;
|
||||
SystemSettings.nrf_network[3] = 0x4c;
|
||||
SystemSettings.nrf_network[4] = 0x61;
|
||||
|
||||
SystemSettings.nrf_address = 1;
|
||||
SystemSettings.nrf_channel = 76;
|
||||
|
||||
SystemSettings.enable_debug_uart = true;
|
||||
}
|
||||
|
||||
/** Load defaults and init flags */
|
||||
@@ -31,18 +58,35 @@ void systemsettings_init(void)
|
||||
void systemsettings_save(PayloadBuilder *pb)
|
||||
{
|
||||
pb_char(pb, 'S');
|
||||
pb_u8(pb, 0); // settings format version
|
||||
pb_u8(pb, 3); // settings format version
|
||||
|
||||
{ // system settings
|
||||
pb_bool(pb, SystemSettings.visible_vcom);
|
||||
pb_bool(pb, SystemSettings.ini_comments);
|
||||
// 1
|
||||
pb_bool(pb, SystemSettings.enable_mco);
|
||||
pb_u8(pb, SystemSettings.mco_prediv);
|
||||
// 2
|
||||
pb_bool(pb, SystemSettings.use_comm_uart);
|
||||
pb_bool(pb, SystemSettings.use_comm_nordic);
|
||||
pb_bool(pb, SystemSettings.use_comm_lora);
|
||||
pb_u32(pb, SystemSettings.comm_uart_baud);
|
||||
pb_bool(pb, SystemSettings.enable_debug_uart);
|
||||
// 3
|
||||
pb_u8(pb, SystemSettings.nrf_channel);
|
||||
pb_u8(pb, SystemSettings.nrf_address);
|
||||
pb_buf(pb, SystemSettings.nrf_network, 4);
|
||||
} // end system settings
|
||||
}
|
||||
|
||||
|
||||
// from binary
|
||||
bool systemsettings_load(PayloadParser *pp)
|
||||
{
|
||||
if (pp_char(pp) != 'S') return false;
|
||||
|
||||
systemsettings_begin_load();
|
||||
|
||||
uint8_t version = pp_u8(pp);
|
||||
|
||||
{ // system settings
|
||||
@@ -50,13 +94,80 @@ bool systemsettings_load(PayloadParser *pp)
|
||||
SystemSettings.ini_comments = pp_bool(pp);
|
||||
|
||||
// conditional fields based on version
|
||||
(void) version;
|
||||
if (version >= 1) {
|
||||
SystemSettings.enable_mco = pp_bool(pp);
|
||||
SystemSettings.mco_prediv = pp_u8(pp);
|
||||
}
|
||||
if (version >= 2) {
|
||||
SystemSettings.use_comm_uart = pp_bool(pp);
|
||||
SystemSettings.use_comm_nordic = pp_bool(pp);
|
||||
SystemSettings.use_comm_lora = pp_bool(pp);
|
||||
SystemSettings.comm_uart_baud = pp_u32(pp);
|
||||
SystemSettings.enable_debug_uart = pp_bool(pp);
|
||||
}
|
||||
if (version >= 3) {
|
||||
SystemSettings.nrf_channel = pp_u8(pp);
|
||||
SystemSettings.nrf_address = pp_u8(pp);
|
||||
pp_buf(pp, SystemSettings.nrf_network, 4);
|
||||
}
|
||||
} // end system settings
|
||||
|
||||
systemsettings_finalize_load();
|
||||
|
||||
return pp->ok;
|
||||
}
|
||||
|
||||
|
||||
void systemsettings_mco_teardown(void)
|
||||
{
|
||||
if (SystemSettings.enable_mco) {
|
||||
rsc_free(&UNIT_SYSTEM, R_PA8);
|
||||
LL_RCC_ConfigMCO(LL_RCC_MCO1SOURCE_NOCLOCK, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void systemsettings_mco_init(void)
|
||||
{
|
||||
if (SystemSettings.enable_mco) {
|
||||
assert_param(rsc_claim(&UNIT_SYSTEM, R_PA8) == E_SUCCESS);
|
||||
|
||||
assert_param(E_SUCCESS == hw_configure_gpiorsc_af(R_PA8, LL_GPIO_AF_0));
|
||||
LL_RCC_ConfigMCO(LL_RCC_MCO1SOURCE_SYSCLK, SystemSettings.mco_prediv << RCC_CFGR_MCOPRE_Pos);
|
||||
} else {
|
||||
LL_RCC_ConfigMCO(LL_RCC_MCO1SOURCE_NOCLOCK, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void systemsettings_debug_uart_init_deinit(void)
|
||||
{
|
||||
#if USE_DEBUG_UART
|
||||
if (SystemSettings.enable_debug_uart) {
|
||||
DebugUart_Init();
|
||||
} else {
|
||||
DebugUart_Teardown();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin load of system settings, releasing resources etc
|
||||
*/
|
||||
void systemsettings_begin_load(void)
|
||||
{
|
||||
systemsettings_mco_teardown();
|
||||
com_release_resources_for_alt_transfers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Claim resources and set up system components based on the loaded settings
|
||||
*/
|
||||
void systemsettings_finalize_load(void)
|
||||
{
|
||||
systemsettings_mco_init();
|
||||
systemsettings_debug_uart_init_deinit();
|
||||
com_claim_resources_for_alt_transfers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write system settings to INI (without section)
|
||||
*/
|
||||
@@ -65,10 +176,53 @@ void systemsettings_build_ini(IniWriter *iw)
|
||||
iw_section(iw, "SYSTEM");
|
||||
|
||||
iw_comment(iw, "Data link accessible as virtual comport (Y, N)");
|
||||
iw_entry(iw, "expose_vcom", str_yn(SystemSettings.visible_vcom));
|
||||
iw_entry_s(iw, "expose-vcom", str_yn(SystemSettings.visible_vcom));
|
||||
|
||||
iw_comment(iw, "Show comments in INI files (Y, N)");
|
||||
iw_entry(iw, "ini_comments", str_yn(SystemSettings.ini_comments));
|
||||
iw_entry_s(iw, "ini-comments", str_yn(SystemSettings.ini_comments));
|
||||
|
||||
iw_comment(iw, "Enable debug UART-Tx on PA9 (Y, N)"); // TODO update if moved to a different pin
|
||||
iw_entry_s(iw, "debug-uart", str_yn(SystemSettings.enable_debug_uart));
|
||||
|
||||
iw_cmt_newline(iw);
|
||||
iw_comment(iw, "Output core clock on PA8 (Y, N)");
|
||||
iw_entry_s(iw, "mco-enable", str_yn(SystemSettings.enable_mco));
|
||||
iw_comment(iw, "Output clock prediv (1,2,...,128)");
|
||||
iw_entry_d(iw, "mco-prediv", (1<<SystemSettings.mco_prediv));
|
||||
|
||||
iw_cmt_newline(iw);
|
||||
iw_comment(iw, "--- Allowed fallback communication ports ---");
|
||||
|
||||
iw_cmt_newline(iw);
|
||||
iw_comment(iw, "UART Tx:PA2, Rx:PA3");
|
||||
iw_entry_s(iw, "com-uart", str_yn(SystemSettings.use_comm_uart));
|
||||
iw_entry_d(iw, "com-uart-baud", SystemSettings.comm_uart_baud);
|
||||
|
||||
#if SUPPORT_NRF
|
||||
iw_cmt_newline(iw);
|
||||
iw_comment(iw, "nRF24L01+ radio");
|
||||
iw_entry_s(iw, "com-nrf", str_yn(SystemSettings.use_comm_nordic));
|
||||
|
||||
iw_comment(iw, "Radio channel (0-125)");
|
||||
iw_entry_d(iw, "nrf-channel", SystemSettings.nrf_channel);
|
||||
|
||||
iw_comment(iw, "Network prefix (hex, 4 bytes)");
|
||||
iw_entry(iw, "nrf-network", "%02X:%02X:%02X:%02X",
|
||||
SystemSettings.nrf_network[0],
|
||||
SystemSettings.nrf_network[1],
|
||||
SystemSettings.nrf_network[2],
|
||||
SystemSettings.nrf_network[3]);
|
||||
|
||||
iw_comment(iw, "Node address (1-255)");
|
||||
iw_entry(iw, "nrf-address", "%d", (int)SystemSettings.nrf_address);
|
||||
#endif // SUPPORT_NRF
|
||||
|
||||
// those aren't implement yet, don't tease the user
|
||||
// TODO show pin-out, extra settings if applicable
|
||||
#if 0
|
||||
iw_comment(iw, "LoRa/GFSK sx127x");
|
||||
iw_entry_s(iw, "com-lora", str_yn(SystemSettings.use_comm_sx127x));
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,15 +231,78 @@ void systemsettings_build_ini(IniWriter *iw)
|
||||
bool systemsettings_load_ini(const char *restrict key, const char *restrict value)
|
||||
{
|
||||
bool suc = true;
|
||||
if (streq(key, "expose_vcom")) {
|
||||
if (streq(key, "expose-vcom")) {
|
||||
bool yn = cfg_bool_parse(value, &suc);
|
||||
if (suc) SystemSettings.visible_vcom = yn;
|
||||
}
|
||||
|
||||
if (streq(key, "ini_comments")) {
|
||||
if (streq(key, "ini-comments")) {
|
||||
bool yn = cfg_bool_parse(value, &suc);
|
||||
if (suc) SystemSettings.ini_comments = yn;
|
||||
}
|
||||
|
||||
if (streq(key, "mco-enable")) {
|
||||
bool yn = cfg_bool_parse(value, &suc);
|
||||
if (suc) SystemSettings.enable_mco = yn;
|
||||
}
|
||||
|
||||
if (streq(key, "mco-prediv")) {
|
||||
int val = cfg_u8_parse(value, &suc);
|
||||
if (suc) {
|
||||
switch (val) {
|
||||
case 1: SystemSettings.mco_prediv = 0; break;
|
||||
case 2: SystemSettings.mco_prediv = 1; break;
|
||||
case 4: SystemSettings.mco_prediv = 2; break;
|
||||
case 8: SystemSettings.mco_prediv = 3; break;
|
||||
case 16: SystemSettings.mco_prediv = 4; break;
|
||||
case 32: SystemSettings.mco_prediv = 5; break;
|
||||
case 64: SystemSettings.mco_prediv = 6; break;
|
||||
default:
|
||||
case 128: SystemSettings.mco_prediv = 7; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (streq(key, "debug-uart")) {
|
||||
bool yn = cfg_bool_parse(value, &suc);
|
||||
if (suc) SystemSettings.enable_debug_uart = yn;
|
||||
}
|
||||
|
||||
if (streq(key, "com-uart")) {
|
||||
bool yn = cfg_bool_parse(value, &suc);
|
||||
if (suc) SystemSettings.use_comm_uart = yn;
|
||||
}
|
||||
|
||||
if (streq(key, "com-uart-baud")) {
|
||||
uint32_t baud = cfg_u32_parse(value, &suc);
|
||||
if (suc) SystemSettings.comm_uart_baud = baud;
|
||||
}
|
||||
|
||||
#if SUPPORT_NRF
|
||||
if (streq(key, "com-nrf")) {
|
||||
bool yn = cfg_bool_parse(value, &suc);
|
||||
if (suc) SystemSettings.use_comm_nordic = yn;
|
||||
}
|
||||
|
||||
if (streq(key, "nrf-channel")) {
|
||||
SystemSettings.nrf_channel = cfg_u8_parse(value, &suc);
|
||||
}
|
||||
|
||||
if (streq(key, "nrf-address")) {
|
||||
SystemSettings.nrf_address = cfg_u8_parse(value, &suc);
|
||||
}
|
||||
|
||||
if (streq(key, "nrf-network")) {
|
||||
cfg_hex_parse(&SystemSettings.nrf_network[0], 4, value, &suc);
|
||||
}
|
||||
#endif // SUPPORT_NRF
|
||||
|
||||
#if 0
|
||||
if (streq(key, "com-lora")) {
|
||||
bool yn = cfg_bool_parse(value, &suc);
|
||||
if (suc) SystemSettings.use_comm_lora = yn;
|
||||
}
|
||||
#endif
|
||||
|
||||
return suc;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,18 @@
|
||||
struct system_settings {
|
||||
bool visible_vcom;
|
||||
bool ini_comments;
|
||||
bool enable_mco;
|
||||
uint8_t mco_prediv;
|
||||
bool enable_debug_uart;
|
||||
|
||||
// enable alternate communication ports if USB doesn't enumerate (e.g. running from battery / solar cell remotely)
|
||||
bool use_comm_uart;
|
||||
uint32_t comm_uart_baud; // baud rate for the uart transport
|
||||
bool use_comm_lora; // SX1276/8
|
||||
bool use_comm_nordic; // nRF24L01+
|
||||
uint8_t nrf_channel;
|
||||
uint8_t nrf_network[4];
|
||||
uint8_t nrf_address;
|
||||
|
||||
// Support flags put here for scoping, but not atcually part of the persistent settings
|
||||
volatile bool editable; //!< True if we booted with the LOCK jumper removed
|
||||
@@ -60,4 +72,9 @@ void systemsettings_build_ini(IniWriter *iw);
|
||||
*/
|
||||
bool systemsettings_load_ini(const char *restrict key, const char *restrict value);
|
||||
|
||||
/** Release system resources before system settings init */
|
||||
void systemsettings_begin_load(void);
|
||||
/** Claim system resources and apply system settings */
|
||||
void systemsettings_finalize_load(void);
|
||||
|
||||
#endif //GEX_SYSTEM_SETTINGS_H
|
||||
|
||||
@@ -276,7 +276,7 @@ bool ureg_instantiate_by_ini(const char *restrict driver_name, const char *restr
|
||||
char *name = NULL;
|
||||
if (delim != NULL) {
|
||||
// not last
|
||||
name = strndup_ck(p, delim - p);
|
||||
name = strndup_ck(p, delim - p + 1);
|
||||
p = delim + 1;
|
||||
} else {
|
||||
// last name
|
||||
@@ -334,6 +334,7 @@ bool ureg_finalize_all_init(void)
|
||||
uint8_t callsign = 1;
|
||||
while (li != NULL) {
|
||||
Unit *const pUnit = &li->unit;
|
||||
dbg("+ %s \"%s\"", pUnit->driver->name, pUnit->name);
|
||||
|
||||
if (pUnit->status == E_SUCCESS) {
|
||||
dbg("! Unit seems already loaded, skipping");
|
||||
|
||||
@@ -1,61 +1,28 @@
|
||||
GEX_SRC_DIR = \
|
||||
User \
|
||||
User/utils \
|
||||
User/comm \
|
||||
User/framework \
|
||||
User/platform \
|
||||
User/units \
|
||||
User/units/neopixel \
|
||||
User/units/test \
|
||||
User/units/digital_out \
|
||||
User/units/digital_in \
|
||||
User/units/usart \
|
||||
User/units/1wire \
|
||||
User/units/i2c \
|
||||
User/units/spi \
|
||||
User/units/adc \
|
||||
User/units/sipo \
|
||||
User/units/fcap \
|
||||
User/units/touch \
|
||||
User/units/simple_pwm \
|
||||
User/units/dac \
|
||||
User/TinyFrame \
|
||||
User/CWPack \
|
||||
User/tasks
|
||||
#####################################################
|
||||
# Platform-independent, not directly related to GEX
|
||||
#####################################################
|
||||
|
||||
GEX_SOURCES = \
|
||||
User/USB/usb_device.c \
|
||||
User/USB/usbd_cdc_if.c \
|
||||
User/USB/usbd_conf.c \
|
||||
User/USB/usbd_desc.c \
|
||||
User/USB/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c \
|
||||
User/USB/STM32_USB_Device_Library/Class/MSC_CDC/usbd_msc_cdc.c \
|
||||
User/USB/STM32_USB_Device_Library/Core/Src/usbd_core.c \
|
||||
User/USB/STM32_USB_Device_Library/Core/Src/usbd_ctlreq.c \
|
||||
User/USB/STM32_USB_Device_Library/Core/Src/usbd_ioreq.c
|
||||
# C
|
||||
COMMON_C_DIRS = \
|
||||
Drivers \
|
||||
Drivers/CMSIS \
|
||||
Middlewares \
|
||||
Middlewares/FreeRTOS
|
||||
|
||||
GEX_INCLUDES = \
|
||||
-IUser \
|
||||
-IUser/USB \
|
||||
-IUser/USB/MSC_CDC \
|
||||
-IUser/TinyFrame \
|
||||
-IUser/vfs \
|
||||
-IUser/utils \
|
||||
-IUser/units \
|
||||
-IUser/framework \
|
||||
-IUser/platform \
|
||||
-IUser/tasks \
|
||||
-IUser/USB/STM32_USB_Device_Library/Core/Inc \
|
||||
-IUser/USB/STM32_USB_Device_Library/Class/AUDIO/Inc \
|
||||
-IUser/USB/STM32_USB_Device_Library/Class/CDC/Inc \
|
||||
-IUser/USB/STM32_USB_Device_Library/Class/CustomHID/Inc \
|
||||
-IUser/USB/STM32_USB_Device_Library/Class/DFU/Inc \
|
||||
-IUser/USB/STM32_USB_Device_Library/Class/HID/Inc \
|
||||
-IUser/USB/STM32_USB_Device_Library/Class/MSC/Inc \
|
||||
-IUser/USB/STM32_USB_Device_Library/Class/MSC_CDC
|
||||
COMMON_C_FILES = \
|
||||
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.c \
|
||||
Middlewares/Third_Party/FreeRTOS/Source/croutine.c \
|
||||
Middlewares/Third_Party/FreeRTOS/Source/event_groups.c \
|
||||
Middlewares/Third_Party/FreeRTOS/Source/list.c \
|
||||
Middlewares/Third_Party/FreeRTOS/Source/queue.c \
|
||||
Middlewares/Third_Party/FreeRTOS/Source/tasks.c \
|
||||
Middlewares/Third_Party/FreeRTOS/Source/timers.c \
|
||||
Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.c \
|
||||
Src/main.c
|
||||
|
||||
GEX_CFLAGS = \
|
||||
-D__weak="__attribute__((weak))" -D__packed="__attribute__((__packed__))" \
|
||||
COMMON_C_FLAGS = \
|
||||
-DUSE_HAL_DRIVER \
|
||||
-DUSE_FULL_LL_DRIVER \
|
||||
-std=gnu99 -Wfatal-errors \
|
||||
-Wall -Wextra -Wshadow \
|
||||
-Wwrite-strings -Wold-style-definition -Winline -Wstrict-prototypes -Wreturn-type \
|
||||
@@ -67,16 +34,88 @@ GEX_CFLAGS = \
|
||||
-fno-exceptions -finline-small-functions -findirect-inlining -Wno-strict-aliasing -Wno-float-equal \
|
||||
-Wno-discarded-qualifiers -fstack-usage
|
||||
|
||||
GEX_CDEFS_BASE = \
|
||||
ifeq ($(DEBUG), 1)
|
||||
COMMON_C_FLAGS += -ggdb -g -gdwarf-2
|
||||
endif
|
||||
|
||||
COMMON_C_INCLUDES = \
|
||||
-IInc \
|
||||
-IMiddlewares/Third_Party/FreeRTOS/Source/include \
|
||||
-IMiddlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS \
|
||||
-IDrivers/CMSIS/Include
|
||||
|
||||
# ASM
|
||||
COMMON_AS_DIRS = \
|
||||
|
||||
COMMON_AS_FILES = \
|
||||
|
||||
COMMON_AS_FLAGS = \
|
||||
-Wall -fdata-sections -ffunction-sections
|
||||
|
||||
COMMON_AS_INCLUDES = \
|
||||
-IInc
|
||||
|
||||
#####################################################
|
||||
# Platform-independent, GEX specific
|
||||
#####################################################
|
||||
|
||||
GEX_UNIT_DIRS := $(foreach x,$(GEX_UNITS), GexUnits/$(x))
|
||||
GEX_UNIT_DEFS := $(foreach x,$(GEX_UNITS), -DENABLE_UNIT_$(shell echo $(x) | tr a-z A-Z)=1) \
|
||||
-DUNITS_REGISTER_CMD="$(foreach x,$(GEX_UNITS),ureg_add_type(&UNIT_$(shell echo $(x) | tr a-z A-Z));)"
|
||||
|
||||
GEX_C_DIRS = \
|
||||
GexCore \
|
||||
GexCore/utils \
|
||||
GexCore/comm \
|
||||
GexCore/framework \
|
||||
GexCore/platform \
|
||||
GexCore/units \
|
||||
GexCore/TinyFrame \
|
||||
GexCore/tasks \
|
||||
$(GEX_UNIT_DIRS)
|
||||
|
||||
GEX_C_FILES = \
|
||||
GexCore/USB/usb_device.c \
|
||||
GexCore/USB/usbd_cdc_if.c \
|
||||
GexCore/USB/usbd_conf.c \
|
||||
GexCore/USB/usbd_desc.c \
|
||||
GexCore/USB/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c \
|
||||
GexCore/USB/STM32_USB_Device_Library/Class/MSC_CDC/usbd_msc_cdc.c \
|
||||
GexCore/USB/STM32_USB_Device_Library/Core/Src/usbd_core.c \
|
||||
GexCore/USB/STM32_USB_Device_Library/Core/Src/usbd_ctlreq.c \
|
||||
GexCore/USB/STM32_USB_Device_Library/Core/Src/usbd_ioreq.c \
|
||||
Src/platform_resources.c
|
||||
|
||||
GEX_C_INCLUDES = \
|
||||
-IGexUnits \
|
||||
-IGexCore \
|
||||
-IGexCore/USB \
|
||||
-IGexCore/USB/MSC_CDC \
|
||||
-IGexCore/TinyFrame \
|
||||
-IGexCore/vfs \
|
||||
-IGexCore/utils \
|
||||
-IGexCore/units \
|
||||
-IGexCore/framework \
|
||||
-IGexCore/platform \
|
||||
-IGexCore/tasks \
|
||||
-IGexCore/USB/STM32_USB_Device_Library/Core/Inc \
|
||||
-IGexCore/USB/STM32_USB_Device_Library/Class/AUDIO/Inc \
|
||||
-IGexCore/USB/STM32_USB_Device_Library/Class/CDC/Inc \
|
||||
-IGexCore/USB/STM32_USB_Device_Library/Class/CustomHID/Inc \
|
||||
-IGexCore/USB/STM32_USB_Device_Library/Class/DFU/Inc \
|
||||
-IGexCore/USB/STM32_USB_Device_Library/Class/HID/Inc \
|
||||
-IGexCore/USB/STM32_USB_Device_Library/Class/MSC/Inc \
|
||||
-IGexCore/USB/STM32_USB_Device_Library/Class/MSC_CDC
|
||||
|
||||
GEX_C_FLAGS := \
|
||||
-DGEX_PLAT_$(GEX_PLAT) \
|
||||
-D__weak="__attribute__((weak))" \
|
||||
-D__packed="__attribute__((__packed__))" \
|
||||
-DUSE_FULL_LL_DRIVER \
|
||||
|
||||
|
||||
$(GEX_UNIT_DEFS)
|
||||
|
||||
# Option to disable debug
|
||||
ifeq '$(DISABLE_DEBUG)' '1'
|
||||
|
||||
GEX_CDEFS = $(GEX_CDEFS_BASE) \
|
||||
GEX_C_FLAGS += \
|
||||
-DUSE_FULL_ASSERT=0 \
|
||||
-DASSERT_FILENAMES=0 \
|
||||
-DDEBUG_VFS=0 \
|
||||
@@ -86,45 +125,177 @@ GEX_CDEFS = $(GEX_CDEFS_BASE) \
|
||||
-DUSE_DEBUG_UART=0 \
|
||||
-DDEBUG_MALLOC=0 \
|
||||
-DDEBUG_RSC=0
|
||||
|
||||
else
|
||||
|
||||
GEX_CDEFS = $(GEX_CDEFS_BASE) \
|
||||
GEX_C_FLAGS += \
|
||||
-DUSE_FULL_ASSERT=1 \
|
||||
-DASSERT_FILENAMES=1 \
|
||||
-DDEBUG_VFS=0 \
|
||||
-DDEBUG_FLASH_WRITE=0 \
|
||||
-DVERBOSE_HARDFAULT=1 \
|
||||
-DUSE_STACK_MONITOR=1 \
|
||||
-DUSE_STACK_MONITOR=0 \
|
||||
-DUSE_DEBUG_UART=1 \
|
||||
-DDEBUG_MALLOC=0 \
|
||||
-DDEBUG_RSC=0
|
||||
|
||||
endif
|
||||
|
||||
|
||||
# Option to disable mass storage
|
||||
ifeq '$(DISABLE_MSC)' '1'
|
||||
|
||||
GEX_CDEFS += -DDISABLE_MSC
|
||||
|
||||
GEX_C_FLAGS += -DDISABLE_MSC
|
||||
else
|
||||
|
||||
GEX_SOURCES += \
|
||||
User/USB/usbd_storage_if.c \
|
||||
User/USB/STM32_USB_Device_Library/Class/MSC/Src/usbd_msc.c \
|
||||
User/USB/STM32_USB_Device_Library/Class/MSC/Src/usbd_msc_bot.c \
|
||||
User/USB/STM32_USB_Device_Library/Class/MSC/Src/usbd_msc_data.c \
|
||||
User/USB/STM32_USB_Device_Library/Class/MSC/Src/usbd_msc_scsi.c
|
||||
|
||||
GEX_SRC_DIR += \
|
||||
User/vfs
|
||||
|
||||
GEX_C_FILES += \
|
||||
GexCore/USB/usbd_storage_if.c \
|
||||
GexCore/USB/STM32_USB_Device_Library/Class/MSC/Src/usbd_msc.c \
|
||||
GexCore/USB/STM32_USB_Device_Library/Class/MSC/Src/usbd_msc_bot.c \
|
||||
GexCore/USB/STM32_USB_Device_Library/Class/MSC/Src/usbd_msc_data.c \
|
||||
GexCore/USB/STM32_USB_Device_Library/Class/MSC/Src/usbd_msc_scsi.c
|
||||
GEX_C_DIRS += \
|
||||
GexCore/vfs
|
||||
endif
|
||||
|
||||
ifeq '$(DISABLE_TEST_UNIT)' '1'
|
||||
GEX_CDEFS += -DDISABLE_TEST_UNIT=1
|
||||
endif
|
||||
|
||||
# Option to enable CDC loopback (testing USB)
|
||||
ifeq '$(CDC_LOOPBACK_TEST)' '1'
|
||||
GEX_CDEFS += -DCDC_LOOPBACK_TEST=1
|
||||
GEX_C_FLAGS += -DCDC_LOOPBACK_TEST=1
|
||||
endif
|
||||
|
||||
|
||||
#####################################################
|
||||
# Common build infrastructure
|
||||
#####################################################
|
||||
|
||||
# Build path
|
||||
BUILD_DIR = build
|
||||
# target
|
||||
TARGET = firmware
|
||||
# Specs (determines which stdlib version is included)
|
||||
SPECS = nano.specs
|
||||
|
||||
C_SOURCES := \
|
||||
$(COMMON_C_FILES) $(PLAT_C_FILES) $(GEX_C_FILES) \
|
||||
$(foreach x,$(COMMON_C_DIRS),$(wildcard $(x)/*.c)) \
|
||||
$(foreach x,$(PLAT_C_DIRS),$(wildcard $(x)/*.c)) \
|
||||
$(foreach x,$(GEX_C_DIRS),$(wildcard $(x)/*.c)) \
|
||||
|
||||
AS_SOURCES := \
|
||||
$(COMMON_AS_FILES) $(PLAT_AS_FILES) $(GEX_AS_FILES)
|
||||
$(foreach x,$(COMMON_AS_DIRS),$(wildcard $(x)/*.s)) \
|
||||
$(foreach x,$(PLAT_AS_DIRS),$(wildcard $(x)/*.s)) \
|
||||
$(foreach x,$(GEX_AS_DIRS),$(wildcard $(x)/*.s))
|
||||
|
||||
# mcu
|
||||
MCU := $(PLAT_CPU) -mthumb $(PLAT_FPU) $(PLAT_FLOAT-ABI)
|
||||
|
||||
#######################################
|
||||
# binaries
|
||||
#######################################
|
||||
BINPATH = /usr/bin
|
||||
PREFIX = arm-none-eabi-
|
||||
CC := $(BINPATH)/$(PREFIX)gcc
|
||||
AS := $(BINPATH)/$(PREFIX)gcc -x assembler-with-cpp
|
||||
CP := $(BINPATH)/$(PREFIX)objcopy
|
||||
AR := $(BINPATH)/$(PREFIX)ar
|
||||
SZ := $(BINPATH)/$(PREFIX)size
|
||||
HEX := $(CP) -O ihex
|
||||
BIN := $(CP) -O binary -S
|
||||
|
||||
|
||||
LIBS = -lc -lm -lnosys
|
||||
LDFLAGS := \
|
||||
$(MCU) -specs=$(SPECS) -T$(PLAT_LDSCRIPT) $(LIBS) \
|
||||
-Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref \
|
||||
-Wl,--gc-sections
|
||||
|
||||
# optimization (possible to set in build.mk)
|
||||
ifeq ($(OPT),)
|
||||
OPT = -Os
|
||||
endif
|
||||
|
||||
C_FLAGS := \
|
||||
$(MCU) $(OPT) \
|
||||
$(GEX_C_INCLUDES) $(PLAT_C_INCLUDES) $(COMMON_C_INCLUDES) \
|
||||
$(GEX_C_FLAGS) $(PLAT_C_FLAGS) $(COMMON_C_FLAGS)
|
||||
|
||||
AS_FLAGS := \
|
||||
$(MCU) $(OPT) \
|
||||
$(GEX_AS_INCLUDES) $(PLAT_AS_INCLUDES) $(COMMON_AS_INCLUDES) \
|
||||
$(GEX_AS_FLAGS) $(PLAT_AS_FLAGS) $(COMMON_AS_FLAGS)
|
||||
|
||||
|
||||
# Generate dependency information
|
||||
DEPENDENCY_TRACKER = -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)"
|
||||
|
||||
|
||||
#######################################
|
||||
# Targets
|
||||
#######################################
|
||||
|
||||
# # list of objects
|
||||
# OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o)))
|
||||
# vpath %.c $(sort $(dir $(C_SOURCES)))
|
||||
# # list of ASM program objects
|
||||
# OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(AS_SOURCES:.s=.o)))
|
||||
# vpath %.s $(sort $(dir $(AS_SOURCES)))
|
||||
|
||||
# list of objects
|
||||
OBJECTS := $(addprefix $(BUILD_DIR)/,$(C_SOURCES:.c=.o)) \
|
||||
$(addprefix $(BUILD_DIR)/,$(AS_SOURCES:.s=.o))
|
||||
|
||||
vpath %.c $(sort $(dir $(C_SOURCES)))
|
||||
vpath %.s $(sort $(dir $(AS_SOURCES)))
|
||||
|
||||
MAKEFILES = Makefile GexCore/gex.mk build.mk
|
||||
|
||||
# default action: build all
|
||||
all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin
|
||||
|
||||
$(BUILD_DIR)/%.o: %.c $(MAKEFILES) | $(BUILD_DIR)
|
||||
@echo -e "\x1b[32mCC\x1b[m $<\n \x1b[90m-> $@\x1b[m"
|
||||
@mkdir -p `dirname "$(BUILD_DIR)/$(<)"`
|
||||
@$(CC) -c $(C_FLAGS) $(DEPENDENCY_TRACKER) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(<:.c=.lst) $< -o $@
|
||||
|
||||
$(BUILD_DIR)/%.o: %.s $(MAKEFILES) | $(BUILD_DIR)
|
||||
@$(AS) -c $(C_FLAGS) $(DEPENDENCY_TRACKER) $< -o $@
|
||||
|
||||
$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) $(MAKEFILES)
|
||||
@printf "LD $< -> $@\n"
|
||||
$(CC) $(OBJECTS) $(LDFLAGS) -o $@
|
||||
@$(SZ) $@
|
||||
|
||||
$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf $(MAKEFILES) | $(BUILD_DIR)
|
||||
@printf "OBJDUMP $< -> $(BUILD_DIR)/$(TARGET).hex \n"
|
||||
@$(HEX) $< $@
|
||||
|
||||
$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf $(MAKEFILES) | $(BUILD_DIR)
|
||||
@printf "OBJDUMP $< -> $(BUILD_DIR)/$(TARGET).bin \n"
|
||||
@$(BIN) $< $@
|
||||
|
||||
flash: $(BUILD_DIR)/$(TARGET).bin $(MAKEFILES)
|
||||
@printf "FLASH $<\n"
|
||||
@st-flash write $< 0x8000000
|
||||
|
||||
$(BUILD_DIR)/$(TARGET).dfu: $(BUILD_DIR)/$(TARGET).hex $(MAKEFILES)
|
||||
@printf "DFU GEN $<\n"
|
||||
dfu-convert -i $< $@
|
||||
|
||||
dfu: $(BUILD_DIR)/$(TARGET).dfu $(MAKEFILES)
|
||||
@printf "DFU UPLOAD $<\n"
|
||||
dfu-util -a 0 -D $<
|
||||
|
||||
dis: $(BUILD_DIR)/$(TARGET).elf $(MAKEFILES)
|
||||
@printf "DIS $<\n"
|
||||
@arm-none-eabi-objdump -Sslrtd build/$(TARGET).elf > disassembly.lst
|
||||
|
||||
$(BUILD_DIR):
|
||||
@mkdir -p $@
|
||||
|
||||
#######################################
|
||||
# clean up
|
||||
#######################################
|
||||
clean:
|
||||
-rm -fR .dep $(BUILD_DIR)
|
||||
|
||||
#######################################
|
||||
# dependencies
|
||||
#######################################
|
||||
-include $(shell mkdir .dep 2>/dev/null) $(wildcard .dep/*)
|
||||
|
||||
+11
@@ -10,12 +10,18 @@
|
||||
#include "platform/debug_uart.h"
|
||||
#include "gex_hooks.h"
|
||||
#include "unit_registry.h"
|
||||
#include "comm/interfaces.h"
|
||||
#include "system_settings.h"
|
||||
|
||||
/**
|
||||
* This is a systick callback for GEX application logic
|
||||
*/
|
||||
void GEX_MsTick(void)
|
||||
{
|
||||
if (gActiveComport == COMPORT_USART) {
|
||||
com_iface_flush_buffer();
|
||||
}
|
||||
|
||||
TF_Tick(comm);
|
||||
Indicator_Tick();
|
||||
ureg_tick_units();
|
||||
@@ -26,6 +32,11 @@ void GEX_MsTick(void)
|
||||
*/
|
||||
void GEX_PreInit(void)
|
||||
{
|
||||
// this is a hack to make logging of the initial messages work
|
||||
// it's problematic because we shouldn't even enable the debug uart if it's disabled in the system settings
|
||||
// TODO move system settings load earlier and check the uart flag in advance
|
||||
SystemSettings.enable_debug_uart = true;
|
||||
|
||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOC_CLK_ENABLE();
|
||||
|
||||
@@ -297,3 +297,46 @@ uint32_t cfg_enum4_parse(const char *value,
|
||||
*suc = false;
|
||||
return na;
|
||||
}
|
||||
|
||||
void cfg_hex_parse(uint8_t *dest, uint32_t count, const char *value, bool *suc)
|
||||
{
|
||||
// discard possible leading 0x
|
||||
if (value[0] == '0' && value[1] == 'x') {
|
||||
value += 2;
|
||||
}
|
||||
|
||||
uint8_t bytebuf = 0;
|
||||
for (uint32_t digit = 0; digit < count * 2;) {
|
||||
char v = *value;
|
||||
if (v != 0) value++;
|
||||
|
||||
uint8_t nibble = 0;
|
||||
if (v == ' ' || v == '.' || v == ',' || v == '-' || v == ':') {
|
||||
continue; // junk
|
||||
}
|
||||
else if (v >= '0' && v <= '9') {
|
||||
nibble = (uint8_t) (v - '0');
|
||||
}
|
||||
else if (v >= 'a' && v <= 'f') {
|
||||
nibble = (uint8_t) (10 + (v - 'a'));
|
||||
}
|
||||
else if (v >= 'A' && v <= 'F') {
|
||||
nibble = (uint8_t) (10 + (v - 'A'));
|
||||
}
|
||||
else if (v == 0) {
|
||||
nibble = 0; // pad with zeros
|
||||
}
|
||||
else {
|
||||
*suc = false;
|
||||
return;
|
||||
}
|
||||
|
||||
digit++;
|
||||
bytebuf <<= 4;
|
||||
bytebuf |= nibble;
|
||||
if ((digit % 2 == 0) && digit > 0) { // whole byte
|
||||
*dest++ = bytebuf;
|
||||
bytebuf = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+15
-3
@@ -2,8 +2,8 @@
|
||||
// Created by MightyPork on 2018/02/23.
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_CFG_UTILS_H
|
||||
#define GEX_F072_CFG_UTILS_H
|
||||
#ifndef GEX_CORE_CFG_UTILS_H
|
||||
#define GEX_CORE_CFG_UTILS_H
|
||||
|
||||
#include "platform.h"
|
||||
#include "rsc_enum.h"
|
||||
@@ -116,6 +116,18 @@ uint32_t cfg_enum4_parse(const char *tpl,
|
||||
const char *d, uint32_t nd,
|
||||
bool *suc);
|
||||
|
||||
/**
|
||||
* Parse a hexa string to a byte array.
|
||||
* Skips 0x prefix, '.', '-', ':', ' '.
|
||||
*
|
||||
* @param[out] dest - storage array
|
||||
* @param[in] count - expected number of bytes
|
||||
* @param[in] value - parsed string
|
||||
* @param[out] suc - success flag
|
||||
*/
|
||||
void cfg_hex_parse(uint8_t *dest, uint32_t count,
|
||||
const char *value, bool *suc);
|
||||
|
||||
/** Convert bool to a Y or N constant string */
|
||||
#define str_yn(cond) ((cond) ? ("Y") : ("N"))
|
||||
|
||||
@@ -150,4 +162,4 @@ static inline int32_t cfg_i32_parse(const char *value, bool *suc)
|
||||
return (int32_t) avr_atoi(value);
|
||||
}
|
||||
|
||||
#endif //GEX_F072_CFG_UTILS_H
|
||||
#endif //GEX_CORE_CFG_UTILS_H
|
||||
|
||||
+36
-43
@@ -5,65 +5,31 @@
|
||||
#include "platform.h"
|
||||
#include "framework/resources.h"
|
||||
#include "debug_uart.h"
|
||||
#include "plat_compat.h"
|
||||
#include "hw_utils.h"
|
||||
#include "framework/system_settings.h"
|
||||
|
||||
#if USE_DEBUG_UART
|
||||
|
||||
#define DEBUG_USART_BAUD 115200
|
||||
|
||||
#if GEX_PLAT_F072_DISCOVERY || GEX_PLAT_F072_HUB
|
||||
|
||||
#define DEBUG_USART USART1
|
||||
#define DEBUG_USART_RSC R_USART1
|
||||
#define DEBUG_USART_PORT 'A'
|
||||
#define DEBUG_USART_PIN 9
|
||||
#define DEBUG_USART_AF 1
|
||||
#define DEBUG_USART_PCLK PLAT_APB1_HZ
|
||||
|
||||
#elif GEX_PLAT_F103_BLUEPILL
|
||||
|
||||
#define DEBUG_USART USART2
|
||||
#define DEBUG_USART_RSC R_USART2
|
||||
#define DEBUG_USART_PORT 'A'
|
||||
#define DEBUG_USART_PIN 2
|
||||
#define DEBUG_USART_PCLK PLAT_APB1_HZ
|
||||
|
||||
#elif GEX_PLAT_F303_DISCOVERY
|
||||
|
||||
#define DEBUG_USART USART3
|
||||
#define DEBUG_USART_RSC R_USART3
|
||||
#define DEBUG_USART_PORT 'D'
|
||||
#define DEBUG_USART_PIN 8
|
||||
#define DEBUG_USART_AF 7
|
||||
#define DEBUG_USART_PCLK PLAT_APB1_HZ
|
||||
|
||||
#elif GEX_PLAT_F407_DISCOVERY
|
||||
|
||||
#define DEBUG_USART USART2
|
||||
#define DEBUG_USART_RSC R_USART2
|
||||
#define DEBUG_USART_PORT 'A'
|
||||
#define DEBUG_USART_PIN 2
|
||||
#define DEBUG_USART_AF 7
|
||||
#define DEBUG_USART_PCLK PLAT_APB1_HZ
|
||||
|
||||
#else
|
||||
#error "BAD PLATFORM!"
|
||||
#endif
|
||||
|
||||
|
||||
static bool debug_uart_inited = false;
|
||||
static bool debug_uart_preinited = false;
|
||||
|
||||
/** Init the submodule. */
|
||||
void DebugUart_Init(void)
|
||||
{
|
||||
if (debug_uart_inited) return;
|
||||
if (!debug_uart_preinited) DebugUart_PreInit();
|
||||
// Debug UART
|
||||
assert_param(E_SUCCESS == rsc_claim(&UNIT_SYSTEM, DEBUG_USART_RSC));
|
||||
assert_param(E_SUCCESS == rsc_claim_pin(&UNIT_SYSTEM, DEBUG_USART_PORT, DEBUG_USART_PIN));
|
||||
|
||||
debug_uart_inited = true;
|
||||
}
|
||||
|
||||
/** Init the hardware peripheral - this is called early in the boot process */
|
||||
void DebugUart_PreInit(void)
|
||||
{
|
||||
debug_uart_preinited = true;
|
||||
|
||||
// configure AF only if platform uses AF numbers
|
||||
#if !PLAT_NO_AFNUM
|
||||
#pragma GCC diagnostic push
|
||||
@@ -89,8 +55,35 @@ void DebugUart_PreInit(void)
|
||||
LL_USART_Enable(DEBUG_USART);
|
||||
}
|
||||
|
||||
void DebugUart_Teardown(void)
|
||||
{
|
||||
if (!debug_uart_inited) return;
|
||||
|
||||
dbg("Disabling debug UART!");
|
||||
|
||||
// TODO wait for Tx (after debug print DMA is implemented)
|
||||
|
||||
LL_USART_Disable(DEBUG_USART);
|
||||
rsc_free(&UNIT_SYSTEM, DEBUG_USART_RSC);
|
||||
hw_periph_clock_disable(DEBUG_USART);
|
||||
|
||||
bool suc = true;
|
||||
Resource r = rsc_portpin2rsc(DEBUG_USART_PORT, DEBUG_USART_PIN, &suc);
|
||||
rsc_free(&UNIT_SYSTEM, r);
|
||||
hw_deinit_pin_rsc(r);
|
||||
|
||||
debug_uart_preinited = false;
|
||||
debug_uart_inited = false;
|
||||
|
||||
assert_param(suc);
|
||||
}
|
||||
|
||||
void debug_write(const char *buf, uint16_t len)
|
||||
{
|
||||
if (!SystemSettings.enable_debug_uart) return;
|
||||
|
||||
// TODO wait for DMA complete
|
||||
// TODO use DMA
|
||||
for (uint16_t i = 0; i < len; i++) {
|
||||
while (!LL_USART_IsActiveFlag_TC(DEBUG_USART));
|
||||
LL_USART_TransmitData8(DEBUG_USART, (uint8_t) *buf++);
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#ifndef GEX_DEBUG_UART_H
|
||||
#define GEX_DEBUG_UART_H
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
/**
|
||||
* Pre-init the debug uart
|
||||
*
|
||||
@@ -17,7 +19,13 @@
|
||||
void DebugUart_PreInit(void);
|
||||
|
||||
/**
|
||||
* Finalize the init (claim resources)
|
||||
* Release the peripheral and deinit pin
|
||||
*/
|
||||
void DebugUart_Teardown(void);
|
||||
|
||||
/**
|
||||
* Finalize the init (claim resources).
|
||||
* If not pre-inited (i.e. Teardown was called before), also pre-init.
|
||||
*/
|
||||
void DebugUart_Init(void);
|
||||
|
||||
|
||||
+7
-2
@@ -99,12 +99,17 @@ void hw_deinit_unit_pins(Unit *unit)
|
||||
{
|
||||
for (uint32_t rsc = R_PA0; rsc <= R_PF15; rsc++) {
|
||||
if (RSC_IS_HELD(unit->resources, (Resource)rsc)) {
|
||||
hw_deinit_pin_rsc((Resource)rsc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hw_deinit_pin_rsc(Resource rsc)
|
||||
{
|
||||
rsc_dbg("Freeing pin %s", rsc_get_name((Resource)rsc));
|
||||
GPIO_TypeDef *port = GPIO_PERIPHS[(rsc-R_PA0) / 16];
|
||||
uint32_t ll_pin = LL_GPIO_PINS[(rsc-R_PA0)%16];
|
||||
LL_GPIO_SetPinMode(port, ll_pin, LL_GPIO_MODE_ANALOG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Configure a pin to alternate function */
|
||||
|
||||
@@ -40,6 +40,8 @@ GPIO_TypeDef *hw_port2periph(char port_name, bool *suc);
|
||||
*/
|
||||
bool hw_pinrsc2ll(Resource rsc, GPIO_TypeDef **port, uint32_t *llpin) __attribute__((warn_unused_result));
|
||||
|
||||
void hw_deinit_pin_rsc(Resource rsc);
|
||||
|
||||
/**
|
||||
* Spread packed port pins using a mask
|
||||
*
|
||||
|
||||
@@ -246,7 +246,12 @@ void* irqd_detach(void *periph, IrqCallback callback)
|
||||
|
||||
return oldArg;
|
||||
} else {
|
||||
// catch bugs, but ignore an attempt to unbind when not bound
|
||||
if (slot->callback != NULL) {
|
||||
trap("Detach IRQ %p() from %p but %p() bound instead", callback, periph, slot->callback);
|
||||
} else {
|
||||
return NULL; // the arg - none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
// Provides a trampoline system for redirecting IRQ calls to assigned callbacks.
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_IRQ_DISPATCHER_H
|
||||
#define GEX_F072_IRQ_DISPATCHER_H
|
||||
#ifndef GEX_CORE_IRQ_DISPATCHER_H
|
||||
#define GEX_CORE_IRQ_DISPATCHER_H
|
||||
|
||||
// Dummy peripherals for use with the
|
||||
extern void * const EXTIS[16];
|
||||
@@ -42,4 +42,4 @@ void irqd_attach(void *periph, IrqCallback callback, void *data);
|
||||
*/
|
||||
void* irqd_detach(void *periph, IrqCallback callback);
|
||||
|
||||
#endif //GEX_F072_IRQ_DISPATCHER_H
|
||||
#endif //GEX_CORE_IRQ_DISPATCHER_H
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Created by MightyPork on 2018/02/04.
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_LL_EXTENSION_H
|
||||
#define GEX_F072_LL_EXTENSION_H
|
||||
#ifndef GEX_CORE_LL_EXTENSION_H
|
||||
#define GEX_CORE_LL_EXTENSION_H
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
@@ -57,4 +57,4 @@ static inline void LL_DMA_ClearFlags(DMA_TypeDef *DMAx, uint8_t channel)
|
||||
}
|
||||
|
||||
|
||||
#endif //GEX_F072_LL_EXTENSION_H
|
||||
#endif //GEX_CORE_LL_EXTENSION_H
|
||||
|
||||
@@ -42,7 +42,11 @@ void LockJumper_Init(void)
|
||||
LL_GPIO_SetPinPull(lock_periph, lock_llpin, LL_GPIO_PULL_UP);
|
||||
#endif
|
||||
|
||||
SystemSettings.editable = (bool) LL_GPIO_IsInputPinSet(lock_periph, lock_llpin);
|
||||
#if PLAT_LOCK_BTN
|
||||
SystemSettings.editable = false;
|
||||
#else
|
||||
SystemSettings.editable = (PLAT_LOCK_1CLOSED != LL_GPIO_IsInputPinSet(lock_periph, lock_llpin));
|
||||
#endif
|
||||
dbg("Settings editable? %d", SystemSettings.editable);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
#ifndef GEX_LOCK_JUMPER_H
|
||||
#define GEX_LOCK_JUMPER_H
|
||||
|
||||
#include "plat_compat.h"
|
||||
|
||||
/**
|
||||
* Init the lock jumper subsystem
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/07/07.
|
||||
//
|
||||
|
||||
#ifndef GEX_CORE_PLAT_CONFIG_H
|
||||
#define GEX_CORE_PLAT_CONFIG_H
|
||||
|
||||
#define VFS_DRIVE_NAME "GEX"
|
||||
|
||||
// -------- Priorities -------------
|
||||
#define TSK_MAIN_PRIO osPriorityNormal
|
||||
#define TSK_JOBS_PRIO osPriorityHigh
|
||||
#define TSK_TIMERS_PRIO 4 // this must be in the 0-7 range
|
||||
|
||||
// -------- Static buffers ---------
|
||||
// USB / VFS task stack size
|
||||
#if DISABLE_MSC
|
||||
#define TSK_STACK_MAIN 100 // without MSC the stack usage is significantly lower
|
||||
#else
|
||||
#define TSK_STACK_MAIN 160
|
||||
#endif
|
||||
|
||||
// 180 is normally enough if not doing extensive debug logging
|
||||
#define TSK_STACK_MSG 220 // TF message handler task stack size (all unit commands run on this thread)
|
||||
#define TSK_STACK_IDLE 64 //configMINIMAL_STACK_SIZE
|
||||
#define TSK_STACK_TIMERS 64 //configTIMER_TASK_STACK_DEPTH
|
||||
|
||||
#define PLAT_HEAP_SIZE 4096
|
||||
|
||||
|
||||
|
||||
#define BULK_READ_BUF_LEN 256 // Buffer for TF bulk reads
|
||||
#define UNIT_TMP_LEN 256 // Buffer for internal unit operations
|
||||
|
||||
#define FLASH_SAVE_BUF_LEN 128 // Malloc'd buffer for saving to flash
|
||||
|
||||
#define MSG_QUE_SLOT_SIZE 64 // FIXME this should be possible to lower, but there's some bug with bulk transfer / INI parser
|
||||
#define RX_QUE_CAPACITY 16 // TinyFrame rx queue size (64 bytes each)
|
||||
|
||||
#define TF_MAX_PAYLOAD_RX 512 // TF max Rx payload
|
||||
#define TF_SENDBUF_LEN 512 // TF transmit buffer (can be less than a full frame)
|
||||
|
||||
#define TF_MAX_ID_LST 4 // Frame ID listener count
|
||||
#define TF_MAX_TYPE_LST 6 // Frame Type listener count
|
||||
#define TF_MAX_GEN_LST 1 // Generic listener count
|
||||
|
||||
#define USBD_MAX_STR_DESC_SIZ 64 // Descriptor conversion buffer (used for converting ASCII to UTF-16, must be 2x the size of the longest descriptor)
|
||||
#define MSC_MEDIA_PACKET 512 // Mass storage sector size (packet)
|
||||
|
||||
#define INI_KEY_MAX 20 // Ini parser key buffer
|
||||
#define INI_VALUE_MAX 30 // Ini parser value buffer
|
||||
|
||||
// -------- Stack buffers ----------
|
||||
#define DBG_BUF_LEN 100 // Size of the snprintf buffer for debug messages
|
||||
#define ERR_MSG_STR_LEN 64 // Error message buffer size
|
||||
#define IWBUFFER_LEN 80 // Ini writer buffer for sprintf
|
||||
|
||||
// -------- Timeouts ------------
|
||||
#define TF_PARSER_TIMEOUT_TICKS 100 // Timeout for receiving & parsing a frame
|
||||
#define BULK_LST_TIMEOUT_MS 2000 // timeout for the bulk transaction to expire
|
||||
#define MSG_QUE_POST_TIMEOUT 200 // Time to post to the messages / jobs queue
|
||||
|
||||
|
||||
#endif //GEX_CORE_PLAT_CONFIG_H
|
||||
+18
-230
@@ -2,253 +2,42 @@
|
||||
// Created by MightyPork on 2017/11/26.
|
||||
//
|
||||
|
||||
#include <units/dac/unit_dac.h>
|
||||
#include "platform.h"
|
||||
#include "usbd_core.h"
|
||||
#include "USB/usb_device.h"
|
||||
#include "framework/resources.h"
|
||||
#include "framework/unit_registry.h"
|
||||
#include "units/digital_out/unit_dout.h"
|
||||
|
||||
#include "units/digital_in/unit_din.h"
|
||||
#include "units/neopixel/unit_neopixel.h"
|
||||
#include "units/i2c/unit_i2c.h"
|
||||
#include "units/1wire/unit_1wire.h"
|
||||
#include "units/adc/unit_adc.h"
|
||||
#include "units/test/unit_test.h"
|
||||
#include "units/usart/unit_usart.h"
|
||||
#include "units/spi/unit_spi.h"
|
||||
#include "units/sipo/unit_sipo.h"
|
||||
#include "units/fcap/unit_fcap.h"
|
||||
#include "units/touch/unit_touch.h"
|
||||
#include "units/simple_pwm/unit_pwmdim.h"
|
||||
#include "comm/interfaces.h"
|
||||
#include "hw_utils.h"
|
||||
|
||||
#include "units_manifest.h"
|
||||
|
||||
extern uint32_t plat_init_resources2(void);
|
||||
|
||||
// TODO split this and the plat_compat files to per-platform ones stored in the platform project
|
||||
|
||||
void plat_init_resources(void)
|
||||
{
|
||||
// we use |= here, even though return values are not booleans - success is 0,
|
||||
// thus any problem results in nonzero and the assert will trip.
|
||||
uint32_t rv = 0;
|
||||
|
||||
// enable clock for units we use
|
||||
// periphs available everywhere - enable clock
|
||||
hw_periph_clock_enable(DMA1);
|
||||
#ifdef DMA2
|
||||
hw_periph_clock_enable(DMA2);
|
||||
#endif
|
||||
|
||||
// --- Common unit drivers ---
|
||||
|
||||
#if HAVE_TEST_UNIT
|
||||
ureg_add_type(&UNIT_TEST);
|
||||
#endif
|
||||
// EXTI are always available
|
||||
rsc_free_range(NULL, R_EXTI0, R_EXTI15);
|
||||
|
||||
// --- platform specific resource releases and claims ---
|
||||
|
||||
#if defined(GEX_PLAT_F103_BLUEPILL)
|
||||
// Platform STM32F103C8T6 Bluepill ($4 board from eBay)
|
||||
|
||||
// Units supported by the platform (known to work correctly)
|
||||
// - this is a macro created in the Makefile, registering all enabled units
|
||||
UNITS_REGISTER_CMD;
|
||||
|
||||
// free all present resources
|
||||
{
|
||||
rsc_free_range(NULL, R_ADC1, R_ADC2);
|
||||
rsc_free_range(NULL, R_I2C1, R_I2C2);
|
||||
rsc_free_range(NULL, R_SPI1, R_SPI2);
|
||||
rsc_free_range(NULL, R_TIM1, R_TIM4);
|
||||
rsc_free_range(NULL, R_USART1, R_USART3);
|
||||
rsc_free_range(NULL, R_PA0, R_PA15);
|
||||
rsc_free_range(NULL, R_PB0, R_PB15);
|
||||
rsc_free_range(NULL, R_PC13, R_PC15);
|
||||
rsc_free_range(NULL, R_PD0, R_PD1);
|
||||
}
|
||||
|
||||
// Claim resources not available due to board layout or internal usage
|
||||
{
|
||||
// HAL timebase
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_TIM1);
|
||||
// HSE crystal
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PD0);
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PD1);
|
||||
// SWD
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA13);
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA14);
|
||||
// USB
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA11);
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA12);
|
||||
// BOOT pin(s)
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PB2); // BOOT1
|
||||
}
|
||||
#elif defined(STM32F072xB)
|
||||
// Platform STM32F073RBT
|
||||
|
||||
// Additional GPIO ports
|
||||
__HAL_RCC_GPIOF_CLK_ENABLE();
|
||||
|
||||
// Units supported by the platform (known to work correctly)
|
||||
ureg_add_type(&UNIT_DOUT);
|
||||
ureg_add_type(&UNIT_DIN);
|
||||
ureg_add_type(&UNIT_NEOPIXEL);
|
||||
ureg_add_type(&UNIT_I2C);
|
||||
ureg_add_type(&UNIT_SPI);
|
||||
ureg_add_type(&UNIT_USART);
|
||||
ureg_add_type(&UNIT_1WIRE);
|
||||
ureg_add_type(&UNIT_ADC);
|
||||
ureg_add_type(&UNIT_SIPO);
|
||||
ureg_add_type(&UNIT_FCAP);
|
||||
ureg_add_type(&UNIT_TOUCH);
|
||||
ureg_add_type(&UNIT_PWMDIM);
|
||||
ureg_add_type(&UNIT_DAC);
|
||||
|
||||
// Free all present resources
|
||||
{
|
||||
rsc_free(NULL, R_ADC1);
|
||||
// rsc_free(NULL, R_CAN1);
|
||||
// rsc_free_range(NULL, R_COMP1, R_COMP2);
|
||||
rsc_free(NULL, R_DAC1);
|
||||
// rsc_free(NULL, R_HDMI_CEC);
|
||||
rsc_free(NULL, R_TSC);
|
||||
rsc_free_range(NULL, R_I2C1, R_I2C2);
|
||||
// rsc_free_range(NULL, R_I2S1, R_I2S2);
|
||||
rsc_free_range(NULL, R_SPI1, R_SPI2);
|
||||
rsc_free_range(NULL, R_TIM1, R_TIM3);
|
||||
rsc_free_range(NULL, R_TIM6, R_TIM7);
|
||||
rsc_free_range(NULL, R_TIM14, R_TIM17);
|
||||
rsc_free_range(NULL, R_USART1, R_USART4);
|
||||
rsc_free_range(NULL, R_DMA1_1, R_DMA1_7);
|
||||
|
||||
rsc_free_range(NULL, R_PA0, R_PA15);
|
||||
rsc_free_range(NULL, R_PB0, R_PB15);
|
||||
rsc_free_range(NULL, R_PC0, R_PC15);
|
||||
rsc_free(NULL, R_PD2);
|
||||
rsc_free_range(NULL, R_PF0, R_PF1);
|
||||
}
|
||||
|
||||
// Claim resources not available due to board layout or internal usage
|
||||
{
|
||||
// HAL timebase
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_TIM17);
|
||||
// HSE crystal
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PF0);
|
||||
|
||||
#if PLAT_FULL_XTAL
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PF1); // - not used in BYPASS mode
|
||||
#endif
|
||||
|
||||
// SWD
|
||||
// rv |= rsc_claim(&UNIT_SYSTEM, R_PA13);
|
||||
// rv |= rsc_claim(&UNIT_SYSTEM, R_PA14);
|
||||
// USB
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA11);
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA12);
|
||||
// BOOT pin(s)
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PB2); // BOOT1
|
||||
}
|
||||
#elif defined(GEX_PLAT_F303_DISCOVERY)
|
||||
// Platform STM32F303VCT
|
||||
|
||||
// Additional GPIO ports
|
||||
__HAL_RCC_GPIOF_CLK_ENABLE();
|
||||
|
||||
// Units supported by the platform (known to work correctly)
|
||||
// ureg_add_type(&UNIT_XYZ);
|
||||
|
||||
// Free all present resources
|
||||
{
|
||||
rsc_free_range(NULL, R_ADC1, R_ADC4);
|
||||
// rsc_free(NULL, R_CAN1);
|
||||
// rsc_free_range(NULL, R_COMP1, R_COMP7);
|
||||
// rsc_free(NULL, R_HDMI_CEC);
|
||||
rsc_free(NULL, R_DAC1);
|
||||
rsc_free_range(NULL, R_I2C1, R_I2C2);
|
||||
rsc_free_range(NULL, R_I2S2, R_I2S3);
|
||||
// rsc_free_range(NULL, R_OPAMP1, R_OPAMP4);
|
||||
rsc_free_range(NULL, R_SPI1, R_SPI3);
|
||||
rsc_free_range(NULL, R_TIM1, R_TIM4);
|
||||
rsc_free_range(NULL, R_TIM6, R_TIM8);
|
||||
rsc_free_range(NULL, R_TIM15, R_TIM17);
|
||||
rsc_free(NULL, R_TSC);
|
||||
rsc_free_range(NULL, R_USART1, R_USART5);
|
||||
|
||||
rsc_free_range(NULL, R_PA0, R_PA15);
|
||||
rsc_free_range(NULL, R_PB0, R_PB15);
|
||||
rsc_free_range(NULL, R_PC0, R_PC15);
|
||||
rsc_free_range(NULL, R_PD0, R_PD15);
|
||||
rsc_free_range(NULL, R_PE0, R_PE15);
|
||||
|
||||
rsc_free_range(NULL, R_PF0, R_PF2);
|
||||
rsc_free(NULL, R_PF4);
|
||||
rsc_free_range(NULL, R_PF9, R_PF10);
|
||||
}
|
||||
|
||||
// Claim resources not available due to board layout or internal usage
|
||||
{
|
||||
// HAL timebase
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_TIM1);
|
||||
// HSE crystal
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PF0);
|
||||
//rv |= rsc_claim(&UNIT_SYSTEM, R_PF1); // - not used in BYPASS mode
|
||||
// SWD
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA13);
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA14);
|
||||
// USB
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA11);
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA12);
|
||||
// BOOT pin(s)
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PB2); // BOOT1
|
||||
}
|
||||
#elif defined(GEX_PLAT_F407_DISCOVERY)
|
||||
// Platform STM32F407VGT
|
||||
|
||||
// Additional GPIO ports
|
||||
__HAL_RCC_GPIOF_CLK_ENABLE();
|
||||
|
||||
// Units supported by the platform (known to work correctly)
|
||||
// ureg_add_type(&UNIT_XYZ);
|
||||
|
||||
// Free all present resources
|
||||
{
|
||||
rsc_free_range(NULL, R_ADC1, R_ADC3);
|
||||
// rsc_free_range(NULL, R_CAN1, R_CAN2);
|
||||
// rsc_free_range(NULL, R_COMP1, R_COMP7);
|
||||
rsc_free(NULL, R_DAC1);
|
||||
// rsc_free(NULL, R_DCMI);
|
||||
// rsc_free(NULL, R_ETH);
|
||||
// rsc_free(NULL, R_FSMC);
|
||||
rsc_free_range(NULL, R_I2C1, R_I2C3);
|
||||
rsc_free_range(NULL, R_I2S2, R_I2S3);
|
||||
// rsc_free(NULL, R_SDIO);
|
||||
rsc_free_range(NULL, R_SPI1, R_SPI3);
|
||||
rsc_free_range(NULL, R_TIM1, R_TIM14);
|
||||
rsc_free_range(NULL, R_USART1, R_USART3);
|
||||
rsc_free(NULL, R_USART6);
|
||||
|
||||
rsc_free_range(NULL, R_PA0, R_PA15);
|
||||
rsc_free_range(NULL, R_PB0, R_PB15);
|
||||
rsc_free_range(NULL, R_PC0, R_PC15);
|
||||
rsc_free_range(NULL, R_PD0, R_PD15);
|
||||
rsc_free_range(NULL, R_PE0, R_PE15);
|
||||
// also has 2 PH pins
|
||||
|
||||
// F407 appears to have fewer GPIOs than F303?
|
||||
}
|
||||
|
||||
// Claim resources not available due to board layout or internal usage
|
||||
{
|
||||
// HAL timebase
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_TIM1);
|
||||
// HSE crystal
|
||||
// H0 and H1
|
||||
// SWD
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA13);
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA14);
|
||||
// USB
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA11);
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA12);
|
||||
// BOOT pin(s)
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PB2); // BOOT1
|
||||
}
|
||||
#else
|
||||
#error "BAD PLATFORM!"
|
||||
#endif
|
||||
|
||||
rv = plat_init_resources2();
|
||||
assert_param(rv == 0);
|
||||
}
|
||||
|
||||
@@ -260,13 +49,12 @@ void plat_init_resources(void)
|
||||
*/
|
||||
void plat_usb_reconnect(void)
|
||||
{
|
||||
// TODO add better reset methods available on different chips
|
||||
if (gActiveComport != COMPORT_USB) return;
|
||||
|
||||
// TODO add other reset methods available on different chips (e.g. externam FET)
|
||||
USBD_LL_Reset(&hUsbDeviceFS);
|
||||
|
||||
#if defined(GEX_PLAT_F103_BLUEPILL)
|
||||
// F103 doesn't have pull-up control
|
||||
#else
|
||||
#if PLAT_USB_PU_CTL
|
||||
HAL_PCD_DevDisconnect(&hpcd_USB_FS);
|
||||
osDelay(100);
|
||||
HAL_PCD_DevConnect(&hpcd_USB_FS);
|
||||
|
||||
+7
-1
@@ -17,8 +17,15 @@
|
||||
|
||||
// FreeRTOS includes
|
||||
#include <cmsis_os.h>
|
||||
// platform-independent GEX config
|
||||
#include "plat_config.h"
|
||||
// platform-specific stuff (includes stm32 driver headers)
|
||||
#include "plat_compat.h"
|
||||
|
||||
#define PLAT_AHB_HZ (PLAT_AHB_MHZ*1000000)
|
||||
#define PLAT_APB1_HZ (PLAT_APB1_MHZ*1000000)
|
||||
#define PLAT_APB2_HZ (PLAT_APB2_MHZ*1000000)
|
||||
|
||||
// assert_param, trap...
|
||||
#include "stm32_assert.h"
|
||||
// inIRQ etc
|
||||
@@ -34,7 +41,6 @@
|
||||
// GEX version string
|
||||
#include "version.h"
|
||||
|
||||
|
||||
// ---
|
||||
|
||||
/**
|
||||
|
||||
+3
-3
@@ -8,8 +8,8 @@
|
||||
// and interrupts.
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_TIMEBASE_H
|
||||
#define GEX_F072_TIMEBASE_H
|
||||
#ifndef GEX_CORE_TIMEBASE_H
|
||||
#define GEX_CORE_TIMEBASE_H
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
@@ -58,4 +58,4 @@ static inline uint32_t PTIM_MicroDelayAlign(void)
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif //GEX_F072_TIMEBASE_H
|
||||
#endif //GEX_CORE_TIMEBASE_H
|
||||
|
||||
+3
-3
@@ -2,8 +2,8 @@
|
||||
// Created by MightyPork on 2018/02/27.
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_WATCHDOG_H
|
||||
#define GEX_F072_WATCHDOG_H
|
||||
#ifndef GEX_CORE_WATCHDOG_H
|
||||
#define GEX_CORE_WATCHDOG_H
|
||||
|
||||
/**
|
||||
* Initialize the application watchdog
|
||||
@@ -29,4 +29,4 @@ void wd_resume(void);
|
||||
*/
|
||||
void wd_restart(void);
|
||||
|
||||
#endif //GEX_F072_WATCHDOG_H
|
||||
#endif //GEX_CORE_WATCHDOG_H
|
||||
|
||||
+12
-2
@@ -12,6 +12,7 @@
|
||||
#include "usb_device.h"
|
||||
#include "usbd_msc.h"
|
||||
#include "task_main.h"
|
||||
#include "comm/interfaces.h"
|
||||
|
||||
/* TaskUsbEvent function */
|
||||
void TaskMain(void const * argument)
|
||||
@@ -25,14 +26,16 @@ void TaskMain(void const * argument)
|
||||
|
||||
Indicator_Effect(STATUS_WELCOME);
|
||||
|
||||
uint32_t startTime = xTaskGetTickCount();
|
||||
const uint32_t bootTime = HAL_GetTick();
|
||||
uint32_t startTime = bootTime;
|
||||
uint32_t cnt = 1;
|
||||
bool waiting_for_usb = true;
|
||||
while(1) {
|
||||
uint32_t msg;
|
||||
xTaskNotifyWait(0, UINT32_MAX, &msg, 100); // time out if nothing happened
|
||||
|
||||
// periodic updates to the VFS driver
|
||||
uint32_t now = xTaskGetTickCount();
|
||||
uint32_t now = HAL_GetTick();
|
||||
uint32_t elapsed = now - startTime;
|
||||
if (elapsed >= 100) {
|
||||
// interval 100ms or more - slow periodic
|
||||
@@ -48,6 +51,9 @@ void TaskMain(void const * argument)
|
||||
Indicator_Heartbeat();
|
||||
|
||||
wd_restart();
|
||||
|
||||
// If USB has no signal, set up alternate communication interface
|
||||
com_switch_transfer_if_needed();
|
||||
}
|
||||
|
||||
// if no message and it just timed out, go wait some more...
|
||||
@@ -61,6 +67,7 @@ void TaskMain(void const * argument)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (gActiveComport == COMPORT_USB) {
|
||||
// Endpoint 0 - control messages for the different classes
|
||||
if (msg & USBEVT_FLAG_EP0_RX_RDY) {
|
||||
USBD_CDC_EP0_RxReady(&hUsbDeviceFS);
|
||||
@@ -85,11 +92,14 @@ void TaskMain(void const * argument)
|
||||
// if (msg & (USBEVT_FLAG_EPx_IN(CDC_IN_EP))) {
|
||||
// USBD_CDC_DataIn(&hUsbDeviceFS, CDC_IN_EP);
|
||||
// }
|
||||
|
||||
if (msg & (USBEVT_FLAG_EPx_IN(CDC_CMD_EP))) {
|
||||
USBD_CDC_DataIn(&hUsbDeviceFS, CDC_CMD_EP);
|
||||
}
|
||||
|
||||
if (msg & (USBEVT_FLAG_EPx_OUT(CDC_OUT_EP))) {
|
||||
USBD_CDC_DataOut(&hUsbDeviceFS, CDC_OUT_EP);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -5,8 +5,8 @@
|
||||
// and TinyFrame message handling
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_TASK_MSG_H
|
||||
#define GEX_F072_TASK_MSG_H
|
||||
#ifndef GEX_CORE_TASK_MSG_H
|
||||
#define GEX_CORE_TASK_MSG_H
|
||||
|
||||
#include "platform.h"
|
||||
#include "sched_queue.h"
|
||||
@@ -39,4 +39,4 @@ void rxQuePostMsg(uint8_t *buf, uint32_t len);
|
||||
extern volatile uint32_t msgQueHighWaterMark;
|
||||
#endif
|
||||
|
||||
#endif //GEX_F072_TASK_MSG_H
|
||||
#endif //GEX_CORE_TASK_MSG_H
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// Definition of nRF24L01+ pin mappings for the platform
|
||||
// This file may be omitted if SUPPORT_NRF is not set in plat_compat.h
|
||||
// See rsc_enum.h for the R_ resource constants
|
||||
//
|
||||
|
||||
#ifndef GEX_NRF_PINS_H
|
||||
#define GEX_NRF_PINS_H
|
||||
|
||||
#include "platform.h"
|
||||
#include "rsc_enum.h"
|
||||
|
||||
#define NRF_SPI SPI2
|
||||
#define NRF_R_SPI R_SPI2
|
||||
|
||||
#define NRF_IRQ_Pin LL_GPIO_PIN_15
|
||||
#define NRF_IRQ_GPIO_Port GPIOC
|
||||
#define NRF_R_IRQ R_PC15
|
||||
#define NRF_EXTI_LINENUM 15
|
||||
#define NRF_SYSCFG_EXTI_PORT LL_SYSCFG_EXTI_PORTC
|
||||
|
||||
#define NRF_NSS_Pin LL_GPIO_PIN_13
|
||||
#define NRF_NSS_GPIO_Port GPIOC
|
||||
#define NRF_R_NSS R_PC13
|
||||
|
||||
#define NRF_CE_Pin LL_GPIO_PIN_14
|
||||
#define NRF_CE_GPIO_Port GPIOC
|
||||
#define NRF_R_CE R_PC14
|
||||
|
||||
#define NRF_RST_Pin LL_GPIO_PIN_12
|
||||
#define NRF_RST_GPIO_Port GPIOC
|
||||
#define NRF_R_RST R_PC12
|
||||
|
||||
#define NRF_R_SCK R_PB13
|
||||
#define NRF_SCK_AF LL_GPIO_AF_0
|
||||
#define NRF_R_MISO R_PC2
|
||||
#define NRF_MISO_AF LL_GPIO_AF_1
|
||||
#define NRF_R_MOSI R_PC3
|
||||
#define NRF_MOSI_AF LL_GPIO_AF_1
|
||||
|
||||
#endif //GEX_NRF_PINS_H
|
||||
@@ -0,0 +1,28 @@
|
||||
#if defined(GEX_PLAT_F103_BLUEPILL) || defined(GEX_PLAT_F303_DISCOVERY) \
|
||||
|| defined(GEX_PLAT_F407_DISCOVERY)
|
||||
// This is for F103+
|
||||
|
||||
/* The lowest interrupt priority that can be used in a call to a "set priority"
|
||||
function. */
|
||||
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
|
||||
|
||||
#define configPRIO_BITS 4
|
||||
|
||||
/* The highest interrupt priority that can be used by any interrupt service
|
||||
routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL
|
||||
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
|
||||
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
|
||||
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
|
||||
|
||||
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
|
||||
#elif defined(STM32F072xB)
|
||||
// This is for F072
|
||||
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 3
|
||||
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 1
|
||||
|
||||
#define configPRIO_BITS 2
|
||||
|
||||
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
|
||||
#else
|
||||
#error "BAD PLATFORM!!"
|
||||
#endif
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
#define DEBUG_USART_BAUD 115200
|
||||
|
||||
#if GEX_PLAT_F072
|
||||
|
||||
#define DEBUG_USART USART1
|
||||
#define DEBUG_USART_RSC R_USART1
|
||||
#define DEBUG_USART_PORT 'A'
|
||||
#define DEBUG_USART_PIN 9
|
||||
#define DEBUG_USART_AF 1
|
||||
#define DEBUG_USART_PCLK PLAT_APB1_HZ
|
||||
|
||||
#elif GEX_PLAT_F103
|
||||
|
||||
#define DEBUG_USART USART2
|
||||
#define DEBUG_USART_RSC R_USART2
|
||||
#define DEBUG_USART_PORT 'A'
|
||||
#define DEBUG_USART_PIN 2
|
||||
#define DEBUG_USART_PCLK PLAT_APB1_HZ
|
||||
|
||||
#elif GEX_PLAT_F303
|
||||
|
||||
#define DEBUG_USART USART3
|
||||
#define DEBUG_USART_RSC R_USART3
|
||||
#define DEBUG_USART_PORT 'D'
|
||||
#define DEBUG_USART_PIN 8
|
||||
#define DEBUG_USART_AF 7
|
||||
#define DEBUG_USART_PCLK PLAT_APB1_HZ
|
||||
|
||||
#elif GEX_PLAT_F407
|
||||
|
||||
#define DEBUG_USART USART2
|
||||
#define DEBUG_USART_RSC R_USART2
|
||||
#define DEBUG_USART_PORT 'A'
|
||||
#define DEBUG_USART_PIN 2
|
||||
#define DEBUG_USART_AF 7
|
||||
#define DEBUG_USART_PCLK PLAT_APB1_HZ
|
||||
|
||||
#else
|
||||
#error "BAD PLATFORM!"
|
||||
#endif
|
||||
@@ -5,73 +5,6 @@
|
||||
#ifndef GEX_PLAT_COMPAT_H
|
||||
#define GEX_PLAT_COMPAT_H
|
||||
|
||||
#define VFS_DRIVE_NAME "GEX"
|
||||
|
||||
// -------- Priorities -------------
|
||||
#define TSK_MAIN_PRIO osPriorityNormal
|
||||
#define TSK_JOBS_PRIO osPriorityHigh
|
||||
#define TSK_TIMERS_PRIO 4 // this must be in the 0-7 range
|
||||
|
||||
// -------- Static buffers ---------
|
||||
// USB / VFS task stack size
|
||||
#if DISABLE_MSC
|
||||
#define TSK_STACK_MAIN 100 // without MSC the stack usage is significantly lower
|
||||
#else
|
||||
#define TSK_STACK_MAIN 160
|
||||
#endif
|
||||
|
||||
// 180 is normally enough if not doing extensive debug logging
|
||||
#define TSK_STACK_MSG 220 // TF message handler task stack size (all unit commands run on this thread)
|
||||
#define TSK_STACK_IDLE 64 //configMINIMAL_STACK_SIZE
|
||||
#define TSK_STACK_TIMERS 64 //configTIMER_TASK_STACK_DEPTH
|
||||
|
||||
#define PLAT_HEAP_SIZE 4096
|
||||
|
||||
|
||||
|
||||
#define BULK_READ_BUF_LEN 256 // Buffer for TF bulk reads
|
||||
#define UNIT_TMP_LEN 256 // Buffer for internal unit operations
|
||||
|
||||
#define FLASH_SAVE_BUF_LEN 128 // Malloc'd buffer for saving to flash
|
||||
|
||||
#define MSG_QUE_SLOT_SIZE 64 // FIXME this should be possible to lower, but there's some bug with bulk transfer / INI parser
|
||||
#define RX_QUE_CAPACITY 16 // TinyFrame rx queue size (64 bytes each)
|
||||
|
||||
#define TF_MAX_PAYLOAD_RX 512 // TF max Rx payload
|
||||
#define TF_SENDBUF_LEN 512 // TF transmit buffer (can be less than a full frame)
|
||||
|
||||
#define TF_MAX_ID_LST 4 // Frame ID listener count
|
||||
#define TF_MAX_TYPE_LST 6 // Frame Type listener count
|
||||
#define TF_MAX_GEN_LST 1 // Generic listener count
|
||||
|
||||
#define USBD_MAX_STR_DESC_SIZ 64 // Descriptor conversion buffer (used for converting ASCII to UTF-16, must be 2x the size of the longest descriptor)
|
||||
#define MSC_MEDIA_PACKET 512 // Mass storage sector size (packet)
|
||||
|
||||
#define INI_KEY_MAX 20 // Ini parser key buffer
|
||||
#define INI_VALUE_MAX 30 // Ini parser value buffer
|
||||
|
||||
// -------- Stack buffers ----------
|
||||
#define DBG_BUF_LEN 100 // Size of the snprintf buffer for debug messages
|
||||
#define ERR_MSG_STR_LEN 64 // Error message buffer size
|
||||
#define IWBUFFER_LEN 80 // Ini writer buffer for sprintf
|
||||
|
||||
// -------- Timeouts ------------
|
||||
#define TF_PARSER_TIMEOUT_TICKS 300 // Timeout for receiving & parsing a frame
|
||||
#define BULK_LST_TIMEOUT_MS 500 // timeout for the bulk transaction to expire
|
||||
#define MSG_QUE_POST_TIMEOUT 100 // Time to post to the messages / jobs queue
|
||||
|
||||
// -------- Platform specific includes and defines ---------
|
||||
|
||||
/// Feature flags:
|
||||
// PLAT_FLASHBANKS - has the Banks field on the Flash config struct
|
||||
// PLAT_NO_FLOATING_INPUTS - can't have digital inputs with no pull resistor
|
||||
// PLAT_USB_PHYCLOCK - requires special config of phy clock for USB
|
||||
// PLAT_USB_OTGFS - uses the USB OTG IP, needs different config code
|
||||
// PLAT_LOCK_BTN - use a lock button instead of a lock jumper (push to toggle)
|
||||
// PLAT_LOCK_1CLOSED - lock jumper is active (closed / button pressed) in logical 1
|
||||
// PLAT_NO_AFNUM - legacy platform without numbered AF alternatives
|
||||
// PLAT_FULL_XTAL - use two-wire xtal attachment
|
||||
|
||||
#if defined(GEX_PLAT_F103_BLUEPILL)
|
||||
|
||||
// platform name for the version string
|
||||
@@ -189,6 +122,23 @@
|
||||
// Status LED config
|
||||
#define STATUS_LED_PORT 'A'
|
||||
#define STATUS_LED_PIN 15 // RED LED "UP"
|
||||
#elif defined(GEX_PLAT_F072_ZERO)
|
||||
// platform name for the version string
|
||||
#define GEX_PLATFORM "STM32F072-ZERO"
|
||||
|
||||
#define PLAT_FULL_XTAL 1
|
||||
|
||||
// Lock jumper config
|
||||
#define LOCK_JUMPER_PORT 'D'
|
||||
#define LOCK_JUMPER_PIN 2
|
||||
#define PLAT_LOCK_BTN 1 // toggle button instead of a jumper
|
||||
#define PLAT_LOCK_1CLOSED 1 // toggle button active in log. 1
|
||||
|
||||
// Status LED config
|
||||
#define STATUS_LED_PORT 'A'
|
||||
#define STATUS_LED_PIN 15 // RED LED "UP"
|
||||
|
||||
#define SUPPORT_NRF 1
|
||||
#else
|
||||
#error Bad platform
|
||||
#endif
|
||||
@@ -0,0 +1,194 @@
|
||||
|
||||
#if defined(GEX_PLAT_F103_BLUEPILL)
|
||||
// Platform STM32F103C8T6 Bluepill ($4 board from eBay)
|
||||
|
||||
// Units supported by the platform (known to work correctly)
|
||||
|
||||
// free all present resources
|
||||
{
|
||||
rsc_free_range(NULL, R_ADC1, R_ADC2);
|
||||
rsc_free_range(NULL, R_I2C1, R_I2C2);
|
||||
rsc_free_range(NULL, R_SPI1, R_SPI2);
|
||||
rsc_free_range(NULL, R_TIM1, R_TIM4);
|
||||
rsc_free_range(NULL, R_USART1, R_USART3);
|
||||
rsc_free_range(NULL, R_PA0, R_PA15);
|
||||
rsc_free_range(NULL, R_PB0, R_PB15);
|
||||
rsc_free_range(NULL, R_PC13, R_PC15);
|
||||
rsc_free_range(NULL, R_PD0, R_PD1);
|
||||
}
|
||||
|
||||
// Claim resources not available due to board layout or internal usage
|
||||
{
|
||||
// HAL timebase
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_TIM1);
|
||||
// HSE crystal
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PD0);
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PD1);
|
||||
// SWD
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA13);
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA14);
|
||||
// USB
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA11);
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA12);
|
||||
// BOOT pin(s)
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PB2); // BOOT1
|
||||
}
|
||||
#elif defined(STM32F072xB)
|
||||
// Platform STM32F073RBT
|
||||
|
||||
|
||||
// Free all present resources
|
||||
{
|
||||
rsc_free(NULL, R_ADC1);
|
||||
// rsc_free(NULL, R_CAN1);
|
||||
// rsc_free_range(NULL, R_COMP1, R_COMP2);
|
||||
rsc_free(NULL, R_DAC1);
|
||||
// rsc_free(NULL, R_HDMI_CEC);
|
||||
rsc_free(NULL, R_TSC);
|
||||
rsc_free_range(NULL, R_I2C1, R_I2C2);
|
||||
// rsc_free_range(NULL, R_I2S1, R_I2S2);
|
||||
rsc_free_range(NULL, R_SPI1, R_SPI2);
|
||||
rsc_free_range(NULL, R_TIM1, R_TIM3);
|
||||
rsc_free_range(NULL, R_TIM6, R_TIM7);
|
||||
rsc_free_range(NULL, R_TIM14, R_TIM17);
|
||||
rsc_free_range(NULL, R_USART1, R_USART4);
|
||||
rsc_free_range(NULL, R_DMA1_1, R_DMA1_7);
|
||||
|
||||
rsc_free_range(NULL, R_PA0, R_PA15);
|
||||
rsc_free_range(NULL, R_PB0, R_PB15);
|
||||
rsc_free_range(NULL, R_PC0, R_PC15);
|
||||
rsc_free(NULL, R_PD2);
|
||||
rsc_free_range(NULL, R_PF0, R_PF1);
|
||||
}
|
||||
|
||||
// Claim resources not available due to board layout or internal usage
|
||||
{
|
||||
// HAL timebase
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_TIM17);
|
||||
// HSE crystal
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PF0);
|
||||
|
||||
#if PLAT_FULL_XTAL
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PF1); // - not used in BYPASS mode
|
||||
#endif
|
||||
|
||||
// SWD
|
||||
// rv |= rsc_claim(&UNIT_SYSTEM, R_PA13);
|
||||
// rv |= rsc_claim(&UNIT_SYSTEM, R_PA14);
|
||||
// USB
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA11);
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA12);
|
||||
|
||||
#if defined(GEX_PLAT_F072_ZERO)
|
||||
// unconnected pins
|
||||
rv |= rsc_claim_range(&UNIT_PLATFORM, R_PC0, R_PC1);
|
||||
rv |= rsc_claim_range(&UNIT_PLATFORM, R_PC4, R_PC9);
|
||||
#endif
|
||||
}
|
||||
#elif defined(GEX_PLAT_F303_DISCOVERY)
|
||||
// Platform STM32F303VCT
|
||||
|
||||
// Additional GPIO ports
|
||||
__HAL_RCC_GPIOF_CLK_ENABLE();
|
||||
|
||||
// Units supported by the platform (known to work correctly)
|
||||
// ureg_add_type(&UNIT_XYZ);
|
||||
|
||||
// Free all present resources
|
||||
{
|
||||
rsc_free_range(NULL, R_ADC1, R_ADC4);
|
||||
// rsc_free(NULL, R_CAN1);
|
||||
// rsc_free_range(NULL, R_COMP1, R_COMP7);
|
||||
// rsc_free(NULL, R_HDMI_CEC);
|
||||
rsc_free(NULL, R_DAC1);
|
||||
rsc_free_range(NULL, R_I2C1, R_I2C2);
|
||||
rsc_free_range(NULL, R_I2S2, R_I2S3);
|
||||
// rsc_free_range(NULL, R_OPAMP1, R_OPAMP4);
|
||||
rsc_free_range(NULL, R_SPI1, R_SPI3);
|
||||
rsc_free_range(NULL, R_TIM1, R_TIM4);
|
||||
rsc_free_range(NULL, R_TIM6, R_TIM8);
|
||||
rsc_free_range(NULL, R_TIM15, R_TIM17);
|
||||
rsc_free(NULL, R_TSC);
|
||||
rsc_free_range(NULL, R_USART1, R_USART5);
|
||||
|
||||
rsc_free_range(NULL, R_PA0, R_PA15);
|
||||
rsc_free_range(NULL, R_PB0, R_PB15);
|
||||
rsc_free_range(NULL, R_PC0, R_PC15);
|
||||
rsc_free_range(NULL, R_PD0, R_PD15);
|
||||
rsc_free_range(NULL, R_PE0, R_PE15);
|
||||
|
||||
rsc_free_range(NULL, R_PF0, R_PF2);
|
||||
rsc_free(NULL, R_PF4);
|
||||
rsc_free_range(NULL, R_PF9, R_PF10);
|
||||
}
|
||||
|
||||
// Claim resources not available due to board layout or internal usage
|
||||
{
|
||||
// HAL timebase
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_TIM1);
|
||||
// HSE crystal
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PF0);
|
||||
//rv |= rsc_claim(&UNIT_SYSTEM, R_PF1); // - not used in BYPASS mode
|
||||
// SWD
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA13);
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA14);
|
||||
// USB
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA11);
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA12);
|
||||
// BOOT pin(s)
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PB2); // BOOT1
|
||||
}
|
||||
#elif defined(GEX_PLAT_F407_DISCOVERY)
|
||||
// Platform STM32F407VGT
|
||||
|
||||
// Additional GPIO ports
|
||||
__HAL_RCC_GPIOF_CLK_ENABLE();
|
||||
|
||||
// Units supported by the platform (known to work correctly)
|
||||
// ureg_add_type(&UNIT_XYZ);
|
||||
|
||||
// Free all present resources
|
||||
{
|
||||
rsc_free_range(NULL, R_ADC1, R_ADC3);
|
||||
// rsc_free_range(NULL, R_CAN1, R_CAN2);
|
||||
// rsc_free_range(NULL, R_COMP1, R_COMP7);
|
||||
rsc_free(NULL, R_DAC1);
|
||||
// rsc_free(NULL, R_DCMI);
|
||||
// rsc_free(NULL, R_ETH);
|
||||
// rsc_free(NULL, R_FSMC);
|
||||
rsc_free_range(NULL, R_I2C1, R_I2C3);
|
||||
rsc_free_range(NULL, R_I2S2, R_I2S3);
|
||||
// rsc_free(NULL, R_SDIO);
|
||||
rsc_free_range(NULL, R_SPI1, R_SPI3);
|
||||
rsc_free_range(NULL, R_TIM1, R_TIM14);
|
||||
rsc_free_range(NULL, R_USART1, R_USART3);
|
||||
rsc_free(NULL, R_USART6);
|
||||
|
||||
rsc_free_range(NULL, R_PA0, R_PA15);
|
||||
rsc_free_range(NULL, R_PB0, R_PB15);
|
||||
rsc_free_range(NULL, R_PC0, R_PC15);
|
||||
rsc_free_range(NULL, R_PD0, R_PD15);
|
||||
rsc_free_range(NULL, R_PE0, R_PE15);
|
||||
// also has 2 PH pins
|
||||
|
||||
// F407 appears to have fewer GPIOs than F303?
|
||||
}
|
||||
|
||||
// Claim resources not available due to board layout or internal usage
|
||||
{
|
||||
// HAL timebase
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_TIM1);
|
||||
// HSE crystal
|
||||
// H0 and H1
|
||||
// SWD
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA13);
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA14);
|
||||
// USB
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA11);
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PA12);
|
||||
// BOOT pin(s)
|
||||
rv |= rsc_claim(&UNIT_SYSTEM, R_PB2); // BOOT1
|
||||
}
|
||||
#else
|
||||
#error "BAD PLATFORM!"
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef GEX_PLAT_COMPAT_H
|
||||
#define GEX_PLAT_COMPAT_H
|
||||
|
||||
// string identifying the GEX board
|
||||
#define GEX_PLATFORM "Discovery-XYZ"
|
||||
#define GEX_PLAT_XYZ 1 // only with the MCU name
|
||||
|
||||
// GEX_PLAT_XYZ_BOARD is defined in build.mk to identify particular board layout
|
||||
|
||||
#define PLAT_AHB_MHZ 72
|
||||
#define PLAT_APB1_MHZ 36
|
||||
|
||||
#include ... // all useful from the peripheral library (HAL / LL)
|
||||
|
||||
// in bytes
|
||||
#define FLASH_SIZE (128*1024)
|
||||
// in bytes
|
||||
#define SETTINGS_BLOCK_SIZE (2*1024)
|
||||
// address where the settings block starts
|
||||
#define SETTINGS_FLASH_ADDR (0x08000000 + FLASH_SIZE - SETTINGS_BLOCK_SIZE)
|
||||
|
||||
#define PORTS_COUNT 5 // number of available GPIO ports A,B,C,D,E,F...
|
||||
#define LOCK_JUMPER_PORT 'A'
|
||||
#define LOCK_JUMPER_PIN 2
|
||||
|
||||
#define STATUS_LED_PORT 'A'
|
||||
#define STATUS_LED_PIN 15
|
||||
|
||||
// Feature flags:
|
||||
#define PLAT_FLASHBANKS 0 // has the Banks field on the Flash config struct
|
||||
#define PLAT_NO_FLOATING_INPUTS 0 // can't have digital inputs with no pull resistor
|
||||
#define PLAT_USB_PHYCLOCK 0 // requires special config of phy clock for USB
|
||||
#define PLAT_USB_OTGFS 0 // uses the USB OTG IP, needs different config code
|
||||
#define PLAT_LOCK_BTN 1 // use a lock button instead of a lock jumper (push to toggle)
|
||||
#define PLAT_LOCK_1CLOSED 1 // lock jumper is active (closed / button pressed) in logical 1
|
||||
#define PLAT_NO_AFNUM 0 // legacy platform without numbered AF alternatives
|
||||
#define PLAT_FULL_XTAL 1 // use two-wire xtal attachment
|
||||
#define PLAT_USB_PU_CTL 1 // platform has USB pullup control
|
||||
|
||||
// FreeRTOS config
|
||||
#define PLAT_FREERTOS_LOWEST_INTERRUPT_PRIORITY 3
|
||||
#define PLAT_FREERTOS_MAX_SYSCALL_INTERRUPT_PRIORITY 1
|
||||
#define PLAT_FREERTOS_PRIO_BITS 2
|
||||
#define PLAT_FREERTOS_USE_PORT_OPTIMISED_TASK_SELECTION 0
|
||||
|
||||
#endif //GEX_PLAT_COMPAT_H
|
||||
Binary file not shown.
@@ -1,129 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_1wire.h"
|
||||
|
||||
// 1WIRE master
|
||||
#define OW_INTERNAL
|
||||
#include "_ow_internal.h"
|
||||
#include "_ow_commands.h"
|
||||
#include "_ow_low_level.h"
|
||||
|
||||
/* Check presence of any devices on the bus */
|
||||
error_t UU_1WIRE_CheckPresence(Unit *unit, bool *presence)
|
||||
{
|
||||
CHECK_TYPE(unit, &UNIT_1WIRE);
|
||||
// reset
|
||||
*presence = ow_reset(unit);
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/* Read address of a lone device on the bus */
|
||||
error_t UU_1WIRE_ReadAddress(Unit *unit, uint64_t *address)
|
||||
{
|
||||
CHECK_TYPE(unit, &UNIT_1WIRE);
|
||||
*address = 0;
|
||||
if (!ow_reset(unit)) return E_HW_TIMEOUT;
|
||||
|
||||
// command
|
||||
ow_write_u8(unit, OW_ROM_READ);
|
||||
|
||||
// read the ROM code
|
||||
*address = ow_read_u64(unit);
|
||||
|
||||
const uint8_t *addr_as_bytes = (void*)address;
|
||||
if (0 != ow_checksum(addr_as_bytes, 8)) {
|
||||
*address = 0;
|
||||
return E_CHECKSUM_MISMATCH; // checksum mismatch
|
||||
}
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/* Write bytes to a device */
|
||||
error_t UU_1WIRE_Write(Unit *unit, uint64_t address, const uint8_t *buff, uint32_t len)
|
||||
{
|
||||
CHECK_TYPE(unit, &UNIT_1WIRE);
|
||||
if (!ow_reset(unit)) return E_HW_TIMEOUT;
|
||||
|
||||
// MATCH_ROM+addr, or SKIP_ROM
|
||||
if (address != 0) {
|
||||
ow_write_u8(unit, OW_ROM_MATCH);
|
||||
ow_write_u64(unit, address);
|
||||
} else {
|
||||
ow_write_u8(unit, OW_ROM_SKIP);
|
||||
}
|
||||
|
||||
// write the payload;
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
ow_write_u8(unit, *buff++);
|
||||
}
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/* Write a request to a device and read a response */
|
||||
error_t UU_1WIRE_Read(Unit *unit, uint64_t address,
|
||||
const uint8_t *request_buff, uint32_t request_len,
|
||||
uint8_t *response_buff, uint32_t response_len, bool check_crc)
|
||||
{
|
||||
CHECK_TYPE(unit, &UNIT_1WIRE);
|
||||
if (!ow_reset(unit)) return E_HW_TIMEOUT;
|
||||
|
||||
uint8_t *rb = response_buff;
|
||||
|
||||
// MATCH_ROM+addr, or SKIP_ROM
|
||||
if (address != 0) {
|
||||
ow_write_u8(unit, OW_ROM_MATCH);
|
||||
ow_write_u64(unit, address);
|
||||
} else {
|
||||
ow_write_u8(unit, OW_ROM_SKIP);
|
||||
}
|
||||
|
||||
// write the payload;
|
||||
for (uint32_t i = 0; i < request_len; i++) {
|
||||
ow_write_u8(unit, *request_buff++);
|
||||
}
|
||||
|
||||
// read the requested number of bytes
|
||||
for (uint32_t i = 0; i < response_len; i++) {
|
||||
*rb++ = ow_read_u8(unit);
|
||||
}
|
||||
|
||||
if (check_crc) {
|
||||
if (0 != ow_checksum(response_buff, response_len)) {
|
||||
return E_CHECKSUM_MISMATCH;
|
||||
}
|
||||
}
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/* Perform the search algorithm (start or continue) */
|
||||
error_t UU_1WIRE_Search(Unit *unit, bool with_alarm, bool restart,
|
||||
uint64_t *buffer, uint32_t capacity, uint32_t *real_count,
|
||||
bool *have_more)
|
||||
{
|
||||
CHECK_TYPE(unit, &UNIT_1WIRE);
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
if (restart) {
|
||||
uint8_t search_cmd = (uint8_t) (with_alarm ? OW_ROM_ALM_SEARCH : OW_ROM_SEARCH);
|
||||
ow_search_init(unit, search_cmd, true);
|
||||
}
|
||||
|
||||
*real_count = ow_search_run(unit, (ow_romcode_t *) buffer, capacity);
|
||||
|
||||
// resolve the code
|
||||
switch (priv->searchState.status) {
|
||||
case OW_SEARCH_MORE:
|
||||
*have_more = priv->searchState.status == OW_SEARCH_MORE;
|
||||
|
||||
case OW_SEARCH_DONE:
|
||||
return E_SUCCESS;
|
||||
|
||||
case OW_SEARCH_FAILED:
|
||||
return priv->searchState.error;
|
||||
}
|
||||
|
||||
return E_INTERNAL_ERROR;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/01/29.
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_OW_COMMANDS_H
|
||||
#define GEX_F072_OW_COMMANDS_H
|
||||
|
||||
#ifndef OW_INTERNAL
|
||||
#error bad include!
|
||||
#endif
|
||||
|
||||
#define OW_ROM_SEARCH 0xF0
|
||||
#define OW_ROM_READ 0x33
|
||||
#define OW_ROM_MATCH 0x55
|
||||
#define OW_ROM_SKIP 0xCC
|
||||
#define OW_ROM_ALM_SEARCH 0xEC
|
||||
|
||||
#endif //GEX_F072_OW_COMMANDS_H
|
||||
@@ -1,59 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
|
||||
#define OW_INTERNAL
|
||||
#include "_ow_internal.h"
|
||||
|
||||
/** Allocate data structure and set defaults */
|
||||
error_t OW_preInit(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data = calloc_ck(1, sizeof(struct priv));
|
||||
if (priv == NULL) return E_OUT_OF_MEM;
|
||||
|
||||
// some defaults
|
||||
priv->pin_number = 0;
|
||||
priv->port_name = 'A';
|
||||
priv->parasitic = false;
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/** Finalize unit set-up */
|
||||
error_t OW_init(Unit *unit)
|
||||
{
|
||||
bool suc = true;
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
// --- Parse config ---
|
||||
priv->ll_pin = hw_pin2ll(priv->pin_number, &suc);
|
||||
priv->port = hw_port2periph(priv->port_name, &suc);
|
||||
Resource rsc = rsc_portpin2rsc(priv->port_name, priv->pin_number, &suc);
|
||||
if (!suc) return E_BAD_CONFIG;
|
||||
|
||||
// --- Claim resources ---
|
||||
TRY(rsc_claim(unit, rsc));
|
||||
|
||||
// --- Init hardware ---
|
||||
LL_GPIO_SetPinMode(priv->port, priv->ll_pin, LL_GPIO_MODE_OUTPUT);
|
||||
LL_GPIO_SetPinOutputType(priv->port, priv->ll_pin, LL_GPIO_OUTPUT_PUSHPULL);
|
||||
LL_GPIO_SetPinSpeed(priv->port, priv->ll_pin, LL_GPIO_SPEED_FREQ_HIGH);
|
||||
LL_GPIO_SetPinPull(priv->port, priv->ll_pin, LL_GPIO_PULL_UP); // pull-up for OD state
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/** Tear down the unit */
|
||||
void OW_deInit(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
// Release all resources
|
||||
rsc_teardown(unit);
|
||||
|
||||
// Free memory
|
||||
free_ck(unit->data);
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/01/29.
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_OW_INTERNAL_H
|
||||
#define GEX_F072_OW_INTERNAL_H
|
||||
|
||||
#ifndef OW_INTERNAL
|
||||
#error bad include!
|
||||
#endif
|
||||
|
||||
#include "_ow_search.h"
|
||||
|
||||
/** Private data structure */
|
||||
struct priv {
|
||||
char port_name;
|
||||
uint8_t pin_number;
|
||||
bool parasitic;
|
||||
|
||||
GPIO_TypeDef *port;
|
||||
uint32_t ll_pin;
|
||||
|
||||
TimerHandle_t busyWaitTimer; // timer used to wait for ds1820 measurement completion
|
||||
bool busy; // flag used when the timer is running
|
||||
uint32_t busyStart;
|
||||
TF_ID busyRequestId;
|
||||
struct ow_search_state searchState;
|
||||
};
|
||||
|
||||
/** Load from a binary buffer stored in Flash */
|
||||
void OW_loadBinary(Unit *unit, PayloadParser *pp);
|
||||
|
||||
/** Write to a binary buffer for storing in Flash */
|
||||
void OW_writeBinary(Unit *unit, PayloadBuilder *pb);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Parse a key-value pair from the INI file */
|
||||
error_t OW_loadIni(Unit *unit, const char *key, const char *value);
|
||||
|
||||
/** Generate INI file section for the unit */
|
||||
void OW_writeIni(Unit *unit, IniWriter *iw);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Allocate data structure and set defaults */
|
||||
error_t OW_preInit(Unit *unit);
|
||||
|
||||
/** Finalize unit set-up */
|
||||
error_t OW_init(Unit *unit);
|
||||
|
||||
/** Tear down the unit */
|
||||
void OW_deInit(Unit *unit);
|
||||
|
||||
/** Callback for the FreeRTOS timer used to wait for device ready */
|
||||
void OW_TimerCb(TimerHandle_t xTimer);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
#endif //GEX_F072_OW_INTERNAL_H
|
||||
@@ -1,223 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/01/29.
|
||||
//
|
||||
// 1-Wire unit low level functions
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#define OW_INTERNAL
|
||||
#include "_ow_internal.h"
|
||||
#include "_ow_low_level.h"
|
||||
|
||||
static inline uint8_t crc8_bits(uint8_t data)
|
||||
{
|
||||
uint8_t crc = 0;
|
||||
if(data & 1) crc ^= 0x5e;
|
||||
if(data & 2) crc ^= 0xbc;
|
||||
if(data & 4) crc ^= 0x61;
|
||||
if(data & 8) crc ^= 0xc2;
|
||||
if(data & 0x10) crc ^= 0x9d;
|
||||
if(data & 0x20) crc ^= 0x23;
|
||||
if(data & 0x40) crc ^= 0x46;
|
||||
if(data & 0x80) crc ^= 0x8c;
|
||||
return crc;
|
||||
}
|
||||
|
||||
static uint8_t crc8_add(uint8_t cksum, uint8_t byte)
|
||||
{
|
||||
return crc8_bits(byte ^ cksum);
|
||||
}
|
||||
|
||||
uint8_t ow_checksum(const uint8_t *buff, uint32_t len)
|
||||
{
|
||||
uint8_t cksum = 0;
|
||||
for(uint32_t i = 0; i < len; i++) {
|
||||
cksum = crc8_add(cksum, buff[i]);
|
||||
}
|
||||
return cksum;
|
||||
}
|
||||
|
||||
// ----------------------------------------------
|
||||
|
||||
static inline void ow_pull_high(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
LL_GPIO_SetOutputPin(priv->port, priv->ll_pin);
|
||||
LL_GPIO_SetPinMode(priv->port, priv->ll_pin, LL_GPIO_MODE_OUTPUT);
|
||||
}
|
||||
|
||||
static inline void ow_pull_low(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
LL_GPIO_ResetOutputPin(priv->port, priv->ll_pin);
|
||||
LL_GPIO_SetPinMode(priv->port, priv->ll_pin, LL_GPIO_MODE_OUTPUT);
|
||||
}
|
||||
|
||||
static inline void ow_release_line(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
LL_GPIO_SetPinMode(priv->port, priv->ll_pin, LL_GPIO_MODE_INPUT);
|
||||
}
|
||||
|
||||
static inline bool ow_sample_line(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
return (bool) LL_GPIO_IsInputPinSet(priv->port, priv->ll_pin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the 1-wire bus
|
||||
*/
|
||||
bool ow_reset(Unit *unit)
|
||||
{
|
||||
ow_pull_low(unit);
|
||||
PTIM_MicroDelay(500);
|
||||
|
||||
bool presence;
|
||||
vPortEnterCritical();
|
||||
{
|
||||
// Strong pull-up (for parasitive power)
|
||||
ow_pull_high(unit);
|
||||
PTIM_MicroDelay(2);
|
||||
|
||||
// switch to open-drain
|
||||
ow_release_line(unit);
|
||||
PTIM_MicroDelay(118);
|
||||
|
||||
presence = !ow_sample_line(unit);
|
||||
}
|
||||
vPortExitCritical();
|
||||
|
||||
PTIM_MicroDelay(130);
|
||||
return presence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a bit to the 1-wire bus
|
||||
*/
|
||||
void ow_write_bit(Unit *unit, bool bit)
|
||||
{
|
||||
vPortEnterCritical();
|
||||
{
|
||||
// start mark
|
||||
ow_pull_low(unit);
|
||||
PTIM_MicroDelay(2);
|
||||
|
||||
if (bit) ow_pull_high(unit);
|
||||
PTIM_MicroDelay(70);
|
||||
|
||||
// Strong pull-up (for parasitive power)
|
||||
ow_pull_high(unit);
|
||||
}
|
||||
vPortExitCritical();
|
||||
|
||||
PTIM_MicroDelay(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a bit from the 1-wire bus
|
||||
*/
|
||||
bool ow_read_bit(Unit *unit)
|
||||
{
|
||||
bool bit;
|
||||
|
||||
vPortEnterCritical();
|
||||
{
|
||||
// start mark
|
||||
ow_pull_low(unit);
|
||||
PTIM_MicroDelay(2);
|
||||
|
||||
ow_release_line(unit);
|
||||
PTIM_MicroDelay(20);
|
||||
|
||||
bit = ow_sample_line(unit);
|
||||
}
|
||||
vPortExitCritical();
|
||||
|
||||
PTIM_MicroDelay(40);
|
||||
|
||||
return bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a byte to the 1-wire bus
|
||||
*/
|
||||
void ow_write_u8(Unit *unit, uint8_t byte)
|
||||
{
|
||||
for (int i = 0; i < 8; i++) {
|
||||
ow_write_bit(unit, 0 != (byte & (1 << i)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a halfword to the 1-wire bus
|
||||
*/
|
||||
void ow_write_u16(Unit *unit, uint16_t halfword)
|
||||
{
|
||||
ow_write_u8(unit, (uint8_t) (halfword & 0xFF));
|
||||
ow_write_u8(unit, (uint8_t) ((halfword >> 8) & 0xFF));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a word to the 1-wire bus
|
||||
*/
|
||||
void ow_write_u32(Unit *unit, uint32_t word)
|
||||
{
|
||||
ow_write_u16(unit, (uint16_t) (word));
|
||||
ow_write_u16(unit, (uint16_t) (word >> 16));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a doubleword to the 1-wire bus
|
||||
*/
|
||||
void ow_write_u64(Unit *unit, uint64_t dword)
|
||||
{
|
||||
ow_write_u32(unit, (uint32_t) (dword));
|
||||
ow_write_u32(unit, (uint32_t) (dword >> 32));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a byte form the 1-wire bus
|
||||
*/
|
||||
uint8_t ow_read_u8(Unit *unit)
|
||||
{
|
||||
uint8_t buf = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
buf |= (1 & ow_read_bit(unit)) << i;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a halfword form the 1-wire bus
|
||||
*/
|
||||
uint16_t ow_read_u16(Unit *unit)
|
||||
{
|
||||
uint16_t acu = 0;
|
||||
acu |= ow_read_u8(unit);
|
||||
acu |= ow_read_u8(unit) << 8;
|
||||
return acu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a word form the 1-wire bus
|
||||
*/
|
||||
uint32_t ow_read_u32(Unit *unit)
|
||||
{
|
||||
uint32_t acu = 0;
|
||||
acu |= ow_read_u16(unit);
|
||||
acu |= (uint32_t)ow_read_u16(unit) << 16;
|
||||
return acu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a doubleword form the 1-wire bus
|
||||
*/
|
||||
uint64_t ow_read_u64(Unit *unit)
|
||||
{
|
||||
uint64_t acu = 0;
|
||||
acu |= ow_read_u32(unit);
|
||||
acu |= (uint64_t)ow_read_u32(unit) << 32;
|
||||
return acu;
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/01.
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_OW_LOW_LEVEL_H
|
||||
#define GEX_F072_OW_LOW_LEVEL_H
|
||||
|
||||
#ifndef OW_INTERNAL
|
||||
#error bad include!
|
||||
#endif
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
#include "_ow_low_level.h"
|
||||
|
||||
/**
|
||||
* Compute a 1-wire type checksum.
|
||||
* If the buffer includes the checksum, the result should be 0.
|
||||
*
|
||||
* (this function may be used externally, or you can delete the implementation
|
||||
* from the c file if another implementation is already available)
|
||||
*
|
||||
* @param[in] buf - buffer of bytes to verify
|
||||
* @param[in] len - buffer length
|
||||
* @return checksum
|
||||
*/
|
||||
uint8_t ow_checksum(const uint8_t *buf, uint32_t len);
|
||||
|
||||
/**
|
||||
* Reset the 1-wire bus
|
||||
*/
|
||||
bool ow_reset(Unit *unit);
|
||||
|
||||
/**
|
||||
* Write a bit to the 1-wire bus
|
||||
*/
|
||||
void ow_write_bit(Unit *unit, bool bit);
|
||||
|
||||
/**
|
||||
* Read a bit from the 1-wire bus
|
||||
*/
|
||||
bool ow_read_bit(Unit *unit);
|
||||
|
||||
/**
|
||||
* Write a byte to the 1-wire bus
|
||||
*/
|
||||
void ow_write_u8(Unit *unit, uint8_t byte);
|
||||
|
||||
/**
|
||||
* Write a halfword to the 1-wire bus
|
||||
*/
|
||||
void ow_write_u16(Unit *unit, uint16_t halfword);
|
||||
|
||||
/**
|
||||
* Write a word to the 1-wire bus
|
||||
*/
|
||||
void ow_write_u32(Unit *unit, uint32_t word);
|
||||
|
||||
/**
|
||||
* Write a doubleword to the 1-wire bus
|
||||
*/
|
||||
void ow_write_u64(Unit *unit, uint64_t dword);
|
||||
|
||||
/**
|
||||
* Read a byte form the 1-wire bus
|
||||
*/
|
||||
uint8_t ow_read_u8(Unit *unit);
|
||||
|
||||
/**
|
||||
* Read a halfword form the 1-wire bus
|
||||
*/
|
||||
uint16_t ow_read_u16(Unit *unit);
|
||||
|
||||
/**
|
||||
* Read a word form the 1-wire bus
|
||||
*/
|
||||
uint32_t ow_read_u32(Unit *unit);
|
||||
|
||||
/**
|
||||
* Read a doubleword form the 1-wire bus
|
||||
*/
|
||||
uint64_t ow_read_u64(Unit *unit);
|
||||
|
||||
#endif //GEX_F072_OW_LOW_LEVEL_H
|
||||
@@ -1,129 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/01.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_1wire.h"
|
||||
|
||||
#define OW_INTERNAL
|
||||
#include "_ow_search.h"
|
||||
#include "_ow_internal.h"
|
||||
#include "_ow_low_level.h"
|
||||
#include "_ow_commands.h"
|
||||
|
||||
void ow_search_init(Unit *unit, uint8_t command, bool test_checksums)
|
||||
{
|
||||
if (unit->driver != &UNIT_1WIRE)
|
||||
trap("Wrong unit type - %s", unit->driver->name);
|
||||
|
||||
assert_param(command == OW_ROM_SEARCH || command == OW_ROM_ALM_SEARCH);
|
||||
|
||||
struct priv *priv = unit->data;
|
||||
struct ow_search_state *state = &priv->searchState;
|
||||
|
||||
state->prev_last_fork = 64;
|
||||
memset(state->prev_code, 0, 8);
|
||||
state->status = OW_SEARCH_MORE;
|
||||
state->error = E_SUCCESS;
|
||||
state->command = command;
|
||||
state->first = true;
|
||||
state->test_checksums = test_checksums;
|
||||
}
|
||||
|
||||
uint32_t ow_search_run(Unit *unit, ow_romcode_t *codes, uint32_t capacity)
|
||||
{
|
||||
if (unit->driver != &UNIT_1WIRE)
|
||||
trap("Wrong unit type - %s", unit->driver->name);
|
||||
|
||||
assert_param(codes);
|
||||
|
||||
struct priv *priv = unit->data;
|
||||
struct ow_search_state *state = &priv->searchState;
|
||||
|
||||
if (state->status != OW_SEARCH_MORE) return 0;
|
||||
|
||||
uint32_t found_devices = 0;
|
||||
|
||||
while (found_devices < capacity) {
|
||||
uint8_t index = 0;
|
||||
ow_romcode_t code = {};
|
||||
int8_t last_fork = -1;
|
||||
|
||||
// Start a new transaction. Devices respond to reset
|
||||
if (!ow_reset(unit)) {
|
||||
state->status = OW_SEARCH_FAILED;
|
||||
state->error = E_HW_TIMEOUT;
|
||||
goto done;
|
||||
}
|
||||
// Send the search command (SEARCH_ROM, SEARCH_ALARM)
|
||||
ow_write_u8(unit, state->command);
|
||||
|
||||
uint8_t *code_byte = &code[0];
|
||||
|
||||
bool p, n;
|
||||
while (index != 64) {
|
||||
// Read a bit and its complement
|
||||
p = ow_read_bit(unit);
|
||||
n = ow_read_bit(unit);
|
||||
|
||||
if (!p && !n) {
|
||||
// A fork: there are devices on the bus with different bit value
|
||||
// (the bus is open-drain, in both cases one device pulls it low)
|
||||
if ((found_devices > 0 || !state->first) && index < state->prev_last_fork) {
|
||||
// earlier than the last fork, take the same turn as before
|
||||
p = ow_code_getbit(state->prev_code, index);
|
||||
if (!p) last_fork = index; // remember for future runs, 1 not explored yet
|
||||
}
|
||||
else if (index == state->prev_last_fork) {
|
||||
p = 1; // both forks are now exhausted
|
||||
}
|
||||
else { // a new fork
|
||||
last_fork = index;
|
||||
}
|
||||
}
|
||||
else if (p && n) {
|
||||
// No devices left connected - this doesn't normally happen
|
||||
state->status = OW_SEARCH_FAILED;
|
||||
state->error = E_BUS_FAULT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
// All devices have a matching bit here, or it was resolved in a fork
|
||||
if (p) *code_byte |= (1 << (index & 7));
|
||||
ow_write_bit(unit, p);
|
||||
|
||||
index++;
|
||||
if((index & 7) == 0) {
|
||||
code_byte++;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(state->prev_code, code, 8);
|
||||
|
||||
if (state->test_checksums) {
|
||||
if (0 != ow_checksum(code, 8)) {
|
||||
state->status = OW_SEARCH_FAILED;
|
||||
state->error = E_CHECKSUM_MISMATCH;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
// Record a found address
|
||||
memcpy(codes[found_devices], code, 8);
|
||||
found_devices++;
|
||||
|
||||
// Stop condition
|
||||
if (last_fork == -1) {
|
||||
state->status = OW_SEARCH_DONE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
state->prev_last_fork = last_fork;
|
||||
}
|
||||
|
||||
done:
|
||||
state->first = false;
|
||||
return found_devices;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/01.
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_OW_SEARCH_H
|
||||
#define GEX_F072_OW_SEARCH_H
|
||||
|
||||
#ifndef OW_INTERNAL
|
||||
#error bad include!
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "unit_base.h"
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Data type holding a romcode
|
||||
*/
|
||||
typedef uint8_t ow_romcode_t[8];
|
||||
|
||||
/**
|
||||
* Get a single bit from a romcode
|
||||
*/
|
||||
#define ow_code_getbit(code, index) (bool)((code)[(index) >> 3] & (1 << ((index) & 7)))
|
||||
|
||||
/**
|
||||
* Convert to unsigned 64-bit integer
|
||||
* (works only on little-endian systems - eg. OK on x86/x86_64, not on PowerPC)
|
||||
*/
|
||||
#define ow_romcode_to_u64(code) (*((uint64_t *) (void *)(code)))
|
||||
|
||||
/**
|
||||
* States of the search algorithm
|
||||
*/
|
||||
enum ow_search_result {
|
||||
OW_SEARCH_DONE = 0,
|
||||
OW_SEARCH_MORE = 1,
|
||||
OW_SEARCH_FAILED = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal state of the search algorithm.
|
||||
* Check status to see if more remain to be read or an error occurred.
|
||||
*/
|
||||
struct ow_search_state {
|
||||
int8_t prev_last_fork;
|
||||
ow_romcode_t prev_code;
|
||||
uint8_t command;
|
||||
enum ow_search_result status;
|
||||
bool first;
|
||||
bool test_checksums;
|
||||
error_t error;
|
||||
};
|
||||
|
||||
/**
|
||||
* Init the search algorithm state structure
|
||||
*
|
||||
* @param[out] state - inited struct
|
||||
* @param[in] command - command to send for requesting the search (e.g. SEARCH_ROM)
|
||||
* @param[in] test_checksums - verify checksums of all read romcodes
|
||||
*/
|
||||
void ow_search_init(Unit *unit, uint8_t command, bool test_checksums);
|
||||
|
||||
/**
|
||||
* Perform a search of the 1-wire bus, with a state struct pre-inited
|
||||
* using ow_search_init().
|
||||
*
|
||||
* Romcodes are stored in the provided array in a numerically ascending order.
|
||||
*
|
||||
* This function may be called repeatedly to retrieve more addresses than could fit
|
||||
* in the address buffer.
|
||||
*
|
||||
* @param[in,out] state - search state, used for multiple calls with limited buffer size
|
||||
* @param[out] codes - buffer for found romcodes
|
||||
* @param[in] capacity - buffer capacity
|
||||
* @return number of romcodes found. Search status is stored in `state->status`,
|
||||
* possible error code in `status->error`
|
||||
*/
|
||||
uint32_t ow_search_run(Unit *unit, ow_romcode_t *codes, uint32_t capacity);
|
||||
|
||||
#endif //GEX_F072_OW_SEARCH_H
|
||||
@@ -1,68 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
|
||||
#define OW_INTERNAL
|
||||
#include "_ow_internal.h"
|
||||
|
||||
/** Load from a binary buffer stored in Flash */
|
||||
void OW_loadBinary(Unit *unit, PayloadParser *pp)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
uint8_t version = pp_u8(pp);
|
||||
(void)version;
|
||||
|
||||
priv->port_name = pp_char(pp);
|
||||
priv->pin_number = pp_u8(pp);
|
||||
priv->parasitic = pp_bool(pp);
|
||||
}
|
||||
|
||||
/** Write to a binary buffer for storing in Flash */
|
||||
void OW_writeBinary(Unit *unit, PayloadBuilder *pb)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
pb_u8(pb, 0); // version
|
||||
|
||||
pb_char(pb, priv->port_name);
|
||||
pb_u8(pb, priv->pin_number);
|
||||
pb_bool(pb, priv->parasitic);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Parse a key-value pair from the INI file */
|
||||
error_t OW_loadIni(Unit *unit, const char *key, const char *value)
|
||||
{
|
||||
bool suc = true;
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
if (streq(key, "pin")) {
|
||||
suc = cfg_portpin_parse(value, &priv->port_name, &priv->pin_number);
|
||||
}
|
||||
else if (streq(key, "parasitic")) {
|
||||
priv->parasitic = cfg_bool_parse(value, &suc);
|
||||
}
|
||||
else {
|
||||
return E_BAD_KEY;
|
||||
}
|
||||
|
||||
if (!suc) return E_BAD_VALUE;
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/** Generate INI file section for the unit */
|
||||
void OW_writeIni(Unit *unit, IniWriter *iw)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
iw_comment(iw, "Data pin");
|
||||
iw_entry(iw, "pin", "%c%d", priv->port_name, priv->pin_number);
|
||||
|
||||
iw_comment(iw, "Parasitic (bus-powered) mode");
|
||||
iw_entry_s(iw, "parasitic", str_yn(priv->parasitic));
|
||||
}
|
||||
@@ -1,248 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/01/29.
|
||||
//
|
||||
|
||||
#include "comm/messages.h"
|
||||
#include "unit_base.h"
|
||||
#include "unit_1wire.h"
|
||||
|
||||
// 1WIRE master
|
||||
#define OW_INTERNAL
|
||||
#include "_ow_internal.h"
|
||||
#include "_ow_low_level.h"
|
||||
|
||||
/** Callback for sending a poll_ready success / failure report */
|
||||
static void OW_TimerRespCb(Job *job)
|
||||
{
|
||||
Unit *unit = job->unit;
|
||||
assert_param(unit);
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
bool success = (bool) job->data1;
|
||||
|
||||
if (success) {
|
||||
com_respond_ok(priv->busyRequestId);
|
||||
} else {
|
||||
com_respond_error(priv->busyRequestId, E_HW_TIMEOUT);
|
||||
}
|
||||
priv->busy = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1-Wire timer callback, used for the 'wait_ready' function.
|
||||
*
|
||||
* - In parasitic mode, this is a simple 750ms wait, after which a SUCCESS response is sent.
|
||||
* - In 3-wire mode, the callback is fired periodically and performs a Read operation on the bus.
|
||||
* The unit responds with 0 while the operation is ongoing. On receiving 1 a SUCCESS response is sent.
|
||||
* The polling is abandoned after a timeout, sending a TIMEOUT response.
|
||||
*
|
||||
* @param xTimer
|
||||
*/
|
||||
void OW_tickHandler(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
if(!priv->busy) {
|
||||
dbg("ow tick should be disabled now!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (priv->parasitic) {
|
||||
// this is the end of the 750ms measurement time
|
||||
goto halt_ok;
|
||||
} else {
|
||||
bool ready = ow_read_bit(unit);
|
||||
if (ready) {
|
||||
goto halt_ok;
|
||||
}
|
||||
|
||||
uint32_t time = PTIM_GetTime();
|
||||
if (time - priv->busyStart > 1000) {
|
||||
unit->tick_interval = 0;
|
||||
unit->_tick_cnt = 0;
|
||||
|
||||
Job j = {
|
||||
.unit = unit,
|
||||
.data1 = 0, // failure
|
||||
.cb = OW_TimerRespCb,
|
||||
};
|
||||
scheduleJob(&j);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
halt_ok:
|
||||
unit->tick_interval = 0;
|
||||
unit->_tick_cnt = 0;
|
||||
|
||||
Job j = {
|
||||
.unit = unit,
|
||||
.data1 = 1, // success
|
||||
.cb = OW_TimerRespCb,
|
||||
};
|
||||
scheduleJob(&j);
|
||||
}
|
||||
|
||||
enum PinCmd_ {
|
||||
CMD_CHECK_PRESENCE = 0, // simply tests that any devices are attached
|
||||
CMD_SEARCH_ADDR = 1, // perform a scan of the bus, retrieving all found device ROMs
|
||||
CMD_SEARCH_ALARM = 2, // like normal scan, but retrieve only devices with alarm
|
||||
CMD_SEARCH_CONTINUE = 3, // continue the previously started scan, retrieving more devices
|
||||
CMD_READ_ADDR = 4, // read the ROM code from a single device (for single-device bus)
|
||||
|
||||
CMD_QUERY = 10, // write multiple bytes using the SKIP_ROM command
|
||||
CMD_READ = 11, // write multiple bytes using a ROM address
|
||||
|
||||
CMD_POLL_FOR_1 = 20,
|
||||
|
||||
CMD_TEST = 100,
|
||||
};
|
||||
|
||||
|
||||
/** Handle a request message */
|
||||
static error_t OW_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
bool presence;
|
||||
uint64_t addr;
|
||||
uint32_t remain;
|
||||
const uint8_t *tail;
|
||||
|
||||
if (priv->busy) return E_BUSY;
|
||||
|
||||
bool with_alarm = false;
|
||||
bool search_reset = false;
|
||||
|
||||
switch (command) {
|
||||
/**
|
||||
* This is the delay function for DS1820 measurements.
|
||||
*
|
||||
* Parasitic: Returns success after the required 750ms
|
||||
* Non-parasitic: Returns SUCCESS after device responds '1', HW_TIMEOUT after 1s
|
||||
*/
|
||||
case CMD_POLL_FOR_1:
|
||||
// This can't be exposed via the UU API, due to being async
|
||||
unit->_tick_cnt = 0;
|
||||
unit->tick_interval = 750;
|
||||
if (priv->parasitic) {
|
||||
unit->tick_interval = 750;
|
||||
} else {
|
||||
unit->tick_interval = 10;
|
||||
}
|
||||
priv->busy = true;
|
||||
priv->busyStart = PTIM_GetTime();
|
||||
priv->busyRequestId = frame_id;
|
||||
return E_SUCCESS; // We will respond when the timer expires
|
||||
|
||||
/** Search devices with alarm. No payload, restarts the search. */
|
||||
case CMD_SEARCH_ALARM:
|
||||
with_alarm = true;
|
||||
// fall-through
|
||||
/** Search all devices. No payload, restarts the search. */
|
||||
case CMD_SEARCH_ADDR:
|
||||
search_reset = true;
|
||||
// fall-through
|
||||
/** Continue a previously begun search. */
|
||||
case CMD_SEARCH_CONTINUE:;
|
||||
uint32_t found_count = 0;
|
||||
bool have_more = false;
|
||||
if (!search_reset && priv->searchState.status != OW_SEARCH_MORE) {
|
||||
dbg("Search not ongoing!");
|
||||
return E_PROTOCOL_BREACH;
|
||||
}
|
||||
|
||||
TRY(UU_1WIRE_Search(unit, with_alarm, search_reset,
|
||||
(void *) unit_tmp512, UNIT_TMP_LEN/8, &found_count,
|
||||
&have_more));
|
||||
|
||||
// use multipart to avoid allocating extra buffer
|
||||
uint8_t status_code = (uint8_t) have_more;
|
||||
TF_Msg msg = {
|
||||
.frame_id = frame_id,
|
||||
.type = MSG_SUCCESS,
|
||||
.len = (TF_LEN) (found_count * 8 + 1),
|
||||
};
|
||||
TF_Respond_Multipart(comm, &msg);
|
||||
TF_Multipart_Payload(comm, &status_code, 1);
|
||||
// the codes are back-to-back stored inside the buffer, we send it directly
|
||||
// (it's already little-endian, as if built by PayloadBuilder)
|
||||
TF_Multipart_Payload(comm, (uint8_t *) unit_tmp512, found_count * 8);
|
||||
TF_Multipart_Close(comm);
|
||||
return E_SUCCESS;
|
||||
|
||||
/** Simply check presence of any devices on the bus. Responds with SUCCESS or HW_TIMEOUT */
|
||||
case CMD_CHECK_PRESENCE:
|
||||
TRY(UU_1WIRE_CheckPresence(unit, &presence));
|
||||
|
||||
com_respond_u8(frame_id, (uint8_t) presence);
|
||||
return E_SUCCESS;
|
||||
|
||||
/** Read address of the single device on the bus - returns u64 */
|
||||
case CMD_READ_ADDR:
|
||||
TRY(UU_1WIRE_ReadAddress(unit, &addr));
|
||||
|
||||
// build response
|
||||
PayloadBuilder pb = pb_start(unit_tmp512, UNIT_TMP_LEN, NULL);
|
||||
pb_u64(&pb, addr);
|
||||
com_respond_pb(frame_id, MSG_SUCCESS, &pb);
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Write payload to the bus, no confirmation (unless requested).
|
||||
*
|
||||
* Payload:
|
||||
* addr:u64, rest:write_data
|
||||
* if addr is 0, use SKIP_ROM
|
||||
*/
|
||||
case CMD_QUERY:
|
||||
addr = pp_u64(pp);
|
||||
tail = pp_tail(pp, &remain);
|
||||
TRY(UU_1WIRE_Write(unit, addr, tail, remain));
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Write and read.
|
||||
*
|
||||
* Payload:
|
||||
* addr:u64, read_len:u16, rest:write_data
|
||||
* if addr is 0, use SKIP_ROM
|
||||
*/
|
||||
case CMD_READ:
|
||||
addr = pp_u64(pp);
|
||||
uint16_t rcount = pp_u16(pp);
|
||||
bool test_crc = pp_bool(pp);
|
||||
tail = pp_tail(pp, &remain);
|
||||
|
||||
TRY(UU_1WIRE_Read(unit, addr,
|
||||
tail, remain,
|
||||
(uint8_t *) unit_tmp512, rcount,
|
||||
test_crc));
|
||||
|
||||
// build response
|
||||
com_respond_buf(frame_id, MSG_SUCCESS, (uint8_t *) unit_tmp512, rcount);
|
||||
return E_SUCCESS;
|
||||
|
||||
default:
|
||||
return E_UNKNOWN_COMMAND;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Unit template */
|
||||
const UnitDriver UNIT_1WIRE = {
|
||||
.name = "1WIRE",
|
||||
.description = "1-Wire master",
|
||||
// Settings
|
||||
.preInit = OW_preInit,
|
||||
.cfgLoadBinary = OW_loadBinary,
|
||||
.cfgWriteBinary = OW_writeBinary,
|
||||
.cfgLoadIni = OW_loadIni,
|
||||
.cfgWriteIni = OW_writeIni,
|
||||
// Init
|
||||
.init = OW_init,
|
||||
.deInit = OW_deInit,
|
||||
// Function
|
||||
.handleRequest = OW_handleRequest,
|
||||
.updateTick = OW_tickHandler,
|
||||
};
|
||||
@@ -1,79 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/01/02.
|
||||
//
|
||||
// Dallas 1-Wire master unit
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_UNIT_1WIRE_H
|
||||
#define GEX_F072_UNIT_1WIRE_H
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
extern const UnitDriver UNIT_1WIRE;
|
||||
|
||||
/**
|
||||
* Check if there are any units present on the bus
|
||||
*
|
||||
* @param[in,out] unit
|
||||
* @param[out] presence - any devices present
|
||||
* @return success
|
||||
*/
|
||||
error_t UU_1WIRE_CheckPresence(Unit *unit, bool *presence);
|
||||
|
||||
/**
|
||||
* Read a device's address (use only with a single device attached)
|
||||
*
|
||||
* @param[in,out] unit
|
||||
* @param[out] address - the device's address, 0 on error or CRC mismatch
|
||||
* @return success
|
||||
*/
|
||||
error_t UU_1WIRE_ReadAddress(Unit *unit, uint64_t *address);
|
||||
|
||||
/**
|
||||
* Write bytes to a device / devices
|
||||
*
|
||||
* @param[in,out] unit
|
||||
* @param[in] address - device address, 0 to skip match (single device or broadcast)
|
||||
* @param[in] buff - bytes to write
|
||||
* @param[in] len - buffer length
|
||||
* @return success
|
||||
*/
|
||||
error_t UU_1WIRE_Write(Unit *unit, uint64_t address, const uint8_t *buff, uint32_t len);
|
||||
|
||||
/**
|
||||
* Read bytes from a device / devices, first writing a query
|
||||
*
|
||||
* @param[in,out] unit
|
||||
* @param[in] address - device address, 0 to skip match (single device ONLY!)
|
||||
* @param[in] request_buff - bytes to write before reading a response
|
||||
* @param[in] request_len - number of bytes to write
|
||||
* @param[out] response_buff - buffer for storing the read response
|
||||
* @param[in] response_len - number of bytes to read
|
||||
* @param[in] check_crc - verify CRC
|
||||
* @return success
|
||||
*/
|
||||
error_t UU_1WIRE_Read(Unit *unit, uint64_t address,
|
||||
const uint8_t *request_buff, uint32_t request_len,
|
||||
uint8_t *response_buff, uint32_t response_len, bool check_crc);
|
||||
|
||||
/**
|
||||
* Perform a ROM search operation.
|
||||
* The algorithm is on a depth-first search without backtracking,
|
||||
* taking advantage of the open-drain topology.
|
||||
*
|
||||
* This function either starts the search, or continues it.
|
||||
*
|
||||
* @param[in,out] unit
|
||||
* @param[in] with_alarm - true to match only devices in alarm state
|
||||
* @param[in] restart - true to restart the search (search from the lowest address)
|
||||
* @param[out] buffer - buffer for storing found addresses
|
||||
* @param[in] capacity - buffer capacity in address entries (8 bytes)
|
||||
* @param[out] real_count - real number of found addresses (for which the CRC matched)
|
||||
* @param[out] have_more - flag indicating there are more devices to be found
|
||||
* @return success
|
||||
*/
|
||||
error_t UU_1WIRE_Search(Unit *unit, bool with_alarm, bool restart,
|
||||
uint64_t *buffer, uint32_t capacity, uint32_t *real_count,
|
||||
bool *have_more);
|
||||
|
||||
#endif //GEX_F072_UNIT_1WIRE_H
|
||||
@@ -1,656 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/04.
|
||||
//
|
||||
// The core functionality of the ADC unit is defined here.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
#include "unit_adc.h"
|
||||
|
||||
#define ADC_INTERNAL
|
||||
#include "_adc_internal.h"
|
||||
|
||||
#define DMA_POS(priv) ((priv)->buf_itemcount - (priv)->DMA_CHx->CNDTR)
|
||||
|
||||
/**
|
||||
* Async job to send a chunk of the DMA buffer to PC.
|
||||
* This can't be done directly because the interrupt couldn't wait for the TinyFrame mutex.
|
||||
*
|
||||
* unit - unit
|
||||
* data1 - start index
|
||||
* data2 - number of samples to send
|
||||
* data3 - bit flags: 0x80 if this is the last sample and we should close
|
||||
* 0x01 if this was the TC interrupt (otherwise it's HT)
|
||||
*/
|
||||
static void UADC_JobSendBlockChunk(Job *job)
|
||||
{
|
||||
Unit *unit = job->unit;
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
const uint32_t start = job->data1;
|
||||
const uint32_t count = job->data2;
|
||||
const bool close = (bool) (job->data3 & 0x80);
|
||||
const bool tc = (bool) (job->data3 & 0x01);
|
||||
|
||||
const TF_TYPE type = close ? EVT_CAPT_DONE : EVT_CAPT_MORE;
|
||||
|
||||
TF_Msg msg = {
|
||||
.frame_id = priv->stream_frame_id,
|
||||
.len = (TF_LEN) (1 /*seq*/ + count * sizeof(uint16_t)),
|
||||
.type = type,
|
||||
};
|
||||
|
||||
assert_param(true == TF_Respond_Multipart(comm, &msg));
|
||||
TF_Multipart_Payload(comm, &priv->stream_serial, 1);
|
||||
TF_Multipart_Payload(comm, (uint8_t *) (priv->dma_buffer + start), count * sizeof(uint16_t));
|
||||
TF_Multipart_Close(comm);
|
||||
|
||||
// Clear the "busy" flags - those are checked in the DMA ISR to detect overrun
|
||||
if (tc) priv->tc_pending = false;
|
||||
else priv->ht_pending = false;
|
||||
|
||||
priv->stream_serial++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Async job to send the trigger header.
|
||||
* The header includes info about the trigger + the pre-trigger buffer.
|
||||
*
|
||||
* data1 - index in the DMA buffer at which the captured data willl start
|
||||
* data2 - edge type - 1 rise, 2 fall, 3 forced
|
||||
* timestamp - event stamp
|
||||
* unit - unit
|
||||
*/
|
||||
static void UADC_JobSendTriggerCaptureHeader(Job *job)
|
||||
{
|
||||
Unit *unit = job->unit;
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
EventReport er = {
|
||||
.unit = unit,
|
||||
.type = EVT_CAPT_START,
|
||||
.timestamp = job->timestamp,
|
||||
.length = (priv->pretrig_len + 1) * // see below why +1
|
||||
priv->nb_channels *
|
||||
sizeof(uint16_t) +
|
||||
4 /*pretrig len*/ +
|
||||
1 /*edge*/ +
|
||||
1 /* seq */
|
||||
};
|
||||
|
||||
uint32_t index_trigd = job->data1;
|
||||
uint8_t edge = (uint8_t) job->data2;
|
||||
|
||||
EventReport_Start(&er);
|
||||
priv->stream_frame_id = er.sent_msg_id;
|
||||
{
|
||||
// preamble
|
||||
uint8_t buf[4];
|
||||
PayloadBuilder pb = pb_start(buf, 4, NULL);
|
||||
pb_u32(&pb, priv->pretrig_len);
|
||||
pb_u8(&pb, edge);
|
||||
pb_u8(&pb, priv->stream_serial++); // This is the serial counter for the first chunk
|
||||
// (containing the pre-trigger, or empty if no pretrig configured)
|
||||
EventReport_PB(&pb);
|
||||
|
||||
if (priv->pretrig_len > 0) {
|
||||
// pretrig
|
||||
|
||||
// +1 because we want pretrig 0 to exactly start with the triggering sample
|
||||
uint32_t pretrig_remain = (priv->pretrig_len + 1) * priv->nb_channels;
|
||||
|
||||
assert_param(index_trigd <= priv->buf_itemcount);
|
||||
|
||||
// this is one past the last entry of the triggering capture group
|
||||
if (pretrig_remain > index_trigd) {
|
||||
// used items in the wrap-around part of the buffer
|
||||
uint32_t items_from_end = pretrig_remain - index_trigd;
|
||||
assert_param(priv->buf_itemcount - items_from_end >= index_trigd);
|
||||
|
||||
EventReport_Data((uint8_t *) &priv->dma_buffer[priv->buf_itemcount - items_from_end],
|
||||
items_from_end * sizeof(uint16_t));
|
||||
|
||||
assert_param(items_from_end <= pretrig_remain);
|
||||
pretrig_remain -= items_from_end;
|
||||
}
|
||||
|
||||
assert_param(pretrig_remain <= index_trigd);
|
||||
EventReport_Data((uint8_t *) &priv->dma_buffer[index_trigd - pretrig_remain],
|
||||
pretrig_remain * sizeof(uint16_t));
|
||||
}
|
||||
}
|
||||
EventReport_End();
|
||||
}
|
||||
|
||||
/**
|
||||
* Async job to notify about end of stream
|
||||
*/
|
||||
static void UADC_JobSendEndOfStreamMsg(Job *job)
|
||||
{
|
||||
TF_Msg msg = {
|
||||
.type = EVT_CAPT_DONE,
|
||||
.frame_id = (TF_ID) job->data1
|
||||
};
|
||||
TF_Respond(comm, &msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule sending a event report to the PC that the current stream has ended.
|
||||
* The client library should handle this appropriately.
|
||||
*/
|
||||
void UADC_ReportEndOfStream(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
Job j = {
|
||||
.unit = unit,
|
||||
.data1 = priv->stream_frame_id, // copy the ID, it may be invalid by the time the cb gets executed
|
||||
.cb = UADC_JobSendEndOfStreamMsg
|
||||
};
|
||||
scheduleJob(&j);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a helper function for the ADC DMA interrupt for handing the different interrupt types (half / full transfer).
|
||||
* It sends the part of the buffer that was just captured via an async job, or aborts on overrun.
|
||||
*
|
||||
* It's split off here to allow calling it for the different flags without repeating code.
|
||||
*
|
||||
* @param unit
|
||||
* @param tc - true if this is the TC interrupt, else HT
|
||||
*/
|
||||
static void handle_httc(Unit *unit, bool tc)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
uint32_t start = priv->stream_startpos;
|
||||
uint32_t end;
|
||||
const bool ht = !tc;
|
||||
|
||||
const bool m_trigd = priv->opmode == ADC_OPMODE_TRIGD;
|
||||
const bool m_stream = priv->opmode == ADC_OPMODE_STREAM;
|
||||
const bool m_fixcpt = priv->opmode == ADC_OPMODE_BLCAP;
|
||||
|
||||
if (ht) {
|
||||
end = (priv->buf_itemcount >> 1); // div2
|
||||
}
|
||||
else {
|
||||
end = priv->buf_itemcount;
|
||||
}
|
||||
|
||||
if (start != end) { // this sometimes happened after a trigger, may be unnecessary now
|
||||
if (end < start) {
|
||||
// this was a trap for a bug with missed TC irq, it's hopefully fixed now
|
||||
trap("end < start! %d < %d, tc %d", (int)end, (int)start, (int)tc);
|
||||
}
|
||||
|
||||
uint32_t sgcount = (end - start) / priv->nb_channels;
|
||||
|
||||
if (m_trigd || m_fixcpt) {
|
||||
sgcount = MIN(priv->trig_stream_remain, sgcount);
|
||||
priv->trig_stream_remain -= sgcount;
|
||||
}
|
||||
|
||||
// Check for the closing condition
|
||||
const bool close = !m_stream && priv->trig_stream_remain == 0;
|
||||
|
||||
if ((tc && priv->tc_pending) || (ht && priv->ht_pending)) {
|
||||
dbg("(!) ADC DMA not handled in time, abort capture");
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_EMERGENCY_SHUTDOWN);
|
||||
return;
|
||||
}
|
||||
|
||||
// Here we set the tc/ht pending flags for detecting overrun
|
||||
|
||||
Job j = {
|
||||
.unit = unit,
|
||||
.data1 = start,
|
||||
.data2 = sgcount * priv->nb_channels,
|
||||
.data3 = (uint32_t) (close*0x80) | (tc*1), // bitfields to indicate what's happening
|
||||
.cb = UADC_JobSendBlockChunk
|
||||
};
|
||||
|
||||
if (tc)
|
||||
priv->tc_pending = true;
|
||||
else
|
||||
priv->ht_pending = true;
|
||||
|
||||
if (!scheduleJob(&j)) {
|
||||
// Abort if we can't queue - the stream would tear and we'd hog the system with error messages
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_EMERGENCY_SHUTDOWN);
|
||||
return;
|
||||
}
|
||||
|
||||
if (close) {
|
||||
// If auto-arm is enabled, we need to re-arm again.
|
||||
// However, EOS irq is disabled during the capture so the trigger edge detection would
|
||||
// work on stale data from before this trigger. We have to wait for the next full
|
||||
// conversion (EOS) before arming.
|
||||
UADC_SwitchMode(unit, (priv->auto_rearm && m_trigd) ? ADC_OPMODE_REARM_PENDING : ADC_OPMODE_IDLE);
|
||||
}
|
||||
}
|
||||
|
||||
// Advance the starting position
|
||||
if (tc) {
|
||||
priv->stream_startpos = 0;
|
||||
}
|
||||
else {
|
||||
priv->stream_startpos = priv->buf_itemcount >> 1; // div2
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IRQ handler for the DMA flags.
|
||||
*
|
||||
* We handle flags:
|
||||
* TC - transfer complete
|
||||
* HT - half transfer
|
||||
* TE - transfer error (this should never happen unless there's a bug)
|
||||
*
|
||||
* The buffer works in a circular mode, so we always handle the previous half
|
||||
* or what of it should be sent (if capture started somewhere inside).
|
||||
*
|
||||
* @param arg - the unit, passed via the irq dispatcher
|
||||
*/
|
||||
void UADC_DMA_Handler(void *arg)
|
||||
{
|
||||
Unit *unit = arg;
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
// First thing, grab the flags. They may change during the function.
|
||||
// Working on the live register might cause race conditions.
|
||||
const uint32_t isrsnapshot = priv->DMAx->ISR;
|
||||
|
||||
if (priv->opmode == ADC_OPMODE_UNINIT) {
|
||||
// the IRQ occured while switching mode, clear flags and do nothing else
|
||||
LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum);
|
||||
LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum);
|
||||
LL_DMA_ClearFlag_TE(priv->DMAx, priv->dma_chnum);
|
||||
return;
|
||||
}
|
||||
|
||||
if (LL_DMA_IsActiveFlag_G(isrsnapshot, priv->dma_chnum)) {
|
||||
// we have some flags set - check which
|
||||
const bool tc = LL_DMA_IsActiveFlag_TC(isrsnapshot, priv->dma_chnum);
|
||||
const bool ht = LL_DMA_IsActiveFlag_HT(isrsnapshot, priv->dma_chnum);
|
||||
const bool te = LL_DMA_IsActiveFlag_TE(isrsnapshot, priv->dma_chnum);
|
||||
|
||||
if (ht) LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum);
|
||||
if (tc) LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum);
|
||||
|
||||
if (te) {
|
||||
// this shouldn't happen - error
|
||||
adc_dbg("ADC DMA TE!");
|
||||
LL_DMA_ClearFlag_TE(priv->DMAx, priv->dma_chnum);
|
||||
return;
|
||||
}
|
||||
|
||||
// check what mode we're in
|
||||
const bool m_trigd = priv->opmode == ADC_OPMODE_TRIGD;
|
||||
const bool m_stream = priv->opmode == ADC_OPMODE_STREAM;
|
||||
const bool m_fixcpt = priv->opmode == ADC_OPMODE_BLCAP;
|
||||
if (m_trigd || m_stream || m_fixcpt) {
|
||||
const uint32_t half = (uint32_t) (priv->buf_itemcount >> 1); // div2
|
||||
if (ht && tc) {
|
||||
// dual event interrupt - may happen if we missed both and they were pending after
|
||||
// interrupts became enabled again (this can happen due to the EOS or other higher prio irq's)
|
||||
|
||||
if (priv->stream_startpos > half) {
|
||||
handle_httc(unit, true); // TC
|
||||
handle_httc(unit, false); // HT
|
||||
} else {
|
||||
handle_httc(unit, false); // HT
|
||||
handle_httc(unit, true); // TC
|
||||
}
|
||||
} else {
|
||||
if (ht && priv->stream_startpos > half) {
|
||||
// We missed the TC interrupt while e.g. setting up the stream / interrupt. catch up!
|
||||
// This fixes a bug with "negative size" for report.
|
||||
handle_httc(unit, true); // TC
|
||||
}
|
||||
|
||||
handle_httc(unit, tc);
|
||||
}
|
||||
} else {
|
||||
// This shouldn't happen, the interrupt should be disabled in this opmode
|
||||
dbg("(!) not streaming, ADC DMA IT should be disabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* End of measurement group interrupt handler.
|
||||
* This interrupt records the measured values and checks for trigger.
|
||||
*
|
||||
* @param arg - unit, passed b y irq dispatcher
|
||||
*/
|
||||
void UADC_ADC_EOS_Handler(void *arg)
|
||||
{
|
||||
Unit *unit = arg;
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
// Normally
|
||||
uint64_t timestamp = 0;
|
||||
if (priv->opmode == ADC_OPMODE_ARMED) timestamp = PTIM_GetMicrotime();
|
||||
|
||||
LL_ADC_ClearFlag_EOS(priv->ADCx);
|
||||
if (priv->opmode == ADC_OPMODE_UNINIT) return;
|
||||
|
||||
// Wait for the DMA to complete copying the last sample
|
||||
uint32_t dmapos = DMA_POS(priv);
|
||||
if ((DMA_POS(priv) % priv->nb_channels) != 0) {
|
||||
hw_wait_while((dmapos = DMA_POS(priv)) % priv->nb_channels != 0, 100); // XXX this could be changed to reading it from the DR instead
|
||||
}
|
||||
|
||||
uint32_t sample_pos;
|
||||
if (dmapos == 0) {
|
||||
sample_pos = (uint32_t) (priv->buf_itemcount);
|
||||
} else {
|
||||
sample_pos = dmapos;
|
||||
}
|
||||
sample_pos -= priv->nb_channels;
|
||||
|
||||
int cnt = 0; // index of the sample within the group
|
||||
|
||||
const bool can_average = priv->real_frequency_int < UADC_MAX_FREQ_FOR_AVERAGING;
|
||||
const uint32_t channels_mask = priv->channels_mask;
|
||||
|
||||
for (uint8_t i = 0; i < 18; i++) {
|
||||
if (channels_mask & (1 << i)) {
|
||||
const uint16_t val = priv->dma_buffer[sample_pos+cnt];
|
||||
cnt++;
|
||||
|
||||
if (can_average) {
|
||||
priv->averaging_bins[i] =
|
||||
priv->averaging_bins[i] * (1.0f - priv->avg_factor_as_float) +
|
||||
((float) val) * priv->avg_factor_as_float;
|
||||
}
|
||||
|
||||
priv->last_samples[i] = val;
|
||||
}
|
||||
}
|
||||
|
||||
if (priv->opmode == ADC_OPMODE_ARMED) {
|
||||
const uint16_t val = priv->last_samples[priv->trigger_source];
|
||||
|
||||
if ((priv->trig_prev_level < priv->trig_level) && val >= priv->trig_level && (bool) (priv->trig_edge & 0b01)) {
|
||||
// Rising edge
|
||||
UADC_HandleTrigger(unit, 0b01, timestamp);
|
||||
}
|
||||
else if ((priv->trig_prev_level > priv->trig_level) && val <= priv->trig_level && (bool) (priv->trig_edge & 0b10)) {
|
||||
// Falling edge
|
||||
UADC_HandleTrigger(unit, 0b10, timestamp);
|
||||
}
|
||||
priv->trig_prev_level = val;
|
||||
}
|
||||
|
||||
// auto-rearm was waiting for the next sample
|
||||
if (priv->opmode == ADC_OPMODE_REARM_PENDING) {
|
||||
if (!priv->auto_rearm) {
|
||||
// It looks like the flag was cleared by DISARM before we got a new sample.
|
||||
// Let's just switch to IDLE
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_IDLE);
|
||||
} else {
|
||||
// Re-arming for a new trigger
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_ARMED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a detected trigger - start capture if we're not in hold-off
|
||||
*
|
||||
* @param unit
|
||||
* @param edge_type - edge type, is included in the report
|
||||
* @param timestamp - event time
|
||||
*/
|
||||
void UADC_HandleTrigger(Unit *unit, uint8_t edge_type, uint64_t timestamp)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
if (priv->opmode == ADC_OPMODE_UNINIT) return;
|
||||
|
||||
if (priv->trig_holdoff != 0 && priv->trig_holdoff_remain > 0) {
|
||||
// Trig discarded due to holdoff
|
||||
return;
|
||||
}
|
||||
|
||||
if (priv->trig_holdoff > 0) {
|
||||
priv->trig_holdoff_remain = priv->trig_holdoff;
|
||||
// Start the tick
|
||||
unit->tick_interval = 1;
|
||||
unit->_tick_cnt = 1;
|
||||
}
|
||||
|
||||
priv->stream_startpos = DMA_POS(priv);
|
||||
priv->trig_stream_remain = priv->trig_len;
|
||||
priv->stream_serial = 0;
|
||||
|
||||
// This func may be called from the EOS interrupt, so it's safer to send the header message asynchronously
|
||||
Job j = {
|
||||
.unit = unit,
|
||||
.timestamp = timestamp,
|
||||
.data1 = priv->stream_startpos,
|
||||
.data2 = edge_type,
|
||||
.cb = UADC_JobSendTriggerCaptureHeader
|
||||
};
|
||||
scheduleJob(&j);
|
||||
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_TRIGD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Abort ongoing capture.
|
||||
*/
|
||||
void UADC_AbortCapture(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
const enum uadc_opmode old_opmode = priv->opmode;
|
||||
|
||||
priv->auto_rearm = false;
|
||||
|
||||
if (old_opmode == ADC_OPMODE_BLCAP ||
|
||||
old_opmode == ADC_OPMODE_STREAM ||
|
||||
old_opmode == ADC_OPMODE_TRIGD) {
|
||||
UADC_ReportEndOfStream(unit);
|
||||
}
|
||||
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_IDLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a manual block capture.
|
||||
*
|
||||
* @param unit
|
||||
* @param len - number of samples (groups)
|
||||
* @param frame_id - TF session to re-use for the report (client has a listener set up)
|
||||
*/
|
||||
void UADC_StartBlockCapture(Unit *unit, uint32_t len, TF_ID frame_id)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
if (priv->opmode == ADC_OPMODE_UNINIT) return;
|
||||
|
||||
priv->stream_frame_id = frame_id;
|
||||
priv->stream_startpos = DMA_POS(priv);
|
||||
priv->trig_stream_remain = len;
|
||||
priv->stream_serial = 0;
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_BLCAP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a stream
|
||||
*
|
||||
* @param frame_id - TF session to re-use for the frames (client has a listener set up)
|
||||
*/
|
||||
void UADC_StartStream(Unit *unit, TF_ID frame_id)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
if (priv->opmode == ADC_OPMODE_UNINIT) return;
|
||||
|
||||
priv->stream_frame_id = frame_id;
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_STREAM);
|
||||
}
|
||||
|
||||
/**
|
||||
* End a stream by user request.
|
||||
*/
|
||||
void UADC_StopStream(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
if (priv->opmode == ADC_OPMODE_UNINIT) return;
|
||||
|
||||
UADC_ReportEndOfStream(unit);
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_IDLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle unit update tick - expire the trigger hold-off.
|
||||
* We also check for the emergency shutdown condition and clear it.
|
||||
*/
|
||||
void UADC_updateTick(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
// Recover from shutdown after a delay
|
||||
if (priv->opmode == ADC_OPMODE_EMERGENCY_SHUTDOWN) {
|
||||
adc_dbg("ADC recovering from emergency shutdown");
|
||||
|
||||
UADC_ReportEndOfStream(unit);
|
||||
LL_TIM_EnableCounter(priv->TIMx);
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_IDLE);
|
||||
unit->tick_interval = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (priv->trig_holdoff_remain > 0) {
|
||||
priv->trig_holdoff_remain--;
|
||||
|
||||
if (priv->trig_holdoff_remain == 0) {
|
||||
unit->tick_interval = 0;
|
||||
unit->_tick_cnt = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch the ADC operational mode.
|
||||
*
|
||||
* @param unit
|
||||
* @param new_mode - mode to set
|
||||
*/
|
||||
void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
const enum uadc_opmode old_mode = priv->opmode;
|
||||
if (new_mode == old_mode) return; // nothing to do
|
||||
|
||||
// if un-itied, can go only to IDLE
|
||||
assert_param((old_mode != ADC_OPMODE_UNINIT) || (new_mode == ADC_OPMODE_IDLE));
|
||||
|
||||
priv->opmode = ADC_OPMODE_UNINIT;
|
||||
|
||||
if (new_mode == ADC_OPMODE_UNINIT) {
|
||||
adc_dbg("ADC switch -> UNINIT");
|
||||
// Stop the DMA, timer and disable ADC - this is called before tearing down the unit
|
||||
LL_TIM_DisableCounter(priv->TIMx);
|
||||
LL_ADC_ClearFlag_EOS(priv->ADCx);
|
||||
LL_ADC_DisableIT_EOS(priv->ADCx);
|
||||
|
||||
// Switch off the ADC
|
||||
if (LL_ADC_IsEnabled(priv->ADCx)) {
|
||||
// Cancel ongoing conversion
|
||||
if (LL_ADC_REG_IsConversionOngoing(priv->ADCx)) {
|
||||
LL_ADC_REG_StopConversion(priv->ADCx);
|
||||
hw_wait_while(LL_ADC_REG_IsStopConversionOngoing(priv->ADCx), 100);
|
||||
}
|
||||
|
||||
LL_ADC_Disable(priv->ADCx);
|
||||
hw_wait_while(LL_ADC_IsDisableOngoing(priv->ADCx), 100);
|
||||
}
|
||||
|
||||
LL_DMA_DisableChannel(priv->DMAx, priv->dma_chnum);
|
||||
LL_DMA_DisableIT_HT(priv->DMAx, priv->dma_chnum);
|
||||
LL_DMA_DisableIT_TC(priv->DMAx, priv->dma_chnum);
|
||||
LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum);
|
||||
LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum);
|
||||
}
|
||||
else if (new_mode == ADC_OPMODE_IDLE || new_mode == ADC_OPMODE_REARM_PENDING) {
|
||||
// IDLE and ARMED are identical with the exception that the trigger condition is not checked
|
||||
// ARMED can be only entered from IDLE, thus we do the init only here.
|
||||
|
||||
priv->tc_pending = false;
|
||||
priv->ht_pending = false;
|
||||
|
||||
// In IDLE, we don't need the DMA interrupts
|
||||
LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum);
|
||||
LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum);
|
||||
LL_DMA_DisableIT_HT(priv->DMAx, priv->dma_chnum);
|
||||
LL_DMA_DisableIT_TC(priv->DMAx, priv->dma_chnum);
|
||||
|
||||
// Use End Of Sequence to recover results for averaging from the DMA buffer and DR
|
||||
LL_ADC_ClearFlag_EOS(priv->ADCx);
|
||||
LL_ADC_EnableIT_EOS(priv->ADCx);
|
||||
|
||||
if (old_mode == ADC_OPMODE_UNINIT) {
|
||||
// Nothing is started yet - this is the only way to leave UNINIT
|
||||
LL_ADC_Enable(priv->ADCx);
|
||||
LL_DMA_EnableChannel(priv->DMAx, priv->dma_chnum);
|
||||
LL_TIM_EnableCounter(priv->TIMx);
|
||||
|
||||
LL_ADC_REG_StartConversion(priv->ADCx);
|
||||
}
|
||||
}
|
||||
else if (new_mode == ADC_OPMODE_EMERGENCY_SHUTDOWN) {
|
||||
adc_dbg("ADC switch -> EMERGENCY_STOP");
|
||||
// Emergency shutdown is used when the job queue overflows and the stream is torn
|
||||
// This however doesn't help in the case when user sets such a high frequency
|
||||
// that the whole app becomes unresponsive due to the completion ISR, need to verify the value manually.
|
||||
|
||||
LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum);
|
||||
LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum);
|
||||
LL_DMA_DisableIT_HT(priv->DMAx, priv->dma_chnum);
|
||||
LL_DMA_DisableIT_TC(priv->DMAx, priv->dma_chnum);
|
||||
|
||||
LL_TIM_DisableCounter(priv->TIMx);
|
||||
UADC_SetSampleRate(unit, 10000); // fallback to a known safe value
|
||||
|
||||
LL_ADC_ClearFlag_EOS(priv->ADCx);
|
||||
LL_ADC_DisableIT_EOS(priv->ADCx);
|
||||
|
||||
unit->tick_interval = 0;
|
||||
unit->_tick_cnt = 250; // 1-off
|
||||
}
|
||||
else if (new_mode == ADC_OPMODE_ARMED) {
|
||||
adc_dbg("ADC switch -> ARMED");
|
||||
assert_param(old_mode == ADC_OPMODE_IDLE || old_mode == ADC_OPMODE_REARM_PENDING);
|
||||
|
||||
// avoid firing immediately by the value jumping across the scale
|
||||
priv->trig_prev_level = priv->last_samples[priv->trigger_source];
|
||||
}
|
||||
else if (new_mode == ADC_OPMODE_TRIGD || new_mode == ADC_OPMODE_STREAM || new_mode == ADC_OPMODE_BLCAP) {
|
||||
adc_dbg("ADC switch -> CAPTURE");
|
||||
|
||||
assert_param(old_mode == ADC_OPMODE_ARMED || old_mode == ADC_OPMODE_IDLE);
|
||||
|
||||
// during the capture, we disallow direct readout and averaging to reduce overhead
|
||||
LL_ADC_DisableIT_EOS(priv->ADCx);
|
||||
|
||||
// Enable the DMA buffer interrupts
|
||||
|
||||
// we must first clear the flags, otherwise it will cause WEIRD bugs in the handler
|
||||
LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum);
|
||||
LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum);
|
||||
|
||||
// those must be as close as possible to the enabling
|
||||
// if not trig'd, we don't care for lost samples before (this could cause a DMA irq miss / ht/tc mismatch with the startpos)
|
||||
if (new_mode != ADC_OPMODE_TRIGD) {
|
||||
priv->stream_startpos = DMA_POS(priv);
|
||||
priv->stream_serial = 0;
|
||||
}
|
||||
LL_DMA_EnableIT_HT(priv->DMAx, priv->dma_chnum);
|
||||
LL_DMA_EnableIT_TC(priv->DMAx, priv->dma_chnum);
|
||||
}
|
||||
|
||||
priv->opmode = new_mode;
|
||||
}
|
||||
@@ -1,264 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
// ADC unit init and de-init functions
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
|
||||
#define ADC_INTERNAL
|
||||
#include "_adc_internal.h"
|
||||
|
||||
/** Allocate data structure and set defaults */
|
||||
error_t UADC_preInit(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data = calloc_ck(1, sizeof(struct priv));
|
||||
if (priv == NULL) return E_OUT_OF_MEM;
|
||||
|
||||
priv->cfg.channels = 1<<16; // Tsense by default - always available, easy testing
|
||||
priv->cfg.sample_time = 0b010; // 13.5c - good enough and the default 0b00 value really is useless
|
||||
priv->cfg.frequency = 1000;
|
||||
priv->cfg.buffer_size = 256; // in half-words
|
||||
priv->cfg.averaging_factor = 500; // 0.5
|
||||
|
||||
priv->opmode = ADC_OPMODE_UNINIT;
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/** Configure frequency */
|
||||
error_t UADC_SetSampleRate(Unit *unit, uint32_t hertz)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
uint16_t presc;
|
||||
uint32_t count;
|
||||
if (!hw_solve_timer(PLAT_APB1_HZ, hertz, true, &presc, &count, &priv->real_frequency)) {
|
||||
dbg("Failed to resolve timer params.");
|
||||
return E_BAD_VALUE;
|
||||
}
|
||||
adc_dbg("Frequency error %d ppm, presc %d, count %d",
|
||||
(int) lrintf(1000000.0f * ((priv->real_frequency - hertz) / (float) hertz)),
|
||||
(int) presc, (int) count);
|
||||
|
||||
LL_TIM_SetPrescaler(priv->TIMx, (uint32_t) (presc - 1));
|
||||
LL_TIM_SetAutoReload(priv->TIMx, count - 1);
|
||||
|
||||
priv->real_frequency_int = hertz;
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the ADC DMA.
|
||||
* This is split to its own function because it's also called when the user adjusts the
|
||||
* enabled channels and we need to re-configure it.
|
||||
*
|
||||
* @param unit
|
||||
*/
|
||||
void UADC_SetupDMA(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
adc_dbg("Setting up DMA");
|
||||
{
|
||||
uint32_t itemcount = priv->nb_channels * (priv->cfg.buffer_size / (priv->nb_channels));
|
||||
if (itemcount % 2 == 1) itemcount -= priv->nb_channels; // ensure the count is even
|
||||
priv->buf_itemcount = itemcount;
|
||||
|
||||
adc_dbg("DMA item count is %d (%d bytes), There are %d samples per group.",
|
||||
(int)priv->buf_itemcount,
|
||||
(int)(priv->buf_itemcount * sizeof(uint16_t)),
|
||||
(int)priv->nb_channels);
|
||||
|
||||
{
|
||||
LL_DMA_InitTypeDef init;
|
||||
LL_DMA_StructInit(&init);
|
||||
init.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
|
||||
|
||||
init.Mode = LL_DMA_MODE_CIRCULAR;
|
||||
init.NbData = itemcount;
|
||||
|
||||
init.PeriphOrM2MSrcAddress = (uint32_t) &priv->ADCx->DR;
|
||||
init.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD;
|
||||
init.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
|
||||
|
||||
init.MemoryOrM2MDstAddress = (uint32_t) priv->dma_buffer;
|
||||
init.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD;
|
||||
init.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
|
||||
|
||||
assert_param(SUCCESS == LL_DMA_Init(priv->DMAx, priv->dma_chnum, &init));
|
||||
}
|
||||
|
||||
// LL_DMA_EnableChannel(priv->DMAx, priv->dma_chnum); // this is done in the switch mode func now
|
||||
}
|
||||
}
|
||||
|
||||
/** Finalize unit set-up */
|
||||
error_t UADC_init(Unit *unit)
|
||||
{
|
||||
bool suc = true;
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
// Written for F072 which has only one ADC
|
||||
|
||||
TRY(rsc_claim(unit, R_ADC1));
|
||||
TRY(rsc_claim(unit, R_DMA1_1));
|
||||
TRY(rsc_claim(unit, R_TIM15));
|
||||
|
||||
priv->DMAx = DMA1;
|
||||
priv->DMA_CHx = DMA1_Channel1;
|
||||
priv->dma_chnum = 1;
|
||||
priv->ADCx = ADC1;
|
||||
priv->ADCx_Common = ADC1_COMMON;
|
||||
priv->TIMx = TIM15;
|
||||
|
||||
// ----------------------- CONFIGURE PINS --------------------------
|
||||
{
|
||||
// Claim and configure all analog pins
|
||||
priv->nb_channels = 0;
|
||||
for (uint8_t i = 0; i <= UADC_MAX_CHANNEL; i++) {
|
||||
if (priv->cfg.channels & (1 << i)) {
|
||||
priv->nb_channels++;
|
||||
|
||||
char c;
|
||||
uint8_t num;
|
||||
if (i <= 7) {
|
||||
c = 'A';
|
||||
num = i;
|
||||
}
|
||||
else if (i <= 9) {
|
||||
c = 'B';
|
||||
num = (uint8_t) (i - 8);
|
||||
}
|
||||
else if (i <= 15) {
|
||||
c = 'C';
|
||||
num = (uint8_t) (i - 10);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
TRY(rsc_claim_pin(unit, c, num));
|
||||
uint32_t ll_pin = hw_pin2ll(num, &suc);
|
||||
GPIO_TypeDef *port = hw_port2periph(c, &suc);
|
||||
assert_param(suc);
|
||||
|
||||
LL_GPIO_SetPinPull(port, ll_pin, LL_GPIO_PULL_NO);
|
||||
LL_GPIO_SetPinMode(port, ll_pin, LL_GPIO_MODE_ANALOG);
|
||||
}
|
||||
}
|
||||
|
||||
if (priv->nb_channels == 0) {
|
||||
dbg("Need at least 1 channel");
|
||||
return E_BAD_CONFIG;
|
||||
}
|
||||
|
||||
// ensure some minimal space is available
|
||||
if (priv->cfg.buffer_size < priv->nb_channels * 2) {
|
||||
dbg("Insufficient buf size");
|
||||
return E_BAD_CONFIG;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------- Alloc the buffer ----------------------
|
||||
adc_dbg("Allocating buffer of size %d half-words", (int)priv->cfg.buffer_size);
|
||||
priv->dma_buffer = calloc_ck(priv->cfg.buffer_size, sizeof(uint16_t));
|
||||
if (NULL == priv->dma_buffer) return E_OUT_OF_MEM;
|
||||
assert_param(((uint32_t) priv->dma_buffer & 3) == 0); // must be aligned
|
||||
|
||||
// ------------------- ENABLE CLOCKS --------------------------
|
||||
{
|
||||
// enable peripherals clock
|
||||
hw_periph_clock_enable(priv->ADCx);
|
||||
hw_periph_clock_enable(priv->TIMx);
|
||||
// DMA and GPIO clocks are enabled on startup automatically
|
||||
}
|
||||
|
||||
// ------------------- CONFIGURE THE TIMER --------------------------
|
||||
adc_dbg("Setting up TIMER");
|
||||
{
|
||||
TRY(UADC_SetSampleRate(unit, priv->cfg.frequency));
|
||||
|
||||
LL_TIM_EnableARRPreload(priv->TIMx);
|
||||
LL_TIM_EnableUpdateEvent(priv->TIMx);
|
||||
LL_TIM_SetTriggerOutput(priv->TIMx, LL_TIM_TRGO_UPDATE);
|
||||
LL_TIM_GenerateEvent_UPDATE(priv->TIMx); // load the prescaller value
|
||||
}
|
||||
|
||||
// --------------------- CONFIGURE THE ADC ---------------------------
|
||||
adc_dbg("Setting up ADC");
|
||||
{
|
||||
// Calibrate the ADC
|
||||
adc_dbg("Wait for calib");
|
||||
LL_ADC_StartCalibration(priv->ADCx);
|
||||
while (LL_ADC_IsCalibrationOnGoing(priv->ADCx)) {}
|
||||
adc_dbg("ADC calibrated.");
|
||||
|
||||
// Let's just enable the internal channels always - makes toggling them on-line easier
|
||||
LL_ADC_SetCommonPathInternalCh(priv->ADCx_Common, LL_ADC_PATH_INTERNAL_VREFINT | LL_ADC_PATH_INTERNAL_TEMPSENSOR);
|
||||
|
||||
LL_ADC_SetDataAlignment(priv->ADCx, LL_ADC_DATA_ALIGN_RIGHT);
|
||||
LL_ADC_SetResolution(priv->ADCx, LL_ADC_RESOLUTION_12B);
|
||||
LL_ADC_REG_SetDMATransfer(priv->ADCx, LL_ADC_REG_DMA_TRANSFER_UNLIMITED);
|
||||
|
||||
// configure channels
|
||||
priv->channels_mask = priv->cfg.channels;
|
||||
|
||||
priv->ADCx->CHSELR = priv->channels_mask;
|
||||
|
||||
LL_ADC_REG_SetTriggerSource(priv->ADCx, LL_ADC_REG_TRIG_EXT_TIM15_TRGO);
|
||||
|
||||
LL_ADC_SetSamplingTimeCommonChannels(priv->ADCx, LL_ADC_SAMPLETIMES[priv->cfg.sample_time]);
|
||||
|
||||
// will be enabled when switching to INIT mode
|
||||
}
|
||||
|
||||
// --------------------- CONFIGURE DMA -------------------------------
|
||||
UADC_SetupDMA(unit);
|
||||
|
||||
// prepare the avg factor float for the ISR
|
||||
if (priv->cfg.averaging_factor > 1000) priv->cfg.averaging_factor = 1000; // normalize
|
||||
priv->avg_factor_as_float = priv->cfg.averaging_factor/1000.0f;
|
||||
|
||||
adc_dbg("ADC peripherals configured.");
|
||||
|
||||
irqd_attach(priv->DMA_CHx, UADC_DMA_Handler, unit);
|
||||
irqd_attach(priv->ADCx, UADC_ADC_EOS_Handler, unit);
|
||||
adc_dbg("irqs attached");
|
||||
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_IDLE);
|
||||
adc_dbg("ADC done");
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/** Tear down the unit */
|
||||
void UADC_deInit(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
// de-init peripherals
|
||||
if (unit->status == E_SUCCESS ) {
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_UNINIT);
|
||||
|
||||
//LL_ADC_DeInit(priv->ADCx);
|
||||
LL_ADC_CommonDeInit(priv->ADCx_Common);
|
||||
LL_TIM_DeInit(priv->TIMx);
|
||||
|
||||
irqd_detach(priv->DMA_CHx, UADC_DMA_Handler);
|
||||
irqd_detach(priv->ADCx, UADC_ADC_EOS_Handler);
|
||||
|
||||
LL_DMA_DeInit(priv->DMAx, priv->dma_chnum);
|
||||
}
|
||||
|
||||
// free buffer if not NULL
|
||||
free_ck(priv->dma_buffer);
|
||||
|
||||
// Release all resources, deinit pins
|
||||
rsc_teardown(unit);
|
||||
|
||||
// Free memory
|
||||
free_ck(unit->data);
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
// Defines and prototypes used internally by the ADC unit.
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_ADC_INTERNAL_H
|
||||
#define GEX_F072_ADC_INTERNAL_H
|
||||
|
||||
#ifndef ADC_INTERNAL
|
||||
#error bad include!
|
||||
#endif
|
||||
|
||||
#include "unit_base.h"
|
||||
|
||||
//#define adc_dbg dbg
|
||||
#define adc_dbg(...) do {} while(0)
|
||||
|
||||
#define UADC_MAX_FREQ_FOR_AVERAGING 20000
|
||||
|
||||
#define UADC_MAX_CHANNEL 17
|
||||
|
||||
enum uadc_opmode {
|
||||
ADC_OPMODE_UNINIT, //!< Not yet switched to any mode
|
||||
ADC_OPMODE_IDLE, //!< Idle. Allows immediate value readout and averaging.
|
||||
ADC_OPMODE_REARM_PENDING, //!< Idle, waiting for the next sample to re-arm (auto trigger).
|
||||
ADC_OPMODE_ARMED, //!< Armed for a trigger. Direct access and averaging are disabled.
|
||||
ADC_OPMODE_TRIGD, //!< Triggered, sending pre-trigger and streaming captured data.
|
||||
ADC_OPMODE_BLCAP, //!< Capture of fixed length without a trigger
|
||||
ADC_OPMODE_STREAM, //!< Unlimited capture
|
||||
ADC_OPMODE_EMERGENCY_SHUTDOWN, //!< Used when the buffers overrun to safely transition to IDLE after a delay
|
||||
};
|
||||
|
||||
enum uadc_event {
|
||||
EVT_CAPT_START = 50, //!< Capture start (used in event in the first frame when trigger is detected)
|
||||
EVT_CAPT_MORE = 51, //!< Capture data payload (used as TYPE for all capture types)
|
||||
EVT_CAPT_DONE = 52, //!< End of trig'd or block capture payload (last frame with data),
|
||||
//!< or a farewell message after closing stream using abort(), in this case without data.
|
||||
};
|
||||
|
||||
/** Private data structure */
|
||||
struct priv {
|
||||
struct {
|
||||
uint32_t channels; //!< bit flags (will be recorded in order 0-15)
|
||||
uint8_t sample_time; //!< 0-7 (corresponds to 1.5-239.5 cycles) - time for the sampling capacitor to charge
|
||||
uint32_t frequency; //!< Timer frequency in Hz. Note: not all frequencies can be achieved accurately
|
||||
uint32_t buffer_size; //!< Buffer size in bytes (count 2 bytes per channel per measurement) - faster sampling freq needs bigger buffer
|
||||
uint16_t averaging_factor; //!< Exponential averaging factor 0-1000
|
||||
} cfg;
|
||||
|
||||
// Peripherals
|
||||
ADC_TypeDef *ADCx; //!< The ADC peripheral used
|
||||
ADC_Common_TypeDef *ADCx_Common; //!< The ADC common control block
|
||||
TIM_TypeDef *TIMx; //!< ADC timing timer instance
|
||||
DMA_TypeDef *DMAx; //!< DMA isnatnce used
|
||||
uint8_t dma_chnum; //!< DMA channel number
|
||||
DMA_Channel_TypeDef *DMA_CHx; //!< DMA channel instance
|
||||
|
||||
// Live config
|
||||
float real_frequency;
|
||||
uint32_t real_frequency_int;
|
||||
uint32_t channels_mask; //!< channels bitfield including tsense and vref
|
||||
float avg_factor_as_float;
|
||||
|
||||
uint16_t *dma_buffer; //!< malloc'd buffer for the samples
|
||||
uint8_t nb_channels; //!< nbr of enabled adc channels
|
||||
uint32_t buf_itemcount; //!< real size of the buffer in samples (adjusted to fit 2x whole multiple of sample group)
|
||||
|
||||
// Trigger state
|
||||
uint32_t trig_stream_remain; //!< Counter of samples remaining to be sent in the post-trigger stream
|
||||
uint16_t trig_holdoff_remain; //!< Tmp counter for the currently active hold-off
|
||||
uint16_t trig_prev_level; //!< Value of the previous sample, used to detect trigger edge
|
||||
uint32_t stream_startpos; //!< Byte offset in the DMA buffer where the next capture for a stream should start.
|
||||
//!< Updated in TH/TC and on trigger (after the preceding data is sent as a pretrig buffer)
|
||||
|
||||
enum uadc_opmode opmode; //!< OpMode (state machine state)
|
||||
float averaging_bins[18]; //!< Averaging buffers, enough space to accommodate all channels (16 external + 2 internal)
|
||||
uint16_t last_samples[18]; //!< If averaging is disabled, the last captured sample is stored here.
|
||||
|
||||
// Trigger config
|
||||
uint8_t trigger_source; //!< number of the pin selected as a trigger source
|
||||
uint32_t pretrig_len; //!< Pre-trigger length, nbr of historical samples to report when trigger occurs
|
||||
uint32_t trig_len; //!< Trigger length, nbr of samples to report AFTER a trigger occurs
|
||||
uint16_t trig_level; //!< Triggering level in LSB
|
||||
uint8_t trig_edge; //!< Which edge we want to trigger on. 1-rising, 2-falling, 3-both
|
||||
bool auto_rearm; //!< Flag that the trigger should be re-armed after the stream finishes
|
||||
uint16_t trig_holdoff; //!< Trigger hold-off time, set when configuring the trigger
|
||||
TF_ID stream_frame_id; //!< Session ID for multi-part stream (response or report)
|
||||
uint8_t stream_serial; //!< Serial nr of a stream frame
|
||||
|
||||
bool tc_pending;
|
||||
bool ht_pending;
|
||||
};
|
||||
|
||||
/** Allocate data structure and set defaults */
|
||||
error_t UADC_preInit(Unit *unit);
|
||||
|
||||
/** Load from a binary buffer stored in Flash */
|
||||
void UADC_loadBinary(Unit *unit, PayloadParser *pp);
|
||||
|
||||
/** Write to a binary buffer for storing in Flash */
|
||||
void UADC_writeBinary(Unit *unit, PayloadBuilder *pb);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Parse a key-value pair from the INI file */
|
||||
error_t UADC_loadIni(Unit *unit, const char *key, const char *value);
|
||||
|
||||
/** Generate INI file section for the unit */
|
||||
void UADC_writeIni(Unit *unit, IniWriter *iw);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Finalize unit set-up */
|
||||
error_t UADC_init(Unit *unit);
|
||||
|
||||
/** Tear down the unit */
|
||||
void UADC_deInit(Unit *unit);
|
||||
|
||||
/** Configure DMA (buffer count etc) */
|
||||
void UADC_SetupDMA(Unit *unit);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** DMA half/complete handler */
|
||||
void UADC_DMA_Handler(void *arg);
|
||||
|
||||
/** ADC eod of sequence handler */
|
||||
void UADC_ADC_EOS_Handler(void *arg);
|
||||
|
||||
/** Switch to a different opmode */
|
||||
void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode);
|
||||
|
||||
/** Handle trigger - process pre-trigger and start streaming the requested number of samples */
|
||||
void UADC_HandleTrigger(Unit *unit, uint8_t edge_type, uint64_t timestamp);
|
||||
|
||||
/** Handle a periodic tick - expiring the hold-off */
|
||||
void UADC_updateTick(Unit *unit);
|
||||
|
||||
/** Send a end-of-stream message to PC's stream listener so it can shut down. */
|
||||
void UADC_ReportEndOfStream(Unit *unit);
|
||||
|
||||
/** Start a block capture */
|
||||
void UADC_StartBlockCapture(Unit *unit, uint32_t len, TF_ID frame_id);
|
||||
|
||||
/** Start stream */
|
||||
void UADC_StartStream(Unit *unit, TF_ID frame_id);
|
||||
|
||||
/** End stream */
|
||||
void UADC_StopStream(Unit *unit);
|
||||
|
||||
/** Configure frequency */
|
||||
error_t UADC_SetSampleRate(Unit *unit, uint32_t hertz);
|
||||
|
||||
/** Abort capture */
|
||||
void UADC_AbortCapture(Unit *unit);
|
||||
|
||||
#endif //GEX_F072_ADC_INTERNAL_H
|
||||
@@ -1,106 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
// ADC unit settings reading / parsing
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
|
||||
#define ADC_INTERNAL
|
||||
#include "_adc_internal.h"
|
||||
|
||||
/** Load from a binary buffer stored in Flash */
|
||||
void UADC_loadBinary(Unit *unit, PayloadParser *pp)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
uint8_t version = pp_u8(pp);
|
||||
(void)version;
|
||||
|
||||
priv->cfg.channels = pp_u32(pp);
|
||||
priv->cfg.sample_time = pp_u8(pp);
|
||||
priv->cfg.frequency = pp_u32(pp);
|
||||
priv->cfg.buffer_size = pp_u32(pp);
|
||||
priv->cfg.averaging_factor = pp_u16(pp);
|
||||
}
|
||||
|
||||
/** Write to a binary buffer for storing in Flash */
|
||||
void UADC_writeBinary(Unit *unit, PayloadBuilder *pb)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
pb_u8(pb, 0); // version
|
||||
|
||||
pb_u32(pb, priv->cfg.channels);
|
||||
pb_u8(pb, priv->cfg.sample_time);
|
||||
pb_u32(pb, priv->cfg.frequency);
|
||||
pb_u32(pb, priv->cfg.buffer_size);
|
||||
pb_u16(pb, priv->cfg.averaging_factor);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Parse a key-value pair from the INI file */
|
||||
error_t UADC_loadIni(Unit *unit, const char *key, const char *value)
|
||||
{
|
||||
bool suc = true;
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
if (streq(key, "channels")) {
|
||||
priv->cfg.channels = cfg_pinmask_parse_32(value, &suc);
|
||||
}
|
||||
else if (streq(key, "sample_time")) {
|
||||
priv->cfg.sample_time = cfg_u8_parse(value, &suc);
|
||||
if (priv->cfg.sample_time > 7) return E_BAD_VALUE;
|
||||
}
|
||||
else if (streq(key, "frequency")) {
|
||||
priv->cfg.frequency = cfg_u32_parse(value, &suc);
|
||||
}
|
||||
else if (streq(key, "buffer_size")) {
|
||||
priv->cfg.buffer_size = cfg_u32_parse(value, &suc);
|
||||
}
|
||||
else if (streq(key, "avg_factor")) {
|
||||
priv->cfg.averaging_factor = cfg_u16_parse(value, &suc);
|
||||
if (priv->cfg.averaging_factor > 1000) return E_BAD_VALUE;
|
||||
}
|
||||
else {
|
||||
return E_BAD_KEY;
|
||||
}
|
||||
|
||||
if (!suc) return E_BAD_VALUE;
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/** Generate INI file section for the unit */
|
||||
void UADC_writeIni(Unit *unit, IniWriter *iw)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
iw_comment(iw, "Enabled channels, comma separated");
|
||||
iw_comment(iw, " 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17");
|
||||
iw_comment(iw, "A0 A1 A2 A3 A4 A5 A6 A7 B0 B1 C0 C1 C2 C3 C4 C5 Tsens Vref");
|
||||
iw_entry_s(iw, "channels", cfg_pinmask_encode(priv->cfg.channels, unit_tmp512, true));
|
||||
|
||||
iw_cmt_newline(iw);
|
||||
iw_comment(iw, "Sampling time (0-7)");
|
||||
iw_entry_d(iw, "sample_time", priv->cfg.sample_time);
|
||||
|
||||
iw_comment(iw, "Sampling frequency (Hz)");
|
||||
iw_entry_d(iw, "frequency", priv->cfg.frequency);
|
||||
|
||||
iw_cmt_newline(iw);
|
||||
iw_comment(iw, "Sample buffer size");
|
||||
iw_comment(iw, "- shared by all enabled channels");
|
||||
iw_comment(iw, "- defines the maximum pre-trigger size (divide by # of channels)");
|
||||
iw_comment(iw, "- captured data is sent in half-buffer chunks");
|
||||
iw_comment(iw, "- buffer overrun aborts the data capture");
|
||||
iw_entry_d(iw, "buffer_size", priv->cfg.buffer_size);
|
||||
|
||||
iw_cmt_newline(iw);
|
||||
iw_comment(iw, "Exponential averaging coefficient (permil, range 0-1000 ~ 0.000-1.000)");
|
||||
iw_comment(iw, "- used formula: y[t]=(1-k)*y[t-1]+k*u[t]");
|
||||
iw_comment(iw, "- not available when a capture is running");
|
||||
iw_entry_d(iw, "avg_factor", priv->cfg.averaging_factor);
|
||||
}
|
||||
|
||||
@@ -1,405 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2017/11/25.
|
||||
//
|
||||
|
||||
#include "unit_base.h"
|
||||
#include "unit_adc.h"
|
||||
|
||||
#define ADC_INTERNAL
|
||||
#include "_adc_internal.h"
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
enum AdcCmd_ {
|
||||
CMD_READ_RAW = 0,
|
||||
CMD_READ_SMOOTHED = 1,
|
||||
|
||||
CMD_GET_ENABLED_CHANNELS = 10,
|
||||
CMD_GET_SAMPLE_RATE = 11,
|
||||
|
||||
CMD_SETUP_TRIGGER = 20,
|
||||
CMD_ARM = 21,
|
||||
CMD_DISARM = 22,
|
||||
CMD_ABORT = 23, // abort any ongoing capture or stream
|
||||
CMD_FORCE_TRIGGER = 24,
|
||||
CMD_BLOCK_CAPTURE = 25,
|
||||
CMD_STREAM_START = 26,
|
||||
CMD_STREAM_STOP = 27,
|
||||
CMD_SET_SMOOTHING_FACTOR = 28,
|
||||
CMD_SET_SAMPLE_RATE = 29,
|
||||
CMD_ENABLE_CHANNELS = 30,
|
||||
CMD_SET_SAMPLE_TIME = 31,
|
||||
};
|
||||
|
||||
/** Handle a request message */
|
||||
static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
PayloadBuilder pb = pb_start(unit_tmp512, UNIT_TMP_LEN, NULL);
|
||||
|
||||
switch (command) {
|
||||
/**
|
||||
* Get enabled channels.
|
||||
* Response: bytes with indices of enabled channels, ascending order.
|
||||
*/
|
||||
case CMD_GET_ENABLED_CHANNELS:
|
||||
for (uint8_t i = 0; i < 18; i++) {
|
||||
if (priv->channels_mask & (1 << i)) {
|
||||
pb_u8(&pb, i);
|
||||
}
|
||||
}
|
||||
com_respond_pb(frame_id, MSG_SUCCESS, &pb);
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Set the sample rate in Hz
|
||||
* plad: hz:u32
|
||||
*/
|
||||
case CMD_SET_SAMPLE_RATE:
|
||||
{
|
||||
uint32_t freq = pp_u32(pp);
|
||||
if (freq == 0) return E_BAD_VALUE;
|
||||
|
||||
TRY(UADC_SetSampleRate(unit, freq));
|
||||
}
|
||||
// Pass through - send back the obtained sample rate
|
||||
/**
|
||||
* Read the real used frequency, expressed as float.
|
||||
* May differ from the configured or requested value due to prescaller limitations.
|
||||
*/
|
||||
case CMD_GET_SAMPLE_RATE:
|
||||
pb_u32(&pb, priv->real_frequency_int);
|
||||
pb_float(&pb, priv->real_frequency);
|
||||
com_respond_pb(frame_id, MSG_SUCCESS, &pb);
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Set smoothing factor 0-1000.
|
||||
* pld: u16:factor
|
||||
*/
|
||||
case CMD_SET_SMOOTHING_FACTOR:
|
||||
{
|
||||
uint16_t fac = pp_u16(pp);
|
||||
if (fac > 1000) return E_BAD_VALUE;
|
||||
priv->avg_factor_as_float = fac / 1000.0f;
|
||||
}
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Set sample time
|
||||
* pld: u8:0-7
|
||||
*/
|
||||
case CMD_SET_SAMPLE_TIME:
|
||||
{
|
||||
uint8_t tim = pp_u8(pp);
|
||||
if (tim > 7) return E_BAD_VALUE;
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_UNINIT);
|
||||
{
|
||||
LL_ADC_SetSamplingTimeCommonChannels(priv->ADCx, LL_ADC_SAMPLETIMES[tim]);
|
||||
}
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_IDLE);
|
||||
}
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Enable channels. The channels must've been configured in the settings (except ch 16 and 17 which are available always)
|
||||
* pld: u32: bitmap of channels
|
||||
*/
|
||||
case CMD_ENABLE_CHANNELS:
|
||||
{
|
||||
uint32_t new_channels = pp_u32(pp);
|
||||
|
||||
// this tears down the peripherals sufficiently so we can re-configure them. Going back to IDLE re-inits this
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_UNINIT);
|
||||
|
||||
uint32_t illegal_channels = new_channels & ~(priv->cfg.channels | (1<<16) | (1<<17)); // 16 and 17 may be enabled always
|
||||
if (illegal_channels != 0) {
|
||||
com_respond_str(MSG_ERROR, frame_id, "Some requested channels not available");
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_IDLE);
|
||||
return E_FAILURE;
|
||||
}
|
||||
|
||||
uint8_t nb_channels = 0;
|
||||
// count the enabled channels
|
||||
for(int i = 0; i < 32; i++) {
|
||||
if (new_channels & (1<<i)) {
|
||||
nb_channels++;
|
||||
}
|
||||
}
|
||||
|
||||
if (nb_channels == 0) {
|
||||
com_respond_str(MSG_ERROR, frame_id, "Need at least 1 channel");
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_IDLE);
|
||||
return E_FAILURE;
|
||||
}
|
||||
|
||||
if (priv->cfg.buffer_size < nb_channels * 2) {
|
||||
com_respond_str(MSG_ERROR, frame_id, "Insufficient buf size");
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_IDLE);
|
||||
return E_BAD_CONFIG;
|
||||
}
|
||||
|
||||
priv->nb_channels = nb_channels;
|
||||
priv->ADCx->CHSELR = new_channels; // apply it to the ADC
|
||||
priv->channels_mask = new_channels;
|
||||
|
||||
UADC_SetupDMA(unit);
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_IDLE);
|
||||
}
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Read raw values from the last measurement.
|
||||
* Response: interleaved (u8:channel, u16:value) for all channels
|
||||
*/
|
||||
case CMD_READ_RAW:
|
||||
if(priv->opmode != ADC_OPMODE_IDLE && priv->opmode != ADC_OPMODE_ARMED) {
|
||||
return E_BUSY;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < 18; i++) {
|
||||
if (priv->channels_mask & (1 << i)) {
|
||||
pb_u16(&pb, priv->last_samples[i]);
|
||||
}
|
||||
}
|
||||
com_respond_pb(frame_id, MSG_SUCCESS, &pb);
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Read smoothed values.
|
||||
* Response: interleaved (u8:channel, f32:value) for all channels
|
||||
*/
|
||||
case CMD_READ_SMOOTHED:
|
||||
if(priv->opmode != ADC_OPMODE_IDLE && priv->opmode != ADC_OPMODE_ARMED) {
|
||||
return E_BUSY;
|
||||
}
|
||||
|
||||
if (priv->real_frequency_int > UADC_MAX_FREQ_FOR_AVERAGING) {
|
||||
com_respond_str(MSG_ERROR, frame_id, "Too fast for smoothing");
|
||||
return E_FAILURE;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < 18; i++) {
|
||||
if (priv->channels_mask & (1 << i)) {
|
||||
pb_float(&pb, priv->averaging_bins[i]);
|
||||
}
|
||||
}
|
||||
com_respond_pb(frame_id, MSG_SUCCESS, &pb);
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Configure a trigger. This is legal only if the current state is IDLE or ARMED (will re-arm).
|
||||
*
|
||||
* Payload:
|
||||
* u8 - source channel
|
||||
* u16 - triggering level
|
||||
* u8 - edge to trigger on: 1-rising, 2-falling, 3-both
|
||||
* u16 - pre-trigger samples count
|
||||
* u32 - post-trigger samples count
|
||||
* u16 - trigger hold-off in ms (dead time after firing, before it cna fire again if armed)
|
||||
* u8(bool) - auto re-arm after firing and completing the capture
|
||||
*/
|
||||
case CMD_SETUP_TRIGGER:
|
||||
adc_dbg("> Setup trigger");
|
||||
if (priv->opmode != ADC_OPMODE_IDLE &&
|
||||
priv->opmode != ADC_OPMODE_ARMED &&
|
||||
priv->opmode != ADC_OPMODE_REARM_PENDING) {
|
||||
return E_BUSY;
|
||||
}
|
||||
|
||||
{
|
||||
const uint8_t source = pp_u8(pp);
|
||||
const uint16_t level = pp_u16(pp);
|
||||
const uint8_t edge = pp_u8(pp);
|
||||
const uint32_t pretrig = pp_u32(pp);
|
||||
const uint32_t count = pp_u32(pp);
|
||||
const uint16_t holdoff = pp_u16(pp);
|
||||
const bool auto_rearm = pp_bool(pp);
|
||||
|
||||
if (source > UADC_MAX_CHANNEL) {
|
||||
com_respond_str(MSG_ERROR, frame_id, "Invalid trig source");
|
||||
return E_FAILURE;
|
||||
}
|
||||
|
||||
if (0 == (priv->channels_mask & (1 << source))) {
|
||||
com_respond_str(MSG_ERROR, frame_id, "Channel not enabled");
|
||||
return E_FAILURE;
|
||||
}
|
||||
|
||||
if (level > 4095) {
|
||||
com_respond_str(MSG_ERROR, frame_id, "Level out of range (0-4095)");
|
||||
return E_FAILURE;
|
||||
}
|
||||
|
||||
if (edge == 0 || edge > 3) {
|
||||
com_respond_str(MSG_ERROR, frame_id, "Bad edge");
|
||||
return E_FAILURE;
|
||||
}
|
||||
|
||||
// XXX the max size may be too much
|
||||
const uint32_t max_pretrig = (priv->buf_itemcount / priv->nb_channels);
|
||||
if (pretrig > max_pretrig) {
|
||||
com_respond_snprintf(frame_id, MSG_ERROR,
|
||||
"Pretrig too large (max %d)", (int) max_pretrig);
|
||||
return E_FAILURE;
|
||||
}
|
||||
|
||||
priv->trigger_source = source;
|
||||
priv->trig_level = level;
|
||||
priv->trig_prev_level = priv->last_samples[source];
|
||||
priv->trig_edge = edge;
|
||||
priv->pretrig_len = pretrig;
|
||||
priv->trig_len = count;
|
||||
priv->trig_holdoff = holdoff;
|
||||
priv->auto_rearm = auto_rearm;
|
||||
}
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Arm (permissible only if idle and the trigger is configured)
|
||||
*/
|
||||
case CMD_ARM:
|
||||
adc_dbg("> Arm");
|
||||
uint8_t sticky = pp_u8(pp);
|
||||
|
||||
if(priv->opmode == ADC_OPMODE_ARMED || priv->opmode == ADC_OPMODE_REARM_PENDING) {
|
||||
// We are armed or will re-arm promptly, act like the call succeeded
|
||||
// The auto flag is set regardless
|
||||
} else {
|
||||
if (priv->opmode != ADC_OPMODE_IDLE) {
|
||||
return E_BUSY; // capture in progress
|
||||
}
|
||||
|
||||
if (priv->trig_len == 0) {
|
||||
com_respond_str(MSG_ERROR, frame_id, "Trigger not configured.");
|
||||
return E_FAILURE;
|
||||
}
|
||||
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_ARMED);
|
||||
}
|
||||
|
||||
if (sticky != 255) {
|
||||
priv->auto_rearm = (bool)sticky;
|
||||
}
|
||||
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Dis-arm. Permissible only when idle or armed.
|
||||
* Switches to idle.
|
||||
*/
|
||||
case CMD_DISARM:
|
||||
adc_dbg("> Disarm");
|
||||
|
||||
priv->auto_rearm = false;
|
||||
|
||||
if(priv->opmode == ADC_OPMODE_IDLE) {
|
||||
return E_SUCCESS; // already idle, success - no work to do
|
||||
}
|
||||
|
||||
// capture in progress
|
||||
if (priv->opmode != ADC_OPMODE_ARMED &&
|
||||
priv->opmode != ADC_OPMODE_REARM_PENDING) {
|
||||
// Capture in progress, we already cleared auto rearm, so we're done for now
|
||||
// auto_rearm is checked in the EOS isr and if cleared, does not re-arm.
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
UADC_SwitchMode(unit, ADC_OPMODE_IDLE);
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Abort any ongoing capture and dis-arm.
|
||||
*/
|
||||
case CMD_ABORT:;
|
||||
adc_dbg("> Abort capture");
|
||||
UADC_AbortCapture(unit);
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Force a trigger (complete with pre-trigger capture and hold-off)
|
||||
* The reported edge will be 0b11, here meaning "manual trigger"
|
||||
*/
|
||||
case CMD_FORCE_TRIGGER:
|
||||
adc_dbg("> Force trigger");
|
||||
// This is similar to block capture, but includes the pre-trig buffer and has fixed size based on trigger config
|
||||
// FORCE is useful for checking if the trigger is set up correctly
|
||||
if (priv->opmode != ADC_OPMODE_ARMED &&
|
||||
priv->opmode != ADC_OPMODE_IDLE &&
|
||||
priv->opmode != ADC_OPMODE_REARM_PENDING) return E_BUSY;
|
||||
|
||||
if (priv->trig_len == 0) {
|
||||
com_respond_str(MSG_ERROR, frame_id, "Trigger not configured.");
|
||||
return E_FAILURE;
|
||||
}
|
||||
|
||||
UADC_HandleTrigger(unit, 0b11, PTIM_GetMicrotime());
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Start a block capture (like manual trigger, but without pre-trigger and arming)
|
||||
*
|
||||
* Payload:
|
||||
* u32 - sample count (for each channel)
|
||||
*/
|
||||
case CMD_BLOCK_CAPTURE:
|
||||
adc_dbg("> Block cpt");
|
||||
if (priv->opmode != ADC_OPMODE_ARMED &&
|
||||
priv->opmode != ADC_OPMODE_REARM_PENDING &&
|
||||
priv->opmode != ADC_OPMODE_IDLE) return E_BUSY;
|
||||
|
||||
uint32_t count = pp_u32(pp);
|
||||
|
||||
UADC_StartBlockCapture(unit, count, frame_id);
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Start streaming (like block capture, but unlimited)
|
||||
* The stream can be terminated by the stop command.
|
||||
*/
|
||||
case CMD_STREAM_START:
|
||||
adc_dbg("> Stream ON");
|
||||
if (priv->opmode != ADC_OPMODE_ARMED &&
|
||||
priv->opmode != ADC_OPMODE_REARM_PENDING &&
|
||||
priv->opmode != ADC_OPMODE_IDLE) return E_BUSY;
|
||||
|
||||
UADC_StartStream(unit, frame_id);
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Stop a stream.
|
||||
*/
|
||||
case CMD_STREAM_STOP:
|
||||
adc_dbg("> Stream OFF");
|
||||
if (priv->opmode != ADC_OPMODE_STREAM) {
|
||||
com_respond_str(MSG_ERROR, frame_id, "Not streaming");
|
||||
return E_FAILURE;
|
||||
}
|
||||
|
||||
UADC_StopStream(unit);
|
||||
return E_SUCCESS;
|
||||
|
||||
default:
|
||||
return E_UNKNOWN_COMMAND;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Unit template */
|
||||
const UnitDriver UNIT_ADC = {
|
||||
.name = "ADC",
|
||||
.description = "Analog/digital converter",
|
||||
// Settings
|
||||
.preInit = UADC_preInit,
|
||||
.cfgLoadBinary = UADC_loadBinary,
|
||||
.cfgWriteBinary = UADC_writeBinary,
|
||||
.cfgLoadIni = UADC_loadIni,
|
||||
.cfgWriteIni = UADC_writeIni,
|
||||
// Init
|
||||
.init = UADC_init,
|
||||
.deInit = UADC_deInit,
|
||||
// Function
|
||||
.handleRequest = UADC_handleRequest,
|
||||
.updateTick = UADC_updateTick,
|
||||
};
|
||||
@@ -1,15 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2017/11/25.
|
||||
//
|
||||
// ADC unit with several DSO-like features, like triggering, pre-trigger, block capture,
|
||||
// streaming, smoothing...
|
||||
//
|
||||
|
||||
#ifndef U_TPL_H
|
||||
#define U_TPL_H
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
extern const UnitDriver UNIT_ADC;
|
||||
|
||||
#endif //U_TPL_H
|
||||
@@ -1,84 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
#include "unit_dac.h"
|
||||
|
||||
#define DAC_INTERNAL
|
||||
#include "_dac_internal.h"
|
||||
|
||||
/**
|
||||
* Re-configure the
|
||||
* @param unit
|
||||
*/
|
||||
void UDAC_Reconfigure(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
// back-up IT state
|
||||
bool IT_enabled = (bool) LL_TIM_IsEnabledIT_UPDATE(priv->TIMx);
|
||||
if (IT_enabled) {
|
||||
LL_TIM_DisableIT_UPDATE(priv->TIMx);
|
||||
}
|
||||
|
||||
// ...
|
||||
DAC->CR &= ~(DAC_CR_EN1 | DAC_CR_EN2);
|
||||
|
||||
uint32_t CR = 0;
|
||||
if (priv->cfg.ch[0].enable) {
|
||||
CR |=
|
||||
(priv->cfg.ch[0].buffered ? 0 : DAC_CR_BOFF1) |
|
||||
(priv->ch[0].noise_type << DAC_CR_WAVE1_Pos) |
|
||||
(priv->ch[0].noise_level & 0xF) << DAC_CR_MAMP1_Pos |
|
||||
DAC_CR_TEN1 |
|
||||
(0b111 << DAC_CR_TSEL1_Pos); // software trigger;
|
||||
|
||||
CR |= DAC_CR_EN1;
|
||||
}
|
||||
|
||||
if (priv->cfg.ch[1].enable) {
|
||||
CR |=
|
||||
(priv->cfg.ch[1].buffered ? 0 : DAC_CR_BOFF2) |
|
||||
(priv->ch[1].noise_type << DAC_CR_WAVE2_Pos) |
|
||||
(priv->ch[1].noise_level & 0xF) << DAC_CR_MAMP2_Pos |
|
||||
DAC_CR_TEN2 |
|
||||
(0b111 << DAC_CR_TSEL2_Pos); // software trigger
|
||||
CR |= DAC_CR_EN2;
|
||||
}
|
||||
|
||||
DAC->CR = CR;
|
||||
// ...
|
||||
|
||||
if (IT_enabled) {
|
||||
LL_TIM_EnableIT_UPDATE(priv->TIMx);
|
||||
}
|
||||
}
|
||||
|
||||
error_t UDAC_SetFreq(Unit *unit, int channel, float freq)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
priv->ch[channel].increment = (uint32_t) roundf(25.0f * freq * UDAC_VALUE_COUNT * // FIXME constant seems derived from the divide ...?
|
||||
((float)UDAC_TIM_FREQ_DIVIDER / PLAT_AHB_MHZ)
|
||||
);
|
||||
|
||||
// dbg("Ch %d: Computed increment: %d", channel+1, (int)priv->ch[channel].increment);
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
void UDAC_ToggleTimerIfNeeded(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
if (priv->ch[0].waveform == UDAC_WAVE_DC && priv->ch[1].waveform == UDAC_WAVE_DC) {
|
||||
LL_TIM_DisableCounter(priv->TIMx);
|
||||
|
||||
// manually call the interrupt function to update the level
|
||||
UDAC_HandleIT(unit);
|
||||
} else {
|
||||
LL_TIM_EnableCounter(priv->TIMx);
|
||||
}
|
||||
}
|
||||
@@ -1,392 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/03/10.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_dac.h"
|
||||
|
||||
#define DAC_INTERNAL
|
||||
#include "_dac_internal.h"
|
||||
|
||||
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("O2")
|
||||
void UDAC_HandleIT(void *arg)
|
||||
{
|
||||
Unit *unit = arg;
|
||||
struct priv *priv = unit->data;
|
||||
LL_TIM_ClearFlag_UPDATE(priv->TIMx);
|
||||
|
||||
// TODO optimize to allow faster update speed
|
||||
|
||||
uint16_t vals[2];
|
||||
for (int i = 0; i < 2; i++) {
|
||||
struct udac_channel_live *const ch = &priv->ch[i];
|
||||
|
||||
if (ch->waveform == UDAC_WAVE_DC) {
|
||||
// skip the whole counter thing for DC
|
||||
vals[i] = ch->dc_level;
|
||||
continue;
|
||||
}
|
||||
|
||||
ch->counter += ch->increment;
|
||||
const uint16_t index = (uint16_t) (ch->counter >> UDAC_INDEX_SHIFT);
|
||||
|
||||
switch (ch->waveform) {
|
||||
case UDAC_WAVE_SINE: {
|
||||
const uint32_t phase = index >> (UDAC_INDEX_WIDTH - 2);
|
||||
|
||||
uint32_t pos = (uint32_t) index & (((1<<(UDAC_INDEX_WIDTH - 2)) - 1));
|
||||
|
||||
if (phase & 0b01) {
|
||||
// 2nd and 4th quarter (01, 11)
|
||||
pos = (UDAC_VALUE_COUNT/4 - 1) - pos;
|
||||
}
|
||||
|
||||
uint32_t value;
|
||||
|
||||
/* unpack */
|
||||
const uint32_t nthbyte = (pos * 3) / 2;
|
||||
const uint8_t topbyte = LUT_sine_8192_quad_packed[nthbyte];
|
||||
const uint8_t botbyte = LUT_sine_8192_quad_packed[nthbyte + 1];
|
||||
if (pos & 1) {
|
||||
// odd index
|
||||
value = (uint32_t) (((topbyte & 0xF) << 8) | botbyte);
|
||||
}
|
||||
else {
|
||||
value = (uint32_t) ((topbyte << 4) | ((botbyte & 0xF0) >> 4));
|
||||
}
|
||||
/* end unpack -> value */
|
||||
|
||||
if (phase & 0b10) {
|
||||
// 3rd and 4th quarter (10, 11)
|
||||
value = 2047 - value;
|
||||
}
|
||||
else {
|
||||
// 1st and 2nd quarter (00, 01)
|
||||
value = 2048 + value;
|
||||
}
|
||||
|
||||
vals[i] = (uint16_t) value;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UDAC_WAVE_RECTANGLE:
|
||||
if (index < ch->rectangle_ontime) {
|
||||
vals[i] = ch->rectangle_high;
|
||||
} else {
|
||||
vals[i] = ch->rectangle_low;
|
||||
}
|
||||
break;
|
||||
|
||||
case UDAC_WAVE_SAWTOOTH_UP:
|
||||
vals[i] = (uint16_t) (index / 2); // for 8192 steps, this will produce 0-4095
|
||||
break;
|
||||
|
||||
case UDAC_WAVE_SAWTOOTH_DOWN:
|
||||
vals[i] = (uint16_t) (4095 - (index / 2)); // for 8192 steps, this will produce 0-4095
|
||||
break;
|
||||
|
||||
case UDAC_WAVE_TRIANGLE: {
|
||||
if (index < 4096) {
|
||||
vals[i] = index;
|
||||
}
|
||||
else {
|
||||
vals[i] = (uint16_t) (8191 - index);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
vals[i] = 0; // this shouldn't happen
|
||||
break;
|
||||
} // end select waveform
|
||||
} // end for 0,1
|
||||
|
||||
DAC->DHR12RD = (vals[1] << 16) | vals[0];
|
||||
|
||||
// Trigger a conversion
|
||||
DAC->SWTRIGR = (DAC_SWTR_CH1 | DAC_SWTR_CH2);
|
||||
}
|
||||
#pragma GCC pop_options
|
||||
|
||||
|
||||
/**
|
||||
* This is the first 1/4 of a 12-bit sine wave sampled in 8192 points, each two points packed into three bytes (saves 1/4 of space used by uint16_t's)
|
||||
* The array holds 2048 data points which can be easily offset / flipped to cover the entire waveform.
|
||||
*
|
||||
* |<-->|
|
||||
* _---_
|
||||
* / \
|
||||
* / \
|
||||
* /---------\---------/
|
||||
* \ /
|
||||
* \_ _/
|
||||
* ---
|
||||
*/
|
||||
#if 0 // this is rail to rail, but the dac doesn't work well at the rails and it gets flattened
|
||||
const uint8_t LUT_sine_8192_quad_packed[] = {
|
||||
0x00, 0x00, 0x02, 0x00, 0x30, 0x05, 0x00, 0x60, 0x08, 0x00, 0x90, 0x0B, 0x00, 0xD0, 0x0E, 0x01, 0x00, 0x11, 0x01, 0x30, 0x14, 0x01, 0x60, 0x18,
|
||||
0x01, 0x90, 0x1B, 0x01, 0xC0, 0x1E, 0x01, 0xF0, 0x21, 0x02, 0x30, 0x24, 0x02, 0x60, 0x27, 0x02, 0x90, 0x2A, 0x02, 0xC0, 0x2E, 0x02, 0xF0, 0x31,
|
||||
0x03, 0x20, 0x34, 0x03, 0x50, 0x37, 0x03, 0x90, 0x3A, 0x03, 0xC0, 0x3D, 0x03, 0xF0, 0x40, 0x04, 0x20, 0x43, 0x04, 0x50, 0x47, 0x04, 0x80, 0x4A,
|
||||
0x04, 0xB0, 0x4D, 0x04, 0xE0, 0x50, 0x05, 0x20, 0x53, 0x05, 0x50, 0x56, 0x05, 0x80, 0x59, 0x05, 0xB0, 0x5D, 0x05, 0xE0, 0x60, 0x06, 0x10, 0x63,
|
||||
0x06, 0x40, 0x66, 0x06, 0x80, 0x69, 0x06, 0xB0, 0x6C, 0x06, 0xE0, 0x6F, 0x07, 0x10, 0x73, 0x07, 0x40, 0x76, 0x07, 0x70, 0x79, 0x07, 0xA0, 0x7C,
|
||||
0x07, 0xE0, 0x7F, 0x08, 0x10, 0x82, 0x08, 0x40, 0x85, 0x08, 0x70, 0x88, 0x08, 0xA0, 0x8C, 0x08, 0xD0, 0x8F, 0x09, 0x00, 0x92, 0x09, 0x30, 0x95,
|
||||
0x09, 0x70, 0x98, 0x09, 0xA0, 0x9B, 0x09, 0xD0, 0x9E, 0x0A, 0x00, 0xA2, 0x0A, 0x30, 0xA5, 0x0A, 0x60, 0xA8, 0x0A, 0x90, 0xAB, 0x0A, 0xC0, 0xAE,
|
||||
0x0B, 0x00, 0xB1, 0x0B, 0x30, 0xB4, 0x0B, 0x60, 0xB7, 0x0B, 0x90, 0xBB, 0x0B, 0xC0, 0xBE, 0x0B, 0xF0, 0xC1, 0x0C, 0x20, 0xC4, 0x0C, 0x60, 0xC7,
|
||||
0x0C, 0x90, 0xCA, 0x0C, 0xC0, 0xCD, 0x0C, 0xF0, 0xD0, 0x0D, 0x20, 0xD4, 0x0D, 0x50, 0xD7, 0x0D, 0x80, 0xDA, 0x0D, 0xB0, 0xDD, 0x0D, 0xF0, 0xE0,
|
||||
0x0E, 0x20, 0xE3, 0x0E, 0x50, 0xE6, 0x0E, 0x80, 0xE9, 0x0E, 0xB0, 0xED, 0x0E, 0xE0, 0xF0, 0x0F, 0x10, 0xF3, 0x0F, 0x40, 0xF6, 0x0F, 0x70, 0xF9,
|
||||
0x0F, 0xB0, 0xFC, 0x0F, 0xE0, 0xFF, 0x10, 0x11, 0x02, 0x10, 0x41, 0x05, 0x10, 0x71, 0x09, 0x10, 0xA1, 0x0C, 0x10, 0xD1, 0x0F, 0x11, 0x01, 0x12,
|
||||
0x11, 0x31, 0x15, 0x11, 0x71, 0x18, 0x11, 0xA1, 0x1B, 0x11, 0xD1, 0x1E, 0x12, 0x01, 0x21, 0x12, 0x31, 0x25, 0x12, 0x61, 0x28, 0x12, 0x91, 0x2B,
|
||||
0x12, 0xC1, 0x2E, 0x12, 0xF1, 0x31, 0x13, 0x31, 0x34, 0x13, 0x61, 0x37, 0x13, 0x91, 0x3A, 0x13, 0xC1, 0x3D, 0x13, 0xF1, 0x41, 0x14, 0x21, 0x44,
|
||||
0x14, 0x51, 0x47, 0x14, 0x81, 0x4A, 0x14, 0xB1, 0x4D, 0x14, 0xE1, 0x50, 0x15, 0x21, 0x53, 0x15, 0x51, 0x56, 0x15, 0x81, 0x59, 0x15, 0xB1, 0x5C,
|
||||
0x15, 0xE1, 0x60, 0x16, 0x11, 0x63, 0x16, 0x41, 0x66, 0x16, 0x71, 0x69, 0x16, 0xA1, 0x6C, 0x16, 0xD1, 0x6F, 0x17, 0x11, 0x72, 0x17, 0x41, 0x75,
|
||||
0x17, 0x71, 0x78, 0x17, 0xA1, 0x7B, 0x17, 0xD1, 0x7E, 0x18, 0x01, 0x81, 0x18, 0x31, 0x85, 0x18, 0x61, 0x88, 0x18, 0x91, 0x8B, 0x18, 0xC1, 0x8E,
|
||||
0x18, 0xF1, 0x91, 0x19, 0x21, 0x94, 0x19, 0x61, 0x97, 0x19, 0x91, 0x9A, 0x19, 0xC1, 0x9D, 0x19, 0xF1, 0xA0, 0x1A, 0x21, 0xA3, 0x1A, 0x51, 0xA6,
|
||||
0x1A, 0x81, 0xA9, 0x1A, 0xB1, 0xAD, 0x1A, 0xE1, 0xB0, 0x1B, 0x11, 0xB3, 0x1B, 0x41, 0xB6, 0x1B, 0x71, 0xB9, 0x1B, 0xA1, 0xBC, 0x1B, 0xD1, 0xBF,
|
||||
0x1C, 0x11, 0xC2, 0x1C, 0x41, 0xC5, 0x1C, 0x71, 0xC8, 0x1C, 0xA1, 0xCB, 0x1C, 0xD1, 0xCE, 0x1D, 0x01, 0xD1, 0x1D, 0x31, 0xD4, 0x1D, 0x61, 0xD7,
|
||||
0x1D, 0x91, 0xDB, 0x1D, 0xC1, 0xDE, 0x1D, 0xF1, 0xE1, 0x1E, 0x21, 0xE4, 0x1E, 0x51, 0xE7, 0x1E, 0x81, 0xEA, 0x1E, 0xB1, 0xED, 0x1E, 0xE1, 0xF0,
|
||||
0x1F, 0x11, 0xF3, 0x1F, 0x41, 0xF6, 0x1F, 0x71, 0xF9, 0x1F, 0xB1, 0xFC, 0x1F, 0xE1, 0xFF, 0x20, 0x12, 0x02, 0x20, 0x42, 0x05, 0x20, 0x72, 0x08,
|
||||
0x20, 0xA2, 0x0B, 0x20, 0xD2, 0x0E, 0x21, 0x02, 0x11, 0x21, 0x32, 0x14, 0x21, 0x62, 0x17, 0x21, 0x92, 0x1A, 0x21, 0xC2, 0x1D, 0x21, 0xF2, 0x20,
|
||||
0x22, 0x22, 0x23, 0x22, 0x52, 0x26, 0x22, 0x82, 0x2A, 0x22, 0xB2, 0x2D, 0x22, 0xE2, 0x30, 0x23, 0x12, 0x33, 0x23, 0x42, 0x36, 0x23, 0x72, 0x39,
|
||||
0x23, 0xA2, 0x3C, 0x23, 0xD2, 0x3F, 0x24, 0x02, 0x42, 0x24, 0x32, 0x45, 0x24, 0x62, 0x48, 0x24, 0x92, 0x4B, 0x24, 0xC2, 0x4E, 0x24, 0xF2, 0x51,
|
||||
0x25, 0x22, 0x54, 0x25, 0x52, 0x57, 0x25, 0x82, 0x5A, 0x25, 0xB2, 0x5D, 0x25, 0xE2, 0x60, 0x26, 0x12, 0x63, 0x26, 0x42, 0x66, 0x26, 0x72, 0x69,
|
||||
0x26, 0xA2, 0x6C, 0x26, 0xD2, 0x6F, 0x27, 0x02, 0x72, 0x27, 0x32, 0x75, 0x27, 0x62, 0x78, 0x27, 0x92, 0x7B, 0x27, 0xC2, 0x7E, 0x27, 0xF2, 0x81,
|
||||
0x28, 0x22, 0x84, 0x28, 0x52, 0x87, 0x28, 0x82, 0x8A, 0x28, 0xB2, 0x8D, 0x28, 0xE2, 0x90, 0x29, 0x12, 0x92, 0x29, 0x42, 0x95, 0x29, 0x72, 0x98,
|
||||
0x29, 0xA2, 0x9B, 0x29, 0xD2, 0x9E, 0x2A, 0x02, 0xA1, 0x2A, 0x32, 0xA4, 0x2A, 0x62, 0xA7, 0x2A, 0x92, 0xAA, 0x2A, 0xC2, 0xAD, 0x2A, 0xF2, 0xB0,
|
||||
0x2B, 0x22, 0xB3, 0x2B, 0x52, 0xB6, 0x2B, 0x82, 0xB9, 0x2B, 0xA2, 0xBC, 0x2B, 0xD2, 0xBF, 0x2C, 0x02, 0xC2, 0x2C, 0x32, 0xC5, 0x2C, 0x62, 0xC8,
|
||||
0x2C, 0x92, 0xCB, 0x2C, 0xC2, 0xCE, 0x2C, 0xF2, 0xD1, 0x2D, 0x22, 0xD4, 0x2D, 0x52, 0xD6, 0x2D, 0x82, 0xD9, 0x2D, 0xB2, 0xDC, 0x2D, 0xE2, 0xDF,
|
||||
0x2E, 0x12, 0xE2, 0x2E, 0x42, 0xE5, 0x2E, 0x72, 0xE8, 0x2E, 0x92, 0xEB, 0x2E, 0xC2, 0xEE, 0x2E, 0xF2, 0xF1, 0x2F, 0x22, 0xF4, 0x2F, 0x52, 0xF7,
|
||||
0x2F, 0x82, 0xFA, 0x2F, 0xB2, 0xFC, 0x2F, 0xE2, 0xFF, 0x30, 0x13, 0x02, 0x30, 0x43, 0x05, 0x30, 0x73, 0x08, 0x30, 0xA3, 0x0B, 0x30, 0xC3, 0x0E,
|
||||
0x30, 0xF3, 0x11, 0x31, 0x23, 0x14, 0x31, 0x53, 0x17, 0x31, 0x83, 0x19, 0x31, 0xB3, 0x1C, 0x31, 0xE3, 0x1F, 0x32, 0x13, 0x22, 0x32, 0x43, 0x25,
|
||||
0x32, 0x73, 0x28, 0x32, 0x93, 0x2B, 0x32, 0xC3, 0x2E, 0x32, 0xF3, 0x31, 0x33, 0x23, 0x33, 0x33, 0x53, 0x36, 0x33, 0x83, 0x39, 0x33, 0xB3, 0x3C,
|
||||
0x33, 0xE3, 0x3F, 0x34, 0x03, 0x42, 0x34, 0x33, 0x45, 0x34, 0x63, 0x48, 0x34, 0x93, 0x4A, 0x34, 0xC3, 0x4D, 0x34, 0xF3, 0x50, 0x35, 0x23, 0x53,
|
||||
0x35, 0x43, 0x56, 0x35, 0x73, 0x59, 0x35, 0xA3, 0x5C, 0x35, 0xD3, 0x5E, 0x36, 0x03, 0x61, 0x36, 0x33, 0x64, 0x36, 0x63, 0x67, 0x36, 0x83, 0x6A,
|
||||
0x36, 0xB3, 0x6D, 0x36, 0xE3, 0x6F, 0x37, 0x13, 0x72, 0x37, 0x43, 0x75, 0x37, 0x73, 0x78, 0x37, 0x93, 0x7B, 0x37, 0xC3, 0x7E, 0x37, 0xF3, 0x80,
|
||||
0x38, 0x23, 0x83, 0x38, 0x53, 0x86, 0x38, 0x73, 0x89, 0x38, 0xA3, 0x8C, 0x38, 0xD3, 0x8F, 0x39, 0x03, 0x91, 0x39, 0x33, 0x94, 0x39, 0x63, 0x97,
|
||||
0x39, 0x83, 0x9A, 0x39, 0xB3, 0x9D, 0x39, 0xE3, 0x9F, 0x3A, 0x13, 0xA2, 0x3A, 0x43, 0xA5, 0x3A, 0x63, 0xA8, 0x3A, 0x93, 0xAB, 0x3A, 0xC3, 0xAD,
|
||||
0x3A, 0xF3, 0xB0, 0x3B, 0x23, 0xB3, 0x3B, 0x43, 0xB6, 0x3B, 0x73, 0xB8, 0x3B, 0xA3, 0xBB, 0x3B, 0xD3, 0xBE, 0x3B, 0xF3, 0xC1, 0x3C, 0x23, 0xC4,
|
||||
0x3C, 0x53, 0xC6, 0x3C, 0x83, 0xC9, 0x3C, 0xA3, 0xCC, 0x3C, 0xD3, 0xCF, 0x3D, 0x03, 0xD1, 0x3D, 0x33, 0xD4, 0x3D, 0x63, 0xD7, 0x3D, 0x83, 0xDA,
|
||||
0x3D, 0xB3, 0xDC, 0x3D, 0xE3, 0xDF, 0x3E, 0x13, 0xE2, 0x3E, 0x33, 0xE5, 0x3E, 0x63, 0xE7, 0x3E, 0x93, 0xEA, 0x3E, 0xB3, 0xED, 0x3E, 0xE3, 0xF0,
|
||||
0x3F, 0x13, 0xF2, 0x3F, 0x43, 0xF5, 0x3F, 0x63, 0xF8, 0x3F, 0x93, 0xFB, 0x3F, 0xC3, 0xFD, 0x3F, 0xF4, 0x00, 0x40, 0x14, 0x03, 0x40, 0x44, 0x05,
|
||||
0x40, 0x74, 0x08, 0x40, 0x94, 0x0B, 0x40, 0xC4, 0x0E, 0x40, 0xF4, 0x10, 0x41, 0x24, 0x13, 0x41, 0x44, 0x16, 0x41, 0x74, 0x18, 0x41, 0xA4, 0x1B,
|
||||
0x41, 0xC4, 0x1E, 0x41, 0xF4, 0x20, 0x42, 0x24, 0x23, 0x42, 0x44, 0x26, 0x42, 0x74, 0x28, 0x42, 0xA4, 0x2B, 0x42, 0xC4, 0x2E, 0x42, 0xF4, 0x30,
|
||||
0x43, 0x24, 0x33, 0x43, 0x54, 0x36, 0x43, 0x74, 0x39, 0x43, 0xA4, 0x3B, 0x43, 0xD4, 0x3E, 0x43, 0xF4, 0x40, 0x44, 0x24, 0x43, 0x44, 0x44, 0x46,
|
||||
0x44, 0x74, 0x48, 0x44, 0xA4, 0x4B, 0x44, 0xC4, 0x4E, 0x44, 0xF4, 0x50, 0x45, 0x24, 0x53, 0x45, 0x44, 0x56, 0x45, 0x74, 0x58, 0x45, 0xA4, 0x5B,
|
||||
0x45, 0xC4, 0x5E, 0x45, 0xF4, 0x60, 0x46, 0x24, 0x63, 0x46, 0x44, 0x65, 0x46, 0x74, 0x68, 0x46, 0x94, 0x6B, 0x46, 0xC4, 0x6D, 0x46, 0xF4, 0x70,
|
||||
0x47, 0x14, 0x73, 0x47, 0x44, 0x75, 0x47, 0x64, 0x78, 0x47, 0x94, 0x7A, 0x47, 0xC4, 0x7D, 0x47, 0xE4, 0x80, 0x48, 0x14, 0x82, 0x48, 0x34, 0x85,
|
||||
0x48, 0x64, 0x87, 0x48, 0x94, 0x8A, 0x48, 0xB4, 0x8D, 0x48, 0xE4, 0x8F, 0x49, 0x04, 0x92, 0x49, 0x34, 0x94, 0x49, 0x64, 0x97, 0x49, 0x84, 0x99,
|
||||
0x49, 0xB4, 0x9C, 0x49, 0xD4, 0x9F, 0x4A, 0x04, 0xA1, 0x4A, 0x24, 0xA4, 0x4A, 0x54, 0xA6, 0x4A, 0x74, 0xA9, 0x4A, 0xA4, 0xAB, 0x4A, 0xD4, 0xAE,
|
||||
0x4A, 0xF4, 0xB0, 0x4B, 0x24, 0xB3, 0x4B, 0x44, 0xB5, 0x4B, 0x74, 0xB8, 0x4B, 0x94, 0xBB, 0x4B, 0xC4, 0xBD, 0x4B, 0xE4, 0xC0, 0x4C, 0x14, 0xC2,
|
||||
0x4C, 0x34, 0xC5, 0x4C, 0x64, 0xC7, 0x4C, 0x84, 0xCA, 0x4C, 0xB4, 0xCC, 0x4C, 0xD4, 0xCF, 0x4D, 0x04, 0xD1, 0x4D, 0x24, 0xD4, 0x4D, 0x54, 0xD6,
|
||||
0x4D, 0x74, 0xD9, 0x4D, 0xA4, 0xDB, 0x4D, 0xC4, 0xDE, 0x4D, 0xF4, 0xE0, 0x4E, 0x14, 0xE3, 0x4E, 0x44, 0xE5, 0x4E, 0x64, 0xE8, 0x4E, 0x94, 0xEA,
|
||||
0x4E, 0xB4, 0xED, 0x4E, 0xE4, 0xEF, 0x4F, 0x04, 0xF2, 0x4F, 0x34, 0xF4, 0x4F, 0x54, 0xF6, 0x4F, 0x84, 0xF9, 0x4F, 0xA4, 0xFB, 0x4F, 0xD4, 0xFE,
|
||||
0x4F, 0xF5, 0x00, 0x50, 0x25, 0x03, 0x50, 0x45, 0x05, 0x50, 0x65, 0x08, 0x50, 0x95, 0x0A, 0x50, 0xB5, 0x0D, 0x50, 0xE5, 0x0F, 0x51, 0x05, 0x11,
|
||||
0x51, 0x35, 0x14, 0x51, 0x55, 0x16, 0x51, 0x75, 0x19, 0x51, 0xA5, 0x1B, 0x51, 0xC5, 0x1D, 0x51, 0xF5, 0x20, 0x52, 0x15, 0x22, 0x52, 0x45, 0x25,
|
||||
0x52, 0x65, 0x27, 0x52, 0x85, 0x2A, 0x52, 0xB5, 0x2C, 0x52, 0xD5, 0x2E, 0x53, 0x05, 0x31, 0x53, 0x25, 0x33, 0x53, 0x45, 0x35, 0x53, 0x75, 0x38,
|
||||
0x53, 0x95, 0x3A, 0x53, 0xB5, 0x3D, 0x53, 0xE5, 0x3F, 0x54, 0x05, 0x41, 0x54, 0x35, 0x44, 0x54, 0x55, 0x46, 0x54, 0x75, 0x48, 0x54, 0xA5, 0x4B,
|
||||
0x54, 0xC5, 0x4D, 0x54, 0xE5, 0x4F, 0x55, 0x15, 0x52, 0x55, 0x35, 0x54, 0x55, 0x55, 0x57, 0x55, 0x85, 0x59, 0x55, 0xA5, 0x5B, 0x55, 0xC5, 0x5E,
|
||||
0x55, 0xF5, 0x60, 0x56, 0x15, 0x62, 0x56, 0x35, 0x64, 0x56, 0x65, 0x67, 0x56, 0x85, 0x69, 0x56, 0xA5, 0x6B, 0x56, 0xD5, 0x6E, 0x56, 0xF5, 0x70,
|
||||
0x57, 0x15, 0x72, 0x57, 0x35, 0x75, 0x57, 0x65, 0x77, 0x57, 0x85, 0x79, 0x57, 0xA5, 0x7C, 0x57, 0xD5, 0x7E, 0x57, 0xF5, 0x80, 0x58, 0x15, 0x82,
|
||||
0x58, 0x35, 0x85, 0x58, 0x65, 0x87, 0x58, 0x85, 0x89, 0x58, 0xA5, 0x8B, 0x58, 0xD5, 0x8E, 0x58, 0xF5, 0x90, 0x59, 0x15, 0x92, 0x59, 0x35, 0x94,
|
||||
0x59, 0x65, 0x97, 0x59, 0x85, 0x99, 0x59, 0xA5, 0x9B, 0x59, 0xC5, 0x9D, 0x59, 0xF5, 0xA0, 0x5A, 0x15, 0xA2, 0x5A, 0x35, 0xA4, 0x5A, 0x55, 0xA6,
|
||||
0x5A, 0x75, 0xA9, 0x5A, 0xA5, 0xAB, 0x5A, 0xC5, 0xAD, 0x5A, 0xE5, 0xAF, 0x5B, 0x05, 0xB1, 0x5B, 0x35, 0xB4, 0x5B, 0x55, 0xB6, 0x5B, 0x75, 0xB8,
|
||||
0x5B, 0x95, 0xBA, 0x5B, 0xB5, 0xBC, 0x5B, 0xD5, 0xBF, 0x5C, 0x05, 0xC1, 0x5C, 0x25, 0xC3, 0x5C, 0x45, 0xC5, 0x5C, 0x65, 0xC7, 0x5C, 0x85, 0xC9,
|
||||
0x5C, 0xB5, 0xCC, 0x5C, 0xD5, 0xCE, 0x5C, 0xF5, 0xD0, 0x5D, 0x15, 0xD2, 0x5D, 0x35, 0xD4, 0x5D, 0x55, 0xD6, 0x5D, 0x75, 0xD9, 0x5D, 0xA5, 0xDB,
|
||||
0x5D, 0xC5, 0xDD, 0x5D, 0xE5, 0xDF, 0x5E, 0x05, 0xE1, 0x5E, 0x25, 0xE3, 0x5E, 0x45, 0xE5, 0x5E, 0x65, 0xE7, 0x5E, 0x95, 0xEA, 0x5E, 0xB5, 0xEC,
|
||||
0x5E, 0xD5, 0xEE, 0x5E, 0xF5, 0xF0, 0x5F, 0x15, 0xF2, 0x5F, 0x35, 0xF4, 0x5F, 0x55, 0xF6, 0x5F, 0x75, 0xF8, 0x5F, 0x95, 0xFA, 0x5F, 0xB5, 0xFC,
|
||||
0x5F, 0xD5, 0xFF, 0x60, 0x06, 0x01, 0x60, 0x26, 0x03, 0x60, 0x46, 0x05, 0x60, 0x66, 0x07, 0x60, 0x86, 0x09, 0x60, 0xA6, 0x0B, 0x60, 0xC6, 0x0D,
|
||||
0x60, 0xE6, 0x0F, 0x61, 0x06, 0x11, 0x61, 0x26, 0x13, 0x61, 0x46, 0x15, 0x61, 0x66, 0x17, 0x61, 0x86, 0x19, 0x61, 0xA6, 0x1B, 0x61, 0xC6, 0x1D,
|
||||
0x61, 0xE6, 0x1F, 0x62, 0x06, 0x21, 0x62, 0x26, 0x23, 0x62, 0x46, 0x25, 0x62, 0x66, 0x27, 0x62, 0x86, 0x29, 0x62, 0xA6, 0x2B, 0x62, 0xC6, 0x2D,
|
||||
0x62, 0xE6, 0x2F, 0x63, 0x06, 0x31, 0x63, 0x26, 0x33, 0x63, 0x46, 0x35, 0x63, 0x66, 0x37, 0x63, 0x86, 0x39, 0x63, 0xA6, 0x3B, 0x63, 0xC6, 0x3D,
|
||||
0x63, 0xE6, 0x3F, 0x64, 0x06, 0x41, 0x64, 0x26, 0x43, 0x64, 0x46, 0x45, 0x64, 0x66, 0x47, 0x64, 0x86, 0x49, 0x64, 0xA6, 0x4B, 0x64, 0xC6, 0x4D,
|
||||
0x64, 0xE6, 0x4F, 0x65, 0x06, 0x51, 0x65, 0x26, 0x53, 0x65, 0x46, 0x54, 0x65, 0x56, 0x56, 0x65, 0x76, 0x58, 0x65, 0x96, 0x5A, 0x65, 0xB6, 0x5C,
|
||||
0x65, 0xD6, 0x5E, 0x65, 0xF6, 0x60, 0x66, 0x16, 0x62, 0x66, 0x36, 0x64, 0x66, 0x56, 0x66, 0x66, 0x76, 0x67, 0x66, 0x86, 0x69, 0x66, 0xA6, 0x6B,
|
||||
0x66, 0xC6, 0x6D, 0x66, 0xE6, 0x6F, 0x67, 0x06, 0x71, 0x67, 0x26, 0x73, 0x67, 0x46, 0x75, 0x67, 0x56, 0x76, 0x67, 0x76, 0x78, 0x67, 0x96, 0x7A,
|
||||
0x67, 0xB6, 0x7C, 0x67, 0xD6, 0x7E, 0x67, 0xF6, 0x80, 0x68, 0x16, 0x81, 0x68, 0x26, 0x83, 0x68, 0x46, 0x85, 0x68, 0x66, 0x87, 0x68, 0x86, 0x89,
|
||||
0x68, 0xA6, 0x8A, 0x68, 0xB6, 0x8C, 0x68, 0xD6, 0x8E, 0x68, 0xF6, 0x90, 0x69, 0x16, 0x92, 0x69, 0x36, 0x93, 0x69, 0x46, 0x95, 0x69, 0x66, 0x97,
|
||||
0x69, 0x86, 0x99, 0x69, 0xA6, 0x9B, 0x69, 0xB6, 0x9C, 0x69, 0xD6, 0x9E, 0x69, 0xF6, 0xA0, 0x6A, 0x16, 0xA2, 0x6A, 0x36, 0xA3, 0x6A, 0x46, 0xA5,
|
||||
0x6A, 0x66, 0xA7, 0x6A, 0x86, 0xA9, 0x6A, 0x96, 0xAA, 0x6A, 0xB6, 0xAC, 0x6A, 0xD6, 0xAE, 0x6A, 0xF6, 0xB0, 0x6B, 0x06, 0xB1, 0x6B, 0x26, 0xB3,
|
||||
0x6B, 0x46, 0xB5, 0x6B, 0x66, 0xB6, 0x6B, 0x76, 0xB8, 0x6B, 0x96, 0xBA, 0x6B, 0xB6, 0xBC, 0x6B, 0xC6, 0xBD, 0x6B, 0xE6, 0xBF, 0x6C, 0x06, 0xC1,
|
||||
0x6C, 0x16, 0xC2, 0x6C, 0x36, 0xC4, 0x6C, 0x56, 0xC6, 0x6C, 0x66, 0xC7, 0x6C, 0x86, 0xC9, 0x6C, 0xA6, 0xCB, 0x6C, 0xB6, 0xCC, 0x6C, 0xD6, 0xCE,
|
||||
0x6C, 0xF6, 0xD0, 0x6D, 0x06, 0xD1, 0x6D, 0x26, 0xD3, 0x6D, 0x46, 0xD4, 0x6D, 0x56, 0xD6, 0x6D, 0x76, 0xD8, 0x6D, 0x96, 0xD9, 0x6D, 0xA6, 0xDB,
|
||||
0x6D, 0xC6, 0xDD, 0x6D, 0xD6, 0xDE, 0x6D, 0xF6, 0xE0, 0x6E, 0x16, 0xE1, 0x6E, 0x26, 0xE3, 0x6E, 0x46, 0xE5, 0x6E, 0x56, 0xE6, 0x6E, 0x76, 0xE8,
|
||||
0x6E, 0x96, 0xE9, 0x6E, 0xA6, 0xEB, 0x6E, 0xC6, 0xEC, 0x6E, 0xD6, 0xEE, 0x6E, 0xF6, 0xF0, 0x6F, 0x06, 0xF1, 0x6F, 0x26, 0xF3, 0x6F, 0x46, 0xF4,
|
||||
0x6F, 0x56, 0xF6, 0x6F, 0x76, 0xF7, 0x6F, 0x86, 0xF9, 0x6F, 0xA6, 0xFA, 0x6F, 0xB6, 0xFC, 0x6F, 0xD6, 0xFE, 0x6F, 0xE6, 0xFF, 0x70, 0x07, 0x01,
|
||||
0x70, 0x17, 0x02, 0x70, 0x37, 0x04, 0x70, 0x47, 0x05, 0x70, 0x67, 0x07, 0x70, 0x77, 0x08, 0x70, 0x97, 0x0A, 0x70, 0xA7, 0x0B, 0x70, 0xC7, 0x0D,
|
||||
0x70, 0xD7, 0x0E, 0x70, 0xF7, 0x10, 0x71, 0x07, 0x11, 0x71, 0x27, 0x12, 0x71, 0x37, 0x14, 0x71, 0x57, 0x15, 0x71, 0x67, 0x17, 0x71, 0x87, 0x18,
|
||||
0x71, 0x97, 0x1A, 0x71, 0xA7, 0x1B, 0x71, 0xC7, 0x1D, 0x71, 0xD7, 0x1E, 0x71, 0xF7, 0x1F, 0x72, 0x07, 0x21, 0x72, 0x27, 0x22, 0x72, 0x37, 0x24,
|
||||
0x72, 0x47, 0x25, 0x72, 0x67, 0x27, 0x72, 0x77, 0x28, 0x72, 0x97, 0x29, 0x72, 0xA7, 0x2B, 0x72, 0xB7, 0x2C, 0x72, 0xD7, 0x2E, 0x72, 0xE7, 0x2F,
|
||||
0x73, 0x07, 0x30, 0x73, 0x17, 0x32, 0x73, 0x27, 0x33, 0x73, 0x47, 0x34, 0x73, 0x57, 0x36, 0x73, 0x67, 0x37, 0x73, 0x87, 0x38, 0x73, 0x97, 0x3A,
|
||||
0x73, 0xA7, 0x3B, 0x73, 0xC7, 0x3C, 0x73, 0xD7, 0x3E, 0x73, 0xE7, 0x3F, 0x74, 0x07, 0x40, 0x74, 0x17, 0x42, 0x74, 0x27, 0x43, 0x74, 0x47, 0x44,
|
||||
0x74, 0x57, 0x46, 0x74, 0x67, 0x47, 0x74, 0x87, 0x48, 0x74, 0x97, 0x4A, 0x74, 0xA7, 0x4B, 0x74, 0xC7, 0x4C, 0x74, 0xD7, 0x4D, 0x74, 0xE7, 0x4F,
|
||||
0x74, 0xF7, 0x50, 0x75, 0x17, 0x51, 0x75, 0x27, 0x53, 0x75, 0x37, 0x54, 0x75, 0x47, 0x55, 0x75, 0x67, 0x56, 0x75, 0x77, 0x58, 0x75, 0x87, 0x59,
|
||||
0x75, 0x97, 0x5A, 0x75, 0xB7, 0x5B, 0x75, 0xC7, 0x5D, 0x75, 0xD7, 0x5E, 0x75, 0xE7, 0x5F, 0x76, 0x07, 0x60, 0x76, 0x17, 0x61, 0x76, 0x27, 0x63,
|
||||
0x76, 0x37, 0x64, 0x76, 0x47, 0x65, 0x76, 0x67, 0x66, 0x76, 0x77, 0x67, 0x76, 0x87, 0x69, 0x76, 0x97, 0x6A, 0x76, 0xA7, 0x6B, 0x76, 0xB7, 0x6C,
|
||||
0x76, 0xD7, 0x6D, 0x76, 0xE7, 0x6E, 0x76, 0xF7, 0x70, 0x77, 0x07, 0x71, 0x77, 0x17, 0x72, 0x77, 0x27, 0x73, 0x77, 0x47, 0x74, 0x77, 0x57, 0x75,
|
||||
0x77, 0x67, 0x76, 0x77, 0x77, 0x78, 0x77, 0x87, 0x79, 0x77, 0x97, 0x7A, 0x77, 0xA7, 0x7B, 0x77, 0xB7, 0x7C, 0x77, 0xD7, 0x7D, 0x77, 0xE7, 0x7E,
|
||||
0x77, 0xF7, 0x7F, 0x78, 0x07, 0x80, 0x78, 0x17, 0x81, 0x78, 0x27, 0x83, 0x78, 0x37, 0x84, 0x78, 0x47, 0x85, 0x78, 0x57, 0x86, 0x78, 0x67, 0x87,
|
||||
0x78, 0x77, 0x88, 0x78, 0x87, 0x89, 0x78, 0x97, 0x8A, 0x78, 0xA7, 0x8B, 0x78, 0xC7, 0x8C, 0x78, 0xD7, 0x8D, 0x78, 0xE7, 0x8E, 0x78, 0xF7, 0x8F,
|
||||
0x79, 0x07, 0x90, 0x79, 0x17, 0x91, 0x79, 0x27, 0x92, 0x79, 0x37, 0x93, 0x79, 0x47, 0x94, 0x79, 0x57, 0x95, 0x79, 0x67, 0x96, 0x79, 0x77, 0x97,
|
||||
0x79, 0x87, 0x98, 0x79, 0x97, 0x99, 0x79, 0xA7, 0x9A, 0x79, 0xB7, 0x9B, 0x79, 0xC7, 0x9C, 0x79, 0xD7, 0x9D, 0x79, 0xE7, 0x9E, 0x79, 0xE7, 0x9F,
|
||||
0x79, 0xF7, 0xA0, 0x7A, 0x07, 0xA1, 0x7A, 0x17, 0xA2, 0x7A, 0x27, 0xA3, 0x7A, 0x37, 0xA4, 0x7A, 0x47, 0xA5, 0x7A, 0x57, 0xA5, 0x7A, 0x67, 0xA6,
|
||||
0x7A, 0x77, 0xA7, 0x7A, 0x87, 0xA8, 0x7A, 0x97, 0xA9, 0x7A, 0xA7, 0xAA, 0x7A, 0xA7, 0xAB, 0x7A, 0xB7, 0xAC, 0x7A, 0xC7, 0xAD, 0x7A, 0xD7, 0xAE,
|
||||
0x7A, 0xE7, 0xAE, 0x7A, 0xF7, 0xAF, 0x7B, 0x07, 0xB0, 0x7B, 0x17, 0xB1, 0x7B, 0x17, 0xB2, 0x7B, 0x27, 0xB3, 0x7B, 0x37, 0xB4, 0x7B, 0x47, 0xB4,
|
||||
0x7B, 0x57, 0xB5, 0x7B, 0x67, 0xB6, 0x7B, 0x77, 0xB7, 0x7B, 0x77, 0xB8, 0x7B, 0x87, 0xB9, 0x7B, 0x97, 0xB9, 0x7B, 0xA7, 0xBA, 0x7B, 0xB7, 0xBB,
|
||||
0x7B, 0xB7, 0xBC, 0x7B, 0xC7, 0xBD, 0x7B, 0xD7, 0xBD, 0x7B, 0xE7, 0xBE, 0x7B, 0xF7, 0xBF, 0x7B, 0xF7, 0xC0, 0x7C, 0x07, 0xC1, 0x7C, 0x17, 0xC1,
|
||||
0x7C, 0x27, 0xC2, 0x7C, 0x27, 0xC3, 0x7C, 0x37, 0xC4, 0x7C, 0x47, 0xC4, 0x7C, 0x57, 0xC5, 0x7C, 0x57, 0xC6, 0x7C, 0x67, 0xC7, 0x7C, 0x77, 0xC7,
|
||||
0x7C, 0x87, 0xC8, 0x7C, 0x87, 0xC9, 0x7C, 0x97, 0xC9, 0x7C, 0xA7, 0xCA, 0x7C, 0xA7, 0xCB, 0x7C, 0xB7, 0xCC, 0x7C, 0xC7, 0xCC, 0x7C, 0xD7, 0xCD,
|
||||
0x7C, 0xD7, 0xCE, 0x7C, 0xE7, 0xCE, 0x7C, 0xF7, 0xCF, 0x7C, 0xF7, 0xD0, 0x7D, 0x07, 0xD0, 0x7D, 0x17, 0xD1, 0x7D, 0x17, 0xD2, 0x7D, 0x27, 0xD2,
|
||||
0x7D, 0x37, 0xD3, 0x7D, 0x37, 0xD4, 0x7D, 0x47, 0xD4, 0x7D, 0x57, 0xD5, 0x7D, 0x57, 0xD5, 0x7D, 0x67, 0xD6, 0x7D, 0x67, 0xD7, 0x7D, 0x77, 0xD7,
|
||||
0x7D, 0x87, 0xD8, 0x7D, 0x87, 0xD9, 0x7D, 0x97, 0xD9, 0x7D, 0x97, 0xDA, 0x7D, 0xA7, 0xDA, 0x7D, 0xB7, 0xDB, 0x7D, 0xB7, 0xDC, 0x7D, 0xC7, 0xDC,
|
||||
0x7D, 0xC7, 0xDD, 0x7D, 0xD7, 0xDD, 0x7D, 0xE7, 0xDE, 0x7D, 0xE7, 0xDE, 0x7D, 0xF7, 0xDF, 0x7D, 0xF7, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE1,
|
||||
0x7E, 0x17, 0xE1, 0x7E, 0x17, 0xE2, 0x7E, 0x27, 0xE2, 0x7E, 0x27, 0xE3, 0x7E, 0x37, 0xE3, 0x7E, 0x37, 0xE4, 0x7E, 0x47, 0xE4, 0x7E, 0x57, 0xE5,
|
||||
0x7E, 0x57, 0xE5, 0x7E, 0x67, 0xE6, 0x7E, 0x67, 0xE6, 0x7E, 0x67, 0xE7, 0x7E, 0x77, 0xE7, 0x7E, 0x77, 0xE8, 0x7E, 0x87, 0xE8, 0x7E, 0x87, 0xE9,
|
||||
0x7E, 0x97, 0xE9, 0x7E, 0x97, 0xEA, 0x7E, 0xA7, 0xEA, 0x7E, 0xA7, 0xEA, 0x7E, 0xB7, 0xEB, 0x7E, 0xB7, 0xEB, 0x7E, 0xC7, 0xEC, 0x7E, 0xC7, 0xEC,
|
||||
0x7E, 0xC7, 0xED, 0x7E, 0xD7, 0xED, 0x7E, 0xD7, 0xED, 0x7E, 0xE7, 0xEE, 0x7E, 0xE7, 0xEE, 0x7E, 0xE7, 0xEF, 0x7E, 0xF7, 0xEF, 0x7E, 0xF7, 0xEF,
|
||||
0x7F, 0x07, 0xF0, 0x7F, 0x07, 0xF0, 0x7F, 0x07, 0xF1, 0x7F, 0x17, 0xF1, 0x7F, 0x17, 0xF1, 0x7F, 0x17, 0xF2, 0x7F, 0x27, 0xF2, 0x7F, 0x27, 0xF2,
|
||||
0x7F, 0x37, 0xF3, 0x7F, 0x37, 0xF3, 0x7F, 0x37, 0xF3, 0x7F, 0x47, 0xF4, 0x7F, 0x47, 0xF4, 0x7F, 0x47, 0xF4, 0x7F, 0x57, 0xF5, 0x7F, 0x57, 0xF5,
|
||||
0x7F, 0x57, 0xF5, 0x7F, 0x57, 0xF6, 0x7F, 0x67, 0xF6, 0x7F, 0x67, 0xF6, 0x7F, 0x67, 0xF6, 0x7F, 0x77, 0xF7, 0x7F, 0x77, 0xF7, 0x7F, 0x77, 0xF7,
|
||||
0x7F, 0x77, 0xF8, 0x7F, 0x87, 0xF8, 0x7F, 0x87, 0xF8, 0x7F, 0x87, 0xF8, 0x7F, 0x87, 0xF9, 0x7F, 0x97, 0xF9, 0x7F, 0x97, 0xF9, 0x7F, 0x97, 0xF9,
|
||||
0x7F, 0x97, 0xFA, 0x7F, 0xA7, 0xFA, 0x7F, 0xA7, 0xFA, 0x7F, 0xA7, 0xFA, 0x7F, 0xA7, 0xFA, 0x7F, 0xB7, 0xFB, 0x7F, 0xB7, 0xFB, 0x7F, 0xB7, 0xFB,
|
||||
0x7F, 0xB7, 0xFB, 0x7F, 0xB7, 0xFB, 0x7F, 0xC7, 0xFC, 0x7F, 0xC7, 0xFC, 0x7F, 0xC7, 0xFC, 0x7F, 0xC7, 0xFC, 0x7F, 0xC7, 0xFC, 0x7F, 0xC7, 0xFC,
|
||||
0x7F, 0xD7, 0xFD, 0x7F, 0xD7, 0xFD, 0x7F, 0xD7, 0xFD, 0x7F, 0xD7, 0xFD, 0x7F, 0xD7, 0xFD, 0x7F, 0xD7, 0xFD, 0x7F, 0xD7, 0xFD, 0x7F, 0xD7, 0xFE,
|
||||
0x7F, 0xE7, 0xFE, 0x7F, 0xE7, 0xFE, 0x7F, 0xE7, 0xFE, 0x7F, 0xE7, 0xFE, 0x7F, 0xE7, 0xFE, 0x7F, 0xE7, 0xFE, 0x7F, 0xE7, 0xFE, 0x7F, 0xE7, 0xFE,
|
||||
0x7F, 0xE7, 0xFE, 0x7F, 0xE7, 0xFE, 0x7F, 0xF7, 0xFF, 0x7F, 0xF7, 0xFF, 0x7F, 0xF7, 0xFF, 0x7F, 0xF7, 0xFF, 0x7F, 0xF7, 0xFF, 0x7F, 0xF7, 0xFF,
|
||||
0x7F, 0xF7, 0xFF, 0x7F, 0xF7, 0xFF, 0x7F, 0xF7, 0xFF, 0x7F, 0xF7, 0xFF, 0x7F, 0xF7, 0xFF, 0x7F, 0xF7, 0xFF, 0x7F, 0xF7, 0xFF, 0x7F, 0xF7, 0xFF,
|
||||
};
|
||||
#else
|
||||
// range 2000
|
||||
const uint8_t LUT_sine_8192_quad_packed[3072] = {
|
||||
0x00, 0x00, 0x02, 0x00, 0x30, 0x05, 0x00, 0x60, 0x08, 0x00, 0x90, 0x0B, 0x00, 0xC0, 0x0E, 0x00, 0xF0, 0x11, 0x01, 0x20, 0x14, 0x01, 0x50, 0x17,
|
||||
0x01, 0x90, 0x1A, 0x01, 0xC0, 0x1D, 0x01, 0xF0, 0x20, 0x02, 0x20, 0x23, 0x02, 0x50, 0x26, 0x02, 0x80, 0x29, 0x02, 0xB0, 0x2C, 0x02, 0xE0, 0x30,
|
||||
0x03, 0x10, 0x33, 0x03, 0x40, 0x36, 0x03, 0x70, 0x39, 0x03, 0xA0, 0x3C, 0x03, 0xD0, 0x3F, 0x04, 0x00, 0x42, 0x04, 0x30, 0x45, 0x04, 0x70, 0x48,
|
||||
0x04, 0xA0, 0x4B, 0x04, 0xD0, 0x4E, 0x05, 0x00, 0x51, 0x05, 0x30, 0x54, 0x05, 0x60, 0x57, 0x05, 0x90, 0x5A, 0x05, 0xC0, 0x5E, 0x05, 0xF0, 0x61,
|
||||
0x06, 0x20, 0x64, 0x06, 0x50, 0x67, 0x06, 0x80, 0x6A, 0x06, 0xB0, 0x6D, 0x06, 0xE0, 0x70, 0x07, 0x10, 0x73, 0x07, 0x50, 0x76, 0x07, 0x80, 0x79,
|
||||
0x07, 0xB0, 0x7C, 0x07, 0xE0, 0x7F, 0x08, 0x10, 0x82, 0x08, 0x40, 0x85, 0x08, 0x70, 0x88, 0x08, 0xA0, 0x8B, 0x08, 0xD0, 0x8F, 0x09, 0x00, 0x92,
|
||||
0x09, 0x30, 0x95, 0x09, 0x60, 0x98, 0x09, 0x90, 0x9B, 0x09, 0xC0, 0x9E, 0x09, 0xF0, 0xA1, 0x0A, 0x20, 0xA4, 0x0A, 0x50, 0xA7, 0x0A, 0x90, 0xAA,
|
||||
0x0A, 0xC0, 0xAD, 0x0A, 0xF0, 0xB0, 0x0B, 0x20, 0xB3, 0x0B, 0x50, 0xB6, 0x0B, 0x80, 0xB9, 0x0B, 0xB0, 0xBC, 0x0B, 0xE0, 0xBF, 0x0C, 0x10, 0xC3,
|
||||
0x0C, 0x40, 0xC6, 0x0C, 0x70, 0xC9, 0x0C, 0xA0, 0xCC, 0x0C, 0xD0, 0xCF, 0x0D, 0x00, 0xD2, 0x0D, 0x30, 0xD5, 0x0D, 0x60, 0xD8, 0x0D, 0x90, 0xDB,
|
||||
0x0D, 0xC0, 0xDE, 0x0D, 0xF0, 0xE1, 0x0E, 0x30, 0xE4, 0x0E, 0x60, 0xE7, 0x0E, 0x90, 0xEA, 0x0E, 0xC0, 0xED, 0x0E, 0xF0, 0xF0, 0x0F, 0x20, 0xF3,
|
||||
0x0F, 0x50, 0xF6, 0x0F, 0x80, 0xF9, 0x0F, 0xB0, 0xFC, 0x0F, 0xE0, 0xFF, 0x10, 0x11, 0x03, 0x10, 0x41, 0x06, 0x10, 0x71, 0x09, 0x10, 0xA1, 0x0C,
|
||||
0x10, 0xD1, 0x0F, 0x11, 0x01, 0x12, 0x11, 0x31, 0x15, 0x11, 0x61, 0x18, 0x11, 0x91, 0x1B, 0x11, 0xC1, 0x1E, 0x11, 0xF1, 0x21, 0x12, 0x21, 0x24,
|
||||
0x12, 0x51, 0x27, 0x12, 0x81, 0x2A, 0x12, 0xC1, 0x2D, 0x12, 0xF1, 0x30, 0x13, 0x21, 0x33, 0x13, 0x51, 0x36, 0x13, 0x81, 0x39, 0x13, 0xB1, 0x3C,
|
||||
0x13, 0xE1, 0x3F, 0x14, 0x11, 0x42, 0x14, 0x41, 0x45, 0x14, 0x71, 0x48, 0x14, 0xA1, 0x4B, 0x14, 0xD1, 0x4E, 0x15, 0x01, 0x51, 0x15, 0x31, 0x54,
|
||||
0x15, 0x61, 0x57, 0x15, 0x91, 0x5A, 0x15, 0xC1, 0x5D, 0x15, 0xF1, 0x60, 0x16, 0x21, 0x64, 0x16, 0x51, 0x67, 0x16, 0x81, 0x6A, 0x16, 0xB1, 0x6D,
|
||||
0x16, 0xE1, 0x70, 0x17, 0x11, 0x73, 0x17, 0x41, 0x76, 0x17, 0x71, 0x79, 0x17, 0xA1, 0x7C, 0x17, 0xD1, 0x7F, 0x18, 0x01, 0x82, 0x18, 0x31, 0x85,
|
||||
0x18, 0x61, 0x88, 0x18, 0x91, 0x8B, 0x18, 0xC1, 0x8E, 0x18, 0xF1, 0x91, 0x19, 0x21, 0x94, 0x19, 0x51, 0x97, 0x19, 0x81, 0x9A, 0x19, 0xB1, 0x9D,
|
||||
0x19, 0xE1, 0xA0, 0x1A, 0x11, 0xA3, 0x1A, 0x41, 0xA6, 0x1A, 0x71, 0xA9, 0x1A, 0xA1, 0xAC, 0x1A, 0xD1, 0xAF, 0x1B, 0x01, 0xB2, 0x1B, 0x31, 0xB5,
|
||||
0x1B, 0x61, 0xB8, 0x1B, 0x91, 0xBB, 0x1B, 0xC1, 0xBE, 0x1B, 0xF1, 0xC1, 0x1C, 0x21, 0xC4, 0x1C, 0x51, 0xC7, 0x1C, 0x81, 0xCA, 0x1C, 0xB1, 0xCD,
|
||||
0x1C, 0xE1, 0xD0, 0x1D, 0x11, 0xD3, 0x1D, 0x41, 0xD6, 0x1D, 0x71, 0xD9, 0x1D, 0xA1, 0xDC, 0x1D, 0xD1, 0xDF, 0x1E, 0x01, 0xE1, 0x1E, 0x31, 0xE4,
|
||||
0x1E, 0x61, 0xE7, 0x1E, 0x91, 0xEA, 0x1E, 0xC1, 0xED, 0x1E, 0xF1, 0xF0, 0x1F, 0x21, 0xF3, 0x1F, 0x51, 0xF6, 0x1F, 0x81, 0xF9, 0x1F, 0xB1, 0xFC,
|
||||
0x1F, 0xE1, 0xFF, 0x20, 0x12, 0x02, 0x20, 0x42, 0x05, 0x20, 0x72, 0x08, 0x20, 0xA2, 0x0B, 0x20, 0xD2, 0x0E, 0x21, 0x02, 0x11, 0x21, 0x22, 0x14,
|
||||
0x21, 0x52, 0x17, 0x21, 0x82, 0x1A, 0x21, 0xB2, 0x1D, 0x21, 0xE2, 0x20, 0x22, 0x12, 0x23, 0x22, 0x42, 0x26, 0x22, 0x72, 0x29, 0x22, 0xA2, 0x2C,
|
||||
0x22, 0xD2, 0x2F, 0x23, 0x02, 0x31, 0x23, 0x32, 0x34, 0x23, 0x62, 0x37, 0x23, 0x92, 0x3A, 0x23, 0xC2, 0x3D, 0x23, 0xF2, 0x40, 0x24, 0x22, 0x43,
|
||||
0x24, 0x52, 0x46, 0x24, 0x82, 0x49, 0x24, 0xA2, 0x4C, 0x24, 0xD2, 0x4F, 0x25, 0x02, 0x52, 0x25, 0x32, 0x55, 0x25, 0x62, 0x58, 0x25, 0x92, 0x5B,
|
||||
0x25, 0xC2, 0x5D, 0x25, 0xF2, 0x60, 0x26, 0x22, 0x63, 0x26, 0x52, 0x66, 0x26, 0x82, 0x69, 0x26, 0xB2, 0x6C, 0x26, 0xE2, 0x6F, 0x27, 0x02, 0x72,
|
||||
0x27, 0x32, 0x75, 0x27, 0x62, 0x78, 0x27, 0x92, 0x7B, 0x27, 0xC2, 0x7E, 0x27, 0xF2, 0x80, 0x28, 0x22, 0x83, 0x28, 0x52, 0x86, 0x28, 0x82, 0x89,
|
||||
0x28, 0xB2, 0x8C, 0x28, 0xE2, 0x8F, 0x29, 0x02, 0x92, 0x29, 0x32, 0x95, 0x29, 0x62, 0x98, 0x29, 0x92, 0x9B, 0x29, 0xC2, 0x9D, 0x29, 0xF2, 0xA0,
|
||||
0x2A, 0x22, 0xA3, 0x2A, 0x52, 0xA6, 0x2A, 0x82, 0xA9, 0x2A, 0xA2, 0xAC, 0x2A, 0xD2, 0xAF, 0x2B, 0x02, 0xB2, 0x2B, 0x32, 0xB5, 0x2B, 0x62, 0xB7,
|
||||
0x2B, 0x92, 0xBA, 0x2B, 0xC2, 0xBD, 0x2B, 0xF2, 0xC0, 0x2C, 0x12, 0xC3, 0x2C, 0x42, 0xC6, 0x2C, 0x72, 0xC9, 0x2C, 0xA2, 0xCB, 0x2C, 0xD2, 0xCE,
|
||||
0x2D, 0x02, 0xD1, 0x2D, 0x32, 0xD4, 0x2D, 0x62, 0xD7, 0x2D, 0x82, 0xDA, 0x2D, 0xB2, 0xDD, 0x2D, 0xE2, 0xE0, 0x2E, 0x12, 0xE2, 0x2E, 0x42, 0xE5,
|
||||
0x2E, 0x72, 0xE8, 0x2E, 0x92, 0xEB, 0x2E, 0xC2, 0xEE, 0x2E, 0xF2, 0xF1, 0x2F, 0x22, 0xF3, 0x2F, 0x52, 0xF6, 0x2F, 0x82, 0xF9, 0x2F, 0xB2, 0xFC,
|
||||
0x2F, 0xD2, 0xFF, 0x30, 0x03, 0x02, 0x30, 0x33, 0x04, 0x30, 0x63, 0x07, 0x30, 0x93, 0x0A, 0x30, 0xC3, 0x0D, 0x30, 0xE3, 0x10, 0x31, 0x13, 0x13,
|
||||
0x31, 0x43, 0x15, 0x31, 0x73, 0x18, 0x31, 0xA3, 0x1B, 0x31, 0xC3, 0x1E, 0x31, 0xF3, 0x21, 0x32, 0x23, 0x23, 0x32, 0x53, 0x26, 0x32, 0x83, 0x29,
|
||||
0x32, 0xA3, 0x2C, 0x32, 0xD3, 0x2F, 0x33, 0x03, 0x31, 0x33, 0x33, 0x34, 0x33, 0x63, 0x37, 0x33, 0x83, 0x3A, 0x33, 0xB3, 0x3D, 0x33, 0xE3, 0x3F,
|
||||
0x34, 0x13, 0x42, 0x34, 0x43, 0x45, 0x34, 0x63, 0x48, 0x34, 0x93, 0x4B, 0x34, 0xC3, 0x4D, 0x34, 0xF3, 0x50, 0x35, 0x23, 0x53, 0x35, 0x43, 0x56,
|
||||
0x35, 0x73, 0x58, 0x35, 0xA3, 0x5B, 0x35, 0xD3, 0x5E, 0x35, 0xF3, 0x61, 0x36, 0x23, 0x64, 0x36, 0x53, 0x66, 0x36, 0x83, 0x69, 0x36, 0xA3, 0x6C,
|
||||
0x36, 0xD3, 0x6F, 0x37, 0x03, 0x71, 0x37, 0x33, 0x74, 0x37, 0x53, 0x77, 0x37, 0x83, 0x7A, 0x37, 0xB3, 0x7C, 0x37, 0xE3, 0x7F, 0x38, 0x03, 0x82,
|
||||
0x38, 0x33, 0x85, 0x38, 0x63, 0x87, 0x38, 0x93, 0x8A, 0x38, 0xB3, 0x8D, 0x38, 0xE3, 0x90, 0x39, 0x13, 0x92, 0x39, 0x43, 0x95, 0x39, 0x63, 0x98,
|
||||
0x39, 0x93, 0x9A, 0x39, 0xC3, 0x9D, 0x39, 0xF3, 0xA0, 0x3A, 0x13, 0xA3, 0x3A, 0x43, 0xA5, 0x3A, 0x73, 0xA8, 0x3A, 0x93, 0xAB, 0x3A, 0xC3, 0xAD,
|
||||
0x3A, 0xF3, 0xB0, 0x3B, 0x13, 0xB3, 0x3B, 0x43, 0xB6, 0x3B, 0x73, 0xB8, 0x3B, 0xA3, 0xBB, 0x3B, 0xC3, 0xBE, 0x3B, 0xF3, 0xC0, 0x3C, 0x23, 0xC3,
|
||||
0x3C, 0x43, 0xC6, 0x3C, 0x73, 0xC8, 0x3C, 0xA3, 0xCB, 0x3C, 0xC3, 0xCE, 0x3C, 0xF3, 0xD0, 0x3D, 0x23, 0xD3, 0x3D, 0x43, 0xD6, 0x3D, 0x73, 0xD8,
|
||||
0x3D, 0xA3, 0xDB, 0x3D, 0xC3, 0xDE, 0x3D, 0xF3, 0xE0, 0x3E, 0x23, 0xE3, 0x3E, 0x43, 0xE6, 0x3E, 0x73, 0xE8, 0x3E, 0xA3, 0xEB, 0x3E, 0xC3, 0xEE,
|
||||
0x3E, 0xF3, 0xF0, 0x3F, 0x23, 0xF3, 0x3F, 0x43, 0xF6, 0x3F, 0x73, 0xF8, 0x3F, 0xA3, 0xFB, 0x3F, 0xC3, 0xFE, 0x3F, 0xF4, 0x00, 0x40, 0x24, 0x03,
|
||||
0x40, 0x44, 0x06, 0x40, 0x74, 0x08, 0x40, 0x94, 0x0B, 0x40, 0xC4, 0x0D, 0x40, 0xF4, 0x10, 0x41, 0x14, 0x13, 0x41, 0x44, 0x15, 0x41, 0x74, 0x18,
|
||||
0x41, 0x94, 0x1A, 0x41, 0xC4, 0x1D, 0x41, 0xE4, 0x20, 0x42, 0x14, 0x22, 0x42, 0x44, 0x25, 0x42, 0x64, 0x28, 0x42, 0x94, 0x2A, 0x42, 0xB4, 0x2D,
|
||||
0x42, 0xE4, 0x2F, 0x43, 0x14, 0x32, 0x43, 0x34, 0x34, 0x43, 0x64, 0x37, 0x43, 0x84, 0x3A, 0x43, 0xB4, 0x3C, 0x43, 0xE4, 0x3F, 0x44, 0x04, 0x41,
|
||||
0x44, 0x34, 0x44, 0x44, 0x54, 0x47, 0x44, 0x84, 0x49, 0x44, 0xA4, 0x4C, 0x44, 0xD4, 0x4E, 0x44, 0xF4, 0x51, 0x45, 0x24, 0x53, 0x45, 0x54, 0x56,
|
||||
0x45, 0x74, 0x58, 0x45, 0xA4, 0x5B, 0x45, 0xC4, 0x5E, 0x45, 0xF4, 0x60, 0x46, 0x14, 0x63, 0x46, 0x44, 0x65, 0x46, 0x64, 0x68, 0x46, 0x94, 0x6A,
|
||||
0x46, 0xB4, 0x6D, 0x46, 0xE4, 0x6F, 0x47, 0x14, 0x72, 0x47, 0x34, 0x74, 0x47, 0x64, 0x77, 0x47, 0x84, 0x79, 0x47, 0xB4, 0x7C, 0x47, 0xD4, 0x7E,
|
||||
0x48, 0x04, 0x81, 0x48, 0x24, 0x83, 0x48, 0x54, 0x86, 0x48, 0x74, 0x88, 0x48, 0xA4, 0x8B, 0x48, 0xC4, 0x8D, 0x48, 0xF4, 0x90, 0x49, 0x14, 0x92,
|
||||
0x49, 0x44, 0x95, 0x49, 0x64, 0x97, 0x49, 0x94, 0x9A, 0x49, 0xB4, 0x9C, 0x49, 0xE4, 0x9F, 0x4A, 0x04, 0xA1, 0x4A, 0x24, 0xA4, 0x4A, 0x54, 0xA6,
|
||||
0x4A, 0x74, 0xA9, 0x4A, 0xA4, 0xAB, 0x4A, 0xC4, 0xAE, 0x4A, 0xF4, 0xB0, 0x4B, 0x14, 0xB2, 0x4B, 0x44, 0xB5, 0x4B, 0x64, 0xB7, 0x4B, 0x94, 0xBA,
|
||||
0x4B, 0xB4, 0xBC, 0x4B, 0xD4, 0xBF, 0x4C, 0x04, 0xC1, 0x4C, 0x24, 0xC4, 0x4C, 0x54, 0xC6, 0x4C, 0x74, 0xC8, 0x4C, 0xA4, 0xCB, 0x4C, 0xC4, 0xCD,
|
||||
0x4C, 0xE4, 0xD0, 0x4D, 0x14, 0xD2, 0x4D, 0x34, 0xD5, 0x4D, 0x64, 0xD7, 0x4D, 0x84, 0xD9, 0x4D, 0xB4, 0xDC, 0x4D, 0xD4, 0xDE, 0x4D, 0xF4, 0xE1,
|
||||
0x4E, 0x24, 0xE3, 0x4E, 0x44, 0xE5, 0x4E, 0x74, 0xE8, 0x4E, 0x94, 0xEA, 0x4E, 0xB4, 0xEC, 0x4E, 0xE4, 0xEF, 0x4F, 0x04, 0xF1, 0x4F, 0x24, 0xF4,
|
||||
0x4F, 0x54, 0xF6, 0x4F, 0x74, 0xF8, 0x4F, 0xA4, 0xFB, 0x4F, 0xC4, 0xFD, 0x4F, 0xE4, 0xFF, 0x50, 0x15, 0x02, 0x50, 0x35, 0x04, 0x50, 0x55, 0x06,
|
||||
0x50, 0x85, 0x09, 0x50, 0xA5, 0x0B, 0x50, 0xC5, 0x0E, 0x50, 0xF5, 0x10, 0x51, 0x15, 0x12, 0x51, 0x35, 0x15, 0x51, 0x65, 0x17, 0x51, 0x85, 0x19,
|
||||
0x51, 0xA5, 0x1C, 0x51, 0xD5, 0x1E, 0x51, 0xF5, 0x20, 0x52, 0x15, 0x22, 0x52, 0x45, 0x25, 0x52, 0x65, 0x27, 0x52, 0x85, 0x29, 0x52, 0xB5, 0x2C,
|
||||
0x52, 0xD5, 0x2E, 0x52, 0xF5, 0x30, 0x53, 0x15, 0x33, 0x53, 0x45, 0x35, 0x53, 0x65, 0x37, 0x53, 0x85, 0x39, 0x53, 0xB5, 0x3C, 0x53, 0xD5, 0x3E,
|
||||
0x53, 0xF5, 0x40, 0x54, 0x15, 0x43, 0x54, 0x45, 0x45, 0x54, 0x65, 0x47, 0x54, 0x85, 0x49, 0x54, 0xA5, 0x4C, 0x54, 0xD5, 0x4E, 0x54, 0xF5, 0x50,
|
||||
0x55, 0x15, 0x52, 0x55, 0x35, 0x55, 0x55, 0x65, 0x57, 0x55, 0x85, 0x59, 0x55, 0xA5, 0x5B, 0x55, 0xC5, 0x5E, 0x55, 0xF5, 0x60, 0x56, 0x15, 0x62,
|
||||
0x56, 0x35, 0x64, 0x56, 0x55, 0x66, 0x56, 0x85, 0x69, 0x56, 0xA5, 0x6B, 0x56, 0xC5, 0x6D, 0x56, 0xE5, 0x6F, 0x57, 0x05, 0x71, 0x57, 0x35, 0x74,
|
||||
0x57, 0x55, 0x76, 0x57, 0x75, 0x78, 0x57, 0x95, 0x7A, 0x57, 0xB5, 0x7C, 0x57, 0xE5, 0x7F, 0x58, 0x05, 0x81, 0x58, 0x25, 0x83, 0x58, 0x45, 0x85,
|
||||
0x58, 0x65, 0x87, 0x58, 0x85, 0x89, 0x58, 0xB5, 0x8C, 0x58, 0xD5, 0x8E, 0x58, 0xF5, 0x90, 0x59, 0x15, 0x92, 0x59, 0x35, 0x94, 0x59, 0x55, 0x96,
|
||||
0x59, 0x75, 0x99, 0x59, 0xA5, 0x9B, 0x59, 0xC5, 0x9D, 0x59, 0xE5, 0x9F, 0x5A, 0x05, 0xA1, 0x5A, 0x25, 0xA3, 0x5A, 0x45, 0xA5, 0x5A, 0x65, 0xA7,
|
||||
0x5A, 0x85, 0xAA, 0x5A, 0xB5, 0xAC, 0x5A, 0xD5, 0xAE, 0x5A, 0xF5, 0xB0, 0x5B, 0x15, 0xB2, 0x5B, 0x35, 0xB4, 0x5B, 0x55, 0xB6, 0x5B, 0x75, 0xB8,
|
||||
0x5B, 0x95, 0xBA, 0x5B, 0xB5, 0xBC, 0x5B, 0xD5, 0xBF, 0x5C, 0x05, 0xC1, 0x5C, 0x25, 0xC3, 0x5C, 0x45, 0xC5, 0x5C, 0x65, 0xC7, 0x5C, 0x85, 0xC9,
|
||||
0x5C, 0xA5, 0xCB, 0x5C, 0xC5, 0xCD, 0x5C, 0xE5, 0xCF, 0x5D, 0x05, 0xD1, 0x5D, 0x25, 0xD3, 0x5D, 0x45, 0xD5, 0x5D, 0x65, 0xD7, 0x5D, 0x85, 0xD9,
|
||||
0x5D, 0xA5, 0xDB, 0x5D, 0xC5, 0xDD, 0x5D, 0xE5, 0xDF, 0x5E, 0x05, 0xE1, 0x5E, 0x25, 0xE3, 0x5E, 0x45, 0xE5, 0x5E, 0x65, 0xE7, 0x5E, 0x85, 0xE9,
|
||||
0x5E, 0xA5, 0xEB, 0x5E, 0xC5, 0xED, 0x5E, 0xE5, 0xEF, 0x5F, 0x05, 0xF1, 0x5F, 0x25, 0xF3, 0x5F, 0x45, 0xF5, 0x5F, 0x65, 0xF7, 0x5F, 0x85, 0xF9,
|
||||
0x5F, 0xA5, 0xFB, 0x5F, 0xC5, 0xFD, 0x5F, 0xE5, 0xFF, 0x60, 0x06, 0x01, 0x60, 0x26, 0x03, 0x60, 0x46, 0x05, 0x60, 0x66, 0x07, 0x60, 0x86, 0x09,
|
||||
0x60, 0xA6, 0x0B, 0x60, 0xC6, 0x0D, 0x60, 0xE6, 0x0F, 0x61, 0x06, 0x11, 0x61, 0x26, 0x13, 0x61, 0x46, 0x15, 0x61, 0x66, 0x17, 0x61, 0x86, 0x19,
|
||||
0x61, 0x96, 0x1A, 0x61, 0xB6, 0x1C, 0x61, 0xD6, 0x1E, 0x61, 0xF6, 0x20, 0x62, 0x16, 0x22, 0x62, 0x36, 0x24, 0x62, 0x56, 0x26, 0x62, 0x76, 0x28,
|
||||
0x62, 0x96, 0x2A, 0x62, 0xB6, 0x2C, 0x62, 0xC6, 0x2D, 0x62, 0xE6, 0x2F, 0x63, 0x06, 0x31, 0x63, 0x26, 0x33, 0x63, 0x46, 0x35, 0x63, 0x66, 0x37,
|
||||
0x63, 0x86, 0x39, 0x63, 0xA6, 0x3A, 0x63, 0xB6, 0x3C, 0x63, 0xD6, 0x3E, 0x63, 0xF6, 0x40, 0x64, 0x16, 0x42, 0x64, 0x36, 0x44, 0x64, 0x56, 0x46,
|
||||
0x64, 0x66, 0x47, 0x64, 0x86, 0x49, 0x64, 0xA6, 0x4B, 0x64, 0xC6, 0x4D, 0x64, 0xE6, 0x4F, 0x65, 0x06, 0x50, 0x65, 0x16, 0x52, 0x65, 0x36, 0x54,
|
||||
0x65, 0x56, 0x56, 0x65, 0x76, 0x58, 0x65, 0x96, 0x59, 0x65, 0xA6, 0x5B, 0x65, 0xC6, 0x5D, 0x65, 0xE6, 0x5F, 0x66, 0x06, 0x61, 0x66, 0x16, 0x62,
|
||||
0x66, 0x36, 0x64, 0x66, 0x56, 0x66, 0x66, 0x76, 0x68, 0x66, 0x86, 0x69, 0x66, 0xA6, 0x6B, 0x66, 0xC6, 0x6D, 0x66, 0xE6, 0x6F, 0x66, 0xF6, 0x70,
|
||||
0x67, 0x16, 0x72, 0x67, 0x36, 0x74, 0x67, 0x56, 0x76, 0x67, 0x66, 0x77, 0x67, 0x86, 0x79, 0x67, 0xA6, 0x7B, 0x67, 0xC6, 0x7C, 0x67, 0xD6, 0x7E,
|
||||
0x67, 0xF6, 0x80, 0x68, 0x16, 0x81, 0x68, 0x26, 0x83, 0x68, 0x46, 0x85, 0x68, 0x66, 0x87, 0x68, 0x76, 0x88, 0x68, 0x96, 0x8A, 0x68, 0xB6, 0x8C,
|
||||
0x68, 0xC6, 0x8D, 0x68, 0xE6, 0x8F, 0x69, 0x06, 0x91, 0x69, 0x16, 0x92, 0x69, 0x36, 0x94, 0x69, 0x56, 0x96, 0x69, 0x66, 0x97, 0x69, 0x86, 0x99,
|
||||
0x69, 0xA6, 0x9B, 0x69, 0xB6, 0x9C, 0x69, 0xD6, 0x9E, 0x69, 0xF6, 0x9F, 0x6A, 0x06, 0xA1, 0x6A, 0x26, 0xA3, 0x6A, 0x36, 0xA4, 0x6A, 0x56, 0xA6,
|
||||
0x6A, 0x76, 0xA8, 0x6A, 0x86, 0xA9, 0x6A, 0xA6, 0xAB, 0x6A, 0xC6, 0xAC, 0x6A, 0xD6, 0xAE, 0x6A, 0xF6, 0xB0, 0x6B, 0x06, 0xB1, 0x6B, 0x26, 0xB3,
|
||||
0x6B, 0x36, 0xB4, 0x6B, 0x56, 0xB6, 0x6B, 0x76, 0xB7, 0x6B, 0x86, 0xB9, 0x6B, 0xA6, 0xBB, 0x6B, 0xB6, 0xBC, 0x6B, 0xD6, 0xBE, 0x6B, 0xE6, 0xBF,
|
||||
0x6C, 0x06, 0xC1, 0x6C, 0x16, 0xC2, 0x6C, 0x36, 0xC4, 0x6C, 0x56, 0xC5, 0x6C, 0x66, 0xC7, 0x6C, 0x86, 0xC8, 0x6C, 0x96, 0xCA, 0x6C, 0xB6, 0xCB,
|
||||
0x6C, 0xC6, 0xCD, 0x6C, 0xE6, 0xCE, 0x6C, 0xF6, 0xD0, 0x6D, 0x16, 0xD1, 0x6D, 0x26, 0xD3, 0x6D, 0x46, 0xD4, 0x6D, 0x56, 0xD6, 0x6D, 0x76, 0xD7,
|
||||
0x6D, 0x86, 0xD9, 0x6D, 0xA6, 0xDA, 0x6D, 0xB6, 0xDC, 0x6D, 0xD6, 0xDD, 0x6D, 0xE6, 0xDF, 0x6D, 0xF6, 0xE0, 0x6E, 0x16, 0xE2, 0x6E, 0x26, 0xE3,
|
||||
0x6E, 0x46, 0xE5, 0x6E, 0x56, 0xE6, 0x6E, 0x76, 0xE7, 0x6E, 0x86, 0xE9, 0x6E, 0xA6, 0xEA, 0x6E, 0xB6, 0xEC, 0x6E, 0xC6, 0xED, 0x6E, 0xE6, 0xEF,
|
||||
0x6E, 0xF6, 0xF0, 0x6F, 0x16, 0xF1, 0x6F, 0x26, 0xF3, 0x6F, 0x36, 0xF4, 0x6F, 0x56, 0xF6, 0x6F, 0x66, 0xF7, 0x6F, 0x86, 0xF8, 0x6F, 0x96, 0xFA,
|
||||
0x6F, 0xA6, 0xFB, 0x6F, 0xC6, 0xFD, 0x6F, 0xD6, 0xFE, 0x6F, 0xF6, 0xFF, 0x70, 0x07, 0x01, 0x70, 0x17, 0x02, 0x70, 0x37, 0x03, 0x70, 0x47, 0x05,
|
||||
0x70, 0x57, 0x06, 0x70, 0x77, 0x07, 0x70, 0x87, 0x09, 0x70, 0x97, 0x0A, 0x70, 0xB7, 0x0B, 0x70, 0xC7, 0x0D, 0x70, 0xD7, 0x0E, 0x70, 0xF7, 0x0F,
|
||||
0x71, 0x07, 0x11, 0x71, 0x17, 0x12, 0x71, 0x37, 0x13, 0x71, 0x47, 0x15, 0x71, 0x57, 0x16, 0x71, 0x67, 0x17, 0x71, 0x87, 0x18, 0x71, 0x97, 0x1A,
|
||||
0x71, 0xA7, 0x1B, 0x71, 0xC7, 0x1C, 0x71, 0xD7, 0x1E, 0x71, 0xE7, 0x1F, 0x71, 0xF7, 0x20, 0x72, 0x17, 0x21, 0x72, 0x27, 0x23, 0x72, 0x37, 0x24,
|
||||
0x72, 0x47, 0x25, 0x72, 0x67, 0x26, 0x72, 0x77, 0x28, 0x72, 0x87, 0x29, 0x72, 0x97, 0x2A, 0x72, 0xB7, 0x2B, 0x72, 0xC7, 0x2C, 0x72, 0xD7, 0x2E,
|
||||
0x72, 0xE7, 0x2F, 0x72, 0xF7, 0x30, 0x73, 0x17, 0x31, 0x73, 0x27, 0x32, 0x73, 0x37, 0x34, 0x73, 0x47, 0x35, 0x73, 0x57, 0x36, 0x73, 0x77, 0x37,
|
||||
0x73, 0x87, 0x38, 0x73, 0x97, 0x3A, 0x73, 0xA7, 0x3B, 0x73, 0xB7, 0x3C, 0x73, 0xC7, 0x3D, 0x73, 0xE7, 0x3E, 0x73, 0xF7, 0x3F, 0x74, 0x07, 0x40,
|
||||
0x74, 0x17, 0x42, 0x74, 0x27, 0x43, 0x74, 0x37, 0x44, 0x74, 0x47, 0x45, 0x74, 0x67, 0x46, 0x74, 0x77, 0x47, 0x74, 0x87, 0x48, 0x74, 0x97, 0x49,
|
||||
0x74, 0xA7, 0x4B, 0x74, 0xB7, 0x4C, 0x74, 0xC7, 0x4D, 0x74, 0xD7, 0x4E, 0x74, 0xE7, 0x4F, 0x74, 0xF7, 0x50, 0x75, 0x17, 0x51, 0x75, 0x27, 0x52,
|
||||
0x75, 0x37, 0x53, 0x75, 0x47, 0x54, 0x75, 0x57, 0x55, 0x75, 0x67, 0x56, 0x75, 0x77, 0x57, 0x75, 0x87, 0x58, 0x75, 0x97, 0x5A, 0x75, 0xA7, 0x5B,
|
||||
0x75, 0xB7, 0x5C, 0x75, 0xC7, 0x5D, 0x75, 0xD7, 0x5E, 0x75, 0xE7, 0x5F, 0x75, 0xF7, 0x60, 0x76, 0x07, 0x61, 0x76, 0x17, 0x62, 0x76, 0x27, 0x63,
|
||||
0x76, 0x37, 0x64, 0x76, 0x47, 0x65, 0x76, 0x57, 0x66, 0x76, 0x67, 0x67, 0x76, 0x77, 0x68, 0x76, 0x87, 0x69, 0x76, 0x97, 0x6A, 0x76, 0xA7, 0x6B,
|
||||
0x76, 0xB7, 0x6C, 0x76, 0xC7, 0x6C, 0x76, 0xD7, 0x6D, 0x76, 0xE7, 0x6E, 0x76, 0xF7, 0x6F, 0x77, 0x07, 0x70, 0x77, 0x17, 0x71, 0x77, 0x27, 0x72,
|
||||
0x77, 0x37, 0x73, 0x77, 0x47, 0x74, 0x77, 0x47, 0x75, 0x77, 0x57, 0x76, 0x77, 0x67, 0x77, 0x77, 0x77, 0x78, 0x77, 0x87, 0x79, 0x77, 0x97, 0x79,
|
||||
0x77, 0xA7, 0x7A, 0x77, 0xB7, 0x7B, 0x77, 0xC7, 0x7C, 0x77, 0xD7, 0x7D, 0x77, 0xD7, 0x7E, 0x77, 0xE7, 0x7F, 0x77, 0xF7, 0x80, 0x78, 0x07, 0x80,
|
||||
0x78, 0x17, 0x81, 0x78, 0x27, 0x82, 0x78, 0x37, 0x83, 0x78, 0x37, 0x84, 0x78, 0x47, 0x85, 0x78, 0x57, 0x85, 0x78, 0x67, 0x86, 0x78, 0x77, 0x87,
|
||||
0x78, 0x87, 0x88, 0x78, 0x87, 0x89, 0x78, 0x97, 0x8A, 0x78, 0xA7, 0x8A, 0x78, 0xB7, 0x8B, 0x78, 0xC7, 0x8C, 0x78, 0xC7, 0x8D, 0x78, 0xD7, 0x8E,
|
||||
0x78, 0xE7, 0x8E, 0x78, 0xF7, 0x8F, 0x79, 0x07, 0x90, 0x79, 0x07, 0x91, 0x79, 0x17, 0x91, 0x79, 0x27, 0x92, 0x79, 0x37, 0x93, 0x79, 0x37, 0x94,
|
||||
0x79, 0x47, 0x94, 0x79, 0x57, 0x95, 0x79, 0x67, 0x96, 0x79, 0x67, 0x97, 0x79, 0x77, 0x97, 0x79, 0x87, 0x98, 0x79, 0x87, 0x99, 0x79, 0x97, 0x9A,
|
||||
0x79, 0xA7, 0x9A, 0x79, 0xB7, 0x9B, 0x79, 0xB7, 0x9C, 0x79, 0xC7, 0x9C, 0x79, 0xD7, 0x9D, 0x79, 0xD7, 0x9E, 0x79, 0xE7, 0x9E, 0x79, 0xF7, 0x9F,
|
||||
0x79, 0xF7, 0xA0, 0x7A, 0x07, 0xA0, 0x7A, 0x17, 0xA1, 0x7A, 0x17, 0xA2, 0x7A, 0x27, 0xA2, 0x7A, 0x37, 0xA3, 0x7A, 0x37, 0xA4, 0x7A, 0x47, 0xA4,
|
||||
0x7A, 0x57, 0xA5, 0x7A, 0x57, 0xA6, 0x7A, 0x67, 0xA6, 0x7A, 0x77, 0xA7, 0x7A, 0x77, 0xA7, 0x7A, 0x87, 0xA8, 0x7A, 0x87, 0xA9, 0x7A, 0x97, 0xA9,
|
||||
0x7A, 0xA7, 0xAA, 0x7A, 0xA7, 0xAA, 0x7A, 0xB7, 0xAB, 0x7A, 0xB7, 0xAC, 0x7A, 0xC7, 0xAC, 0x7A, 0xD7, 0xAD, 0x7A, 0xD7, 0xAD, 0x7A, 0xE7, 0xAE,
|
||||
0x7A, 0xE7, 0xAE, 0x7A, 0xF7, 0xAF, 0x7A, 0xF7, 0xB0, 0x7B, 0x07, 0xB0, 0x7B, 0x07, 0xB1, 0x7B, 0x17, 0xB1, 0x7B, 0x17, 0xB2, 0x7B, 0x27, 0xB2,
|
||||
0x7B, 0x37, 0xB3, 0x7B, 0x37, 0xB3, 0x7B, 0x47, 0xB4, 0x7B, 0x47, 0xB4, 0x7B, 0x57, 0xB5, 0x7B, 0x57, 0xB5, 0x7B, 0x67, 0xB6, 0x7B, 0x67, 0xB6,
|
||||
0x7B, 0x77, 0xB7, 0x7B, 0x77, 0xB7, 0x7B, 0x87, 0xB8, 0x7B, 0x87, 0xB8, 0x7B, 0x97, 0xB9, 0x7B, 0x97, 0xB9, 0x7B, 0x97, 0xBA, 0x7B, 0xA7, 0xBA,
|
||||
0x7B, 0xA7, 0xBB, 0x7B, 0xB7, 0xBB, 0x7B, 0xB7, 0xBB, 0x7B, 0xC7, 0xBC, 0x7B, 0xC7, 0xBC, 0x7B, 0xD7, 0xBD, 0x7B, 0xD7, 0xBD, 0x7B, 0xD7, 0xBE,
|
||||
0x7B, 0xE7, 0xBE, 0x7B, 0xE7, 0xBE, 0x7B, 0xF7, 0xBF, 0x7B, 0xF7, 0xBF, 0x7B, 0xF7, 0xC0, 0x7C, 0x07, 0xC0, 0x7C, 0x07, 0xC0, 0x7C, 0x17, 0xC1,
|
||||
0x7C, 0x17, 0xC1, 0x7C, 0x17, 0xC2, 0x7C, 0x27, 0xC2, 0x7C, 0x27, 0xC2, 0x7C, 0x27, 0xC3, 0x7C, 0x37, 0xC3, 0x7C, 0x37, 0xC3, 0x7C, 0x37, 0xC4,
|
||||
0x7C, 0x47, 0xC4, 0x7C, 0x47, 0xC4, 0x7C, 0x47, 0xC5, 0x7C, 0x57, 0xC5, 0x7C, 0x57, 0xC5, 0x7C, 0x57, 0xC6, 0x7C, 0x67, 0xC6, 0x7C, 0x67, 0xC6,
|
||||
0x7C, 0x67, 0xC7, 0x7C, 0x77, 0xC7, 0x7C, 0x77, 0xC7, 0x7C, 0x77, 0xC7, 0x7C, 0x87, 0xC8, 0x7C, 0x87, 0xC8, 0x7C, 0x87, 0xC8, 0x7C, 0x87, 0xC8,
|
||||
0x7C, 0x97, 0xC9, 0x7C, 0x97, 0xC9, 0x7C, 0x97, 0xC9, 0x7C, 0x97, 0xCA, 0x7C, 0xA7, 0xCA, 0x7C, 0xA7, 0xCA, 0x7C, 0xA7, 0xCA, 0x7C, 0xA7, 0xCA,
|
||||
0x7C, 0xB7, 0xCB, 0x7C, 0xB7, 0xCB, 0x7C, 0xB7, 0xCB, 0x7C, 0xB7, 0xCB, 0x7C, 0xB7, 0xCC, 0x7C, 0xC7, 0xCC, 0x7C, 0xC7, 0xCC, 0x7C, 0xC7, 0xCC,
|
||||
0x7C, 0xC7, 0xCC, 0x7C, 0xC7, 0xCD, 0x7C, 0xD7, 0xCD, 0x7C, 0xD7, 0xCD, 0x7C, 0xD7, 0xCD, 0x7C, 0xD7, 0xCD, 0x7C, 0xD7, 0xCD, 0x7C, 0xD7, 0xCE,
|
||||
0x7C, 0xE7, 0xCE, 0x7C, 0xE7, 0xCE, 0x7C, 0xE7, 0xCE, 0x7C, 0xE7, 0xCE, 0x7C, 0xE7, 0xCE, 0x7C, 0xE7, 0xCE, 0x7C, 0xE7, 0xCE, 0x7C, 0xF7, 0xCF,
|
||||
0x7C, 0xF7, 0xCF, 0x7C, 0xF7, 0xCF, 0x7C, 0xF7, 0xCF, 0x7C, 0xF7, 0xCF, 0x7C, 0xF7, 0xCF, 0x7C, 0xF7, 0xCF, 0x7C, 0xF7, 0xCF, 0x7C, 0xF7, 0xCF,
|
||||
0x7C, 0xF7, 0xCF, 0x7C, 0xF7, 0xD0, 0x7D, 0x07, 0xD0, 0x7D, 0x07, 0xD0, 0x7D, 0x07, 0xD0, 0x7D, 0x07, 0xD0, 0x7D, 0x07, 0xD0, 0x7D, 0x07, 0xD0,
|
||||
0x7D, 0x07, 0xD0, 0x7D, 0x07, 0xD0, 0x7D, 0x07, 0xD0, 0x7D, 0x07, 0xD0, 0x7D, 0x07, 0xD0, 0x7D, 0x07, 0xD0, 0x7D, 0x07, 0xD0, 0x7D, 0x07, 0xD0,
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,147 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
|
||||
#define DAC_INTERNAL
|
||||
#include "_dac_internal.h"
|
||||
|
||||
/** Allocate data structure and set defaults */
|
||||
error_t UDAC_preInit(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data = calloc_ck(1, sizeof(struct priv));
|
||||
if (priv == NULL) return E_OUT_OF_MEM;
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
priv->cfg.ch[i].buffered = true;
|
||||
priv->cfg.ch[i].enable = true;
|
||||
priv->cfg.ch[i].noise_level = 2;
|
||||
priv->cfg.ch[i].noise_type = NOISE_NONE;
|
||||
|
||||
priv->ch[i].waveform = UDAC_WAVE_DC;
|
||||
priv->ch[i].dc_level = 2047;
|
||||
|
||||
priv->ch[i].rectangle_ontime = 4096; // half
|
||||
priv->ch[i].rectangle_high = 4095;
|
||||
priv->ch[i].rectangle_low = 0;
|
||||
|
||||
priv->ch[i].counter = 0;
|
||||
priv->ch[i].increment = 0; // stopped
|
||||
priv->ch[i].phase = 0;
|
||||
}
|
||||
|
||||
UDAC_SetFreq(unit, 0, 1000);
|
||||
UDAC_SetFreq(unit, 1, 1000);
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/** Finalize unit set-up */
|
||||
error_t UDAC_init(Unit *unit)
|
||||
{
|
||||
bool suc = true;
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
// copy noise config
|
||||
priv->ch[0].noise_type = priv->cfg.ch[0].noise_type;
|
||||
priv->ch[0].noise_level = priv->cfg.ch[0].noise_level;
|
||||
|
||||
priv->ch[1].noise_type = priv->cfg.ch[1].noise_type;
|
||||
priv->ch[1].noise_level = priv->cfg.ch[1].noise_level;
|
||||
|
||||
// this may change for different devices
|
||||
const Resource r_ch1 = R_PA4;
|
||||
const Resource r_ch2 = R_PA5;
|
||||
|
||||
TRY(rsc_claim(unit, R_TIM6));
|
||||
priv->TIMx = TIM6;
|
||||
|
||||
const bool e1 = priv->cfg.ch[0].enable;
|
||||
const bool e2 = priv->cfg.ch[1].enable;
|
||||
|
||||
if (e1) {
|
||||
TRY(rsc_claim(unit, r_ch1));
|
||||
}
|
||||
|
||||
if (e2) {
|
||||
TRY(rsc_claim(unit, r_ch2));
|
||||
}
|
||||
|
||||
TRY(rsc_claim(unit, R_DAC1));
|
||||
|
||||
// ensure the peripheral is clean (this may be redundant)
|
||||
__HAL_RCC_DAC1_FORCE_RESET();
|
||||
__HAL_RCC_DAC1_RELEASE_RESET();
|
||||
|
||||
hw_periph_clock_enable(DAC1);
|
||||
hw_periph_clock_enable(priv->TIMx);
|
||||
|
||||
GPIO_TypeDef *port;
|
||||
uint32_t ll;
|
||||
if (e1) {
|
||||
assert_param(hw_pinrsc2ll(r_ch1, &port, &ll));
|
||||
LL_GPIO_SetPinMode(port, ll, LL_GPIO_MODE_ANALOG);
|
||||
}
|
||||
if (e2) {
|
||||
assert_param(hw_pinrsc2ll(r_ch1, &port, &ll));
|
||||
LL_GPIO_SetPinMode(port, ll, LL_GPIO_MODE_ANALOG);
|
||||
}
|
||||
|
||||
uint16_t presc = 1;
|
||||
|
||||
// presets... TODO pick the highest useable one (or find a new one)
|
||||
#if UDAC_TIM_FREQ_DIVIDER == 1
|
||||
uint32_t count = PLAT_AHB_MHZ;
|
||||
#elif UDAC_TIM_FREQ_DIVIDER == 2
|
||||
uint32_t count = PLAT_AHB_MHZ * 2;
|
||||
#elif UDAC_TIM_FREQ_DIVIDER == 4
|
||||
uint32_t count = PLAT_AHB_MHZ * 4;
|
||||
#elif UDAC_TIM_FREQ_DIVIDER == 8
|
||||
uint32_t count = PLAT_AHB_MHZ * 8;
|
||||
#else
|
||||
#error "bad freq"
|
||||
#endif
|
||||
|
||||
// dbg("Presc %d, count %d", (int)presc, (int)count);
|
||||
|
||||
LL_TIM_SetPrescaler(priv->TIMx, (uint32_t) (presc - 1));
|
||||
LL_TIM_SetAutoReload(priv->TIMx, count - 1);
|
||||
LL_TIM_EnableARRPreload(priv->TIMx);
|
||||
LL_TIM_GenerateEvent_UPDATE(priv->TIMx);
|
||||
|
||||
irqd_attach(priv->TIMx, UDAC_HandleIT, unit);
|
||||
LL_TIM_EnableIT_UPDATE(priv->TIMx);
|
||||
|
||||
UDAC_Reconfigure(unit); // works with the timer - it should be inited already
|
||||
|
||||
// do not enbale counter initially - no need
|
||||
// LL_TIM_EnableCounter(priv->TIMx);
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/** Tear down the unit */
|
||||
void UDAC_deInit(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
// de-init peripherals
|
||||
if (unit->status == E_SUCCESS ) {
|
||||
LL_DAC_DeInit(DAC);
|
||||
LL_TIM_DisableCounter(priv->TIMx);
|
||||
|
||||
hw_periph_clock_disable(DAC1);
|
||||
hw_periph_clock_disable(priv->TIMx);
|
||||
|
||||
irqd_detach(priv->TIMx, UDAC_HandleIT);
|
||||
}
|
||||
|
||||
// Release all resources, deinit pins
|
||||
rsc_teardown(unit);
|
||||
|
||||
// Free memory
|
||||
free_ck(unit->data);
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_DAC_INTERNAL_H
|
||||
#define GEX_F072_DAC_INTERNAL_H
|
||||
|
||||
#ifndef DAC_INTERNAL
|
||||
#error bad include!
|
||||
#endif
|
||||
|
||||
#include "unit_base.h"
|
||||
|
||||
enum UDAC_Noise {
|
||||
NOISE_NONE = 0b00, // 0
|
||||
NOISE_WHITE = 0b01, // 1
|
||||
NOISE_TRIANGLE = 0b10, // 2
|
||||
};
|
||||
|
||||
enum UDAC_Waveform {
|
||||
UDAC_WAVE_DC,
|
||||
UDAC_WAVE_SINE,
|
||||
UDAC_WAVE_TRIANGLE,
|
||||
UDAC_WAVE_SAWTOOTH_UP,
|
||||
UDAC_WAVE_SAWTOOTH_DOWN,
|
||||
UDAC_WAVE_RECTANGLE,
|
||||
};
|
||||
|
||||
struct udac_channel_cfg {
|
||||
bool enable;
|
||||
bool buffered;
|
||||
enum UDAC_Noise noise_type;
|
||||
uint8_t noise_level; // 0-11
|
||||
};
|
||||
|
||||
// 0 - 1 MHz, 2-500k, 4-250k, 8-125k
|
||||
#define UDAC_TIM_FREQ_DIVIDER 8
|
||||
|
||||
#define UDAC_INDEX_WIDTH 13 // corresponds to 8192 places
|
||||
#define UDAC_INDEX_SHIFT (32 - UDAC_INDEX_WIDTH)
|
||||
#define UDAC_MAX_INDEX ((1 << UDAC_INDEX_WIDTH) - 1)
|
||||
#define UDAC_VALUE_COUNT (1 << UDAC_INDEX_WIDTH)
|
||||
|
||||
extern const uint8_t LUT_sine_8192_quad_packed[];
|
||||
|
||||
struct udac_channel_live {
|
||||
enum UDAC_Noise noise_type;
|
||||
uint8_t noise_level; // 0-11
|
||||
enum UDAC_Waveform waveform;
|
||||
|
||||
uint16_t rectangle_ontime; // for rectangle wave, 0-8191
|
||||
uint16_t rectangle_high;
|
||||
uint16_t rectangle_low;
|
||||
uint16_t dc_level; // for DC wave
|
||||
|
||||
uint32_t counter;
|
||||
uint32_t increment;
|
||||
|
||||
// last set phase if the frequencies are the same
|
||||
// - can be used for live frequency changes without reset (meaningful only with matching increment values)
|
||||
uint16_t phase;
|
||||
uint16_t last_index;
|
||||
uint16_t last_value;
|
||||
};
|
||||
|
||||
/** Private data structure */
|
||||
struct priv {
|
||||
// settings
|
||||
struct {
|
||||
struct udac_channel_cfg ch[2];
|
||||
} cfg;
|
||||
|
||||
// internal state
|
||||
struct udac_channel_live ch[2];
|
||||
TIM_TypeDef *TIMx; // timer used for the DDS function
|
||||
};
|
||||
|
||||
/** Allocate data structure and set defaults */
|
||||
error_t UDAC_preInit(Unit *unit);
|
||||
|
||||
/** Load from a binary buffer stored in Flash */
|
||||
void UDAC_loadBinary(Unit *unit, PayloadParser *pp);
|
||||
|
||||
/** Write to a binary buffer for storing in Flash */
|
||||
void UDAC_writeBinary(Unit *unit, PayloadBuilder *pb);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Parse a key-value pair from the INI file */
|
||||
error_t UDAC_loadIni(Unit *unit, const char *key, const char *value);
|
||||
|
||||
/** Generate INI file section for the unit */
|
||||
void UDAC_writeIni(Unit *unit, IniWriter *iw);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Finalize unit set-up */
|
||||
error_t UDAC_init(Unit *unit);
|
||||
|
||||
/** Tear down the unit */
|
||||
void UDAC_deInit(Unit *unit);
|
||||
|
||||
void UDAC_Reconfigure(Unit *unit);
|
||||
|
||||
void UDAC_HandleIT(void *arg);
|
||||
|
||||
error_t UDAC_SetFreq(Unit *unit, int channel, float freq);
|
||||
|
||||
void UDAC_ToggleTimerIfNeeded(Unit *unit);
|
||||
|
||||
#endif //GEX_F072_DAC_INTERNAL_H
|
||||
@@ -1,131 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
|
||||
#define DAC_INTERNAL
|
||||
#include "_dac_internal.h"
|
||||
|
||||
/** Load from a binary buffer stored in Flash */
|
||||
void UDAC_loadBinary(Unit *unit, PayloadParser *pp)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
uint8_t version = pp_u8(pp);
|
||||
(void)version;
|
||||
|
||||
priv->cfg.ch[0].enable = pp_bool(pp);
|
||||
priv->cfg.ch[0].buffered = pp_bool(pp);
|
||||
priv->cfg.ch[0].noise_type = (enum UDAC_Noise) pp_u8(pp);
|
||||
priv->cfg.ch[0].noise_level = pp_u8(pp);
|
||||
|
||||
priv->cfg.ch[1].enable = pp_bool(pp);
|
||||
priv->cfg.ch[1].buffered = pp_bool(pp);
|
||||
priv->cfg.ch[1].noise_type = (enum UDAC_Noise) pp_u8(pp);
|
||||
priv->cfg.ch[1].noise_level = pp_u8(pp);
|
||||
}
|
||||
|
||||
/** Write to a binary buffer for storing in Flash */
|
||||
void UDAC_writeBinary(Unit *unit, PayloadBuilder *pb)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
pb_u8(pb, 0); // version
|
||||
|
||||
pb_bool(pb, priv->cfg.ch[0].enable);
|
||||
pb_bool(pb, priv->cfg.ch[0].buffered);
|
||||
pb_u8(pb, priv->cfg.ch[0].noise_type);
|
||||
pb_u8(pb, priv->cfg.ch[0].noise_level);
|
||||
|
||||
pb_bool(pb, priv->cfg.ch[1].enable);
|
||||
pb_bool(pb, priv->cfg.ch[1].buffered);
|
||||
pb_u8(pb, priv->cfg.ch[1].noise_type);
|
||||
pb_u8(pb, priv->cfg.ch[1].noise_level);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Parse a key-value pair from the INI file */
|
||||
error_t UDAC_loadIni(Unit *unit, const char *key, const char *value)
|
||||
{
|
||||
bool suc = true;
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
// Ch1
|
||||
if (streq(key, "ch1_enable")) {
|
||||
priv->cfg.ch[0].enable = cfg_bool_parse(value, &suc);
|
||||
}
|
||||
else if (streq(key, "ch1_buff")) {
|
||||
priv->cfg.ch[0].buffered = cfg_bool_parse(value, &suc);
|
||||
}
|
||||
else if (streq(key, "ch1_noise")) {
|
||||
priv->cfg.ch[0].noise_type =
|
||||
(enum UDAC_Noise) cfg_enum3_parse(value,
|
||||
"NONE", NOISE_NONE,
|
||||
"WHITE", NOISE_WHITE,
|
||||
"TRIANGLE", NOISE_TRIANGLE, &suc);
|
||||
}
|
||||
else if (streq(key, "ch1_noise-level")) {
|
||||
uint8_t x = cfg_u8_parse(value, &suc);
|
||||
if (x == 0) x = 1;
|
||||
if (x > 12) x = 12;
|
||||
priv->cfg.ch[0].noise_level = (uint8_t) (x - 1);
|
||||
}
|
||||
// Ch2
|
||||
else if (streq(key, "ch2_enable")) {
|
||||
priv->cfg.ch[1].enable = cfg_bool_parse(value, &suc);
|
||||
}
|
||||
else if (streq(key, "ch2_buff")) {
|
||||
priv->cfg.ch[1].buffered = cfg_bool_parse(value, &suc);
|
||||
}
|
||||
else if (streq(key, "ch2_noise")) {
|
||||
priv->cfg.ch[1].noise_type =
|
||||
(enum UDAC_Noise) cfg_enum3_parse(value,
|
||||
"NONE", NOISE_NONE,
|
||||
"WHITE", NOISE_WHITE,
|
||||
"TRIANGLE", NOISE_TRIANGLE, &suc);
|
||||
}
|
||||
else if (streq(key, "ch2_noise-level")) {
|
||||
uint8_t x = cfg_u8_parse(value, &suc);
|
||||
if (x == 0) x = 1;
|
||||
if (x > 12) x = 12;
|
||||
priv->cfg.ch[1].noise_level = (uint8_t) (x - 1);
|
||||
}
|
||||
// end
|
||||
else {
|
||||
return E_BAD_KEY;
|
||||
}
|
||||
|
||||
if (!suc) return E_BAD_VALUE;
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/** Generate INI file section for the unit */
|
||||
void UDAC_writeIni(Unit *unit, IniWriter *iw)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
iw_comment(iw, "Enabled channels (1:A4, 2:A5)");
|
||||
iw_entry_s(iw, "ch1_enable", str_yn(priv->cfg.ch[0].enable));
|
||||
iw_entry_s(iw, "ch2_enable", str_yn(priv->cfg.ch[1].enable));
|
||||
|
||||
iw_comment(iw, "Enable output buffer");
|
||||
iw_entry_s(iw, "ch1_buff", str_yn(priv->cfg.ch[0].buffered));
|
||||
iw_entry_s(iw, "ch2_buff", str_yn(priv->cfg.ch[1].buffered));
|
||||
|
||||
iw_comment(iw, "Superimposed noise type (NONE,WHITE,TRIANGLE) and nbr. of bits (1-12)");
|
||||
|
||||
iw_entry_s(iw, "ch1_noise", cfg_enum3_encode(priv->cfg.ch[0].noise_type,
|
||||
NOISE_NONE, "NONE",
|
||||
NOISE_WHITE, "WHITE",
|
||||
NOISE_TRIANGLE, "TRIANGLE"));
|
||||
iw_entry_d(iw, "ch1_noise-level", priv->cfg.ch[0].noise_level + 1);
|
||||
|
||||
iw_entry_s(iw, "ch2_noise", cfg_enum3_encode(priv->cfg.ch[1].noise_type,
|
||||
NOISE_NONE, "NONE",
|
||||
NOISE_WHITE, "WHITE",
|
||||
NOISE_TRIANGLE, "TRIANGLE"));
|
||||
iw_entry_d(iw, "ch2_noise-level", priv->cfg.ch[1].noise_level + 1);
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2017/11/25.
|
||||
//
|
||||
|
||||
#include "unit_base.h"
|
||||
#include "unit_dac.h"
|
||||
|
||||
#define DAC_INTERNAL
|
||||
#include "_dac_internal.h"
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// Works OK up to about 20 kHz, could work faster with a faster interrupt
|
||||
// (may be possible with some optimizations / adjusting priorities...)
|
||||
|
||||
enum DacCmd_ {
|
||||
CMD_WAVE_DC = 0,
|
||||
CMD_WAVE_SINE = 1,
|
||||
CMD_WAVE_TRIANGLE = 2,
|
||||
CMD_WAVE_SAWTOOTH_UP = 3,
|
||||
CMD_WAVE_SAWTOOTH_DOWN = 4,
|
||||
CMD_WAVE_RECTANGLE = 5,
|
||||
|
||||
CMD_SYNC = 10,
|
||||
|
||||
CMD_SET_FREQUENCY = 20,
|
||||
CMD_SET_PHASE = 21,
|
||||
CMD_SET_DITHER = 22,
|
||||
};
|
||||
|
||||
/** Handle a request message */
|
||||
static error_t UDAC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
// Exceptions that aren't per-channel
|
||||
switch (command) {
|
||||
case CMD_SYNC:
|
||||
dbg("Sync");
|
||||
priv->ch[0].counter = priv->ch[0].phase << UDAC_INDEX_SHIFT;
|
||||
priv->ch[1].counter = priv->ch[1].phase << UDAC_INDEX_SHIFT;
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t channels = pp_u8(pp);
|
||||
|
||||
bool want_reinit = false;
|
||||
bool want_tog_timer = false;
|
||||
|
||||
// TODO move this stuff to the api file
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (channels & (1<<i)) {
|
||||
switch (command) {
|
||||
case CMD_SET_FREQUENCY:;
|
||||
float freq = pp_float(pp);
|
||||
TRY(UDAC_SetFreq(unit, i, freq));
|
||||
break;
|
||||
|
||||
case CMD_WAVE_DC:
|
||||
priv->ch[i].dc_level = pp_u16(pp);
|
||||
priv->ch[i].waveform = UDAC_WAVE_DC;
|
||||
want_tog_timer = true;
|
||||
break;
|
||||
|
||||
case CMD_WAVE_SINE:
|
||||
priv->ch[i].waveform = UDAC_WAVE_SINE;
|
||||
want_tog_timer = true;
|
||||
break;
|
||||
|
||||
case CMD_WAVE_TRIANGLE:
|
||||
priv->ch[i].waveform = UDAC_WAVE_TRIANGLE;
|
||||
want_tog_timer = true;
|
||||
break;
|
||||
|
||||
case CMD_WAVE_SAWTOOTH_UP:
|
||||
priv->ch[i].waveform = UDAC_WAVE_SAWTOOTH_UP;
|
||||
want_tog_timer = true;
|
||||
break;
|
||||
|
||||
case CMD_WAVE_SAWTOOTH_DOWN:
|
||||
priv->ch[i].waveform = UDAC_WAVE_SAWTOOTH_DOWN;
|
||||
want_tog_timer = true;
|
||||
break;
|
||||
|
||||
case CMD_WAVE_RECTANGLE:;
|
||||
uint16_t ontime = pp_u16(pp);
|
||||
uint16_t high = pp_u16(pp);
|
||||
uint16_t low = pp_u16(pp);
|
||||
|
||||
// use 0xFFFF to skip setting the value
|
||||
if (high < 4096) priv->ch[i].rectangle_high = high;
|
||||
if (low < 4096) priv->ch[i].rectangle_low = low;
|
||||
if (ontime <= UDAC_VALUE_COUNT) priv->ch[i].rectangle_ontime = ontime;
|
||||
|
||||
// dbg("Set rect hi %d, low %d", (int)priv->ch[i].rectangle_high, (int)priv->ch[i].rectangle_low);
|
||||
|
||||
priv->ch[i].waveform = UDAC_WAVE_RECTANGLE;
|
||||
want_tog_timer = true;
|
||||
break;
|
||||
|
||||
case CMD_SET_PHASE:;
|
||||
uint16_t ph = pp_u16(pp);
|
||||
uint32_t newphase = ph << UDAC_INDEX_SHIFT;
|
||||
uint32_t oldphase = priv->ch[i].phase << UDAC_INDEX_SHIFT;
|
||||
int32_t diff = newphase - oldphase;
|
||||
priv->ch[i].counter += diff;
|
||||
priv->ch[i].phase = ph;
|
||||
break;
|
||||
|
||||
case CMD_SET_DITHER:;
|
||||
uint8_t noisetype = pp_u8(pp); // 0-none, 1-random, 2-triangle
|
||||
uint8_t noisebits = pp_u8(pp);
|
||||
|
||||
// type 0xFF = not set
|
||||
if (noisetype <= 2) {
|
||||
priv->ch[i].noise_type = (enum UDAC_Noise) noisetype;
|
||||
}
|
||||
|
||||
// bits 0xFF = not set
|
||||
if (noisebits >= 1 && noisebits <= 12) {
|
||||
priv->ch[i].noise_level = (uint8_t) (noisebits - 1);
|
||||
}
|
||||
|
||||
// dbg("Ch %d: Dither type %d, level %d", i,
|
||||
// (int)priv->ch[i].noise_type,
|
||||
// (int)priv->ch[i].noise_level);
|
||||
|
||||
want_reinit = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
return E_UNKNOWN_COMMAND;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (want_reinit) {
|
||||
UDAC_Reconfigure(unit);
|
||||
}
|
||||
|
||||
if (want_tog_timer) {
|
||||
UDAC_ToggleTimerIfNeeded(unit);
|
||||
}
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Unit template */
|
||||
const UnitDriver UNIT_DAC = {
|
||||
.name = "DAC",
|
||||
.description = "Two-channel analog output with waveforms",
|
||||
// Settings
|
||||
.preInit = UDAC_preInit,
|
||||
.cfgLoadBinary = UDAC_loadBinary,
|
||||
.cfgWriteBinary = UDAC_writeBinary,
|
||||
.cfgLoadIni = UDAC_loadIni,
|
||||
.cfgWriteIni = UDAC_writeIni,
|
||||
// Init
|
||||
.init = UDAC_init,
|
||||
.deInit = UDAC_deInit,
|
||||
// Function
|
||||
.handleRequest = UDAC_handleRequest,
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2017/11/25.
|
||||
//
|
||||
// Digital input unit; single or multiple pin read access on one port (A-F)
|
||||
//
|
||||
|
||||
#ifndef U_DAC_H
|
||||
#define U_DAC_H
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
extern const UnitDriver UNIT_DAC;
|
||||
|
||||
// UU_ prototypes
|
||||
|
||||
#endif //U_DAC_H
|
||||
@@ -1,71 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
#include "unit_din.h"
|
||||
|
||||
#define DIN_INTERNAL
|
||||
|
||||
#include "_din_internal.h"
|
||||
|
||||
/** Read request */
|
||||
error_t UU_DIn_Read(Unit *unit, uint16_t *packed)
|
||||
{
|
||||
CHECK_TYPE(unit, &UNIT_DIN);
|
||||
struct priv *priv = unit->data;
|
||||
*packed = pinmask_pack((uint16_t) priv->port->IDR, priv->pins);
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/** Arm pins */
|
||||
error_t UU_DIn_Arm(Unit *unit, uint16_t arm_single_packed, uint16_t arm_auto_packed)
|
||||
{
|
||||
CHECK_TYPE(unit, &UNIT_DIN);
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
uint16_t arm_single = pinmask_spread(arm_single_packed, priv->pins);
|
||||
uint16_t arm_auto = pinmask_spread(arm_auto_packed, priv->pins);
|
||||
|
||||
// abort if user tries to arm pin that doesn't have a trigger configured
|
||||
if (0 != ((arm_single | arm_auto) & ~(priv->trig_fall | priv->trig_rise))) {
|
||||
return E_BAD_VALUE;
|
||||
}
|
||||
|
||||
// arm and reset hold-offs
|
||||
// we use critical section to avoid irq between the two steps
|
||||
vPortEnterCritical();
|
||||
{
|
||||
priv->arm_auto |= arm_single;
|
||||
priv->arm_single |= arm_auto;
|
||||
const uint16_t combined = arm_single | arm_auto;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (combined & (1 << i)) {
|
||||
priv->holdoff_countdowns[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
vPortExitCritical();
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/** DisArm pins */
|
||||
error_t UU_DIn_DisArm(Unit *unit, uint16_t disarm_packed)
|
||||
{
|
||||
CHECK_TYPE(unit, &UNIT_DIN);
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
uint16_t disarm = pinmask_spread(disarm_packed, priv->pins);
|
||||
|
||||
// abort if user tries to disarm pin that doesn't have a trigger configured
|
||||
if (0 != ((disarm) & ~(priv->trig_fall | priv->trig_rise))) {
|
||||
return E_BAD_VALUE;
|
||||
}
|
||||
|
||||
priv->arm_auto &= ~disarm;
|
||||
priv->arm_single &= ~disarm;
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
|
||||
#define DIN_INTERNAL
|
||||
|
||||
#include "_din_internal.h"
|
||||
|
||||
/**
|
||||
* Send a trigger event to master (called on the message queue thread).
|
||||
*
|
||||
* unit - unit
|
||||
* timestamp - timestamp
|
||||
* data1 - packed, triggering pin
|
||||
* data2 - snapshot
|
||||
*/
|
||||
static void DIn_SendTriggerReportToMaster(Job *job)
|
||||
{
|
||||
PayloadBuilder pb = pb_start(unit_tmp512, UNIT_TMP_LEN, NULL);
|
||||
pb_u16(&pb, (uint16_t) job->data1); // packed, 1 on the triggering pin
|
||||
pb_u16(&pb, (uint16_t) job->data2); // packed, snapshot
|
||||
assert_param(pb.ok);
|
||||
|
||||
EventReport event = {
|
||||
.unit = job->unit,
|
||||
.timestamp = job->timestamp,
|
||||
.data = pb.start,
|
||||
.length = (uint16_t) pb_length(&pb),
|
||||
};
|
||||
|
||||
EventReport_Send(&event);
|
||||
}
|
||||
|
||||
/**
|
||||
* EXTI callback for pin change interrupts
|
||||
*
|
||||
* @param arg - the unit is passed here
|
||||
*/
|
||||
void DIn_handleExti(void *arg)
|
||||
{
|
||||
const uint64_t ts = PTIM_GetMicrotime();
|
||||
|
||||
Unit *unit = arg;
|
||||
struct priv *priv = unit->data;
|
||||
const uint16_t snapshot = (uint16_t) priv->port->IDR;
|
||||
|
||||
uint16_t trigger_map = 0;
|
||||
|
||||
uint16_t mask = 1;
|
||||
const uint16_t armed_pins = priv->arm_single | priv->arm_auto;
|
||||
for (int i = 0; i < 16; i++, mask <<= 1) {
|
||||
if (!LL_EXTI_ReadFlag_0_31(LL_EXTI_LINES[i])) continue;
|
||||
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINES[i]);
|
||||
|
||||
// Armed and ready
|
||||
if ((armed_pins & mask) && (priv->holdoff_countdowns[i] == 0)) {
|
||||
// Mark as captured
|
||||
trigger_map |= (1 << i);
|
||||
// Start hold-off (no-op if zero hold-off)
|
||||
priv->holdoff_countdowns[i] = priv->trig_holdoff;
|
||||
}
|
||||
}
|
||||
|
||||
// Disarm all possibly used single triggers
|
||||
priv->arm_single &= ~trigger_map;
|
||||
|
||||
if (trigger_map != 0) {
|
||||
Job j = {
|
||||
.unit = unit,
|
||||
.timestamp = ts,
|
||||
.data1 = pinmask_pack(trigger_map, priv->pins),
|
||||
.data2 = pinmask_pack(snapshot, priv->pins),
|
||||
.cb = DIn_SendTriggerReportToMaster
|
||||
};
|
||||
scheduleJob(&j);
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
|
||||
#define DIN_INTERNAL
|
||||
#include "_din_internal.h"
|
||||
|
||||
/** Allocate data structure and set defaults */
|
||||
error_t DIn_preInit(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data = calloc_ck(1, sizeof(struct priv));
|
||||
if (priv == NULL) return E_OUT_OF_MEM;
|
||||
|
||||
// some defaults
|
||||
priv->port_name = 'A';
|
||||
priv->pins = 0x0001;
|
||||
priv->pulldown = 0x0000;
|
||||
priv->pullup = 0x0000;
|
||||
|
||||
priv->trig_rise = 0x0000;
|
||||
priv->trig_fall = 0x0000;
|
||||
priv->trig_holdoff = 100;
|
||||
priv->def_auto = 0x0000;
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/** Finalize unit set-up */
|
||||
error_t DIn_init(Unit *unit)
|
||||
{
|
||||
bool suc = true;
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
priv->pulldown &= priv->pins;
|
||||
priv->pullup &= priv->pins;
|
||||
priv->trig_rise &= priv->pins;
|
||||
priv->trig_fall &= priv->pins;
|
||||
priv->def_auto &= (priv->trig_rise|priv->trig_fall);
|
||||
|
||||
// copy auto-arm defaults to the auto-arm register (the register may be manipulated by commands)
|
||||
priv->arm_auto = priv->def_auto;
|
||||
priv->arm_single = 0;
|
||||
|
||||
// clear countdowns
|
||||
memset(priv->holdoff_countdowns, 0, sizeof(priv->holdoff_countdowns));
|
||||
|
||||
// --- Parse config ---
|
||||
priv->port = hw_port2periph(priv->port_name, &suc);
|
||||
if (!suc) return E_BAD_CONFIG;
|
||||
|
||||
// Claim all needed pins
|
||||
TRY(rsc_claim_gpios(unit, priv->port_name, priv->pins));
|
||||
|
||||
uint16_t mask = 1;
|
||||
for (int i = 0; i < 16; i++, mask <<= 1) {
|
||||
if (priv->pins & mask) {
|
||||
uint32_t ll_pin = hw_pin2ll((uint8_t) i, &suc);
|
||||
|
||||
// --- Init hardware ---
|
||||
LL_GPIO_SetPinMode(priv->port, ll_pin, LL_GPIO_MODE_INPUT);
|
||||
|
||||
uint32_t pull = 0;
|
||||
|
||||
#if PLAT_NO_FLOATING_INPUTS
|
||||
pull = LL_GPIO_PULL_UP;
|
||||
#else
|
||||
pull = LL_GPIO_PULL_NO;
|
||||
#endif
|
||||
|
||||
if (priv->pulldown & mask) pull = LL_GPIO_PULL_DOWN;
|
||||
if (priv->pullup & mask) pull = LL_GPIO_PULL_UP;
|
||||
LL_GPIO_SetPinPull(priv->port, ll_pin, pull);
|
||||
|
||||
if ((priv->trig_rise|priv->trig_fall) & mask) {
|
||||
LL_EXTI_EnableIT_0_31(LL_EXTI_LINES[i]);
|
||||
|
||||
if (priv->trig_rise & mask) {
|
||||
LL_EXTI_EnableRisingTrig_0_31(LL_EXTI_LINES[i]);
|
||||
}
|
||||
if (priv->trig_fall & mask) {
|
||||
LL_EXTI_EnableFallingTrig_0_31(LL_EXTI_LINES[i]);
|
||||
}
|
||||
|
||||
LL_SYSCFG_SetEXTISource(LL_SYSCFG_EXTI_PORTS[priv->port_name-'A'], LL_SYSCFG_EXTI_LINES[i]);
|
||||
|
||||
irqd_attach(EXTIS[i], DIn_handleExti, unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// request ticks if we have triggers and any hold-offs configured
|
||||
if ((priv->trig_rise|priv->trig_fall) && priv->trig_holdoff > 0) {
|
||||
unit->tick_interval = 1;
|
||||
}
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/** Tear down the unit */
|
||||
void DIn_deInit(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
// pins are de-inited during teardown
|
||||
|
||||
// Detach EXTI handlers and disable interrupts
|
||||
const uint16_t triggs = priv->trig_rise | priv->trig_fall;
|
||||
if (unit->status == E_SUCCESS && triggs) {
|
||||
uint16_t mask = 1;
|
||||
for (int i = 0; i < 16; i++, mask <<= 1) {
|
||||
if (triggs & mask) {
|
||||
LL_EXTI_DisableIT_0_31(LL_EXTI_LINES[i]);
|
||||
irqd_detach(EXTIS[i], DIn_handleExti);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Release all resources
|
||||
rsc_teardown(unit);
|
||||
|
||||
// Free memory
|
||||
free_ck(unit->data);
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_DIN_INTERNAL_H
|
||||
#define GEX_F072_DIN_INTERNAL_H
|
||||
|
||||
#ifndef DIN_INTERNAL
|
||||
#error bad include!
|
||||
#endif
|
||||
|
||||
#include "unit_base.h"
|
||||
|
||||
/** Private data structure */
|
||||
struct priv {
|
||||
char port_name;
|
||||
uint16_t pins; // pin mask
|
||||
uint16_t pulldown; // pull-downs (default is pull-up)
|
||||
uint16_t pullup; // pull-ups
|
||||
uint16_t trig_rise; // pins generating events on rising edge
|
||||
uint16_t trig_fall; // pins generating events on falling edge
|
||||
uint16_t trig_holdoff; // ms
|
||||
uint16_t def_auto; // initial auto triggers
|
||||
|
||||
uint16_t arm_auto; // pins armed for auto reporting
|
||||
uint16_t arm_single; // pins armed for single event
|
||||
uint16_t holdoff_countdowns[16]; // countdowns to arm for each pin in the bit map
|
||||
GPIO_TypeDef *port;
|
||||
};
|
||||
|
||||
/** Allocate data structure and set defaults */
|
||||
error_t DIn_preInit(Unit *unit);
|
||||
|
||||
/** Load from a binary buffer stored in Flash */
|
||||
void DIn_loadBinary(Unit *unit, PayloadParser *pp);
|
||||
|
||||
/** Write to a binary buffer for storing in Flash */
|
||||
void DIn_writeBinary(Unit *unit, PayloadBuilder *pb);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Parse a key-value pair from the INI file */
|
||||
error_t DIn_loadIni(Unit *unit, const char *key, const char *value);
|
||||
|
||||
/** Generate INI file section for the unit */
|
||||
void DIn_writeIni(Unit *unit, IniWriter *iw);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Finalize unit set-up */
|
||||
error_t DIn_init(Unit *unit);
|
||||
|
||||
/** Tear down the unit */
|
||||
void DIn_deInit(Unit *unit);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* EXTI callback for pin change interrupts
|
||||
*
|
||||
* @param arg - the unit is passed here
|
||||
*/
|
||||
void DIn_handleExti(void *arg);
|
||||
|
||||
#endif //GEX_F072_DIN_INTERNAL_H
|
||||
@@ -1,118 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
|
||||
#define DIN_INTERNAL
|
||||
#include "_din_internal.h"
|
||||
|
||||
/** Load from a binary buffer stored in Flash */
|
||||
void DIn_loadBinary(Unit *unit, PayloadParser *pp)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
uint8_t version = pp_u8(pp);
|
||||
(void)version;
|
||||
|
||||
priv->port_name = pp_char(pp);
|
||||
priv->pins = pp_u16(pp);
|
||||
priv->pulldown = pp_u16(pp);
|
||||
priv->pullup = pp_u16(pp);
|
||||
priv->trig_rise = pp_u16(pp);
|
||||
priv->trig_fall = pp_u16(pp);
|
||||
priv->trig_holdoff = pp_u16(pp);
|
||||
priv->def_auto = pp_u16(pp);
|
||||
}
|
||||
|
||||
/** Write to a binary buffer for storing in Flash */
|
||||
void DIn_writeBinary(Unit *unit, PayloadBuilder *pb)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
pb_u8(pb, 0); // version
|
||||
|
||||
pb_char(pb, priv->port_name);
|
||||
pb_u16(pb, priv->pins);
|
||||
pb_u16(pb, priv->pulldown);
|
||||
pb_u16(pb, priv->pullup);
|
||||
pb_u16(pb, priv->trig_rise);
|
||||
pb_u16(pb, priv->trig_fall);
|
||||
pb_u16(pb, priv->trig_holdoff);
|
||||
pb_u16(pb, priv->def_auto);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Parse a key-value pair from the INI file */
|
||||
error_t DIn_loadIni(Unit *unit, const char *key, const char *value)
|
||||
{
|
||||
bool suc = true;
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
if (streq(key, "port")) {
|
||||
suc = cfg_port_parse(value, &priv->port_name);
|
||||
}
|
||||
else if (streq(key, "pins")) {
|
||||
priv->pins = cfg_pinmask_parse(value, &suc);
|
||||
}
|
||||
else if (streq(key, "pull-up")) {
|
||||
priv->pullup = cfg_pinmask_parse(value, &suc);
|
||||
}
|
||||
else if (streq(key, "pull-down")) {
|
||||
priv->pulldown = cfg_pinmask_parse(value, &suc);
|
||||
}
|
||||
else if (streq(key, "trig-rise")) {
|
||||
priv->trig_rise = cfg_pinmask_parse(value, &suc);
|
||||
}
|
||||
else if (streq(key, "trig-fall")) {
|
||||
priv->trig_fall = cfg_pinmask_parse(value, &suc);
|
||||
}
|
||||
else if (streq(key, "auto-trigger")) {
|
||||
priv->def_auto = cfg_pinmask_parse(value, &suc);
|
||||
}
|
||||
else if (streq(key, "hold-off")) {
|
||||
priv->trig_holdoff = cfg_u16_parse(value, &suc);
|
||||
}
|
||||
else {
|
||||
return E_BAD_KEY;
|
||||
}
|
||||
|
||||
if (!suc) return E_BAD_VALUE;
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/** Generate INI file section for the unit */
|
||||
void DIn_writeIni(Unit *unit, IniWriter *iw)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
iw_comment(iw, "Port name");
|
||||
iw_entry(iw, "port", "%c", priv->port_name);
|
||||
|
||||
iw_comment(iw, "Pins (comma separated, supports ranges)");
|
||||
iw_entry_s(iw, "pins", cfg_pinmask_encode(priv->pins, unit_tmp512, 0));
|
||||
|
||||
iw_comment(iw, "Pins with pull-up");
|
||||
iw_entry_s(iw, "pull-up", cfg_pinmask_encode(priv->pullup, unit_tmp512, 0));
|
||||
|
||||
iw_comment(iw, "Pins with pull-down");
|
||||
iw_entry_s(iw, "pull-down", cfg_pinmask_encode(priv->pulldown, unit_tmp512, 0));
|
||||
|
||||
iw_cmt_newline(iw);
|
||||
iw_comment(iw, "Trigger pins activated by rising/falling edge");
|
||||
iw_entry_s(iw, "trig-rise", cfg_pinmask_encode(priv->trig_rise, unit_tmp512, 0));
|
||||
iw_entry_s(iw, "trig-fall", cfg_pinmask_encode(priv->trig_fall, unit_tmp512, 0));
|
||||
|
||||
iw_comment(iw, "Trigger pins auto-armed by default");
|
||||
iw_entry_s(iw, "auto-trigger", cfg_pinmask_encode(priv->def_auto, unit_tmp512, 0));
|
||||
|
||||
iw_comment(iw, "Triggers hold-off time (ms)");
|
||||
iw_entry_d(iw, "hold-off", priv->trig_holdoff);
|
||||
|
||||
#if PLAT_NO_FLOATING_INPUTS
|
||||
iw_comment(iw, "NOTE: Pins use pull-up by default.\r\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2017/11/25.
|
||||
//
|
||||
|
||||
#include "unit_base.h"
|
||||
#include "unit_din.h"
|
||||
|
||||
#define DIN_INTERNAL
|
||||
#include "_din_internal.h"
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
enum PinCmd_ {
|
||||
CMD_READ = 0,
|
||||
CMD_ARM_SINGLE = 1,
|
||||
CMD_ARM_AUTO = 2,
|
||||
CMD_DISARM = 3,
|
||||
};
|
||||
|
||||
/** Handle a request message */
|
||||
static error_t DIn_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp)
|
||||
{
|
||||
uint16_t pins = 0;
|
||||
|
||||
switch (command) {
|
||||
case CMD_READ:;
|
||||
TRY(UU_DIn_Read(unit, &pins));
|
||||
|
||||
PayloadBuilder pb = pb_start((uint8_t*)unit_tmp512, UNIT_TMP_LEN, NULL);
|
||||
pb_u16(&pb, pins); // packed input pins
|
||||
com_respond_buf(frame_id, MSG_SUCCESS, (uint8_t *) unit_tmp512, pb_length(&pb));
|
||||
return E_SUCCESS;
|
||||
|
||||
case CMD_ARM_SINGLE:;
|
||||
pins = pp_u16(pp);
|
||||
if (!pp->ok) return E_MALFORMED_COMMAND;
|
||||
|
||||
TRY(UU_DIn_Arm(unit, pins, 0));
|
||||
return E_SUCCESS;
|
||||
|
||||
case CMD_ARM_AUTO:;
|
||||
pins = pp_u16(pp);
|
||||
if (!pp->ok) return E_MALFORMED_COMMAND;
|
||||
|
||||
TRY(UU_DIn_Arm(unit, 0, pins));
|
||||
return E_SUCCESS;
|
||||
|
||||
case CMD_DISARM:;
|
||||
pins = pp_u16(pp);
|
||||
if (!pp->ok) return E_MALFORMED_COMMAND;
|
||||
|
||||
TRY(UU_DIn_DisArm(unit, pins));
|
||||
return E_SUCCESS;
|
||||
|
||||
default:
|
||||
return E_UNKNOWN_COMMAND;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement all the hold-off timers on tick
|
||||
*
|
||||
* @param unit
|
||||
*/
|
||||
static void DIn_updateTick(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (priv->holdoff_countdowns[i] > 0) {
|
||||
priv->holdoff_countdowns[i]--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Unit template */
|
||||
const UnitDriver UNIT_DIN = {
|
||||
.name = "DI",
|
||||
.description = "Digital input with triggers",
|
||||
// Settings
|
||||
.preInit = DIn_preInit,
|
||||
.cfgLoadBinary = DIn_loadBinary,
|
||||
.cfgWriteBinary = DIn_writeBinary,
|
||||
.cfgLoadIni = DIn_loadIni,
|
||||
.cfgWriteIni = DIn_writeIni,
|
||||
// Init
|
||||
.init = DIn_init,
|
||||
.deInit = DIn_deInit,
|
||||
// Function
|
||||
.handleRequest = DIn_handleRequest,
|
||||
.updateTick = DIn_updateTick,
|
||||
};
|
||||
@@ -1,42 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2017/11/25.
|
||||
//
|
||||
// Digital input unit; single or multiple pin read access on one port (A-F)
|
||||
//
|
||||
|
||||
#ifndef U_DIN_H
|
||||
#define U_DIN_H
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
extern const UnitDriver UNIT_DIN;
|
||||
|
||||
/**
|
||||
* Read pins
|
||||
*
|
||||
* @param unit - unit instance
|
||||
* @param packed - output; the packed (right aligned) bits representing the pins, highest to lowest, are written here.
|
||||
* @return success
|
||||
*/
|
||||
error_t UU_DIn_Read(Unit *unit, uint16_t *packed);
|
||||
|
||||
/**
|
||||
* Arm pins for trigger generation
|
||||
*
|
||||
* @param unit - unit instance
|
||||
* @param arm_single_packed - packed bit map of pins to arm for single trigger
|
||||
* @param arm_auto_packed - packed bit map of pins to arm for auto trigger (repeated)
|
||||
* @return success
|
||||
*/
|
||||
error_t UU_DIn_Arm(Unit *unit, uint16_t arm_single_packed, uint16_t arm_auto_packed);
|
||||
|
||||
/**
|
||||
* Dis-arm pins to not generate events
|
||||
*
|
||||
* @param unit - unit instance
|
||||
* @param disarm_packed - packed bit map of pins to dis-arm
|
||||
* @return success
|
||||
*/
|
||||
error_t UU_DIn_DisArm(Unit *unit, uint16_t disarm_packed);
|
||||
|
||||
#endif //U_DIN_H
|
||||
@@ -1,173 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
#include "unit_dout.h"
|
||||
|
||||
#define DOUT_INTERNAL
|
||||
|
||||
#include "_dout_internal.h"
|
||||
|
||||
static void clear_pulse_by_mask(struct priv *priv, uint16_t spread)
|
||||
{
|
||||
assert_param(priv);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (spread & (1 << i)) {
|
||||
priv->msec_pulse_cnt[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
error_t UU_DOut_Write(Unit *unit, uint16_t packed)
|
||||
{
|
||||
CHECK_TYPE(unit, &UNIT_DOUT);
|
||||
|
||||
struct priv *priv = unit->data;
|
||||
uint16_t mask = priv->pins;
|
||||
uint16_t spread = pinmask_spread(packed, mask);
|
||||
clear_pulse_by_mask(priv, spread);
|
||||
|
||||
uint16_t set = spread;
|
||||
uint16_t reset = ((~spread) & mask);
|
||||
priv->port->BSRR = set | (reset << 16);
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
error_t UU_DOut_Set(Unit *unit, uint16_t packed)
|
||||
{
|
||||
CHECK_TYPE(unit, &UNIT_DOUT);
|
||||
|
||||
struct priv *priv = unit->data;
|
||||
uint16_t mask = priv->pins;
|
||||
uint16_t spread = pinmask_spread(packed, mask);
|
||||
clear_pulse_by_mask(priv, spread);
|
||||
|
||||
priv->port->BSRR = spread;
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
error_t UU_DOut_Clear(Unit *unit, uint16_t packed)
|
||||
{
|
||||
CHECK_TYPE(unit, &UNIT_DOUT);
|
||||
|
||||
struct priv *priv = unit->data;
|
||||
uint16_t mask = priv->pins;
|
||||
uint16_t spread = pinmask_spread(packed, mask);
|
||||
clear_pulse_by_mask(priv, spread);
|
||||
|
||||
priv->port->BSRR = (spread << 16);
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
error_t UU_DOut_Toggle(Unit *unit, uint16_t packed)
|
||||
{
|
||||
CHECK_TYPE(unit, &UNIT_DOUT);
|
||||
|
||||
struct priv *priv = unit->data;
|
||||
uint16_t mask = priv->pins;
|
||||
uint16_t spread = pinmask_spread(packed, mask);
|
||||
clear_pulse_by_mask(priv, spread);
|
||||
|
||||
uint16_t flipped = (uint16_t) (~priv->port->ODR) & mask;
|
||||
uint16_t set = flipped & spread;
|
||||
uint16_t reset = ((~flipped) & mask) & spread;
|
||||
priv->port->BSRR = set | (reset << 16);
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
error_t UU_DOut_GetPinCount(Unit *unit, uint8_t *count)
|
||||
{
|
||||
CHECK_TYPE(unit, &UNIT_DOUT);
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
uint32_t packed = pinmask_pack(0xFFFF, priv->pins);
|
||||
*count = (uint8_t) (32 - __CLZ(packed));
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
error_t UU_DOut_Pulse(Unit *unit, uint16_t packed, bool polarity, bool is_usec, uint16_t count)
|
||||
{
|
||||
CHECK_TYPE(unit, &UNIT_DOUT);
|
||||
struct priv *priv = unit->data;
|
||||
assert_param(priv);
|
||||
|
||||
uint16_t mask = priv->pins;
|
||||
uint16_t spread = pinmask_spread(packed, mask);
|
||||
clear_pulse_by_mask(priv, spread);
|
||||
|
||||
vPortEnterCritical();
|
||||
|
||||
if (is_usec) {
|
||||
// we're gonna do this right here as a delay loop.
|
||||
if (count >= 1000) {
|
||||
// too long, fall back to msec
|
||||
count /= 1000;
|
||||
is_usec = false;
|
||||
}
|
||||
else {
|
||||
const uint32_t bsrr1 = spread << (polarity ? 0 : 16);
|
||||
const uint32_t bsrr0 = spread << (polarity ? 16 : 0);
|
||||
|
||||
const uint32_t start = PTIM_MicroDelayAlign();
|
||||
priv->port->BSRR = bsrr1;
|
||||
PTIM_MicroDelayAligned(count, start);
|
||||
priv->port->BSRR = bsrr0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_usec) {
|
||||
// Load the counters
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (spread & (1 << i)) {
|
||||
priv->msec_pulse_cnt[i] = (uint16_t) (count + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (polarity) {
|
||||
priv->msec_pulse_scheduled_1 |= spread;
|
||||
} else {
|
||||
priv->msec_pulse_scheduled_0 |= spread;
|
||||
}
|
||||
|
||||
unit->_tick_cnt = 0;
|
||||
unit->tick_interval = 1;
|
||||
}
|
||||
|
||||
vPortExitCritical();
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
void DOut_Tick(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
uint16_t odr = (uint16_t) priv->port->ODR;
|
||||
int live_cnt = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (priv->msec_pulse_scheduled_1 & (1<<i)) {
|
||||
odr |= (1<<i);
|
||||
} else if (priv->msec_pulse_scheduled_0 & (1<<i)) {
|
||||
odr &= ~(1<<i);
|
||||
}
|
||||
|
||||
if (priv->msec_pulse_cnt[i] > 0) {
|
||||
live_cnt++;
|
||||
priv->msec_pulse_cnt[i]--;
|
||||
if (priv->msec_pulse_cnt[i] == 0) {
|
||||
odr ^= 1 << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
priv->port->ODR = odr;
|
||||
priv->msec_pulse_scheduled_1 = 0;
|
||||
priv->msec_pulse_scheduled_0 = 0;
|
||||
|
||||
if (live_cnt == 0) {
|
||||
unit->_tick_cnt = 0;
|
||||
unit->tick_interval = 0;
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
|
||||
#define DOUT_INTERNAL
|
||||
#include "_dout_internal.h"
|
||||
|
||||
/** Allocate data structure and set defaults */
|
||||
error_t DOut_preInit(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data = calloc_ck(1, sizeof(struct priv));
|
||||
if (priv == NULL) return E_OUT_OF_MEM;
|
||||
|
||||
// some defaults
|
||||
priv->port_name = 'A';
|
||||
priv->pins = 0x0001;
|
||||
priv->open_drain = 0x0000;
|
||||
priv->initial = 0x0000;
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/** Finalize unit set-up */
|
||||
error_t DOut_init(Unit *unit)
|
||||
{
|
||||
bool suc = true;
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
priv->initial &= priv->pins;
|
||||
priv->open_drain &= priv->pins;
|
||||
|
||||
// --- Parse config ---
|
||||
priv->port = hw_port2periph(priv->port_name, &suc);
|
||||
if (!suc) return E_BAD_CONFIG;
|
||||
|
||||
// Claim all needed pins
|
||||
TRY(rsc_claim_gpios(unit, priv->port_name, priv->pins));
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (priv->pins & (1 << i)) {
|
||||
uint32_t ll_pin = hw_pin2ll((uint8_t) i, &suc);
|
||||
|
||||
// --- Init hardware ---
|
||||
LL_GPIO_SetPinMode(priv->port, ll_pin, LL_GPIO_MODE_OUTPUT);
|
||||
LL_GPIO_SetPinOutputType(priv->port, ll_pin,
|
||||
(priv->open_drain & (1 << i)) ? LL_GPIO_OUTPUT_OPENDRAIN : LL_GPIO_OUTPUT_PUSHPULL);
|
||||
LL_GPIO_SetPinSpeed(priv->port, ll_pin, LL_GPIO_SPEED_FREQ_HIGH);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the initial state
|
||||
priv->port->ODR &= ~priv->pins;
|
||||
priv->port->ODR |= priv->initial;
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/** Tear down the unit */
|
||||
void DOut_deInit(Unit *unit)
|
||||
{
|
||||
// pins are de-inited during teardown
|
||||
|
||||
// Release all resources
|
||||
rsc_teardown(unit);
|
||||
|
||||
// Free memory
|
||||
free_ck(unit->data);
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_DOUT_INTERNAL_H
|
||||
#define GEX_F072_DOUT_INTERNAL_H
|
||||
|
||||
#ifndef DOUT_INTERNAL
|
||||
#error bad include!
|
||||
#endif
|
||||
|
||||
#include "unit_base.h"
|
||||
|
||||
/** Private data structure */
|
||||
struct priv {
|
||||
char port_name;
|
||||
uint16_t pins; // pin mask
|
||||
uint16_t initial; // initial pin states
|
||||
uint16_t open_drain; // open drain pins
|
||||
|
||||
GPIO_TypeDef *port;
|
||||
uint16_t msec_pulse_cnt[16];
|
||||
uint16_t msec_pulse_scheduled_1;
|
||||
uint16_t msec_pulse_scheduled_0;
|
||||
};
|
||||
|
||||
/** Allocate data structure and set defaults */
|
||||
error_t DOut_preInit(Unit *unit);
|
||||
|
||||
/** Load from a binary buffer stored in Flash */
|
||||
void DOut_loadBinary(Unit *unit, PayloadParser *pp);
|
||||
|
||||
/** Write to a binary buffer for storing in Flash */
|
||||
void DOut_writeBinary(Unit *unit, PayloadBuilder *pb);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Parse a key-value pair from the INI file */
|
||||
error_t DOut_loadIni(Unit *unit, const char *key, const char *value);
|
||||
|
||||
/** Generate INI file section for the unit */
|
||||
void DOut_writeIni(Unit *unit, IniWriter *iw);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Finalize unit set-up */
|
||||
error_t DOut_init(Unit *unit);
|
||||
|
||||
/** Tear down the unit */
|
||||
void DOut_deInit(Unit *unit);
|
||||
|
||||
void DOut_Tick(Unit *unit);
|
||||
|
||||
#endif //GEX_F072_DOUT_INTERNAL_H
|
||||
@@ -1,82 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
|
||||
#define DOUT_INTERNAL
|
||||
#include "_dout_internal.h"
|
||||
|
||||
/** Load from a binary buffer stored in Flash */
|
||||
void DOut_loadBinary(Unit *unit, PayloadParser *pp)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
uint8_t version = pp_u8(pp);
|
||||
(void)version;
|
||||
|
||||
priv->port_name = pp_char(pp);
|
||||
priv->pins = pp_u16(pp);
|
||||
priv->initial = pp_u16(pp);
|
||||
priv->open_drain = pp_u16(pp);
|
||||
}
|
||||
|
||||
/** Write to a binary buffer for storing in Flash */
|
||||
void DOut_writeBinary(Unit *unit, PayloadBuilder *pb)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
pb_u8(pb, 0); // version
|
||||
|
||||
pb_char(pb, priv->port_name);
|
||||
pb_u16(pb, priv->pins);
|
||||
pb_u16(pb, priv->initial);
|
||||
pb_u16(pb, priv->open_drain);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Parse a key-value pair from the INI file */
|
||||
error_t DOut_loadIni(Unit *unit, const char *key, const char *value)
|
||||
{
|
||||
bool suc = true;
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
if (streq(key, "port")) {
|
||||
suc = cfg_port_parse(value, &priv->port_name);
|
||||
}
|
||||
else if (streq(key, "pins")) {
|
||||
priv->pins = cfg_pinmask_parse(value, &suc);
|
||||
}
|
||||
else if (streq(key, "initial")) {
|
||||
priv->initial = cfg_pinmask_parse(value, &suc);
|
||||
}
|
||||
else if (streq(key, "open-drain")) {
|
||||
priv->open_drain = cfg_pinmask_parse(value, &suc);
|
||||
}
|
||||
else {
|
||||
return E_BAD_KEY;
|
||||
}
|
||||
|
||||
if (!suc) return E_BAD_VALUE;
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/** Generate INI file section for the unit */
|
||||
void DOut_writeIni(Unit *unit, IniWriter *iw)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
iw_comment(iw, "Port name");
|
||||
iw_entry(iw, "port", "%c", priv->port_name);
|
||||
|
||||
iw_comment(iw, "Pins (comma separated, supports ranges)");
|
||||
iw_entry_s(iw, "pins", cfg_pinmask_encode(priv->pins, unit_tmp512, 0));
|
||||
|
||||
iw_comment(iw, "Initially high pins");
|
||||
iw_entry_s(iw, "initial", cfg_pinmask_encode(priv->initial, unit_tmp512, 0));
|
||||
|
||||
iw_comment(iw, "Open-drain pins");
|
||||
iw_entry_s(iw, "open-drain", cfg_pinmask_encode(priv->open_drain, unit_tmp512, 0));
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2017/11/25.
|
||||
//
|
||||
|
||||
#include "unit_base.h"
|
||||
#include "unit_dout.h"
|
||||
|
||||
#define DOUT_INTERNAL
|
||||
#include "_dout_internal.h"
|
||||
|
||||
enum PinCmd_ {
|
||||
CMD_QUERY = 0,
|
||||
CMD_SET = 1,
|
||||
CMD_CLEAR = 2,
|
||||
CMD_TOGGLE = 3,
|
||||
CMD_PULSE = 4,
|
||||
};
|
||||
|
||||
/** Handle a request message */
|
||||
static error_t DOut_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp)
|
||||
{
|
||||
uint16_t packed = pp_u16(pp);
|
||||
|
||||
switch (command) {
|
||||
case CMD_QUERY:
|
||||
return UU_DOut_Write(unit, packed);
|
||||
|
||||
case CMD_SET:
|
||||
return UU_DOut_Set(unit, packed);
|
||||
|
||||
case CMD_CLEAR:
|
||||
return UU_DOut_Clear(unit, packed);
|
||||
|
||||
case CMD_TOGGLE:
|
||||
return UU_DOut_Toggle(unit, packed);
|
||||
|
||||
case CMD_PULSE:;
|
||||
bool polarity = pp_bool(pp);
|
||||
bool is_usec = pp_bool(pp);
|
||||
uint16_t count = pp_u16(pp);
|
||||
return UU_DOut_Pulse(unit, packed, polarity, is_usec, count);
|
||||
|
||||
default:
|
||||
return E_UNKNOWN_COMMAND;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Unit template */
|
||||
const UnitDriver UNIT_DOUT = {
|
||||
.name = "DO",
|
||||
.description = "Digital output",
|
||||
// Settings
|
||||
.preInit = DOut_preInit,
|
||||
.cfgLoadBinary = DOut_loadBinary,
|
||||
.cfgWriteBinary = DOut_writeBinary,
|
||||
.cfgLoadIni = DOut_loadIni,
|
||||
.cfgWriteIni = DOut_writeIni,
|
||||
// Init
|
||||
.init = DOut_init,
|
||||
.deInit = DOut_deInit,
|
||||
// Function
|
||||
.handleRequest = DOut_handleRequest,
|
||||
.updateTick = DOut_Tick,
|
||||
};
|
||||
@@ -1,71 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2017/11/25.
|
||||
//
|
||||
// Digital output unit; single or multiple pin write access on one port (A-F)
|
||||
//
|
||||
|
||||
#ifndef U_DOUT_H
|
||||
#define U_DOUT_H
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
extern const UnitDriver UNIT_DOUT;
|
||||
|
||||
/**
|
||||
* Write pins (e.g. writing 0b10 if configured pins are PA5 and PA0 sets PA5=1,PA0=0)
|
||||
*
|
||||
* @param unit
|
||||
* @param packed - packed pin states (aligned to right)
|
||||
* @return success
|
||||
*/
|
||||
error_t UU_DOut_Write(Unit *unit, uint16_t packed);
|
||||
|
||||
/**
|
||||
* Set pins (clear none)
|
||||
*
|
||||
* @param unit
|
||||
* @param packed - packed pins, 1 if pin should be set
|
||||
* @return success
|
||||
*/
|
||||
error_t UU_DOut_Set(Unit *unit, uint16_t packed);
|
||||
|
||||
/**
|
||||
* Clear multiple pins
|
||||
*
|
||||
* @param unit
|
||||
* @param packed - packed pins, 1 if pin should be cleared
|
||||
* @return
|
||||
*/
|
||||
error_t UU_DOut_Clear(Unit *unit, uint16_t packed);
|
||||
|
||||
/**
|
||||
* Toggle pins
|
||||
*
|
||||
* @param unit
|
||||
* @param packed - packed pins, 1 if pin should be toggled
|
||||
* @return
|
||||
*/
|
||||
error_t UU_DOut_Toggle(Unit *unit, uint16_t packed);
|
||||
|
||||
/**
|
||||
* Get number of configured pins
|
||||
*
|
||||
* @param unit
|
||||
* @param count output, written with 0-16
|
||||
* @return success
|
||||
*/
|
||||
error_t UU_DOut_GetPinCount(Unit *unit, uint8_t *count);
|
||||
|
||||
/**
|
||||
* Send a pulse
|
||||
*
|
||||
* @param unit
|
||||
* @param packed - pins to pulse
|
||||
* @param polarity - pulse active level
|
||||
* @param is_usec - use usec precision (for < 1 ms)
|
||||
* @param count - number of units (msec or usec)
|
||||
* @return success
|
||||
*/
|
||||
error_t UU_DOut_Pulse(Unit *unit, uint16_t packed, bool polarity, bool is_usec, uint16_t count);
|
||||
|
||||
#endif //U_DOUT_H
|
||||
@@ -1,11 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
#include "unit_fcap.h"
|
||||
|
||||
#define FCAP_INTERNAL
|
||||
#include "_fcap_internal.h"
|
||||
|
||||
@@ -1,461 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/20.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#define FCAP_INTERNAL
|
||||
#include "_fcap_internal.h"
|
||||
|
||||
static void UFCAP_StopMeasurement(Unit *unit);
|
||||
static void UFCAP_ConfigureForIndirectCapture(Unit *unit);
|
||||
static void UFCAP_ConfigureForDirectCapture(Unit *unit, uint16_t msec);
|
||||
static void UFCAP_ConfigureForFreeCapture(Unit *unit);
|
||||
|
||||
uint32_t UFCAP_GetFreeCounterValue(Unit *unit)
|
||||
{
|
||||
struct priv * const priv = unit->data;
|
||||
TIM_TypeDef * const TIMx = priv->TIMx;
|
||||
return TIMx->CNT;
|
||||
}
|
||||
|
||||
uint32_t UFCAP_FreeCounterClear(Unit *unit)
|
||||
{
|
||||
struct priv * const priv = unit->data;
|
||||
TIM_TypeDef * const TIMx = priv->TIMx;
|
||||
|
||||
// this isn't perfect, we can miss one clock
|
||||
// but it's probably the best we can do here ...
|
||||
vPortEnterCritical();
|
||||
uint32_t val = TIMx->CNT;
|
||||
TIMx->CNT = 0;
|
||||
vPortExitCritical();
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void UFCAP_IndirectBurstReportJob(Job *job)
|
||||
{
|
||||
Unit *unit = job->unit;
|
||||
struct priv * const priv = unit->data;
|
||||
|
||||
uint8_t buf[20];
|
||||
PayloadBuilder pb = pb_start(buf, 20, NULL);
|
||||
|
||||
pb_u16(&pb, PLAT_AHB_MHZ);
|
||||
pb_u16(&pb, priv->ind_burst.n_count);
|
||||
pb_u64(&pb, priv->ind_burst.period_acu);
|
||||
pb_u64(&pb, priv->ind_burst.ontime_acu);
|
||||
|
||||
assert_param(pb.ok);
|
||||
|
||||
com_respond_pb(priv->request_id, MSG_SUCCESS, &pb);
|
||||
|
||||
// timer is already stopped, now in OPMODE_BUSY
|
||||
priv->opmode = OPMODE_IDLE;
|
||||
}
|
||||
|
||||
static void UFCAP_SinglePulseReportJob(Job *job)
|
||||
{
|
||||
Unit *unit = job->unit;
|
||||
struct priv * const priv = unit->data;
|
||||
|
||||
uint8_t buf[6];
|
||||
PayloadBuilder pb = pb_start(buf, 6, NULL);
|
||||
|
||||
pb_u16(&pb, PLAT_AHB_MHZ);
|
||||
pb_u32(&pb, job->data1);
|
||||
assert_param(pb.ok);
|
||||
|
||||
com_respond_pb(priv->request_id, MSG_SUCCESS, &pb);
|
||||
|
||||
// timer is already stopped, now in OPMODE_BUSY
|
||||
priv->opmode = OPMODE_IDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count is passed in data1
|
||||
* @param job
|
||||
*/
|
||||
static void UFCAP_DirectBurstReportJob(Job *job)
|
||||
{
|
||||
Unit *unit = job->unit;
|
||||
struct priv * const priv = unit->data;
|
||||
|
||||
uint8_t buf[8];
|
||||
PayloadBuilder pb = pb_start(buf, 8, NULL);
|
||||
|
||||
pb_u8(&pb, priv->direct_presc);
|
||||
pb_u16(&pb, priv->dir_burst.msec);
|
||||
pb_u32(&pb, job->data1);
|
||||
|
||||
assert_param(pb.ok);
|
||||
|
||||
com_respond_pb(priv->request_id, MSG_SUCCESS, &pb);
|
||||
|
||||
// timer is already stopped, now in OPMODE_BUSY
|
||||
priv->opmode = OPMODE_IDLE;
|
||||
}
|
||||
|
||||
void UFCAP_TIMxHandler(void *arg)
|
||||
{
|
||||
Unit *unit = arg;
|
||||
assert_param(unit);
|
||||
struct priv * const priv = unit->data;
|
||||
assert_param(priv);
|
||||
|
||||
TIM_TypeDef * const TIMx = priv->TIMx;
|
||||
|
||||
if (priv->opmode == OPMODE_INDIRECT_CONT) {
|
||||
if (LL_TIM_IsActiveFlag_CC1(TIMx)) {
|
||||
if (priv->n_skip > 0) {
|
||||
priv->n_skip--;
|
||||
} else {
|
||||
priv->ind_cont.last_period = LL_TIM_IC_GetCaptureCH1(TIMx);
|
||||
priv->ind_cont.last_ontime = priv->ind_cont.ontime;
|
||||
}
|
||||
LL_TIM_ClearFlag_CC1(TIMx);
|
||||
LL_TIM_ClearFlag_CC1OVR(TIMx);
|
||||
}
|
||||
|
||||
if (LL_TIM_IsActiveFlag_CC2(TIMx)) {
|
||||
priv->ind_cont.ontime = LL_TIM_IC_GetCaptureCH2(TIMx);
|
||||
LL_TIM_ClearFlag_CC2(TIMx);
|
||||
LL_TIM_ClearFlag_CC2OVR(TIMx);
|
||||
}
|
||||
}
|
||||
else if (priv->opmode == OPMODE_SINGLE_PULSE) {
|
||||
if (LL_TIM_IsActiveFlag_CC2(TIMx)) {
|
||||
// single pulse - does not wait for the second edge
|
||||
uint32_t len = LL_TIM_IC_GetCaptureCH2(TIMx);
|
||||
|
||||
priv->opmode = OPMODE_BUSY;
|
||||
UFCAP_StopMeasurement(unit);
|
||||
|
||||
Job j = {
|
||||
.cb = UFCAP_SinglePulseReportJob,
|
||||
.unit = unit,
|
||||
.data1 = len,
|
||||
};
|
||||
scheduleJob(&j);
|
||||
}
|
||||
}
|
||||
else if (priv->opmode == OPMODE_INDIRECT_BURST) {
|
||||
if (LL_TIM_IsActiveFlag_CC1(TIMx)) {
|
||||
const uint32_t period = LL_TIM_IC_GetCaptureCH1(TIMx);
|
||||
const uint32_t ontime = priv->ind_burst.ontime;
|
||||
|
||||
if (priv->n_skip > 0) {
|
||||
priv->n_skip--;
|
||||
} else {
|
||||
priv->ind_burst.ontime_acu += ontime;
|
||||
priv->ind_burst.period_acu += period;
|
||||
if (++priv->ind_burst.n_count == priv->ind_burst.n_target) {
|
||||
priv->opmode = OPMODE_BUSY;
|
||||
UFCAP_StopMeasurement(unit);
|
||||
|
||||
Job j = {
|
||||
.cb = UFCAP_IndirectBurstReportJob,
|
||||
.unit = unit,
|
||||
};
|
||||
scheduleJob(&j);
|
||||
}
|
||||
}
|
||||
|
||||
LL_TIM_ClearFlag_CC1(TIMx);
|
||||
LL_TIM_ClearFlag_CC1OVR(TIMx);
|
||||
}
|
||||
|
||||
if (LL_TIM_IsActiveFlag_CC2(TIMx)) {
|
||||
priv->ind_burst.ontime = LL_TIM_IC_GetCaptureCH2(TIMx);
|
||||
LL_TIM_ClearFlag_CC2(TIMx);
|
||||
LL_TIM_ClearFlag_CC2OVR(TIMx);
|
||||
}
|
||||
}
|
||||
else if (priv->opmode == OPMODE_IDLE) {
|
||||
// clear everything - in idle it would cycle in the handler forever
|
||||
TIMx->SR = 0;
|
||||
}
|
||||
else {
|
||||
trap("Unhandled fcap TIMx irq");
|
||||
}
|
||||
}
|
||||
|
||||
void UFCAP_TIMyHandler(void *arg)
|
||||
{
|
||||
Unit *unit = arg;
|
||||
assert_param(unit);
|
||||
struct priv *const priv = unit->data;
|
||||
assert_param(priv);
|
||||
|
||||
TIM_TypeDef * const TIMx = priv->TIMx;
|
||||
TIM_TypeDef * const TIMy = priv->TIMy;
|
||||
uint32_t cnt = TIMx->CNT; // TIMx should be stopped now
|
||||
|
||||
// dbg("> TIMy Handler, TIMx cntr is %d", cnt);
|
||||
priv->dir_cont.last_count = cnt;
|
||||
|
||||
if (priv->opmode == OPMODE_DIRECT_CONT) {
|
||||
LL_TIM_DisableCounter(TIMx);
|
||||
LL_TIM_DisableCounter(TIMy);
|
||||
LL_TIM_SetCounter(TIMx, 0);
|
||||
LL_TIM_SetCounter(TIMy, 0);
|
||||
LL_TIM_EnableCounter(TIMy); // next loop
|
||||
LL_TIM_EnableCounter(TIMx);
|
||||
}
|
||||
else if (priv->opmode == OPMODE_DIRECT_BURST) {
|
||||
priv->opmode = OPMODE_BUSY;
|
||||
UFCAP_StopMeasurement(unit);
|
||||
|
||||
Job j = {
|
||||
.cb = UFCAP_DirectBurstReportJob,
|
||||
.unit = unit,
|
||||
.data1 = cnt,
|
||||
};
|
||||
scheduleJob(&j);
|
||||
}
|
||||
else if (priv->opmode == OPMODE_IDLE) {
|
||||
// clear everything - in idle it would cycle in the handler forever
|
||||
TIMy->SR = 0;
|
||||
}
|
||||
else {
|
||||
trap("Unhandled fcap TIMy irq");
|
||||
}
|
||||
|
||||
LL_TIM_ClearFlag_UPDATE(TIMy);
|
||||
}
|
||||
|
||||
static void UFCAP_ClearTimerConfig(Unit *unit)
|
||||
{
|
||||
struct priv * const priv = unit->data;
|
||||
TIM_TypeDef * const TIMx = priv->TIMx;
|
||||
|
||||
// CLEAR CURRENT STATE, STOP
|
||||
UFCAP_StopMeasurement(unit);
|
||||
|
||||
// CONFIGURE TIMER BASIC PARAMS
|
||||
LL_TIM_SetPrescaler(TIMx, 0);
|
||||
LL_TIM_SetAutoReload(TIMx, 0xFFFFFFFF);
|
||||
LL_TIM_EnableARRPreload(TIMx);
|
||||
LL_TIM_GenerateEvent_UPDATE(TIMx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all timer registers
|
||||
*
|
||||
* @param unit
|
||||
*/
|
||||
static void UFCAP_StopMeasurement(Unit *unit)
|
||||
{
|
||||
struct priv * const priv = unit->data;
|
||||
|
||||
LL_TIM_DeInit(priv->TIMx); // clear all flags and settings
|
||||
LL_TIM_DeInit(priv->TIMy); // clear all flags and settings
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch the FCAP module opmode
|
||||
*
|
||||
* @param unit
|
||||
* @param opmode
|
||||
*/
|
||||
void UFCAP_SwitchMode(Unit *unit, enum fcap_opmode opmode)
|
||||
{
|
||||
struct priv * const priv = unit->data;
|
||||
|
||||
if (opmode == priv->opmode) return;
|
||||
|
||||
priv->opmode = opmode;
|
||||
|
||||
switch (opmode) {
|
||||
case OPMODE_IDLE:
|
||||
// XXX maybe we should report the abort to the PC-side listener
|
||||
UFCAP_StopMeasurement(unit);
|
||||
break;
|
||||
|
||||
case OPMODE_INDIRECT_CONT:
|
||||
priv->ind_cont.last_ontime = 0;
|
||||
priv->ind_cont.last_period = 0;
|
||||
priv->ind_cont.ontime = 0;
|
||||
priv->n_skip = 1; // discard the first cycle (will be incomplete)
|
||||
UFCAP_ConfigureForIndirectCapture(unit); // is also stopped and restarted
|
||||
break;
|
||||
|
||||
case OPMODE_INDIRECT_BURST:
|
||||
priv->ind_burst.ontime = 0;
|
||||
priv->ind_burst.n_count = 0;
|
||||
priv->ind_burst.period_acu = 0;
|
||||
priv->ind_burst.ontime_acu = 0;
|
||||
priv->n_skip = 1; // discard the first cycle (will be incomplete)
|
||||
UFCAP_ConfigureForIndirectCapture(unit); // is also stopped and restarted
|
||||
break;
|
||||
|
||||
case OPMODE_SINGLE_PULSE:
|
||||
priv->n_skip = 0;
|
||||
UFCAP_ConfigureForIndirectCapture(unit); // is also stopped and restarted
|
||||
break;
|
||||
|
||||
case OPMODE_DIRECT_CONT:
|
||||
// msec is set by caller
|
||||
priv->dir_cont.last_count = 0;
|
||||
priv->n_skip = 1; // discard the first cycle (will be incomplete)
|
||||
UFCAP_ConfigureForDirectCapture(unit, priv->direct_msec);
|
||||
break;
|
||||
|
||||
case OPMODE_DIRECT_BURST:
|
||||
// msec is set by caller
|
||||
priv->n_skip = 0; // no skip here (if there was any)
|
||||
UFCAP_ConfigureForDirectCapture(unit, (uint16_t) priv->dir_burst.msec);
|
||||
break;
|
||||
|
||||
case OPMODE_FREE_COUNTER:
|
||||
UFCAP_ConfigureForFreeCapture(unit);
|
||||
break;
|
||||
|
||||
default:
|
||||
trap("Unhandled opmode %d", (int)opmode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure peripherals for an indirect capture (PWM measurement) - continuous or burst
|
||||
* @param unit
|
||||
*/
|
||||
static void UFCAP_ConfigureForIndirectCapture(Unit *unit)
|
||||
{
|
||||
struct priv * const priv = unit->data;
|
||||
TIM_TypeDef * const TIMx = priv->TIMx;
|
||||
const uint32_t ll_ch_a = priv->ll_ch_a;
|
||||
const uint32_t ll_ch_b = priv->ll_ch_b;
|
||||
|
||||
UFCAP_ClearTimerConfig(unit);
|
||||
|
||||
// Enable channels and select mapping to TIx signals
|
||||
|
||||
// A - will be used to measure period
|
||||
// B - will be used to measure the duty cycle
|
||||
|
||||
// _________ ______
|
||||
// _______| |________________|
|
||||
// A B A
|
||||
// irq irq,cap irq
|
||||
// reset
|
||||
|
||||
// B irq may be used if we want to measure a pulse width
|
||||
|
||||
// Normally TI1 = CH1, TI2 = CH2.
|
||||
// It's possible to select the other channel, which we use to connect both TIx to the shame CHx.
|
||||
LL_TIM_IC_SetActiveInput(TIMx, ll_ch_a, priv->a_direct ? LL_TIM_ACTIVEINPUT_DIRECTTI : LL_TIM_ACTIVEINPUT_INDIRECTTI);
|
||||
LL_TIM_IC_SetActiveInput(TIMx, ll_ch_b, priv->a_direct ? LL_TIM_ACTIVEINPUT_INDIRECTTI : LL_TIM_ACTIVEINPUT_DIRECTTI);
|
||||
LL_TIM_IC_SetPolarity(TIMx, ll_ch_a, priv->active_level ? LL_TIM_IC_POLARITY_RISING : LL_TIM_IC_POLARITY_FALLING);
|
||||
LL_TIM_IC_SetPolarity(TIMx, ll_ch_b, priv->active_level ? LL_TIM_IC_POLARITY_FALLING : LL_TIM_IC_POLARITY_RISING);
|
||||
|
||||
if (priv->dfilter > 15) priv->dfilter = 15;
|
||||
uint32_t filter = LL_TIM_IC_FILTERS[priv->dfilter];
|
||||
LL_TIM_IC_SetFilter(TIMx, ll_ch_a, filter);
|
||||
LL_TIM_IC_SetFilter(TIMx, ll_ch_b, filter);
|
||||
|
||||
LL_TIM_CC_EnableChannel(TIMx, ll_ch_a | ll_ch_b);
|
||||
|
||||
LL_TIM_SetSlaveMode(TIMx, LL_TIM_SLAVEMODE_RESET);
|
||||
LL_TIM_SetTriggerInput(TIMx, LL_TIM_TS_TI1FP1); // Use Filtered Input 1 (TI1)
|
||||
|
||||
LL_TIM_EnableMasterSlaveMode(TIMx);
|
||||
|
||||
LL_TIM_ClearFlag_CC1(TIMx);
|
||||
LL_TIM_ClearFlag_CC1OVR(TIMx);
|
||||
LL_TIM_ClearFlag_CC2(TIMx);
|
||||
LL_TIM_ClearFlag_CC2OVR(TIMx);
|
||||
|
||||
LL_TIM_EnableIT_CC1(TIMx);
|
||||
LL_TIM_EnableIT_CC2(TIMx);
|
||||
LL_TIM_EnableCounter(TIMx);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configure peripherals for an indirect capture (PWM measurement) - continuous or burst
|
||||
* @param unit
|
||||
*/
|
||||
static void UFCAP_ConfigureForDirectCapture(Unit *unit, uint16_t msec)
|
||||
{
|
||||
struct priv * const priv = unit->data;
|
||||
|
||||
// dbg("Configuring Direct capture...");
|
||||
|
||||
UFCAP_ClearTimerConfig(unit);
|
||||
|
||||
{
|
||||
TIM_TypeDef *const TIMy = priv->TIMy;
|
||||
assert_param(PLAT_AHB_MHZ<=65);
|
||||
uint16_t presc = PLAT_AHB_MHZ*1000;
|
||||
uint32_t count = msec+1; // it's one tick longer because we generate OCREF on the exact msec count - it must be at least 1 tick long
|
||||
|
||||
LL_TIM_SetPrescaler(TIMy, (uint32_t) (presc - 1));
|
||||
LL_TIM_SetAutoReload(TIMy, count - 1);
|
||||
LL_TIM_EnableARRPreload(TIMy);
|
||||
LL_TIM_GenerateEvent_UPDATE(TIMy);
|
||||
LL_TIM_SetOnePulseMode(TIMy, LL_TIM_ONEPULSEMODE_SINGLE);
|
||||
LL_TIM_OC_EnableFast(TIMy, LL_TIM_CHANNEL_CH1);
|
||||
|
||||
// dbg("TIMy presc %d, count %d", (int) presc, (int) count);
|
||||
|
||||
LL_TIM_SetTriggerOutput(TIMy, LL_TIM_TRGO_OC1REF);
|
||||
LL_TIM_OC_SetMode(TIMy, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_PWM1); // 1 until CC, then 0
|
||||
LL_TIM_OC_SetCompareCH1(TIMy, count-1);
|
||||
LL_TIM_CC_EnableChannel(TIMy, LL_TIM_CHANNEL_CH1); // enable the output channel that produces a trigger
|
||||
|
||||
LL_TIM_ClearFlag_UPDATE(TIMy);
|
||||
LL_TIM_EnableIT_UPDATE(TIMy);
|
||||
}
|
||||
|
||||
{
|
||||
// TIMx - the slave
|
||||
TIM_TypeDef *const TIMx = priv->TIMx;
|
||||
LL_TIM_SetSlaveMode(TIMx, LL_TIM_SLAVEMODE_GATED);
|
||||
LL_TIM_SetTriggerInput(TIMx, LL_TIM_TS_ITR3); // ITR3 is TIM14 which we use as TIMy
|
||||
LL_TIM_EnableMasterSlaveMode(TIMx);
|
||||
|
||||
uint32_t presc = LL_TIM_ETR_PRESCALER_DIV1;
|
||||
switch (priv->direct_presc) {
|
||||
case 1: presc = LL_TIM_ETR_PRESCALER_DIV1; break;
|
||||
case 2: presc = LL_TIM_ETR_PRESCALER_DIV2; break;
|
||||
case 4: presc = LL_TIM_ETR_PRESCALER_DIV4; break;
|
||||
case 8: presc = LL_TIM_ETR_PRESCALER_DIV8; break;
|
||||
default:
|
||||
priv->direct_presc = 1; // will be sent with the response
|
||||
}
|
||||
|
||||
if (priv->dfilter > 15) priv->dfilter = 15;
|
||||
uint32_t filter = LL_TIM_ETR_FILTERS[priv->dfilter];
|
||||
|
||||
LL_TIM_ConfigETR(TIMx,
|
||||
priv->active_level ? LL_TIM_ETR_POLARITY_NONINVERTED : LL_TIM_ETR_POLARITY_INVERTED,
|
||||
presc,
|
||||
filter);
|
||||
|
||||
LL_TIM_EnableExternalClock(TIMx); // TODO must check and deny this mode if the pin is not on CH1 = external trigger input
|
||||
|
||||
LL_TIM_SetCounter(TIMx, 0);
|
||||
LL_TIM_EnableCounter(TIMx);
|
||||
}
|
||||
|
||||
LL_TIM_EnableCounter(priv->TIMy); // XXX this will start the first pulse (maybe)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Freerunning capture (counting pulses - geiger)
|
||||
* @param unit
|
||||
*/
|
||||
static void UFCAP_ConfigureForFreeCapture(Unit *unit)
|
||||
{
|
||||
struct priv * const priv = unit->data;
|
||||
|
||||
UFCAP_ClearTimerConfig(unit);
|
||||
|
||||
TIM_TypeDef *const TIMx = priv->TIMx;
|
||||
LL_TIM_EnableExternalClock(TIMx);
|
||||
LL_TIM_SetCounter(TIMx, 0);
|
||||
LL_TIM_EnableCounter(TIMx);
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
|
||||
#define FCAP_INTERNAL
|
||||
#include "_fcap_internal.h"
|
||||
|
||||
/** Allocate data structure and set defaults */
|
||||
error_t UFCAP_preInit(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data = calloc_ck(1, sizeof(struct priv));
|
||||
if (priv == NULL) return E_OUT_OF_MEM;
|
||||
|
||||
priv->conf.signal_pname = 'A';
|
||||
priv->conf.signal_pnum = 0;
|
||||
|
||||
priv->conf.active_level = 1;
|
||||
priv->conf.direct_presc = 1;
|
||||
priv->conf.dfilter = 0;
|
||||
priv->conf.direct_msec = 1000;
|
||||
priv->conf.startmode = OPMODE_IDLE;
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/** Finalize unit set-up */
|
||||
error_t UFCAP_init(Unit *unit)
|
||||
{
|
||||
bool suc = true;
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
// ---- Resolve what to configure ----
|
||||
|
||||
TIM_TypeDef * const TIMx = TIM2;
|
||||
Resource timRsc = R_TIM2;
|
||||
|
||||
TIM_TypeDef * const TIMy = TIM14;
|
||||
Resource tim2Rsc = R_TIM14;
|
||||
|
||||
uint32_t ll_ch_a = 0;
|
||||
uint32_t ll_ch_b = 0;
|
||||
|
||||
switch (priv->conf.signal_pname) {
|
||||
case 'A':
|
||||
switch (priv->conf.signal_pnum) {
|
||||
case 5:
|
||||
case 15:
|
||||
case 0: ll_ch_a = LL_TIM_CHANNEL_CH1; break;
|
||||
case 1: ll_ch_a = LL_TIM_CHANNEL_CH2; break;
|
||||
default:
|
||||
dbg("Bad signal pin!");
|
||||
return E_BAD_CONFIG;
|
||||
}
|
||||
break;
|
||||
case 'B':
|
||||
switch (priv->conf.signal_pnum) {
|
||||
case 3: ll_ch_a = LL_TIM_CHANNEL_CH2; break;
|
||||
default:
|
||||
dbg("Bad signal pin!");
|
||||
return E_BAD_CONFIG;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
dbg("Bad signal pin port!");
|
||||
return E_BAD_CONFIG;
|
||||
}
|
||||
const uint32_t ll_timpin_af = LL_GPIO_AF_2;
|
||||
|
||||
bool a_direct = true;
|
||||
switch (ll_ch_a) {
|
||||
case LL_TIM_CHANNEL_CH1:
|
||||
ll_ch_b = LL_TIM_CHANNEL_CH2;
|
||||
break;
|
||||
|
||||
case LL_TIM_CHANNEL_CH2:
|
||||
ll_ch_b = LL_TIM_CHANNEL_CH1;
|
||||
a_direct = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// ---- CLAIM ----
|
||||
|
||||
TRY(rsc_claim_pin(unit, priv->conf.signal_pname, priv->conf.signal_pnum));
|
||||
TRY(rsc_claim(unit, timRsc));
|
||||
TRY(rsc_claim(unit, tim2Rsc));
|
||||
|
||||
// ---- INIT ----
|
||||
assert_param(ll_ch_a != ll_ch_b);
|
||||
|
||||
priv->TIMx = TIMx;
|
||||
priv->TIMy = TIMy;
|
||||
priv->ll_ch_a = ll_ch_a;
|
||||
priv->ll_ch_b = ll_ch_b;
|
||||
priv->a_direct = a_direct;
|
||||
|
||||
// Load defaults
|
||||
priv->active_level = priv->conf.active_level;
|
||||
priv->direct_presc = priv->conf.direct_presc;
|
||||
priv->dfilter = priv->conf.dfilter;
|
||||
priv->direct_msec = priv->conf.direct_msec;
|
||||
priv->opmode = priv->conf.startmode;
|
||||
|
||||
TRY(hw_configure_gpio_af(priv->conf.signal_pname, priv->conf.signal_pnum, ll_timpin_af));
|
||||
|
||||
GPIO_TypeDef *gpio = hw_port2periph(priv->conf.signal_pname, &suc);
|
||||
uint32_t ll_pin = hw_pin2ll(priv->conf.signal_pnum, &suc);
|
||||
LL_GPIO_SetPinPull(gpio, ll_pin, LL_GPIO_PULL_DOWN); // XXX change to pull-up if the polarity is inverted
|
||||
|
||||
hw_periph_clock_enable(TIMx);
|
||||
hw_periph_clock_enable(TIMy);
|
||||
irqd_attach(TIMx, UFCAP_TIMxHandler, unit);
|
||||
irqd_attach(TIMy, UFCAP_TIMyHandler, unit);
|
||||
|
||||
UFCAP_SwitchMode(unit, priv->opmode); // switch to the default opmode
|
||||
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/** Tear down the unit */
|
||||
void UFCAP_deInit(Unit *unit)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
// de-init peripherals
|
||||
if (unit->status == E_SUCCESS ) {
|
||||
UFCAP_SwitchMode(unit, OPMODE_IDLE);
|
||||
|
||||
TIM_TypeDef *TIMx = priv->TIMx;
|
||||
TIM_TypeDef *TIMy = priv->TIMy;
|
||||
LL_TIM_DeInit(TIMx);
|
||||
LL_TIM_DeInit(TIMy);
|
||||
irqd_detach(TIMx, UFCAP_TIMxHandler);
|
||||
irqd_detach(TIMy, UFCAP_TIMyHandler);
|
||||
hw_periph_clock_disable(TIMx);
|
||||
hw_periph_clock_disable(TIMy);
|
||||
}
|
||||
|
||||
// Release all resources, deinit pins
|
||||
rsc_teardown(unit);
|
||||
|
||||
// Free memory
|
||||
free_ck(unit->data);
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_FCAP_INTERNAL_H
|
||||
#define GEX_F072_FCAP_INTERNAL_H
|
||||
|
||||
#ifndef FCAP_INTERNAL
|
||||
#error bad include!
|
||||
#endif
|
||||
|
||||
#include "unit_base.h"
|
||||
|
||||
enum fcap_opmode {
|
||||
OPMODE_IDLE = 0,
|
||||
OPMODE_BUSY = 1, // used after capture is done, before it's reported
|
||||
OPMODE_INDIRECT_CONT = 2,
|
||||
OPMODE_INDIRECT_BURST = 3, // averaging
|
||||
OPMODE_DIRECT_CONT = 4,
|
||||
OPMODE_DIRECT_BURST = 5,
|
||||
OPMODE_FREE_COUNTER = 6,
|
||||
OPMODE_SINGLE_PULSE = 7,
|
||||
};
|
||||
|
||||
/** Private data structure */
|
||||
struct priv {
|
||||
// settings
|
||||
struct {
|
||||
char signal_pname; // the input pin - one of TIM2 channels
|
||||
uint8_t signal_pnum;
|
||||
|
||||
bool active_level;
|
||||
uint8_t direct_presc;
|
||||
uint8_t dfilter;
|
||||
uint16_t direct_msec;
|
||||
|
||||
enum fcap_opmode startmode;
|
||||
} conf;
|
||||
|
||||
// internal state
|
||||
TIM_TypeDef *TIMx;
|
||||
TIM_TypeDef *TIMy; // used as a timebase source for TIMx in direct mode
|
||||
uint32_t ll_ch_b;
|
||||
uint32_t ll_ch_a;
|
||||
bool a_direct;
|
||||
|
||||
enum fcap_opmode opmode;
|
||||
|
||||
TF_ID request_id;
|
||||
uint8_t n_skip; //!< Periods to skip before starting the real capture
|
||||
|
||||
bool active_level; // in PWM mode, the first part that is measured. (if 1, HHHLLL, else LLLHHH). In direct mode, clock polarity
|
||||
uint8_t direct_presc;
|
||||
uint16_t direct_msec;
|
||||
uint8_t dfilter;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32_t ontime; // length of the captured positive pulse in the current interval
|
||||
uint32_t last_period; //!< length of the captured interval between two rising edges
|
||||
uint32_t last_ontime; //!< length of the last captured ontime
|
||||
} ind_cont;
|
||||
|
||||
struct {
|
||||
uint32_t ontime; // length of the captured positive pulse in the current interval
|
||||
uint64_t period_acu; //!< length of the captured interval between two rising edges, sum
|
||||
uint64_t ontime_acu; //!< length of the last captured ontime, sum
|
||||
uint16_t n_count; //!< Periods captured
|
||||
uint16_t n_target; //!< Periods captured - requested count
|
||||
} ind_burst;
|
||||
|
||||
struct {
|
||||
uint32_t last_count; //!< Pulse count in the last capture window
|
||||
} dir_cont;
|
||||
|
||||
struct {
|
||||
uint16_t msec; // capture window length (used in the report callback) - different from the cont time, which is a semi-persistent config
|
||||
} dir_burst;
|
||||
};
|
||||
};
|
||||
|
||||
/** Allocate data structure and set defaults */
|
||||
error_t UFCAP_preInit(Unit *unit);
|
||||
|
||||
/** Load from a binary buffer stored in Flash */
|
||||
void UFCAP_loadBinary(Unit *unit, PayloadParser *pp);
|
||||
|
||||
/** Write to a binary buffer for storing in Flash */
|
||||
void UFCAP_writeBinary(Unit *unit, PayloadBuilder *pb);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Parse a key-value pair from the INI file */
|
||||
error_t UFCAP_loadIni(Unit *unit, const char *key, const char *value);
|
||||
|
||||
/** Generate INI file section for the unit */
|
||||
void UFCAP_writeIni(Unit *unit, IniWriter *iw);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Finalize unit set-up */
|
||||
error_t UFCAP_init(Unit *unit);
|
||||
|
||||
/** Tear down the unit */
|
||||
void UFCAP_deInit(Unit *unit);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
void UFCAP_SwitchMode(Unit *unit, enum fcap_opmode opmode);
|
||||
|
||||
void UFCAP_TIMxHandler(void *arg);
|
||||
void UFCAP_TIMyHandler(void *arg);
|
||||
|
||||
uint32_t UFCAP_GetFreeCounterValue(Unit *unit);
|
||||
uint32_t UFCAP_FreeCounterClear(Unit *unit);
|
||||
|
||||
#endif //GEX_F072_FCAP_INTERNAL_H
|
||||
@@ -1,114 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/02/03.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "unit_base.h"
|
||||
|
||||
#define FCAP_INTERNAL
|
||||
#include "_fcap_internal.h"
|
||||
|
||||
/** Load from a binary buffer stored in Flash */
|
||||
void UFCAP_loadBinary(Unit *unit, PayloadParser *pp)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
uint8_t version = pp_u8(pp);
|
||||
(void)version;
|
||||
|
||||
priv->conf.signal_pname = pp_char(pp);
|
||||
priv->conf.signal_pnum = pp_u8(pp);
|
||||
priv->conf.active_level = pp_bool(pp);
|
||||
priv->conf.dfilter = pp_u8(pp);
|
||||
priv->conf.direct_presc = pp_u8(pp);
|
||||
priv->conf.direct_msec = pp_u16(pp);
|
||||
priv->conf.startmode = (enum fcap_opmode) pp_u8(pp);
|
||||
}
|
||||
|
||||
/** Write to a binary buffer for storing in Flash */
|
||||
void UFCAP_writeBinary(Unit *unit, PayloadBuilder *pb)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
pb_u8(pb, 0); // version
|
||||
|
||||
pb_char(pb, priv->conf.signal_pname);
|
||||
pb_u8(pb, priv->conf.signal_pnum);
|
||||
pb_bool(pb, priv->conf.active_level);
|
||||
pb_u8(pb, priv->conf.dfilter);
|
||||
pb_u8(pb, priv->conf.direct_presc);
|
||||
pb_u16(pb, priv->conf.direct_msec);
|
||||
pb_u8(pb, priv->conf.startmode);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Parse a key-value pair from the INI file */
|
||||
error_t UFCAP_loadIni(Unit *unit, const char *key, const char *value)
|
||||
{
|
||||
bool suc = true;
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
if (streq(key, "pin")) {
|
||||
suc = cfg_portpin_parse(value, &priv->conf.signal_pname, &priv->conf.signal_pnum);
|
||||
}
|
||||
else if (streq(key, "active-level")) {
|
||||
priv->conf.active_level = cfg_bool_parse(value, &suc);
|
||||
}
|
||||
else if (streq(key, "input-filter")) {
|
||||
priv->conf.dfilter = cfg_u8_parse(value, &suc);
|
||||
}
|
||||
else if (streq(key, "direct-presc")) {
|
||||
priv->conf.direct_presc = cfg_u8_parse(value, &suc);
|
||||
}
|
||||
else if (streq(key, "direct-time")) {
|
||||
priv->conf.direct_msec = cfg_u16_parse(value, &suc);
|
||||
}
|
||||
else if (streq(key, "initial-mode")) {
|
||||
priv->conf.startmode = (enum fcap_opmode) cfg_enum4_parse(value,
|
||||
"N", OPMODE_IDLE,
|
||||
"I", OPMODE_INDIRECT_CONT,
|
||||
"D", OPMODE_DIRECT_CONT,
|
||||
"F", OPMODE_FREE_COUNTER,
|
||||
&suc);
|
||||
}
|
||||
else{
|
||||
return E_BAD_KEY;
|
||||
}
|
||||
|
||||
if (!suc) return E_BAD_VALUE;
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
/** Generate INI file section for the unit */
|
||||
void UFCAP_writeIni(Unit *unit, IniWriter *iw)
|
||||
{
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
iw_comment(iw, "Signal input pin - one of:");
|
||||
iw_comment(iw, " Full support: A0, A5, A15");
|
||||
iw_comment(iw, " Indirect only: A1, B3");
|
||||
iw_entry(iw, "pin", "%c%d", priv->conf.signal_pname, priv->conf.signal_pnum);
|
||||
iw_cmt_newline(iw);
|
||||
|
||||
iw_comment(iw, "Active level or edge (0-low,falling; 1-high,rising)");
|
||||
iw_entry_d(iw, "active-level", priv->conf.active_level);
|
||||
|
||||
iw_comment(iw, "Input filtering (0-15)");
|
||||
iw_entry_d(iw, "input-filter", priv->conf.dfilter);
|
||||
|
||||
iw_comment(iw, "Pulse counter pre-divider (1,2,4,8)");
|
||||
iw_entry_d(iw, "direct-presc", priv->conf.direct_presc);
|
||||
|
||||
iw_comment(iw, "Pulse counting interval (ms)");
|
||||
iw_entry_d(iw, "direct-time", priv->conf.direct_msec);
|
||||
iw_cmt_newline(iw);
|
||||
|
||||
iw_comment(iw, "Mode on startup: N-none, I-indirect, D-direct, F-free count");
|
||||
iw_entry_s(iw, "initial-mode", cfg_enum4_encode(priv->conf.startmode,
|
||||
OPMODE_IDLE, "N",
|
||||
OPMODE_INDIRECT_CONT, "I",
|
||||
OPMODE_DIRECT_CONT, "D",
|
||||
OPMODE_FREE_COUNTER, "F"));
|
||||
}
|
||||
|
||||
@@ -1,322 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2017/11/25.
|
||||
//
|
||||
|
||||
#include "unit_base.h"
|
||||
#include "unit_fcap.h"
|
||||
|
||||
#define FCAP_INTERNAL
|
||||
#include "_fcap_internal.h"
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
enum FcapCmd_ {
|
||||
CMD_STOP = 0,
|
||||
|
||||
// Measuring a waveform
|
||||
CMD_INDIRECT_CONT_START = 1, // keep measuring, read on demand
|
||||
CMD_INDIRECT_BURST_START = 2, // wait and reply
|
||||
|
||||
// Counting pulses
|
||||
CMD_DIRECT_CONT_START = 3, // keep measuring, read on demand
|
||||
CMD_DIRECT_BURST_START = 4, // wait and reply
|
||||
CMD_FREECOUNT_START = 5, // keep counting pulses until stopped, read on reply
|
||||
|
||||
CMD_MEASURE_SINGLE_PULSE = 6, // measure the first incoming pulse of the right polarity. NOTE: can glitch if the signal starts in the active level
|
||||
CMD_FREECOUNT_CLEAR = 7, // clear the free counter, return last value
|
||||
|
||||
// Results readout for continuous modes
|
||||
CMD_INDIRECT_CONT_READ = 10,
|
||||
CMD_DIRECT_CONT_READ = 11,
|
||||
CMD_FREECOUNT_READ = 12,
|
||||
|
||||
// configs
|
||||
CMD_SET_POLARITY = 20,
|
||||
CMD_SET_DIR_PRESC = 21,
|
||||
CMD_SET_INPUT_FILTER = 22,
|
||||
CMD_SET_DIR_MSEC = 23,
|
||||
|
||||
// go back to the configured settings
|
||||
CMD_RESTORE_DEFAULTS = 30,
|
||||
};
|
||||
|
||||
/** Handle a request message */
|
||||
static error_t UFCAP_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp)
|
||||
{
|
||||
uint8_t presc;
|
||||
uint16_t msec;
|
||||
|
||||
struct priv *priv = unit->data;
|
||||
PayloadBuilder pb = pb_start(unit_tmp512, UNIT_TMP_LEN, NULL);
|
||||
const char* msg_denied_on_pin = "Not available on the selected pin!";
|
||||
|
||||
switch (command) {
|
||||
/**
|
||||
* Stop any ongoing measurement and return to base state.
|
||||
*/
|
||||
case CMD_STOP:
|
||||
UFCAP_SwitchMode(unit, OPMODE_IDLE);
|
||||
return E_SUCCESS;
|
||||
|
||||
// ----------------------- CONFIG --------------------------
|
||||
|
||||
/**
|
||||
* Set the active polarity, or triggering edge (for direct)
|
||||
*
|
||||
* pld: pol:u8 (0,1)
|
||||
*/
|
||||
case CMD_SET_POLARITY:
|
||||
{
|
||||
priv->active_level = pp_bool(pp);
|
||||
}
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Set the direct measurement prescaller 1,2,4,8
|
||||
*
|
||||
* pld: presc:u8
|
||||
*/
|
||||
case CMD_SET_DIR_PRESC:
|
||||
{
|
||||
presc = pp_u8(pp);
|
||||
if (presc != 1 && presc != 2 && presc != 4 && presc != 8) return E_BAD_VALUE;
|
||||
priv->direct_presc = presc;
|
||||
}
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Set the input filter for all modes
|
||||
*
|
||||
* pld: filter:u8 (0-15)
|
||||
*/
|
||||
case CMD_SET_INPUT_FILTER:
|
||||
{
|
||||
uint8_t input_filter = pp_u8(pp);
|
||||
if (input_filter >= 16) return E_BAD_VALUE;
|
||||
priv->dfilter = input_filter;
|
||||
}
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Set the direct sampling time.
|
||||
*
|
||||
* pld: msec:u16
|
||||
*/
|
||||
case CMD_SET_DIR_MSEC:
|
||||
{
|
||||
msec = pp_u16(pp);
|
||||
priv->direct_msec = msec;
|
||||
}
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Reset all SET* settings to their default values, stop any ongoing measure.
|
||||
*/
|
||||
case CMD_RESTORE_DEFAULTS:
|
||||
UFCAP_SwitchMode(unit, OPMODE_IDLE);
|
||||
|
||||
priv->active_level = priv->conf.active_level;
|
||||
priv->direct_presc = priv->conf.direct_presc;
|
||||
priv->direct_msec = priv->conf.direct_msec;
|
||||
priv->dfilter = priv->conf.dfilter;
|
||||
return E_SUCCESS;
|
||||
|
||||
// ------------------ COMMANDS ------------------------
|
||||
|
||||
/**
|
||||
* Start indirect continuous measurement.
|
||||
*/
|
||||
case CMD_INDIRECT_CONT_START:
|
||||
if (priv->opmode == OPMODE_INDIRECT_CONT) return E_SUCCESS; // no-op
|
||||
if (priv->opmode != OPMODE_IDLE) return E_BUSY;
|
||||
|
||||
UFCAP_SwitchMode(unit, OPMODE_INDIRECT_CONT);
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Start a continuous direct measurement (counting pulses in fixed time intervals)
|
||||
*
|
||||
* - meas_time_ms 0 = no change
|
||||
* - prescaller 0 = no change
|
||||
*
|
||||
* pld: meas_time_ms:u16, prescaller:u8
|
||||
* - prescaller is 1,2,4,8; 0 = no change
|
||||
*/
|
||||
case CMD_DIRECT_CONT_START:
|
||||
if (!priv->a_direct) {
|
||||
// This works only if we use the ETR pin. TIM2 shares CH1 with ETR.
|
||||
// If CH2 is selected as input, ETR is not available.
|
||||
com_respond_str(MSG_ERROR, frame_id, msg_denied_on_pin);
|
||||
return E_FAILURE;
|
||||
}
|
||||
if (priv->opmode == OPMODE_DIRECT_CONT) return E_SUCCESS; // no-op
|
||||
if (priv->opmode != OPMODE_IDLE) return E_BUSY;
|
||||
|
||||
msec = pp_u16(pp);
|
||||
presc = pp_u8(pp);
|
||||
if (msec != 0) priv->direct_msec = msec;
|
||||
if (presc != 0) priv->direct_presc = presc;
|
||||
|
||||
UFCAP_SwitchMode(unit, OPMODE_DIRECT_CONT);
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Start a burst of direct measurements with averaging.
|
||||
* The measurement is performed on N consecutive pulses.
|
||||
*
|
||||
* pld: count:u16
|
||||
*
|
||||
* resp: core_mhz:u16, count:u16, period_sum:u64, ontime_sum:u64
|
||||
*/
|
||||
case CMD_INDIRECT_BURST_START:
|
||||
if (priv->opmode != OPMODE_IDLE) return E_BAD_MODE;
|
||||
|
||||
priv->ind_burst.n_target = pp_u16(pp);
|
||||
priv->request_id = frame_id;
|
||||
UFCAP_SwitchMode(unit, OPMODE_INDIRECT_BURST);
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Start a single direct measurement of the given length (pulses in time period)
|
||||
* If 'prescaller' is not 0, it is changed via the param field.
|
||||
*
|
||||
* pld: meas_time_ms:u16, prescaller:u8
|
||||
* - prescaller is 1,2,4,8; 0 = no change
|
||||
*
|
||||
* resp: prescaller:u8, meas_time_ms:u16, pulse_count:u32
|
||||
*/
|
||||
case CMD_DIRECT_BURST_START:
|
||||
if (priv->opmode != OPMODE_IDLE) return E_BAD_MODE;
|
||||
|
||||
priv->dir_burst.msec = pp_u16(pp);
|
||||
|
||||
presc = pp_u8(pp);
|
||||
if (presc != 0) priv->direct_presc = presc;
|
||||
|
||||
priv->request_id = frame_id;
|
||||
UFCAP_SwitchMode(unit, OPMODE_DIRECT_BURST);
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Measure a single pulse length of the given polarity.
|
||||
* Measures time from a rising to a falling edge (or falling to rising, if polarity is 0)
|
||||
*
|
||||
* resp: core_mhz:u16, ontime:u32
|
||||
*/
|
||||
case CMD_MEASURE_SINGLE_PULSE:
|
||||
if (priv->opmode != OPMODE_IDLE) return E_BAD_MODE;
|
||||
priv->request_id = frame_id;
|
||||
UFCAP_SwitchMode(unit, OPMODE_SINGLE_PULSE);
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Start a free-running pulse counter.
|
||||
*
|
||||
* pld: prescaller:u8
|
||||
* - prescaller is 1,2,4,8; 0 = no change
|
||||
*/
|
||||
case CMD_FREECOUNT_START:
|
||||
if (priv->opmode != OPMODE_IDLE) return E_BAD_MODE;
|
||||
|
||||
presc = pp_u8(pp);
|
||||
if (presc != 0) priv->direct_presc = presc;
|
||||
|
||||
UFCAP_SwitchMode(unit, OPMODE_FREE_COUNTER);
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Reset the free-running pulse counter.
|
||||
*
|
||||
* resp: last_val:u32
|
||||
*/
|
||||
case CMD_FREECOUNT_CLEAR:
|
||||
if (priv->opmode != OPMODE_FREE_COUNTER) {
|
||||
return E_BAD_MODE;
|
||||
}
|
||||
|
||||
pb_u32(&pb, UFCAP_FreeCounterClear(unit));
|
||||
com_respond_pb(frame_id, MSG_SUCCESS, &pb);
|
||||
return E_SUCCESS;
|
||||
|
||||
// ------------------ READING ---------------------
|
||||
|
||||
/**
|
||||
* Read the most recent pulse measurement during continuous indirect measure.
|
||||
*
|
||||
* resp: core_mhz:u16, period:u32, ontime:u32
|
||||
*/
|
||||
case CMD_INDIRECT_CONT_READ:
|
||||
if (priv->opmode != OPMODE_INDIRECT_CONT) {
|
||||
return E_BAD_MODE;
|
||||
}
|
||||
if (priv->ind_cont.last_period == 0) {
|
||||
return E_BUSY;
|
||||
}
|
||||
|
||||
pb_u16(&pb, PLAT_AHB_MHZ);
|
||||
pb_u32(&pb, priv->ind_cont.last_period);
|
||||
pb_u32(&pb, priv->ind_cont.last_ontime);
|
||||
|
||||
com_respond_pb(frame_id, MSG_SUCCESS, &pb);
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Read the most recent result of a continuous direct measurement.
|
||||
*
|
||||
* resp: prescaller:u8, meas_time_ms:u16, pulse_count:u32
|
||||
*/
|
||||
case CMD_DIRECT_CONT_READ:
|
||||
if (!priv->a_direct) { // see above
|
||||
com_respond_str(MSG_ERROR, frame_id, msg_denied_on_pin);
|
||||
return E_FAILURE;
|
||||
}
|
||||
if (priv->opmode != OPMODE_DIRECT_CONT) return E_BAD_MODE;
|
||||
if (priv->dir_cont.last_count == 0) return E_BUSY;
|
||||
|
||||
pb_u8(&pb, priv->direct_presc);
|
||||
pb_u16(&pb, priv->direct_msec);
|
||||
pb_u32(&pb, priv->dir_cont.last_count);
|
||||
|
||||
com_respond_pb(frame_id, MSG_SUCCESS, &pb);
|
||||
return E_SUCCESS;
|
||||
|
||||
/**
|
||||
* Read the current value of the free-running pulse counter.
|
||||
*
|
||||
* The timing may have a significant jitter, this function is practically useful only for
|
||||
* slow pulse sources (like a geiger counter, item counting etc)
|
||||
*
|
||||
* resp: count:u32
|
||||
*/
|
||||
case CMD_FREECOUNT_READ:
|
||||
if (priv->opmode != OPMODE_FREE_COUNTER) {
|
||||
return E_BAD_MODE;
|
||||
}
|
||||
|
||||
pb_u32(&pb, UFCAP_GetFreeCounterValue(unit));
|
||||
com_respond_pb(frame_id, MSG_SUCCESS, &pb);
|
||||
return E_SUCCESS;
|
||||
|
||||
default:
|
||||
return E_UNKNOWN_COMMAND;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/** Frequency capture */
|
||||
const UnitDriver UNIT_FCAP = {
|
||||
.name = "FCAP",
|
||||
.description = "Frequency and pulse measurement",
|
||||
// Settings
|
||||
.preInit = UFCAP_preInit,
|
||||
.cfgLoadBinary = UFCAP_loadBinary,
|
||||
.cfgWriteBinary = UFCAP_writeBinary,
|
||||
.cfgLoadIni = UFCAP_loadIni,
|
||||
.cfgWriteIni = UFCAP_writeIni,
|
||||
// Init
|
||||
.init = UFCAP_init,
|
||||
.deInit = UFCAP_deInit,
|
||||
// Function
|
||||
.handleRequest = UFCAP_handleRequest,
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
//
|
||||
// Created by MightyPork on 2017/11/25.
|
||||
//
|
||||
// Digital input unit; single or multiple pin read access on one port (A-F)
|
||||
//
|
||||
|
||||
#ifndef U_FCAP_H
|
||||
#define U_FCAP_H
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
extern const UnitDriver UNIT_FCAP;
|
||||
|
||||
// UU_ prototypes
|
||||
|
||||
#endif //U_FCAP_H
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user