diff --git a/doc/03_软件需求规格说明书.docx b/doc/03_软件需求规格说明书.docx new file mode 100644 index 0000000..fa73a15 Binary files /dev/null and b/doc/03_软件需求规格说明书.docx differ diff --git a/doc/04_软件设计规格说明书.docx b/doc/04_软件设计规格说明书.docx new file mode 100644 index 0000000..705929e Binary files /dev/null and b/doc/04_软件设计规格说明书.docx differ diff --git a/README.md b/doc/README.md similarity index 100% rename from README.md rename to doc/README.md diff --git a/doc/设计模型_盲人智能出行系统_赵杰(v2.0).docx b/doc/设计模型_盲人智能出行系统_赵杰(v2.0).docx new file mode 100644 index 0000000..b5284b3 Binary files /dev/null and b/doc/设计模型_盲人智能出行系统_赵杰(v2.0).docx differ diff --git a/doc/设计模型_盲人智能出行系统_赵杰(v3.0).docx b/doc/设计模型_盲人智能出行系统_赵杰(v3.0).docx new file mode 100644 index 0000000..bb4664d Binary files /dev/null and b/doc/设计模型_盲人智能出行系统_赵杰(v3.0).docx differ diff --git a/doc/设计模型_盲人智能出行系统_赵杰.docx b/doc/设计模型_盲人智能出行系统_赵杰.docx new file mode 100644 index 0000000..c964588 Binary files /dev/null and b/doc/设计模型_盲人智能出行系统_赵杰.docx differ diff --git a/doc/需求模型_盲人智能出行系统_赵杰(v2.0).docx b/doc/需求模型_盲人智能出行系统_赵杰(v2.0).docx new file mode 100644 index 0000000..3044a74 Binary files /dev/null and b/doc/需求模型_盲人智能出行系统_赵杰(v2.0).docx differ diff --git a/doc/需求模型_盲人智能出行系统_赵杰(v3.0).docx b/doc/需求模型_盲人智能出行系统_赵杰(v3.0).docx new file mode 100644 index 0000000..83ac53b Binary files /dev/null and b/doc/需求模型_盲人智能出行系统_赵杰(v3.0).docx differ diff --git a/doc/需求模型_盲人智能出行系统_赵杰.docx b/doc/需求模型_盲人智能出行系统_赵杰.docx new file mode 100644 index 0000000..6fc8b5d Binary files /dev/null and b/doc/需求模型_盲人智能出行系统_赵杰.docx differ diff --git a/other/05_开发软件系统实践的汇报PPT.pptx b/other/05_开发软件系统实践的汇报PPT.pptx new file mode 100644 index 0000000..7f662d5 Binary files /dev/null and b/other/05_开发软件系统实践的汇报PPT.pptx differ diff --git a/other/06_软件开发项目的个人自评报告.xlsx b/other/06_软件开发项目的个人自评报告.xlsx new file mode 100644 index 0000000..a9b1107 Binary files /dev/null and b/other/06_软件开发项目的个人自评报告.xlsx differ diff --git a/other/07_软件开发项目的团队自评报告.xlsx b/other/07_软件开发项目的团队自评报告.xlsx new file mode 100644 index 0000000..56a59a3 Binary files /dev/null and b/other/07_软件开发项目的团队自评报告.xlsx differ diff --git a/other/08_210340015凌子杰-实践总结报告.docx b/other/08_210340015凌子杰-实践总结报告.docx new file mode 100644 index 0000000..cc3fe6b Binary files /dev/null and b/other/08_210340015凌子杰-实践总结报告.docx differ diff --git a/other/08_210340016刘定衢-实践总结报告.docx b/other/08_210340016刘定衢-实践总结报告.docx new file mode 100644 index 0000000..5d1d4ea Binary files /dev/null and b/other/08_210340016刘定衢-实践总结报告.docx differ diff --git a/other/08_210340017刘锋-实践总结报告.docx b/other/08_210340017刘锋-实践总结报告.docx new file mode 100644 index 0000000..34db5a7 Binary files /dev/null and b/other/08_210340017刘锋-实践总结报告.docx differ diff --git a/other/08_210340021聂玉洁-实践总结报告.docx b/other/08_210340021聂玉洁-实践总结报告.docx new file mode 100644 index 0000000..e52bca4 Binary files /dev/null and b/other/08_210340021聂玉洁-实践总结报告.docx differ diff --git a/other/08_210340039赵杰-实践总结报告.docx b/other/08_210340039赵杰-实践总结报告.docx new file mode 100644 index 0000000..498e217 Binary files /dev/null and b/other/08_210340039赵杰-实践总结报告.docx differ diff --git a/other/09_系统演示视频.mp4 b/other/09_系统演示视频.mp4 new file mode 100644 index 0000000..36bd52e Binary files /dev/null and b/other/09_系统演示视频.mp4 differ diff --git a/other/10_系统宣传海报.jpg b/other/10_系统宣传海报.jpg new file mode 100644 index 0000000..3c66f9a Binary files /dev/null and b/other/10_系统宣传海报.jpg differ diff --git a/src/EspSoftwareSerial/LICENSE b/src/EspSoftwareSerial/LICENSE new file mode 100644 index 0000000..f166cc5 --- /dev/null +++ b/src/EspSoftwareSerial/LICENSE @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! \ No newline at end of file diff --git a/src/EspSoftwareSerial/README.md b/src/EspSoftwareSerial/README.md new file mode 100644 index 0000000..37b106c --- /dev/null +++ b/src/EspSoftwareSerial/README.md @@ -0,0 +1,171 @@ +# EspSoftwareSerial + +## Implementation of the Arduino software serial library for the ESP8266 / ESP32 family + +This fork implements interrupt service routine best practice. +In the receive interrupt, instead of blocking for whole bytes +at a time - voiding any near-realtime behavior of the CPU - only level +change and timestamp are recorded. The more time consuming phase +detection and byte assembly are done in the main code. + +Except at high bitrates, depending on other ongoing activity, +interrupts in particular, this software serial adapter +supports full duplex receive and send. At high bitrates (115200bps) +send bit timing can be improved at the expense of blocking concurrent +full duplex receives, with the +`EspSoftwareSerial::UART::enableIntTx(false)` function call. + +The same functionality is given as the corresponding AVR library but +several instances can be active at the same time. Speed up to 115200 baud +is supported. Besides a constructor compatible to the AVR SoftwareSerial class, +and updated constructor that takes no arguments exists, instead the `begin()` +function can handle the pin assignments and logic inversion. +It also has optional input buffer capacity arguments for byte buffer and ISR bit buffer. +This way, it is a better drop-in replacement for the hardware serial APIs on the ESP MCUs. + +Please note that due to the fact that the ESPs always have other activities +ongoing, there will be some inexactness in interrupt timings. This may +lead to inevitable, but few, bit errors when having heavy data traffic +at high baud rates. + +This library supports ESP8266, ESP32, ESP32-S2 and ESP32-C3 devices. + +## Resource optimization + +The memory footprint can be optimized to just fit the amount of expected +incoming asynchronous data. +For this, the `EspSoftwareSerial::UART` constructor provides two arguments. First, the +octet buffer capacity for assembled received octets can be set. Read calls are +satisfied from this buffer, freeing it in return. +Second, the signal edge detection buffer of 32bit fields can be resized. +One octet may require up to to 10 fields, but fewer may be needed, +depending on the bit pattern. Any read or write calls check this buffer +to assemble received octets, thus promoting completed octets to the octet +buffer, freeing fields in the edge detection buffer. + +Look at the swsertest.ino example. There, on reset, ASCII characters ' ' to 'z' +are sent. This happens not as a block write, but in a single write call per +character. As the example uses a local loopback wire, every outgoing bit is +immediately received back. Therefore, any single write call causes up to +10 fields - depending on the exact bit pattern - to be occupied in the signal +edge detection buffer. In turn, as explained before, each single write call +also causes received bit assembly to be performed, promoting these bits from +the signal edge detection buffer to the octet buffer as soon as possible. +Explaining by way of contrast, if during a a single write call, perhaps because +of using block writing, more than a single octet is received, there will be a +need for more than 10 fields in the signal edge detection buffer. +The necessary capacity of the octet buffer only depends on the amount of incoming +data until the next read call. + +For the swsertest.ino example, this results in the following optimized +constructor arguments to spend only the minimum RAM on buffers required: + +The octet buffer capacity (`bufCapacity`) is 95 (93 characters net plus two tolerance). +The signal edge detection buffer capacity (`isrBufCapacity`) is 11, as each +single octet can have up to 11 bits on the wire, +which are immediately received during the write, and each +write call causes the signal edge detection to promote the previously sent and +received bits to the octet buffer. + +In a more generalized scenario, calculate the bits (use message size in octets +times 10) that may be asynchronously received to determine the value for +`isrBufCapacity` in the constructor. Also use the number of received octets +that must be buffered for reading as the value of `bufCapacity`. +The more frequently your code calls write or read functions, the greater the +chances are that you can reduce the `isrBufCapacity` footprint without losing data, +and each time you call read to fetch from the octet buffer, you reduce the +need for space there. + +## EspSoftwareSerial::Config and parity +The configuration of the data stream is done via a `EspSoftwareSerial::Config` +argument to `begin()`. Word lengths can be set to between 5 and 8 bits, parity +can be N(one), O(dd) or E(ven) and 1 or 2 stop bits can be used. The default is +`SWSERIAL_8N1` using 8 bits, no parity and 1 stop bit but any combination can +be used, e.g. `SWSERIAL_7E2`. If using EVEN or ODD parity, any parity errors +can be detected with the `readParity()` and `parityEven()` or `parityOdd()` +functions respectively. Note that the result of `readParity()` always applies +to the preceding `read()` or `peek()` call, and is undefined if they report +no data or an error. + +To allow flexible 9-bit and data/addressing protocols, the additional parity +modes MARK and SPACE are also available. Furthermore, the parity mode can be +individually set in each call to `write()`. + +This allows a simple implementation of protocols where the parity bit is used to +distinguish between data and addresses/commands ("9-bit" protocols). First set +up EspSoftwareSerial::UART with parity mode SPACE, e.g. `SWSERIAL_8S1`. This will add a +parity bit to every byte sent, setting it to logical zero (SPACE parity). + +To detect incoming bytes with the parity bit set (MARK parity), use the +`readParity()` function. To send a byte with the parity bit set, just add +`MARK` as the second argument when writing, e.g. `write(ch, SWSERIAL_PARITY_MARK)`. + +## Checking for correct pin selection / configuration +In general, most pins on the ESP8266 and ESP32 devices can be used by EspSoftwareSerial, +however each device has a number of pins that have special functions or require careful +handling to prevent undesirable situations, for example they are connected to the +on-board SPI flash memory or they are used to determine boot and programming modes +after powerup or brownouts. These pins are not able to be configured by this library. + +The exact list for each device can be found in the +[ESP32 data sheet](https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf) +in sections 2.2 (Pin Descriptions) and 2.4 (Strapping pins). There is a discussion +dedicated to the use of GPIO12 in this +[note about GPIO12](https://github.com/espressif/esp-idf/tree/release/v3.2/examples/storage/sd_card#note-about-gpio12). +Refer to the `isValidPin()`, `isValidRxPin()` and `isValidTxPin()` +functions in the `EspSoftwareSerial::GpioCapabilities` class for the GPIO restrictions +enforced by this library by default. + +The easiest and safest method is to test the object returned at runtime, to see if +it is valid. For example: + +``` +#include + +#define MYPORT_TX 12 +#define MYPORT_RX 13 + +EspSoftwareSerial::UART myPort; + +[...] + +Serial.begin(115200); // Standard hardware serial port + +myPort.begin(38400, SWSERIAL_8N1, MYPORT_RX, MYPORT_TX, false); +if (!myPort) { // If the object did not initialize, then its configuration is invalid + Serial.println("Invalid EspSoftwareSerial pin configuration, check config"); + while (1) { // Don't continue with invalid configuration + delay (1000); + } +} + +[...] +``` + +## Using and updating EspSoftwareSerial in the esp8266com/esp8266 Arduino build environment + +EspSoftwareSerial is both part of the BSP download for ESP8266 in Arduino, +and it is set up as a Git submodule in the esp8266 source tree, +specifically in `.../esp8266/libraries/SoftwareSerial` when using a Github +repository clone in your Arduino sketchbook hardware directory. +This supersedes any version of EspSoftwareSerial installed for instance via +the Arduino library manager, it is not required to install EspSoftwareSerial +for the ESP8266 separately at all, but doing so has ill effect. + +The responsible maintainer of the esp8266 repository has kindly shared the +following command line instructions to use, if one wishes to manually +update EspSoftwareSerial to a newer release than pulled in via the ESP8266 Arduino BSP: + +To update esp8266/arduino EspSoftwareSerial submodule to lastest master: + +Clean it (optional): +```shell +$ rm -rf libraries/SoftwareSerial +$ git submodule update --init +``` +Now update it: +```shell +$ cd libraries/SoftwareSerial +$ git checkout master +$ git pull +``` diff --git a/src/EspSoftwareSerial/examples/bitpattern/bitpattern.ino b/src/EspSoftwareSerial/examples/bitpattern/bitpattern.ino new file mode 100644 index 0000000..06eb170 --- /dev/null +++ b/src/EspSoftwareSerial/examples/bitpattern/bitpattern.ino @@ -0,0 +1,71 @@ +#include "SoftwareSerial.h" + +#ifndef D5 +#if defined(ESP8266) +#define D8 (15) +#define D5 (14) +#define D7 (13) +#define D6 (12) +#define RX (3) +#define TX (1) +#elif defined(ESP32) +#define D8 (5) +#define D5 (18) +#define D7 (23) +#define D6 (19) +#define RX (3) +#define TX (1) +#endif +#endif + +EspSoftwareSerial::UART swSer; +#ifdef ESP8266 +auto logSer = EspSoftwareSerial::UART(-1, TX); +auto hwSer = Serial; +#else +auto logSer = Serial; +auto hwSer = Serial1; +#endif + +constexpr uint32_t TESTBPS = 115200; + +void setup() { + delay(2000); +#ifdef ESP8266 + hwSer.begin(TESTBPS, ::SERIAL_8N1); + hwSer.swap(); +#else + hwSer.begin(TESTBPS, ::SERIAL_8N1, D6, D5); +#endif + logSer.begin(115200); + logSer.println(PSTR("\nOne Wire Half Duplex Bitpattern and Datarate Test")); + swSer.begin(TESTBPS, EspSoftwareSerial::SWSERIAL_8N1, D6, D5); + swSer.enableIntTx(true); + logSer.println(PSTR("Tx on swSer")); +} + +uint8_t val = 0xff; + +void loop() { + swSer.write((uint8_t)0x00); + swSer.write(val); + swSer.write(val); + auto start = ESP.getCycleCount(); + int rxCnt = 0; + while (ESP.getCycleCount() - start < ESP.getCpuFreqMHz() * 1000000 / 10) { + if (hwSer.available()) { + auto rxVal = hwSer.read(); + if ((!rxCnt && rxVal) || (rxCnt && rxVal != val)) { + logSer.printf(PSTR("Rx bit error: tx = 0x%02x, rx = 0x%02x\n"), val, rxVal); + } + ++rxCnt; + } + } + if (rxCnt != 3) { + logSer.printf(PSTR("Rx cnt error, tx = 0x%02x\n"), val); + } + ++val; + if (!val) { + logSer.println("Starting over"); + } +} diff --git a/src/EspSoftwareSerial/examples/circular_queue_mp_test/mp_queue_test.cpp b/src/EspSoftwareSerial/examples/circular_queue_mp_test/mp_queue_test.cpp new file mode 100644 index 0000000..66c2ac3 --- /dev/null +++ b/src/EspSoftwareSerial/examples/circular_queue_mp_test/mp_queue_test.cpp @@ -0,0 +1,74 @@ +// circular_mp_test.cpp : This file contains the 'main' function. Program execution begins and ends there. +// + +#include +#include +#include +#include +#include "circular_queue/circular_queue_mp.h" + +struct qitem +{ + // produer id + int id; + // monotonic increasing value + int val = 0; +}; + +constexpr int TOTALMESSAGESTARGET = 60000000; +// reserve one thread as consumer +const auto THREADS = std::thread::hardware_concurrency() / 2 - 1; +const int MESSAGES = TOTALMESSAGESTARGET / THREADS; +circular_queue threads(THREADS); +circular_queue_mp queue(threads.capacity()* MESSAGES / 10); +std::vector checks(threads.capacity()); + +int main() +{ + using namespace std::chrono_literals; + std::cerr << "Utilizing " << THREADS << " producer threads" << std::endl; + for (int i = 0; i < threads.capacity(); ++i) + { + threads.push(std::thread([i]() { + for (int c = 0; c < MESSAGES;) + { + // simulate some load + auto start = std::chrono::system_clock::now(); + while (std::chrono::system_clock::now() - start < 1us); + if (queue.push({ i, c })) + { + ++c; + } + else + { + //std::cerr << "queue full" << std::endl; + //std::this_thread::sleep_for(10us); + } + //if (0 == c % 10000) std::this_thread::sleep_for(10us); + } + })); + } + for (int o = 0; o < threads.available() * MESSAGES; ++o) + { + auto now = std::chrono::system_clock::now(); + while (!queue.available()) + { + auto starvedFor = std::chrono::system_clock::now() - now; + if (starvedFor > 20s) std::cerr << "queue starved for > 20s" << std::endl; + //std::this_thread::sleep_for(20ms); + } + auto item = queue.pop(); + if (checks[item.id] != item.val) + { + std::cerr << "item mismatch" << std::endl; + } + checks[item.id] = item.val + 1; + if (0 == item.val % 1000) std::this_thread::sleep_for(100us); + } + while (threads.available()) + { + auto thread = threads.pop(); + thread.join(); + } + return 0; +} diff --git a/src/EspSoftwareSerial/examples/loopback/loopback.ino b/src/EspSoftwareSerial/examples/loopback/loopback.ino new file mode 100644 index 0000000..d72544f --- /dev/null +++ b/src/EspSoftwareSerial/examples/loopback/loopback.ino @@ -0,0 +1,279 @@ +#include + +// On ESP8266: +// Local EspSoftwareSerial loopback, connect D5 (rx) and D6 (tx). +// For local hardware loopback, connect D5 to D8 (tx), D6 to D7 (rx). +// For hardware send/sink, connect D7 (rx) and D8 (tx). +// Hint: The logger is run at 9600bps such that enableIntTx(true) can remain unchanged. Blocking +// interrupts severely impacts the ability of the EspSoftwareSerial devices to operate concurrently +// and/or in duplex mode. +// Operating in software serial full duplex mode, runs at 19200bps and few errors (~2.5%). +// Operating in software serial half duplex mode (both loopback and repeater), +// runs at 57600bps with nearly no errors. +// Operating loopback in full duplex, and repeater in half duplex, runs at 38400bps with nearly no errors. +// On ESP32: +// For EspSoftwareSerial or hardware send/sink, connect D5 (rx) and D6 (tx). +// Hardware Serial2 defaults to D4 (rx), D3 (tx). +// For local hardware loopback, connect D5 (rx) to D3 (tx), D6 (tx) to D4 (rx). + +#ifndef D5 +#if defined(ESP8266) +#define D8 (15) +#define D5 (14) +#define D7 (13) +#define D6 (12) +#define RX (3) +#define TX (1) +#elif defined(ESP32) +#define D8 (5) +#define D5 (18) +#define D7 (23) +#define D6 (19) +#define RX (3) +#define TX (1) +#endif +#endif + +// Pick only one of HWLOOPBACK, HWSOURCESWSINK, or HWSOURCESINK +//#define HWLOOPBACK 1 +//#define HWSOURCESWSINK 1 +//#define HWSOURCESINK 1 +#define HALFDUPLEX 1 + +#ifdef ESP32 +constexpr int IUTBITRATE = 19200; +#else +constexpr int IUTBITRATE = 19200; +#endif + +#if defined(ESP8266) +constexpr EspSoftwareSerial::Config swSerialConfig = EspSoftwareSerial::SWSERIAL_8E1; +constexpr SerialConfig hwSerialConfig = ::SERIAL_8E1; +#elif defined(ESP32) +constexpr EspSoftwareSerial::Config swSerialConfig = EspSoftwareSerial::SWSERIAL_8E1; +constexpr uint32_t hwSerialConfig = ::SERIAL_8E1; +#else +constexpr unsigned swSerialConfig = 3; +#endif +constexpr bool invert = false; + +constexpr int BLOCKSIZE = 16; // use fractions of 256 + +unsigned long start; +const char effTxTxt[] PROGMEM = "eff. tx: "; +const char effRxTxt[] PROGMEM = "eff. rx: "; +int txCount; +int rxCount; +int expected; +int rxErrors; +int rxParityErrors; +constexpr int ReportInterval = IUTBITRATE / 8; + +#if defined(ESP8266) +#if defined(HWLOOPBACK) || defined(HWSOURCESWSINK) +HardwareSerial& hwSerial(Serial); +EspSoftwareSerial::UART serialIUT; +EspSoftwareSerial::UART logger; +#elif defined(HWSOURCESINK) +HardwareSerial& serialIUT(Serial); +EspSoftwareSerial::UART logger; +#else +EspSoftwareSerial::UART serialIUT; +HardwareSerial& logger(Serial); +#endif +#elif defined(ESP32) +#if defined(HWLOOPBACK) || defined (HWSOURCESWSINK) +HardwareSerial& hwSerial(Serial2); +EspSoftwareSerial::UART serialIUT; +#elif defined(HWSOURCESINK) +HardwareSerial& serialIUT(Serial2); +#else +EspSoftwareSerial::UART serialIUT; +#endif +HardwareSerial& logger(Serial); +#else +EspSoftwareSerial::UART serialIUT(14, 12); +HardwareSerial& logger(Serial); +#endif + +void setup() { +#if defined(ESP8266) +#if defined(HWLOOPBACK) || defined(HWSOURCESINK) || defined(HWSOURCESWSINK) + Serial.begin(IUTBITRATE, hwSerialConfig, ::SERIAL_FULL, 1, invert); + Serial.swap(); + Serial.setRxBufferSize(2 * BLOCKSIZE); + logger.begin(9600, EspSoftwareSerial::SWSERIAL_8N1, -1, TX); +#else + logger.begin(9600); +#endif +#if !defined(HWSOURCESINK) + serialIUT.begin(IUTBITRATE, swSerialConfig, D5, D6, invert, 2 * BLOCKSIZE); +#ifdef HALFDUPLEX + serialIUT.enableIntTx(false); +#endif +#endif +#elif defined(ESP32) +#if defined(HWLOOPBACK) || defined(HWSOURCESWSINK) + Serial2.begin(IUTBITRATE, hwSerialConfig, D4, D3, invert); + Serial2.setRxBufferSize(2 * BLOCKSIZE); +#elif defined(HWSOURCESINK) + serialIUT.begin(IUTBITRATE, hwSerialConfig, D5, D6, invert); + serialIUT.setRxBufferSize(2 * BLOCKSIZE); +#endif +#if !defined(HWSOURCESINK) + serialIUT.begin(IUTBITRATE, swSerialConfig, D5, D6, invert, 2 * BLOCKSIZE); +#ifdef HALFDUPLEX + serialIUT.enableIntTx(false); +#endif +#endif + logger.begin(9600); +#else +#if !defined(HWSOURCESINK) + serialIUT.begin(IUTBITRATE); +#endif + logger.begin(9600); +#endif + + logger.println(PSTR("Loopback example for EspEspSoftwareSerial")); + + start = micros(); + txCount = 0; + rxCount = 0; + rxErrors = 0; + rxParityErrors = 0; + expected = -1; +} + +unsigned char c = 0; + +void loop() { +#ifdef HALFDUPLEX + char block[BLOCKSIZE]; +#endif + char inBuf[BLOCKSIZE]; + for (int i = 0; i < BLOCKSIZE; ++i) { +#ifndef HALFDUPLEX +#ifdef HWSOURCESWSINK + hwSerial.write(c); +#else + serialIUT.write(c); +#endif +#ifdef HWLOOPBACK + int avail = hwSerial.available(); + while ((0 == (i % 8)) && avail > 0) { + int inCnt = hwSerial.read(inBuf, min(avail, min(BLOCKSIZE, hwSerial.availableForWrite()))); + hwSerial.write(inBuf, inCnt); + avail -= inCnt; + } +#endif +#else + block[i] = c; +#endif + c = (c + 1) % 256; + ++txCount; + } +#ifdef HALFDUPLEX +#ifdef HWSOURCESWSINK + hwSerial.write(block, BLOCKSIZE); +#else + serialIUT.write(block, BLOCKSIZE); +#endif +#endif +#ifdef HWSOURCESINK +#if defined(ESP8266) + if (serialIUT.hasOverrun()) { logger.println(PSTR("serialIUT.overrun")); } +#endif +#else + if (serialIUT.overflow()) { logger.println(PSTR("serialIUT.overflow")); } +#endif + + int inCnt; + uint32_t deadlineStart; + +#ifdef HWLOOPBACK + // starting deadline for the first bytes to become readable + deadlineStart = ESP.getCycleCount(); + inCnt = 0; + while ((ESP.getCycleCount() - deadlineStart) < (1000000UL * 12 * BLOCKSIZE) / IUTBITRATE * 24 * ESP.getCpuFreqMHz()) { + int avail = hwSerial.available(); + inCnt += hwSerial.read(&inBuf[inCnt], min(avail, min(BLOCKSIZE - inCnt, hwSerial.availableForWrite()))); + if (inCnt >= BLOCKSIZE) { break; } + // wait for more outstanding bytes to trickle in + if (avail) deadlineStart = ESP.getCycleCount(); + } + hwSerial.write(inBuf, inCnt); +#endif + + // starting deadline for the first bytes to come in + deadlineStart = ESP.getCycleCount(); + inCnt = 0; + while ((ESP.getCycleCount() - deadlineStart) < (1000000UL * 12 * BLOCKSIZE) / IUTBITRATE * 8 * ESP.getCpuFreqMHz()) { + int avail; + if (0 != (swSerialConfig & 070)) + avail = serialIUT.available(); + else + avail = serialIUT.read(inBuf, BLOCKSIZE); + for (int i = 0; i < avail; ++i) + { + unsigned char r; + if (0 != (swSerialConfig & 070)) + r = serialIUT.read(); + else + r = inBuf[i]; + if (expected == -1) { expected = r; } + else { + expected = (expected + 1) % (1UL << (5 + swSerialConfig % 4)); + } + if (r != expected) { + ++rxErrors; + expected = -1; + } +#ifndef HWSOURCESINK + if (serialIUT.readParity() != (static_cast(swSerialConfig & 010) ? serialIUT.parityOdd(r) : serialIUT.parityEven(r))) + { + ++rxParityErrors; + } +#elif defined(ESP8266) + // current ESP8266 API does not flag parity errors separately + if (serialIUT.hasRxError()) + { + ++rxParityErrors; + } +#endif + ++rxCount; + ++inCnt; + } + + if (inCnt >= BLOCKSIZE) { break; } + // wait for more outstanding bytes to trickle in + if (avail) deadlineStart = ESP.getCycleCount(); + } + + const uint32_t interval = micros() - start; + if (txCount >= ReportInterval && interval) { + uint8_t wordBits = (5 + swSerialConfig % 4) + static_cast(swSerialConfig & 070) + 1 + ((swSerialConfig & 0300) ? 1 : 0); + logger.println(String(PSTR("tx/rx: ")) + txCount + PSTR("/") + rxCount); + const long txCps = txCount * (1000000.0 / interval); + const long rxCps = rxCount * (1000000.0 / interval); + logger.print(String(FPSTR(effTxTxt)) + wordBits * txCps + PSTR("bps, ") + + effRxTxt + wordBits * rxCps + PSTR("bps, ") + + rxErrors + PSTR(" errors (") + 100.0 * rxErrors / (!rxErrors ? 1 : rxCount) + PSTR("%)")); + if (0 != (swSerialConfig & 070)) + { + logger.print(PSTR(" (")); logger.print(rxParityErrors); logger.println(PSTR(" parity errors)")); + } + else + { + logger.println(); + } + txCount = 0; + rxCount = 0; + rxErrors = 0; + rxParityErrors = 0; + expected = -1; + // resync + delay(1000UL * 12 * BLOCKSIZE / IUTBITRATE * 16); + serialIUT.flush(); + start = micros(); + } +} diff --git a/src/EspSoftwareSerial/examples/onewiretest/onewiretest.ino b/src/EspSoftwareSerial/examples/onewiretest/onewiretest.ino new file mode 100644 index 0000000..aeb3bf8 --- /dev/null +++ b/src/EspSoftwareSerial/examples/onewiretest/onewiretest.ino @@ -0,0 +1,59 @@ +#include "SoftwareSerial.h" + +#ifndef D5 +#if defined(ESP8266) +#define D5 (14) +#define D6 (12) +#elif defined(ESP32) +#define D5 (18) +#define D6 (19) +#endif +#endif + +EspSoftwareSerial::UART swSer1; +EspSoftwareSerial::UART swSer2; + +void checkSwSerial(EspSoftwareSerial::UART* ss) { + byte ch; + while (!Serial.available()); + ss->enableTx(true); + while (Serial.available()) { + ch = Serial.read(); + ss->write(ch); + } + ss->enableTx(false); + // wait 1 second for the reply from EspSoftwareSerial if any + delay(1000); + if (ss->available()) { + Serial.print(PSTR("\nResult:")); + while (ss->available()) { + ch = (byte)ss->read(); + Serial.print(ch < 0x10 ? PSTR(" 0") : PSTR(" ")); + Serial.print(ch, HEX); + } + Serial.println(); + } +} + +void setup() { + delay(2000); + Serial.begin(115200); + Serial.println(PSTR("\nOne Wire Half Duplex Serial Tester")); + swSer1.begin(115200, EspSoftwareSerial::SWSERIAL_8N1, D6, D6, false, 256); + // high speed half duplex, turn off interrupts during tx + swSer1.enableIntTx(false); + swSer2.begin(115200, EspSoftwareSerial::SWSERIAL_8N1, D5, D5, false, 256); + // high speed half duplex, turn off interrupts during tx + swSer2.enableIntTx(false); +} + +void loop() { + Serial.println(PSTR("\n\nTesting on swSer1")); + Serial.print(PSTR("Enter something to send using swSer1.")); + checkSwSerial(&swSer1); + + Serial.println(PSTR("\n\nTesting on swSer2")); + Serial.print(PSTR("Enter something to send using swSer2.")); + checkSwSerial(&swSer2); + +} diff --git a/src/EspSoftwareSerial/examples/onreceive/onreceive.ino b/src/EspSoftwareSerial/examples/onreceive/onreceive.ino new file mode 100644 index 0000000..4e5b04a --- /dev/null +++ b/src/EspSoftwareSerial/examples/onreceive/onreceive.ino @@ -0,0 +1,80 @@ +// On ESP8266: +// Runs up to 115200bps at 80MHz, 250000bps at 160MHz, with nearly zero errors. +// This example is currently not ported to ESP32, which is based on FreeRTOS. + +#include + +#ifndef D5 +#define D8 (15) +#define D5 (14) +#define D7 (13) +#define D6 (12) +#define RX (3) +#define TX (1) +#endif + +#define BAUD_RATE 115200 +#define MAX_FRAMEBITS (1 + 8 + 1 + 2) + +EspSoftwareSerial::UART testSerial; + +// Becomes set from ISR / IRQ callback function. +std::atomic rxPending(false); + +void IRAM_ATTR receiveHandler() { + rxPending.store(true); + esp_schedule(); +} + +void setup() { + Serial.begin(115200); + Serial.setDebugOutput(false); + Serial.swap(); + testSerial.begin(BAUD_RATE, EspSoftwareSerial::SWSERIAL_8N1, RX, TX); + // Only half duplex this way, but reliable TX timings for high bps + testSerial.enableIntTx(false); + testSerial.onReceive(receiveHandler); + + testSerial.println(PSTR("\nSoftware serial onReceive() event test started")); + + for (char ch = ' '; ch <= 'z'; ch++) { + testSerial.write(ch); + } + testSerial.println(); +} + +void loop() { +#ifdef ESP8266 + bool isRxPending = rxPending.load(); + if (isRxPending) { + rxPending.store(false); + } +#else + bool isRxPending = m_isrOverflow.exchange(false); +#endif + auto avail = testSerial.available(); + if (isRxPending && !avail) { + // event fired on start bit, wait until first stop bit of longest frame + delayMicroseconds(1 + MAX_FRAMEBITS * 1000000 / BAUD_RATE); + avail = testSerial.available(); + } + if (!avail) { + // On development board, idle power draw at USB: + // with yield() 77mA, 385mW (160MHz: 82mA, 410mW) + // with esp_suspend() 20mA, 100mW (at 160MHz, too) + //yield(); + esp_suspend(); + return; + } + // try to force to half-duplex + decltype(avail) prev_avail; + do { + delayMicroseconds(1 + MAX_FRAMEBITS * 1000000 / BAUD_RATE); + prev_avail = avail; + } while (prev_avail != (avail = testSerial.available())); + while (avail > 0) { + testSerial.write(testSerial.read()); + avail = testSerial.available(); + } + testSerial.println(); +} diff --git a/src/EspSoftwareSerial/examples/repeater/repeater.ino b/src/EspSoftwareSerial/examples/repeater/repeater.ino new file mode 100644 index 0000000..57a29c0 --- /dev/null +++ b/src/EspSoftwareSerial/examples/repeater/repeater.ino @@ -0,0 +1,199 @@ +#include + +// On ESP8266: +// EspSoftwareSerial loopback for remote source (loopback.ino), or hardware loopback. +// Connect source D5 (rx) to local D8 (tx), source D6 (tx) to local D7 (rx). +// Hint: The logger is run at 9600bps such that enableIntTx(true) can remain unchanged. Blocking +// interrupts severely impacts the ability of the EspSoftwareSerial devices to operate concurrently +// and/or in duplex mode. +// On ESP32: +// For software or hardware loopback, connect source rx to local D8 (tx), source tx to local D7 (rx). + +#ifndef D5 +#if defined(ESP8266) +#define D8 (15) +#define D5 (14) +#define D7 (13) +#define D6 (12) +#define RX (3) +#define TX (1) +#elif defined(ESP32) +#define D8 (5) +#define D5 (18) +#define D7 (23) +#define D6 (19) +#define RX (3) +#define TX (1) +#endif +#endif + +#define HWLOOPBACK 1 +#define HALFDUPLEX 1 + +#ifdef ESP32 +constexpr int IUTBITRATE = 19200; +#else +constexpr int IUTBITRATE = 19200; +#endif + +#if defined(ESP8266) +constexpr EspSoftwareSerial::Config swSerialConfig = EspSoftwareSerial::SWSERIAL_8E1; +constexpr SerialConfig hwSerialConfig = ::SERIAL_8E1; +#elif defined(ESP32) +constexpr EspSoftwareSerial::Config swSerialConfig = EspSoftwareSerial::SWSERIAL_8E1; +constexpr uint32_t hwSerialConfig = ::SERIAL_8E1; +#else +constexpr unsigned swSerialConfig = 3; +#endif +constexpr bool invert = false; + +constexpr int BLOCKSIZE = 16; // use fractions of 256 + +unsigned long start; +const char bitRateTxt[] PROGMEM = "Effective data rate: "; +int rxCount; +int seqErrors; +int parityErrors; +int expected; +constexpr int ReportInterval = IUTBITRATE / 8; + +#if defined(ESP8266) +#if defined(HWLOOPBACK) +HardwareSerial& repeater(Serial); +EspSoftwareSerial::UART logger; +#else +EspSoftwareSerial::UART repeater; +HardwareSerial& logger(Serial); +#endif +#elif defined(ESP32) +#if defined(HWLOOPBACK) +HardwareSerial& repeater(Serial2); +#else +EspSoftwareSerial::UART repeater; +#endif +HardwareSerial& logger(Serial); +#else +EspSoftwareSerial::UART repeater(14, 12); +HardwareSerial& logger(Serial); +#endif + +void setup() { +#if defined(ESP8266) +#if defined(HWLOOPBACK) + repeater.begin(IUTBITRATE, hwSerialConfig, ::SERIAL_FULL, 1, invert); + repeater.swap(); + repeater.setRxBufferSize(2 * BLOCKSIZE); + logger.begin(9600, EspSoftwareSerial::SWSERIAL_8N1, -1, TX); +#else + repeater.begin(IUTBITRATE, swSerialConfig, D7, D8, invert, 4 * BLOCKSIZE); +#ifdef HALFDUPLEX + repeater.enableIntTx(false); +#endif + logger.begin(9600); +#endif +#elif defined(ESP32) +#if defined(HWLOOPBACK) + repeater.begin(IUTBITRATE, hwSerialConfig, D7, D8, invert); + repeater.setRxBufferSize(2 * BLOCKSIZE); +#else + repeater.begin(IUTBITRATE, swSerialConfig, D7, D8, invert, 4 * BLOCKSIZE); +#ifdef HALFDUPLEX + repeater.enableIntTx(false); +#endif +#endif + logger.begin(9600); +#else + repeater.begin(IUTBITRATE); + logger.begin(9600); +#endif + + logger.println(PSTR("Repeater example for EspEspSoftwareSerial")); + start = micros(); + rxCount = 0; + seqErrors = 0; + parityErrors = 0; + expected = -1; +} + +void loop() { +#ifdef HWLOOPBACK +#if defined(ESP8266) + if (repeater.hasOverrun()) { logger.println(PSTR("repeater.overrun")); } +#endif +#else + if (repeater.overflow()) { logger.println(PSTR("repeater.overflow")); } +#endif + +#ifdef HALFDUPLEX + char block[BLOCKSIZE]; +#endif + // starting deadline for the first bytes to come in + uint32_t deadlineStart = ESP.getCycleCount(); + int inCnt = 0; + while ((ESP.getCycleCount() - deadlineStart) < (1000000UL * 12 * BLOCKSIZE) / IUTBITRATE * 24 * ESP.getCpuFreqMHz()) { + int avail = repeater.available(); + for (int i = 0; i < avail; ++i) + { + int r = repeater.read(); + if (r == -1) { logger.println(PSTR("read() == -1")); } + if (expected == -1) { expected = r; } + else { + expected = (expected + 1) % (1UL << (5 + swSerialConfig % 4)); + } + if (r != expected) { + ++seqErrors; + expected = -1; + } +#ifndef HWLOOPBACK + if (repeater.readParity() != (static_cast(swSerialConfig & 010) ? repeater.parityOdd(r) : repeater.parityEven(r))) + { + ++parityErrors; + } +#elif defined(ESP8266) + // current ESP8266 API does not flag parity errors separately + if (repeater.hasRxError()) + { + ++parityErrors; + } +#endif + ++rxCount; +#ifdef HALFDUPLEX + block[inCnt] = r; +#else + repeater.write(r); +#endif + if (++inCnt >= BLOCKSIZE) { break; } + } + if (inCnt >= BLOCKSIZE) { break; } + // wait for more outstanding bytes to trickle in + if (avail) deadlineStart = ESP.getCycleCount(); + } + +#ifdef HALFDUPLEX + repeater.write(block, inCnt); +#endif + + if (rxCount >= ReportInterval) { + auto end = micros(); + unsigned long interval = end - start; + long cps = rxCount * (1000000.0 / interval); + long seqErrorsps = seqErrors * (1000000.0 / interval); + logger.print(String(FPSTR(bitRateTxt)) + 10 * cps + PSTR("bps, ") + + seqErrorsps + PSTR("cps seq. errors (") + 100.0 * seqErrors / rxCount + PSTR("%)")); +#ifndef HWLOOPBACK + if (0 != (swSerialConfig & 070)) + { + logger.print(PSTR(" (")); logger.print(parityErrors); logger.println(PSTR(" parity errors)")); + } + else +#endif + { + logger.println(); + } + start = end; + rxCount = 0; + seqErrors = 0; + parityErrors = 0; + expected = -1; + } +} diff --git a/src/EspSoftwareSerial/examples/swsertest/swsertest.ino b/src/EspSoftwareSerial/examples/swsertest/swsertest.ino new file mode 100644 index 0000000..1a24844 --- /dev/null +++ b/src/EspSoftwareSerial/examples/swsertest/swsertest.ino @@ -0,0 +1,79 @@ +// On ESP8266: +// At 80MHz runs up 57600bps, and at 160MHz CPU frequency up to 115200bps with only negligible errors. +// Connect pin 13 to 15. +// For verification and as a example for how to use SW serial on the USB to PC connection, +// which allows the use of HW Serial on GPIO13 and GPIO15 instead, #define SWAPSERIAL below. +// Notice how the bitrates are also swapped then between RX/TX and GPIO13/GPIO15. +// Builtin debug output etc. must be stopped on HW Serial in this case, as it would interfere with the +// external communication on GPIO13/GPIO15. + +#include + +#ifndef D5 +#if defined(ESP8266) +#define D8 (15) +#define D5 (14) +#define D7 (13) +#define D6 (12) +#define RX (3) +#define TX (1) +#elif defined(ESP32) +#define D8 (5) +#define D5 (18) +#define D7 (23) +#define D6 (19) +#define RX (3) +#define TX (1) +#endif +#endif + +#ifdef ESP32 +#define BAUD_RATE 57600 +#else +#define BAUD_RATE 57600 +#endif + +#undef SWAPSERIAL + +#ifndef SWAPSERIAL +auto& usbSerial = Serial; +EspSoftwareSerial::UART testSerial; +#else +EspSoftwareSerial::UART usbSerial; +auto& testSerial = Serial; +#endif + +void setup() { +#ifndef SWAPSERIAL + usbSerial.begin(115200); + // Important: the buffer size optimizations here, in particular the isrBufSize (11) that is only sufficiently + // large to hold a single word (up to start - 8 data - parity - stop), are on the basis that any char written + // to the loopback EspSoftwareSerial adapter gets read before another write is performed. + // Block writes with a size greater than 1 would usually fail. Do not copy this into your own project without + // reading the documentation. + testSerial.begin(BAUD_RATE, EspSoftwareSerial::SWSERIAL_8N1, D7, D8, false, 95, 11); +#else + testSerial.begin(115200); + testSerial.setDebugOutput(false); + testSerial.swap(); + usbSerial.begin(BAUD_RATE, EspSoftwareSerial::SWSERIAL_8N1, RX, TX, false, 95); +#endif + + usbSerial.println(PSTR("\nSoftware serial test started")); + + for (char ch = ' '; ch <= 'z'; ch++) { + testSerial.write(ch); + } + testSerial.println(); +} + +void loop() { + while (testSerial.available() > 0) { + usbSerial.write(testSerial.read()); + yield(); + } + while (usbSerial.available() > 0) { + testSerial.write(usbSerial.read()); + yield(); + } +} diff --git a/src/EspSoftwareSerial/keywords.txt b/src/EspSoftwareSerial/keywords.txt new file mode 100644 index 0000000..df0426d --- /dev/null +++ b/src/EspSoftwareSerial/keywords.txt @@ -0,0 +1,43 @@ +####################################### +# Syntax Coloring Map for EspSoftwareSerial +# (esp8266) +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +EspSoftwareSerial KEYWORD1 +SoftwareSerial KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +baudRate KEYWORD2 +setTransmitEnablePin KEYWORD2 +enableIntTx KEYWORD2 +overflow KEYWORD2 +available KEYWORD2 +peek KEYWORD2 +read KEYWORD2 +flush KEYWORD2 +write KEYWORD2 +enableRx KEYWORD2 +enableTx KEYWORD2 +listen KEYWORD2 +end KEYWORD2 +isListening KEYWORD2 +stopListening KEYWORD2 +onReceive KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + +SW_SERIAL_UNUSED_PIN LITERAL1 +SWSERIAL_5N1 LITERAL1 +SWSERIAL_6N1 LITERAL1 +SWSERIAL_7N1 LITERAL1 +SWSERIAL_8N1 LITERAL1 diff --git a/src/EspSoftwareSerial/library.json b/src/EspSoftwareSerial/library.json new file mode 100644 index 0000000..ef549eb --- /dev/null +++ b/src/EspSoftwareSerial/library.json @@ -0,0 +1,26 @@ +{ + "name": "EspSoftwareSerial", + "version": "8.1.0", + "description": "Implementation of the Arduino software serial for ESP8266/ESP32.", + "keywords": [ + "serial", "io", "softwareserial" + ], + "repository": + { + "type": "git", + "url": "https://github.com/plerup/espsoftwareserial" + }, + "authors": [ + { + "name": "Dirk Kaar" + }, + { + "name": "Peter Lerup" + } + ], + "license": "LGPL-2.1+", + "frameworks": "arduino", + "platforms": [ + "espressif8266", "espressif32" + ] +} diff --git a/src/EspSoftwareSerial/library.properties b/src/EspSoftwareSerial/library.properties new file mode 100644 index 0000000..66ac389 --- /dev/null +++ b/src/EspSoftwareSerial/library.properties @@ -0,0 +1,9 @@ +name=EspSoftwareSerial +version=8.1.0 +author=Dirk Kaar, Peter Lerup +maintainer=Dirk Kaar +sentence=Implementation of the Arduino software serial for ESP8266/ESP32. +paragraph= +category=Signal Input/Output +url=https://github.com/plerup/espsoftwareserial/ +architectures=esp8266,esp32 diff --git a/src/EspSoftwareSerial/src/SoftwareSerial.cpp b/src/EspSoftwareSerial/src/SoftwareSerial.cpp new file mode 100644 index 0000000..61461d2 --- /dev/null +++ b/src/EspSoftwareSerial/src/SoftwareSerial.cpp @@ -0,0 +1,621 @@ +/* + +SoftwareSerial.cpp - Implementation of the Arduino software serial for ESP8266/ESP32. +Copyright (c) 2015-2016 Peter Lerup. All rights reserved. +Copyright (c) 2018-2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "SoftwareSerial.h" +#include + +using namespace EspSoftwareSerial; + +#ifndef ESP32 +uint32_t UARTBase::m_savedPS = 0; +#else +portMUX_TYPE UARTBase::m_interruptsMux = portMUX_INITIALIZER_UNLOCKED; +#endif + +ALWAYS_INLINE_ATTR inline void IRAM_ATTR UARTBase::disableInterrupts() +{ +#ifndef ESP32 + m_savedPS = xt_rsil(15); +#else + taskENTER_CRITICAL(&m_interruptsMux); +#endif +} + +ALWAYS_INLINE_ATTR inline void IRAM_ATTR UARTBase::restoreInterrupts() +{ +#ifndef ESP32 + xt_wsr_ps(m_savedPS); +#else + taskEXIT_CRITICAL(&m_interruptsMux); +#endif +} + +constexpr uint8_t BYTE_ALL_BITS_SET = ~static_cast(0); + +UARTBase::UARTBase() { +} + +UARTBase::UARTBase(int8_t rxPin, int8_t txPin, bool invert) +{ + m_rxPin = rxPin; + m_txPin = txPin; + m_invert = invert; +} + +UARTBase::~UARTBase() { + end(); +} + +void UARTBase::setRxGPIOPinMode() { + if (m_rxValid) { + pinMode(m_rxPin, m_rxGPIOHasPullUp && m_rxGPIOPullUpEnabled ? INPUT_PULLUP : INPUT); + } +} + +void UARTBase::setTxGPIOPinMode() { + if (m_txValid) { + pinMode(m_txPin, m_txGPIOOpenDrain ? OUTPUT_OPEN_DRAIN : OUTPUT); + } +} + +void UARTBase::begin(uint32_t baud, Config config, + int8_t rxPin, int8_t txPin, + bool invert) { + if (-1 != rxPin) m_rxPin = rxPin; + if (-1 != txPin) m_txPin = txPin; + m_oneWire = (m_rxPin == m_txPin); + m_invert = invert; + m_dataBits = 5 + (config & 07); + m_parityMode = static_cast(config & 070); + m_stopBits = 1 + ((config & 0300) ? 1 : 0); + m_pduBits = m_dataBits + static_cast(m_parityMode) + m_stopBits; + m_bitTicks = (microsToTicks(1000000UL) + baud / 2) / baud; + m_intTxEnabled = true; +} + +void UARTBase::beginRx(bool hasPullUp, int bufCapacity, int isrBufCapacity) { + m_rxGPIOHasPullUp = hasPullUp; + m_rxReg = portInputRegister(digitalPinToPort(m_rxPin)); + m_rxBitMask = digitalPinToBitMask(m_rxPin); + m_buffer.reset(new circular_queue((bufCapacity > 0) ? bufCapacity : 64)); + if (m_parityMode) + { + m_parityBuffer.reset(new circular_queue((m_buffer->capacity() + 7) / 8)); + m_parityInPos = m_parityOutPos = 1; + } + m_isrBuffer.reset(new circular_queue((isrBufCapacity > 0) ? + isrBufCapacity : m_buffer->capacity() * (2 + m_dataBits + static_cast(m_parityMode)))); + if (m_buffer && (!m_parityMode || m_parityBuffer) && m_isrBuffer) { + m_rxValid = true; + setRxGPIOPinMode(); + } +} + +void UARTBase::beginTx() { +#if !defined(ESP8266) + m_txReg = portOutputRegister(digitalPinToPort(m_txPin)); +#endif + m_txBitMask = digitalPinToBitMask(m_txPin); + m_txValid = true; + if (!m_oneWire) { + setTxGPIOPinMode(); + digitalWrite(m_txPin, !m_invert); + } +} + +void UARTBase::end() +{ + enableRx(false); + m_txValid = false; + if (m_buffer) { + m_buffer.reset(); + } + m_parityBuffer.reset(); + if (m_isrBuffer) { + m_isrBuffer.reset(); + } +} + +uint32_t UARTBase::baudRate() { + return 1000000UL / ticksToMicros(m_bitTicks); +} + +void UARTBase::setTransmitEnablePin(int8_t txEnablePin) { + if (-1 != txEnablePin) { + m_txEnableValid = true; + m_txEnablePin = txEnablePin; + pinMode(m_txEnablePin, OUTPUT); + digitalWrite(m_txEnablePin, LOW); + } + else { + m_txEnableValid = false; + } +} + +void UARTBase::enableIntTx(bool on) { + m_intTxEnabled = on; +} + +void UARTBase::enableRxGPIOPullUp(bool on) { + m_rxGPIOPullUpEnabled = on; + setRxGPIOPinMode(); +} + +void UARTBase::enableTxGPIOOpenDrain(bool on) { + m_txGPIOOpenDrain = on; + setTxGPIOPinMode(); +} + +void UARTBase::enableTx(bool on) { + if (m_txValid && m_oneWire) { + if (on) { + enableRx(false); + setTxGPIOPinMode(); + digitalWrite(m_txPin, !m_invert); + } + else { + setRxGPIOPinMode(); + enableRx(true); + } + } +} + +void UARTBase::enableRx(bool on) { + if (m_rxValid && on != m_rxEnabled) { + if (on) { + m_rxLastBit = m_pduBits - 1; + // Init to stop bit level and current tick + m_isrLastTick = (microsToTicks(micros()) | 1) ^ m_invert; + if (m_bitTicks >= microsToTicks(1000000UL / 74880UL)) + attachInterruptArg(digitalPinToInterrupt(m_rxPin), reinterpret_cast(rxBitISR), this, CHANGE); + else + attachInterruptArg(digitalPinToInterrupt(m_rxPin), reinterpret_cast(rxBitSyncISR), this, m_invert ? RISING : FALLING); + } + else { + detachInterrupt(digitalPinToInterrupt(m_rxPin)); + } + m_rxEnabled = on; + } +} + +int UARTBase::read() { + if (!m_rxValid) { return -1; } + if (!m_buffer->available()) { + rxBits(); + if (!m_buffer->available()) { return -1; } + } + auto val = m_buffer->pop(); + if (m_parityBuffer) + { + m_lastReadParity = m_parityBuffer->peek() & m_parityOutPos; + m_parityOutPos <<= 1; + if (!m_parityOutPos) + { + m_parityOutPos = 1; + m_parityBuffer->pop(); + } + } + return val; +} + +int UARTBase::read(uint8_t* buffer, size_t size) { + if (!m_rxValid) { return 0; } + int avail; + if (0 == (avail = m_buffer->pop_n(buffer, size))) { + rxBits(); + avail = m_buffer->pop_n(buffer, size); + } + if (!avail) return 0; + if (m_parityBuffer) { + uint32_t parityBits = avail; + while (m_parityOutPos >>= 1) ++parityBits; + m_parityOutPos = (1 << (parityBits % 8)); + m_parityBuffer->pop_n(nullptr, parityBits / 8); + } + return avail; +} + +size_t UARTBase::readBytes(uint8_t* buffer, size_t size) { + if (!m_rxValid || !size) { return 0; } + size_t count = 0; + auto start = millis(); + do { + auto readCnt = read(&buffer[count], size - count); + count += readCnt; + if (count >= size) break; + if (readCnt) { + start = millis(); + } + else { + optimistic_yield(1000UL); + } + } while (millis() - start < _timeout); + return count; +} + +int UARTBase::available() { + if (!m_rxValid) { return 0; } + rxBits(); + int avail = m_buffer->available(); + if (!avail) { + optimistic_yield(10000UL); + } + return avail; +} + +void UARTBase::lazyDelay() { + // Reenable interrupts while delaying to avoid other tasks piling up + if (!m_intTxEnabled) { restoreInterrupts(); } + const auto expired = microsToTicks(micros()) - m_periodStart; + const int32_t remaining = m_periodDuration - expired; + const uint32_t ms = remaining > 0 ? ticksToMicros(remaining) / 1000UL : 0; + if (ms > 0) + { + delay(ms); + } + else + { + optimistic_yield(10000UL); + } + // Assure that below-ms part of delays are not elided + preciseDelay(); + // Disable interrupts again if applicable + if (!m_intTxEnabled) { disableInterrupts(); } +} + +void IRAM_ATTR UARTBase::preciseDelay() { + uint32_t ticks; + do { + ticks = microsToTicks(micros()); + } while ((ticks - m_periodStart) < m_periodDuration); + m_periodDuration = 0; + m_periodStart = ticks; +} + +void IRAM_ATTR UARTBase::writePeriod( + uint32_t dutyCycle, uint32_t offCycle, bool withStopBit) { + preciseDelay(); + if (dutyCycle) + { +#if defined(ESP8266) + if (16 == m_txPin) { + GP16O = 1; + } + else { + GPOS = m_txBitMask; + } +#else + *m_txReg = *m_txReg | m_txBitMask; +#endif + m_periodDuration += dutyCycle; + if (offCycle || (withStopBit && !m_invert)) { + if (!withStopBit || m_invert) { + preciseDelay(); + } + else { + lazyDelay(); + } + } + } + if (offCycle) + { +#if defined(ESP8266) + if (16 == m_txPin) { + GP16O = 0; + } + else { + GPOC = m_txBitMask; + } +#else + *m_txReg = *m_txReg & ~m_txBitMask; +#endif + m_periodDuration += offCycle; + if (withStopBit && m_invert) lazyDelay(); + } +} + +size_t UARTBase::write(uint8_t byte) { + return write(&byte, 1); +} + +size_t UARTBase::write(uint8_t byte, Parity parity) { + return write(&byte, 1, parity); +} + +size_t UARTBase::write(const uint8_t* buffer, size_t size) { + return write(buffer, size, m_parityMode); +} + +size_t IRAM_ATTR UARTBase::write(const uint8_t* buffer, size_t size, Parity parity) { + if (m_rxValid) { rxBits(); } + if (!m_txValid) { return -1; } + + if (m_txEnableValid) { + digitalWrite(m_txEnablePin, HIGH); + } + // Stop bit: if inverted, LOW, otherwise HIGH + bool b = !m_invert; + uint32_t dutyCycle = 0; + uint32_t offCycle = 0; + if (!m_intTxEnabled) { + // Disable interrupts in order to get a clean transmit timing + disableInterrupts(); + } + const uint32_t dataMask = ((1UL << m_dataBits) - 1); + bool withStopBit = true; + m_periodDuration = 0; + m_periodStart = microsToTicks(micros()); + for (size_t cnt = 0; cnt < size; ++cnt) { + uint8_t byte = pgm_read_byte(buffer + cnt) & dataMask; + // push LSB start-data-parity-stop bit pattern into uint32_t + // Stop bits: HIGH + uint32_t word = ~0UL; + // inverted parity bit, performance tweak for xor all-bits-set word + if (parity && m_parityMode) + { + uint32_t parityBit; + switch (parity) + { + case PARITY_EVEN: + // from inverted, so use odd parity + parityBit = byte; + parityBit ^= parityBit >> 4; + parityBit &= 0xf; + parityBit = (0x9669 >> parityBit) & 1; + break; + case PARITY_ODD: + // from inverted, so use even parity + parityBit = byte; + parityBit ^= parityBit >> 4; + parityBit &= 0xf; + parityBit = (0x6996 >> parityBit) & 1; + break; + case PARITY_MARK: + parityBit = 0; + break; + case PARITY_SPACE: + // suppresses warning parityBit uninitialized + default: + parityBit = 1; + break; + } + word ^= parityBit; + } + word <<= m_dataBits; + word |= byte; + // Start bit: LOW + word <<= 1; + if (m_invert) word = ~word; + for (int i = 0; i <= m_pduBits; ++i) { + bool pb = b; + b = word & (1UL << i); + if (!pb && b) { + writePeriod(dutyCycle, offCycle, withStopBit); + withStopBit = false; + dutyCycle = offCycle = 0; + } + if (b) { + dutyCycle += m_bitTicks; + } + else { + offCycle += m_bitTicks; + } + } + withStopBit = true; + } + writePeriod(dutyCycle, offCycle, true); + if (!m_intTxEnabled) { + // restore the interrupt state if applicable + restoreInterrupts(); + } + if (m_txEnableValid) { + digitalWrite(m_txEnablePin, LOW); + } + return size; +} + +void UARTBase::flush() { + if (!m_rxValid) { return; } + m_buffer->flush(); + if (m_parityBuffer) + { + m_parityInPos = m_parityOutPos = 1; + m_parityBuffer->flush(); + } +} + +bool UARTBase::overflow() { + bool res = m_overflow; + m_overflow = false; + return res; +} + +int UARTBase::peek() { + if (!m_rxValid) { return -1; } + if (!m_buffer->available()) { + rxBits(); + if (!m_buffer->available()) return -1; + } + auto val = m_buffer->peek(); + if (m_parityBuffer) m_lastReadParity = m_parityBuffer->peek() & m_parityOutPos; + return val; +} + +void UARTBase::rxBits() { +#ifdef ESP8266 + if (m_isrOverflow.load()) { + m_overflow = true; + m_isrOverflow.store(false); + } +#else + if (m_isrOverflow.exchange(false)) { + m_overflow = true; + } +#endif + + m_isrBuffer->for_each(m_isrBufferForEachDel); + + // A stop bit can go undetected if leading data bits are at same level + // and there was also no next start bit yet, so one word may be pending. + // Check that there was no new ISR data received in the meantime, inserting an + // extraneous stop level bit out of sequence breaks rx. + if (m_rxLastBit < m_pduBits - 1) { + const uint32_t detectionTicks = (m_pduBits - 1 - m_rxLastBit) * m_bitTicks; + if (!m_isrBuffer->available() && microsToTicks(micros()) - m_isrLastTick > detectionTicks) { + // Produce faux stop bit level, prevents start bit maldetection + // tick's LSB is repurposed for the level bit + rxBits(((m_isrLastTick + detectionTicks) | 1) ^ m_invert); + } + } +} + +void UARTBase::rxBits(const uint32_t isrTick) { + const bool level = (m_isrLastTick & 1) ^ m_invert; + + // error introduced by edge value in LSB of isrTick is negligible + uint32_t ticks = isrTick - m_isrLastTick; + m_isrLastTick = isrTick; + + uint32_t bits = ticks / m_bitTicks; + if (ticks % m_bitTicks > (m_bitTicks >> 1)) ++bits; + while (bits > 0) { + // start bit detection + if (m_rxLastBit >= (m_pduBits - 1)) { + // leading edge of start bit? + if (level) break; + m_rxLastBit = -1; + --bits; + continue; + } + // data bits + if (m_rxLastBit < (m_dataBits - 1)) { + uint8_t dataBits = min(bits, static_cast(m_dataBits - 1 - m_rxLastBit)); + m_rxLastBit += dataBits; + bits -= dataBits; + m_rxCurByte >>= dataBits; + if (level) { m_rxCurByte |= (BYTE_ALL_BITS_SET << (8 - dataBits)); } + continue; + } + // parity bit + if (m_parityMode && m_rxLastBit == (m_dataBits - 1)) { + ++m_rxLastBit; + --bits; + m_rxCurParity = level; + continue; + } + // stop bits + // Store the received value in the buffer unless we have an overflow + // if not high stop bit level, discard word + if (bits >= static_cast(m_pduBits - 1 - m_rxLastBit) && level) { + m_rxCurByte >>= (sizeof(uint8_t) * 8 - m_dataBits); + if (!m_buffer->push(m_rxCurByte)) { + m_overflow = true; + } + else { + if (m_parityBuffer) + { + if (m_rxCurParity) { + m_parityBuffer->pushpeek() |= m_parityInPos; + } + else { + m_parityBuffer->pushpeek() &= ~m_parityInPos; + } + m_parityInPos <<= 1; + if (!m_parityInPos) + { + m_parityBuffer->push(); + m_parityInPos = 1; + } + } + } + } + m_rxLastBit = m_pduBits - 1; + // reset to 0 is important for masked bit logic + m_rxCurByte = 0; + m_rxCurParity = false; + break; + } +} + +void IRAM_ATTR UARTBase::rxBitISR(UARTBase* self) { + const bool level = *self->m_rxReg & self->m_rxBitMask; + const uint32_t curTick = microsToTicks(micros()); + const bool empty = !self->m_isrBuffer->available(); + + // Store level and tick in the buffer unless we have an overflow + // tick's LSB is repurposed for the level bit + if (!self->m_isrBuffer->push((curTick | 1U) ^ !level)) self->m_isrOverflow.store(true); + // Trigger rx callback only when receiver is starved + if (empty) self->m_rxHandler(); +} + +void IRAM_ATTR UARTBase::rxBitSyncISR(UARTBase* self) { + bool level = self->m_invert; + const uint32_t start = microsToTicks(micros()); + uint32_t wait = self->m_bitTicks; + const bool empty = !self->m_isrBuffer->available(); + + // Store level and tick in the buffer unless we have an overflow + // tick's LSB is repurposed for the level bit + if (!self->m_isrBuffer->push(((start + wait) | 1U) ^ !level)) self->m_isrOverflow.store(true); + + for (uint32_t i = 0; i < self->m_pduBits; ++i) { + while (microsToTicks(micros()) - start < wait) {}; + wait += self->m_bitTicks; + + // Store level and tick in the buffer unless we have an overflow + // tick's LSB is repurposed for the level bit + if (static_cast(*self->m_rxReg & self->m_rxBitMask) != level) + { + if (!self->m_isrBuffer->push(((start + wait) | 1U) ^ level)) self->m_isrOverflow.store(true); + level = !level; + } + } + // Trigger rx callback only when receiver is starved + if (empty) self->m_rxHandler(); +} + +void UARTBase::onReceive(const Delegate& handler) { + disableInterrupts(); + m_rxHandler = handler; + restoreInterrupts(); +} + +void UARTBase::onReceive(Delegate&& handler) { + disableInterrupts(); + m_rxHandler = std::move(handler); + restoreInterrupts(); +} + +#if __GNUC__ < 12 +// The template member functions below must be in IRAM, but due to a bug GCC doesn't currently +// honor the attribute. Instead, it is possible to do explicit specialization and adorn +// these with the IRAM attribute: +// Delegate<>::operator (), circular_queue<>::available, +// circular_queue<>::available_for_push, circular_queue<>::push_peek, circular_queue<>::push + +template void IRAM_ATTR delegate::detail::DelegateImpl::operator()() const; +template size_t IRAM_ATTR circular_queue::available() const; +template bool IRAM_ATTR circular_queue::push(uint32_t&&); +template bool IRAM_ATTR circular_queue::push(const uint32_t&); +#endif // __GNUC__ < 12 + diff --git a/src/EspSoftwareSerial/src/SoftwareSerial.h b/src/EspSoftwareSerial/src/SoftwareSerial.h new file mode 100644 index 0000000..13a11b1 --- /dev/null +++ b/src/EspSoftwareSerial/src/SoftwareSerial.h @@ -0,0 +1,449 @@ +/* +SoftwareSerial.h - Implementation of the Arduino software serial for ESP8266/ESP32. +Copyright (c) 2015-2016 Peter Lerup. All rights reserved. +Copyright (c) 2018-2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef __SoftwareSerial_h +#define __SoftwareSerial_h + +#include "circular_queue/circular_queue.h" +#include + +namespace EspSoftwareSerial { + +// Interface definition for template argument of BasicUART +class IGpioCapabilities { +public: + static constexpr bool isValidPin(int8_t pin); + static constexpr bool isValidInputPin(int8_t pin); + static constexpr bool isValidOutputPin(int8_t pin); + // result is only defined for a valid Rx pin + static constexpr bool hasPullUp(int8_t pin); +}; + +class GpioCapabilities : private IGpioCapabilities { +public: + static constexpr bool isValidPin(int8_t pin) { + #if defined(ESP8266) + return (pin >= 0 && pin <= 16) && !isFlashInterfacePin(pin); + #elif defined(ESP32) + // Remove the strapping pins as defined in the datasheets, they affect bootup and other critical operations + // Remmove the flash memory pins on related devices, since using these causes memory access issues. + #ifdef CONFIG_IDF_TARGET_ESP32 + // Datasheet https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf, + // Pinout https://docs.espressif.com/projects/esp-idf/en/latest/esp32/_images/esp32-devkitC-v4-pinout.jpg + return (pin == 1) || (pin >= 3 && pin <= 5) || + (pin >= 12 && pin <= 15) || + (!psramFound() && pin >= 16 && pin <= 17) || + (pin >= 18 && pin <= 19) || + (pin >= 21 && pin <= 23) || (pin >= 25 && pin <= 27) || (pin >= 32 && pin <= 39); + #elif CONFIG_IDF_TARGET_ESP32S2 + // Datasheet https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf, + // Pinout https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/_images/esp32-s2_saola1-pinout.jpg + return (pin >= 1 && pin <= 21) || (pin >= 33 && pin <= 44); + #elif CONFIG_IDF_TARGET_ESP32C3 + // Datasheet https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf, + // Pinout https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/_images/esp32-c3-devkitm-1-v1-pinout.jpg + return (pin >= 0 && pin <= 1) || (pin >= 3 && pin <= 7) || (pin >= 18 && pin <= 21); + #else + return pin >= 0; + #endif + #else + return pin >= 0; + #endif + } + + static constexpr bool isValidInputPin(int8_t pin) { + return isValidPin(pin) + #if defined(ESP8266) + && (pin != 16) + #endif + ; + } + + static constexpr bool isValidOutputPin(int8_t pin) { + return isValidPin(pin) + #if defined(ESP32) + #ifdef CONFIG_IDF_TARGET_ESP32 + && (pin < 34) + #elif CONFIG_IDF_TARGET_ESP32S2 + && (pin <= 45) + #elif CONFIG_IDF_TARGET_ESP32C3 + // no restrictions + #endif + #endif + ; + } + + // result is only defined for a valid Rx pin + static constexpr bool hasPullUp(int8_t pin) { + #if defined(ESP32) + return !(pin >= 34 && pin <= 39); + #else + (void)pin; + return true; + #endif + } +}; + +enum Parity : uint8_t { + PARITY_NONE = 000, + PARITY_EVEN = 020, + PARITY_ODD = 030, + PARITY_MARK = 040, + PARITY_SPACE = 070, +}; + +enum Config { + SWSERIAL_5N1 = PARITY_NONE, + SWSERIAL_6N1, + SWSERIAL_7N1, + SWSERIAL_8N1, + SWSERIAL_5E1 = PARITY_EVEN, + SWSERIAL_6E1, + SWSERIAL_7E1, + SWSERIAL_8E1, + SWSERIAL_5O1 = PARITY_ODD, + SWSERIAL_6O1, + SWSERIAL_7O1, + SWSERIAL_8O1, + SWSERIAL_5M1 = PARITY_MARK, + SWSERIAL_6M1, + SWSERIAL_7M1, + SWSERIAL_8M1, + SWSERIAL_5S1 = PARITY_SPACE, + SWSERIAL_6S1, + SWSERIAL_7S1, + SWSERIAL_8S1, + SWSERIAL_5N2 = 0200 | PARITY_NONE, + SWSERIAL_6N2, + SWSERIAL_7N2, + SWSERIAL_8N2, + SWSERIAL_5E2 = 0200 | PARITY_EVEN, + SWSERIAL_6E2, + SWSERIAL_7E2, + SWSERIAL_8E2, + SWSERIAL_5O2 = 0200 | PARITY_ODD, + SWSERIAL_6O2, + SWSERIAL_7O2, + SWSERIAL_8O2, + SWSERIAL_5M2 = 0200 | PARITY_MARK, + SWSERIAL_6M2, + SWSERIAL_7M2, + SWSERIAL_8M2, + SWSERIAL_5S2 = 0200 | PARITY_SPACE, + SWSERIAL_6S2, + SWSERIAL_7S2, + SWSERIAL_8S2, +}; + +/// This class is compatible with the corresponding AVR one, however, +/// the constructor takes no arguments, for compatibility with the +/// HardwareSerial class. +/// Instead, the begin() function handles pin assignments and logic inversion. +/// It also has optional input buffer capacity arguments for byte buffer and ISR bit buffer. +/// Bitrates up to at least 115200 can be used. +class UARTBase : public Stream { +public: + UARTBase(); + /// Ctor to set defaults for pins. + /// @param rxPin the GPIO pin used for RX + /// @param txPin -1 for onewire protocol, GPIO pin used for twowire TX + UARTBase(int8_t rxPin, int8_t txPin = -1, bool invert = false); + UARTBase(const UARTBase&) = delete; + UARTBase& operator= (const UARTBase&) = delete; + virtual ~UARTBase(); + /// Configure the UARTBase object for use. + /// @param baud the TX/RX bitrate + /// @param config sets databits, parity, and stop bit count + /// @param rxPin -1 or default: either no RX pin, or keeps the rxPin set in the ctor + /// @param txPin -1 or default: either no TX pin (onewire), or keeps the txPin set in the ctor + /// @param invert true: uses invert line level logic + /// @param bufCapacity the capacity for the received bytes buffer + /// @param isrBufCapacity 0: derived from bufCapacity. The capacity of the internal asynchronous + /// bit receive buffer, a suggested size is bufCapacity times the sum of + /// start, data, parity and stop bit count. + void begin(uint32_t baud, Config config, + int8_t rxPin, int8_t txPin, bool invert); + + uint32_t baudRate(); + /// Transmit control pin. + void setTransmitEnablePin(int8_t txEnablePin); + /// Enable (default) or disable interrupts during tx. + void enableIntTx(bool on); + /// Enable (default) or disable internal rx GPIO pull-up. + void enableRxGPIOPullUp(bool on); + /// Enable or disable (default) tx GPIO output mode. + void enableTxGPIOOpenDrain(bool on); + + bool overflow(); + + int available() override; +#if defined(ESP8266) + int availableForWrite() override { +#else + int availableForWrite() { +#endif + if (!m_txValid) return 0; + return 1; + } + int peek() override; + int read() override; + /// @returns The verbatim parity bit associated with the last successful read() or peek() call + bool readParity() + { + return m_lastReadParity; + } + /// @returns The calculated bit for even parity of the parameter byte + static bool parityEven(uint8_t byte) { + byte ^= byte >> 4; + byte &= 0xf; + return (0x6996 >> byte) & 1; + } + /// @returns The calculated bit for odd parity of the parameter byte + static bool parityOdd(uint8_t byte) { + byte ^= byte >> 4; + byte &= 0xf; + return (0x9669 >> byte) & 1; + } + /// The read(buffer, size) functions are non-blocking, the same as readBytes but without timeout + int read(uint8_t* buffer, size_t size) +#if defined(ESP8266) + override +#endif + ; + /// The read(buffer, size) functions are non-blocking, the same as readBytes but without timeout + int read(char* buffer, size_t size) { + return read(reinterpret_cast(buffer), size); + } + /// @returns The number of bytes read into buffer, up to size. Times out if the limit set through + /// Stream::setTimeout() is reached. + size_t readBytes(uint8_t* buffer, size_t size) override; + /// @returns The number of bytes read into buffer, up to size. Times out if the limit set through + /// Stream::setTimeout() is reached. + size_t readBytes(char* buffer, size_t size) override { + return readBytes(reinterpret_cast(buffer), size); + } + void flush() override; + size_t write(uint8_t byte) override; + size_t write(uint8_t byte, Parity parity); + size_t write(const uint8_t* buffer, size_t size) override; + size_t write(const char* buffer, size_t size) { + return write(reinterpret_cast(buffer), size); + } + size_t write(const uint8_t* buffer, size_t size, Parity parity); + size_t write(const char* buffer, size_t size, Parity parity) { + return write(reinterpret_cast(buffer), size, parity); + } + operator bool() const { + return (-1 == m_rxPin || m_rxValid) && (-1 == m_txPin || m_txValid) && !(-1 == m_rxPin && m_oneWire); + } + + /// Disable or enable interrupts on the rx pin. + void enableRx(bool on); + /// One wire control. + void enableTx(bool on); + + // AVR compatibility methods. + bool listen() { enableRx(true); return true; } + void end(); + bool isListening() { return m_rxEnabled; } + bool stopListening() { enableRx(false); return true; } + + /// onReceive sets a callback that will be called in interrupt context + /// when data is received. + /// More precisely, the callback is triggered when UARTBase detects + /// a new reception, which may not yet have completed on invocation. + /// Reading - never from this interrupt context - should therefore be + /// delayed at least for the duration of one incoming word. + void onReceive(const Delegate& handler); + /// onReceive sets a callback that will be called in interrupt context + /// when data is received. + /// More precisely, the callback is triggered when UARTBase detects + /// a new reception, which may not yet have completed on invocation. + /// Reading - never from this interrupt context - should therefore be + /// delayed at least for the duration of one incoming word. + void onReceive(Delegate&& handler); + + [[deprecated("function removed; semantics of onReceive() changed; check the header file.")]] + void perform_work(); + + using Print::write; + +protected: + void beginRx(bool hasPullUp, int bufCapacity, int isrBufCapacity); + void beginTx(); + // Member variables + int8_t m_rxPin = -1; + int8_t m_txPin = -1; + bool m_invert = false; + +private: + // It's legal to exceed the deadline, for instance, + // by enabling interrupts. + void lazyDelay(); + // Synchronous precise delay + void preciseDelay(); + // If withStopBit is set, either cycle contains a stop bit. + // If dutyCycle == 0, the level is not forced to HIGH. + // If offCycle == 0, the level remains unchanged from dutyCycle. + void writePeriod( + uint32_t dutyCycle, uint32_t offCycle, bool withStopBit); + // safely set the pin mode for the Rx GPIO pin + void setRxGPIOPinMode(); + // safely set the pin mode for the Tx GPIO pin + void setTxGPIOPinMode(); + /* check m_rxValid that calling is safe */ + void rxBits(); + void rxBits(const uint32_t isrTick); + static void disableInterrupts(); + static void restoreInterrupts(); + + static void rxBitISR(UARTBase* self); + static void rxBitSyncISR(UARTBase* self); + + static inline uint32_t IRAM_ATTR microsToTicks(uint32_t micros) ALWAYS_INLINE_ATTR { + return micros << 1; + } + static inline uint32_t ticksToMicros(uint32_t ticks) ALWAYS_INLINE_ATTR { + return ticks >> 1; + } + + // Member variables + volatile uint32_t* m_rxReg; + uint32_t m_rxBitMask; +#if !defined(ESP8266) + volatile uint32_t* m_txReg; +#endif + uint32_t m_txBitMask; + int8_t m_txEnablePin = -1; + uint8_t m_dataBits; + bool m_oneWire; + bool m_rxValid = false; + bool m_rxEnabled = false; + bool m_txValid = false; + bool m_txEnableValid = false; + /// PDU bits include data, parity and stop bits; the start bit is not counted. + uint8_t m_pduBits; + bool m_intTxEnabled; + bool m_rxGPIOHasPullUp = false; + bool m_rxGPIOPullUpEnabled = true; + bool m_txGPIOOpenDrain = false; + Parity m_parityMode; + uint8_t m_stopBits; + bool m_lastReadParity; + bool m_overflow = false; + uint32_t m_bitTicks; + uint8_t m_parityInPos; + uint8_t m_parityOutPos; + int8_t m_rxLastBit; // 0 thru (m_pduBits - m_stopBits - 1): data/parity bits. -1: start bit. (m_pduBits - 1): stop bit. + uint8_t m_rxCurByte = 0; + std::unique_ptr > m_buffer; + std::unique_ptr > m_parityBuffer; + uint32_t m_periodStart; + uint32_t m_periodDuration; +#ifndef ESP32 + static uint32_t m_savedPS; +#else + static portMUX_TYPE m_interruptsMux; +#endif + // the ISR stores the relative bit times in the buffer. The inversion corrected level is used as sign bit (2's complement): + // 1 = positive including 0, 0 = negative. + std::unique_ptr > m_isrBuffer; + const Delegate m_isrBufferForEachDel { [](UARTBase* self, uint32_t&& isrTick) { self->rxBits(isrTick); }, this }; + std::atomic m_isrOverflow { false }; + uint32_t m_isrLastTick; + bool m_rxCurParity = false; + Delegate m_rxHandler; +}; + +template< class GpioCapabilities > class BasicUART : public UARTBase { + static_assert(std::is_base_of::value, + "template argument is not derived from IGpioCapabilities"); +public: + BasicUART() : UARTBase() { + } + /// Ctor to set defaults for pins. + /// @param rxPin the GPIO pin used for RX + /// @param txPin -1 for onewire protocol, GPIO pin used for twowire TX + BasicUART(int8_t rxPin, int8_t txPin = -1, bool invert = false) : + UARTBase(rxPin, txPin, invert) { + } + + /// Configure the BasicUART object for use. + /// @param baud the TX/RX bitrate + /// @param config sets databits, parity, and stop bit count + /// @param rxPin -1 or default: either no RX pin, or keeps the rxPin set in the ctor + /// @param txPin -1 or default: either no TX pin (onewire), or keeps the txPin set in the ctor + /// @param invert true: uses invert line level logic + /// @param bufCapacity the capacity for the received bytes buffer + /// @param isrBufCapacity 0: derived from bufCapacity. The capacity of the internal asynchronous + /// bit receive buffer, a suggested size is bufCapacity times the sum of + /// start, data, parity and stop bit count. + void begin(uint32_t baud, Config config, + int8_t rxPin, int8_t txPin, bool invert, + int bufCapacity = 64, int isrBufCapacity = 0) { + UARTBase::begin(baud, config, rxPin, txPin, invert); + if (GpioCapabilities::isValidInputPin(rxPin)) { + beginRx(GpioCapabilities:: hasPullUp(rxPin), bufCapacity, isrBufCapacity); + } + if (GpioCapabilities::isValidOutputPin(txPin)) { + beginTx(); + } + enableRx(true); + } + void begin(uint32_t baud, Config config, + int8_t rxPin, int8_t txPin) { + begin(baud, config, rxPin, txPin, m_invert); + } + void begin(uint32_t baud, Config config, + int8_t rxPin) { + begin(baud, config, rxPin, m_txPin, m_invert); + } + void begin(uint32_t baud, Config config = SWSERIAL_8N1) { + begin(baud, config, m_rxPin, m_txPin, m_invert); + } + void setTransmitEnablePin(int8_t txEnablePin) { + UARTBase::setTransmitEnablePin( + GpioCapabilities::isValidOutputPin(txEnablePin) ? txEnablePin : -1); + } +}; + +using UART = BasicUART< GpioCapabilities >; + +}; // namespace EspSoftwareSerial + +using SoftwareSerial = EspSoftwareSerial::UART; +using namespace EspSoftwareSerial; + +#if __GNUC__ < 12 +// The template member functions below must be in IRAM, but due to a bug GCC doesn't currently +// honor the attribute. Instead, it is possible to do explicit specialization and adorn +// these with the IRAM attribute: +// Delegate<>::operator (), circular_queue<>::available, +// circular_queue<>::available_for_push, circular_queue<>::push_peek, circular_queue<>::push + +extern template void delegate::detail::DelegateImpl::operator()() const; +extern template size_t circular_queue::available() const; +extern template bool circular_queue::push(uint32_t&&); +extern template bool circular_queue::push(const uint32_t&); +#endif // __GNUC__ < 12 + +#endif // __SoftwareSerial_h + diff --git a/src/EspSoftwareSerial/src/circular_queue/Delegate.h b/src/EspSoftwareSerial/src/circular_queue/Delegate.h new file mode 100644 index 0000000..6bede69 --- /dev/null +++ b/src/EspSoftwareSerial/src/circular_queue/Delegate.h @@ -0,0 +1,2209 @@ +/* +Delegate.h - An efficient interchangeable C function ptr and C++ std::function delegate +Copyright (c) 2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __Delegate_h +#define __Delegate_h + +#if defined(ESP8266) +#include +#elif defined(ESP32) +#include +#else +#define IRAM_ATTR +#endif + +#if defined(__GNUC__) +#undef ALWAYS_INLINE_ATTR +#define ALWAYS_INLINE_ATTR __attribute__((always_inline)) +#else +#define ALWAYS_INLINE_ATTR +#endif + +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) +#include +#include +#else +#include "circular_queue/ghostl.h" +#endif + +namespace +{ + + template + ALWAYS_INLINE_ATTR inline R IRAM_ATTR vPtrToFunPtrExec(void* fn, P... args) + { + using target_type = R(P...); + return reinterpret_cast(fn)(std::forward(args...)); + } + +} + +namespace delegate +{ + namespace detail + { + +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + template + class DelegatePImpl { + public: + using target_type = R(P...); + protected: + using FunPtr = target_type*; + using FunAPtr = R(*)(A, P...); + using FunVPPtr = R(*)(void*, P...); + using FunctionType = std::function; + public: + DelegatePImpl() + { + kind = FP; + fn = nullptr; + } + + DelegatePImpl(std::nullptr_t) + { + kind = FP; + fn = nullptr; + } + + ~DelegatePImpl() + { + if (FUNC == kind) + functional.~FunctionType(); + else if (FPA == kind) + obj.~A(); + } + + DelegatePImpl(const DelegatePImpl& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(del.functional); + } + else if (FPA == del.kind) + { + fnA = del.fnA; + new (&obj) A(del.obj); + } + else + { + fn = del.fn; + } + } + + DelegatePImpl(DelegatePImpl&& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(std::move(del.functional)); + } + else if (FPA == del.kind) + { + fnA = del.fnA; + new (&obj) A(std::move(del.obj)); + } + else + { + fn = del.fn; + } + } + + DelegatePImpl(FunAPtr fnA, const A& obj) + { + kind = FPA; + DelegatePImpl::fnA = fnA; + new (&this->obj) A(obj); + } + + DelegatePImpl(FunAPtr fnA, A&& obj) + { + kind = FPA; + DelegatePImpl::fnA = fnA; + new (&this->obj) A(std::move(obj)); + } + + DelegatePImpl(FunPtr fn) + { + kind = FP; + DelegatePImpl::fn = fn; + } + + template DelegatePImpl(F functional) + { + kind = FUNC; + new (&this->functional) FunctionType(std::forward(functional)); + } + + DelegatePImpl& operator=(const DelegatePImpl& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + if (FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + else if (FPA == del.kind) + { + new (&obj) A; + } + kind = del.kind; + } + if (FUNC == del.kind) + { + functional = del.functional; + } + else if (FPA == del.kind) + { + fnA = del.fnA; + obj = del.obj; + } + else + { + fn = del.fn; + } + return *this; + } + + DelegatePImpl& operator=(DelegatePImpl&& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + if (FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + else if (FPA == del.kind) + { + new (&obj) A; + } + kind = del.kind; + } + if (FUNC == del.kind) + { + functional = std::move(del.functional); + } + else if (FPA == del.kind) + { + fnA = del.fnA; + obj = std::move(del.obj); + } + else + { + fn = del.fn; + } + return *this; + } + + DelegatePImpl& operator=(FunPtr fn) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + kind = FP; + this->fn = fn; + return *this; + } + + DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + kind = FP; + fn = nullptr; + return *this; + } + + IRAM_ATTR operator bool() const + { + if (FP == kind) + { + return fn; + } + else if (FPA == kind) + { + return fnA; + } + else + { + return functional ? true : false; + } + } + + static inline R IRAM_ATTR vPtrToFunAPtrExec(void* self, P... args) ALWAYS_INLINE_ATTR + { + return static_cast(self)->fnA( + static_cast(self)->obj, + std::forward(args...)); + }; + + operator FunVPPtr() const + { + if (FP == kind) + { + return vPtrToFunPtrExec; + } + else if (FPA == kind) + { + return vPtrToFunAPtrExec; + } + else + { + return [](void* self, P... args) -> R + { + return static_cast(self)->functional(std::forward(args...)); + }; + } + } + + void* arg() const + { + if (FP == kind) + { + return reinterpret_cast(fn); + } + else + { + return const_cast(this); + } + } + + operator FunctionType() const + { + if (FP == kind) + { + return fn; + } + else if (FPA == kind) + { + return [this](P... args) { return fnA(obj, std::forward(args...)); }; + } + else + { + return functional; + } + } + + /// Calling is safe without checking for nullptr. + /// If non-void, returns the default value. + /// In ISR context, where faults and exceptions must not + /// occurs, this saves the extra check for nullptr, + /// and allows the compiler to optimize out checks + /// in std::function which may not be ISR-safe or + /// cause linker errors, like l32r relocation errors + /// on the Xtensa ISA. + R IRAM_ATTR operator()(P... args) const + { + if (FP == kind) + { + if (fn) return fn(std::forward(args...)); + } + else if (FPA == kind) + { + if (fnA) return fnA(obj, std::forward(args...)); + } + else + { + if (functional) return functional(std::forward(args...)); + } + return R(); + } + + protected: + union { + FunctionType functional; + FunPtr fn; + struct { + FunAPtr fnA; + A obj; + }; + }; + enum { FUNC, FP, FPA } kind; + }; +#else + template + class DelegatePImpl { + public: + using target_type = R(P...); + protected: + using FunPtr = target_type*; + using FunAPtr = R(*)(A, P...); + using FunVPPtr = R(*)(void*, P...); + public: + DelegatePImpl() + { + kind = FP; + fn = nullptr; + } + + DelegatePImpl(std::nullptr_t) + { + kind = FP; + fn = nullptr; + } + + DelegatePImpl(const DelegatePImpl& del) + { + kind = del.kind; + if (FPA == del.kind) + { + fnA = del.fnA; + obj = del.obj; + } + else + { + fn = del.fn; + } + } + + DelegatePImpl(DelegatePImpl&& del) + { + kind = del.kind; + if (FPA == del.kind) + { + fnA = del.fnA; + obj = std::move(del.obj); + } + else + { + fn = del.fn; + } + } + + DelegatePImpl(FunAPtr fnA, const A& obj) + { + kind = FPA; + DelegatePImpl::fnA = fnA; + this->obj = obj; + } + + DelegatePImpl(FunAPtr fnA, A&& obj) + { + kind = FPA; + DelegatePImpl::fnA = fnA; + this->obj = std::move(obj); + } + + DelegatePImpl(FunPtr fn) + { + kind = FP; + DelegatePImpl::fn = fn; + } + + template DelegatePImpl(F functional) + { + kind = FP; + fn = std::forward(functional); + } + + DelegatePImpl& operator=(const DelegatePImpl& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FPA == kind) + { + obj = {}; + } + kind = del.kind; + } + if (FPA == del.kind) + { + fnA = del.fnA; + obj = del.obj; + } + else + { + fn = del.fn; + } + return *this; + } + + DelegatePImpl& operator=(DelegatePImpl&& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FPA == kind) + { + obj = {}; + } + kind = del.kind; + } + if (FPA == del.kind) + { + fnA = del.fnA; + obj = std::move(del.obj); + } + else + { + fn = del.fn; + } + return *this; + } + + DelegatePImpl& operator=(FunPtr fn) + { + if (FPA == kind) + { + obj = {}; + } + kind = FP; + this->fn = fn; + return *this; + } + + DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) + { + if (FPA == kind) + { + obj = {}; + } + kind = FP; + fn = nullptr; + return *this; + } + + IRAM_ATTR operator bool() const + { + if (FP == kind) + { + return fn; + } + else + { + return fnA; + } + } + + static inline R IRAM_ATTR vPtrToFunAPtrExec(void* self, P... args) ALWAYS_INLINE_ATTR + { + return static_cast(self)->fnA( + static_cast(self)->obj, + std::forward(args...)); + }; + + operator FunVPPtr() const + { + if (FP == kind) + { + return vPtrToFunPtrExec; + } + else + { + return vPtrToFunAPtrExec; + } + } + + void* arg() const + { + if (FP == kind) + { + return reinterpret_cast(fn); + } + else + { + return const_cast(this); + } + } + + /// Calling is safe without checking for nullptr. + /// If non-void, returns the default value. + /// In ISR context, where faults and exceptions must not + /// occurs, this saves the extra check for nullptr, + /// and allows the compiler to optimize out checks + /// in std::function which may not be ISR-safe or + /// cause linker errors, like l32r relocation errors + /// on the Xtensa ISA. + R IRAM_ATTR operator()(P... args) const + { + if (FP == kind) + { + if (fn) return fn(std::forward(args...)); + } + else + { + if (fnA) return fnA(obj, std::forward(args...)); + } + return R(); + } + + protected: + union { + FunPtr fn; + FunAPtr fnA; + }; + A obj; + enum { FP, FPA } kind; + }; +#endif + +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + template + class DelegatePImpl { + public: + using target_type = R(P...); + protected: + using FunPtr = target_type*; + using FunctionType = std::function; + using FunVPPtr = R(*)(void*, P...); + public: + DelegatePImpl() + { + kind = FP; + fn = nullptr; + } + + DelegatePImpl(std::nullptr_t) + { + kind = FP; + fn = nullptr; + } + + ~DelegatePImpl() + { + if (FUNC == kind) + functional.~FunctionType(); + } + + DelegatePImpl(const DelegatePImpl& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(del.functional); + } + else + { + fn = del.fn; + } + } + + DelegatePImpl(DelegatePImpl&& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(std::move(del.functional)); + } + else + { + fn = del.fn; + } + } + + DelegatePImpl(FunPtr fn) + { + kind = FP; + DelegatePImpl::fn = fn; + } + + template DelegatePImpl(F functional) + { + kind = FUNC; + new (&this->functional) FunctionType(std::forward(functional)); + } + + DelegatePImpl& operator=(const DelegatePImpl& del) + { + if (this == &del) return *this; + if (FUNC == kind && FUNC != del.kind) + { + functional.~FunctionType(); + } + else if (FUNC != kind && FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + kind = del.kind; + if (FUNC == del.kind) + { + functional = del.functional; + } + else + { + fn = del.fn; + } + return *this; + } + + DelegatePImpl& operator=(DelegatePImpl&& del) + { + if (this == &del) return *this; + if (FUNC == kind && FUNC != del.kind) + { + functional.~FunctionType(); + } + else if (FUNC != kind && FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + kind = del.kind; + if (FUNC == del.kind) + { + functional = std::move(del.functional); + } + else + { + fn = del.fn; + } + return *this; + } + + DelegatePImpl& operator=(FunPtr fn) + { + if (FUNC == kind) + { + functional.~FunctionType(); + kind = FP; + } + DelegatePImpl::fn = fn; + return *this; + } + + DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + kind = FP; + fn = nullptr; + return *this; + } + + IRAM_ATTR operator bool() const + { + if (FP == kind) + { + return fn; + } + else + { + return functional ? true : false; + } + } + + operator FunVPPtr() const + { + if (FP == kind) + { + return vPtrToFunPtrExec; + } + else + { + return [](void* self, P... args) -> R + { + return static_cast(self)->functional(std::forward(args...)); + }; + } + } + + void* arg() const + { + if (FP == kind) + { + return reinterpret_cast(fn); + } + else + { + return const_cast(this); + } + } + + operator FunctionType() const + { + if (FP == kind) + { + return fn; + } + else + { + return functional; + } + } + + /// Calling is safe without checking for nullptr. + /// If non-void, returns the default value. + /// In ISR context, where faults and exceptions must not + /// occurs, this saves the extra check for nullptr, + /// and allows the compiler to optimize out checks + /// in std::function which may not be ISR-safe or + /// cause linker errors, like l32r relocation errors + /// on the Xtensa ISA. + R IRAM_ATTR operator()(P... args) const + { + if (FP == kind) + { + if (fn) return fn(std::forward(args...)); + } + else + { + if (functional) return functional(std::forward(args...)); + } + return R(); + } + + protected: + union { + FunctionType functional; + FunPtr fn; + }; + enum { FUNC, FP } kind; + }; +#else + template + class DelegatePImpl { + public: + using target_type = R(P...); + protected: + using FunPtr = target_type*; + using FunVPPtr = R(*)(void*, P...); + public: + DelegatePImpl() + { + fn = nullptr; + } + + DelegatePImpl(std::nullptr_t) + { + fn = nullptr; + } + + DelegatePImpl(const DelegatePImpl& del) + { + fn = del.fn; + } + + DelegatePImpl(DelegatePImpl&& del) + { + fn = std::move(del.fn); + } + + DelegatePImpl(FunPtr fn) + { + DelegatePImpl::fn = fn; + } + + template DelegatePImpl(F fn) + { + DelegatePImpl::fn = std::forward(fn); + } + + DelegatePImpl& operator=(const DelegatePImpl& del) + { + if (this == &del) return *this; + fn = del.fn; + return *this; + } + + DelegatePImpl& operator=(DelegatePImpl&& del) + { + if (this == &del) return *this; + fn = std::move(del.fn); + return *this; + } + + DelegatePImpl& operator=(FunPtr fn) + { + DelegatePImpl::fn = fn; + return *this; + } + + inline DelegatePImpl& IRAM_ATTR operator=(std::nullptr_t) ALWAYS_INLINE_ATTR + { + fn = nullptr; + return *this; + } + + inline IRAM_ATTR operator bool() const ALWAYS_INLINE_ATTR + { + return fn; + } + + operator FunVPPtr() const + { + return vPtrToFunPtrExec; + } + + void* arg() const + { + return reinterpret_cast(fn); + } + + /// Calling is safe without checking for nullptr. + /// If non-void, returns the default value. + /// In ISR context, where faults and exceptions must not + /// occurs, this saves the extra check for nullptr, + /// and allows the compiler to optimize out checks + /// in std::function which may not be ISR-safe or + /// cause linker errors, like l32r relocation errors + /// on the Xtensa ISA. + inline R IRAM_ATTR operator()(P... args) const ALWAYS_INLINE_ATTR + { + if (fn) return fn(std::forward(args...)); + return R(); + } + + protected: + FunPtr fn; + }; +#endif + +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + template + class DelegateImpl { + public: + using target_type = R(); + protected: + using FunPtr = target_type*; + using FunAPtr = R(*)(A); + using FunctionType = std::function; + using FunVPPtr = R(*)(void*); + public: + DelegateImpl() + { + kind = FP; + fn = nullptr; + } + + DelegateImpl(std::nullptr_t) + { + kind = FP; + fn = nullptr; + } + + ~DelegateImpl() + { + if (FUNC == kind) + functional.~FunctionType(); + else if (FPA == kind) + obj.~A(); + } + + DelegateImpl(const DelegateImpl& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(del.functional); + } + else if (FPA == del.kind) + { + fnA = del.fnA; + new (&obj) A(del.obj); + } + else + { + fn = del.fn; + } + } + + DelegateImpl(DelegateImpl&& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(std::move(del.functional)); + } + else if (FPA == del.kind) + { + fnA = del.fnA; + new (&obj) A(std::move(del.obj)); + } + else + { + fn = del.fn; + } + } + + DelegateImpl(FunAPtr fnA, const A& obj) + { + kind = FPA; + DelegateImpl::fnA = fnA; + new (&this->obj) A(obj); + } + + DelegateImpl(FunAPtr fnA, A&& obj) + { + kind = FPA; + DelegateImpl::fnA = fnA; + new (&this->obj) A(std::move(obj)); + } + + DelegateImpl(FunPtr fn) + { + kind = FP; + DelegateImpl::fn = fn; + } + + template DelegateImpl(F functional) + { + kind = FUNC; + new (&this->functional) FunctionType(std::forward(functional)); + } + + DelegateImpl& operator=(const DelegateImpl& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + if (FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + else if (FPA == del.kind) + { + new (&obj) A; + } + kind = del.kind; + } + if (FUNC == del.kind) + { + functional = del.functional; + } + else if (FPA == del.kind) + { + fnA = del.fnA; + obj = del.obj; + } + else + { + fn = del.fn; + } + return *this; + } + + DelegateImpl& operator=(DelegateImpl&& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + if (FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + else if (FPA == del.kind) + { + new (&obj) A; + } + kind = del.kind; + } + if (FUNC == del.kind) + { + functional = std::move(del.functional); + } + else if (FPA == del.kind) + { + fnA = del.fnA; + obj = std::move(del.obj); + } + else + { + fn = del.fn; + } + return *this; + } + + DelegateImpl& operator=(FunPtr fn) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + kind = FP; + this->fn = fn; + return *this; + } + + DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + else if (FPA == kind) + { + obj.~A(); + } + kind = FP; + fn = nullptr; + return *this; + } + + IRAM_ATTR operator bool() const + { + if (FP == kind) + { + return fn; + } + else if (FPA == kind) + { + return fnA; + } + else + { + return functional ? true : false; + } + } + + static inline R IRAM_ATTR vPtrToFunAPtrExec(void* self) ALWAYS_INLINE_ATTR + { + return static_cast(self)->fnA( + static_cast(self)->obj); + }; + + operator FunVPPtr() const + { + if (FP == kind) + { + return reinterpret_cast(fn); + } + else if (FPA == kind) + { + return vPtrToFunAPtrExec; + } + else + { + return [](void* self) -> R + { + return static_cast(self)->functional(); + }; + } + } + + void* arg() const + { + if (FP == kind) + { + return nullptr; + } + else + { + return const_cast(this); + } + } + + operator FunctionType() const + { + if (FP == kind) + { + return fn; + } + else if (FPA == kind) + { + return [this]() { return fnA(obj); }; + } + else + { + return functional; + } + } + + /// Calling is safe without checking for nullptr. + /// If non-void, returns the default value. + /// In ISR context, where faults and exceptions must not + /// occurs, this saves the extra check for nullptr, + /// and allows the compiler to optimize out checks + /// in std::function which may not be ISR-safe or + /// cause linker errors, like l32r relocation errors + /// on the Xtensa ISA. + R IRAM_ATTR operator()() const + { + if (FP == kind) + { + if (fn) return fn(); + } + else if (FPA == kind) + { + if (fnA) return fnA(obj); + } + else + { + if (functional) return functional(); + } + return R(); + } + + protected: + union { + FunctionType functional; + FunPtr fn; + struct { + FunAPtr fnA; + A obj; + }; + }; + enum { FUNC, FP, FPA } kind; + }; +#else + template + class DelegateImpl { + public: + using target_type = R(); + protected: + using FunPtr = target_type*; + using FunAPtr = R(*)(A); + using FunVPPtr = R(*)(void*); + public: + DelegateImpl() + { + kind = FP; + fn = nullptr; + } + + DelegateImpl(std::nullptr_t) + { + kind = FP; + fn = nullptr; + } + + DelegateImpl(const DelegateImpl& del) + { + kind = del.kind; + if (FPA == del.kind) + { + fnA = del.fnA; + obj = del.obj; + } + else + { + fn = del.fn; + } + } + + DelegateImpl(DelegateImpl&& del) + { + kind = del.kind; + if (FPA == del.kind) + { + fnA = del.fnA; + obj = std::move(del.obj); + } + else + { + fn = del.fn; + } + } + + DelegateImpl(FunAPtr fnA, const A& obj) + { + kind = FPA; + DelegateImpl::fnA = fnA; + this->obj = obj; + } + + DelegateImpl(FunAPtr fnA, A&& obj) + { + kind = FPA; + DelegateImpl::fnA = fnA; + this->obj = std::move(obj); + } + + DelegateImpl(FunPtr fn) + { + kind = FP; + DelegateImpl::fn = fn; + } + + template DelegateImpl(F fn) + { + kind = FP; + DelegateImpl::fn = std::forward(fn); + } + + DelegateImpl& operator=(const DelegateImpl& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FPA == kind) + { + obj = {}; + } + kind = del.kind; + } + if (FPA == del.kind) + { + fnA = del.fnA; + obj = del.obj; + } + else + { + fn = del.fn; + } + return *this; + } + + DelegateImpl& operator=(DelegateImpl&& del) + { + if (this == &del) return *this; + if (kind != del.kind) + { + if (FPA == kind) + { + obj = {}; + } + kind = del.kind; + } + if (FPA == del.kind) + { + fnA = del.fnA; + obj = std::move(del.obj); + } + else + { + fn = del.fn; + } + return *this; + } + + DelegateImpl& operator=(FunPtr fn) + { + if (FPA == kind) + { + obj = {}; + } + kind = FP; + this->fn = fn; + return *this; + } + + DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) + { + if (FPA == kind) + { + obj = {}; + } + kind = FP; + fn = nullptr; + return *this; + } + + IRAM_ATTR operator bool() const + { + if (FP == kind) + { + return fn; + } + else + { + return fnA; + } + } + + static inline R IRAM_ATTR vPtrToFunAPtrExec(void* self) ALWAYS_INLINE_ATTR + { + return static_cast(self)->fnA( + static_cast(self)->obj); + }; + + operator FunVPPtr() const + { + if (FP == kind) + { + return reinterpret_cast(fn); + } + else + { + return vPtrToFunAPtrExec; + } + } + + void* arg() const + { + if (FP == kind) + { + return nullptr; + } + else + { + return const_cast(this); + } + } + + /// Calling is safe without checking for nullptr. + /// If non-void, returns the default value. + /// In ISR context, where faults and exceptions must not + /// occurs, this saves the extra check for nullptr, + /// and allows the compiler to optimize out checks + /// in std::function which may not be ISR-safe or + /// cause linker errors, like l32r relocation errors + /// on the Xtensa ISA. + R IRAM_ATTR operator()() const + { + if (FP == kind) + { + if (fn) return fn(); + } + else + { + if (fnA) return fnA(obj); + } + return R(); + } + + protected: + union { + FunPtr fn; + FunAPtr fnA; + }; + A obj; + enum { FP, FPA } kind; + }; +#endif + +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + template + class DelegateImpl { + public: + using target_type = R(); + protected: + using FunPtr = target_type*; + using FunctionType = std::function; + using FunVPPtr = R(*)(void*); + public: + DelegateImpl() + { + kind = FP; + fn = nullptr; + } + + DelegateImpl(std::nullptr_t) + { + kind = FP; + fn = nullptr; + } + + ~DelegateImpl() + { + if (FUNC == kind) + functional.~FunctionType(); + } + + DelegateImpl(const DelegateImpl& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(del.functional); + } + else + { + fn = del.fn; + } + } + + DelegateImpl(DelegateImpl&& del) + { + kind = del.kind; + if (FUNC == del.kind) + { + new (&functional) FunctionType(std::move(del.functional)); + } + else + { + fn = del.fn; + } + } + + DelegateImpl(FunPtr fn) + { + kind = FP; + DelegateImpl::fn = fn; + } + + template DelegateImpl(F functional) + { + kind = FUNC; + new (&this->functional) FunctionType(std::forward(functional)); + } + + DelegateImpl& operator=(const DelegateImpl& del) + { + if (this == &del) return *this; + if (FUNC == kind && FUNC != del.kind) + { + functional.~FunctionType(); + } + else if (FUNC != kind && FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + kind = del.kind; + if (FUNC == del.kind) + { + functional = del.functional; + } + else + { + fn = del.fn; + } + return *this; + } + + DelegateImpl& operator=(DelegateImpl&& del) + { + if (this == &del) return *this; + if (FUNC == kind && FUNC != del.kind) + { + functional.~FunctionType(); + } + else if (FUNC != kind && FUNC == del.kind) + { + new (&this->functional) FunctionType(); + } + kind = del.kind; + if (FUNC == del.kind) + { + functional = std::move(del.functional); + } + else + { + fn = del.fn; + } + return *this; + } + + DelegateImpl& operator=(FunPtr fn) + { + if (FUNC == kind) + { + functional.~FunctionType(); + kind = FP; + } + DelegateImpl::fn = fn; + return *this; + } + + DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) + { + if (FUNC == kind) + { + functional.~FunctionType(); + } + kind = FP; + fn = nullptr; + return *this; + } + + IRAM_ATTR operator bool() const + { + if (FP == kind) + { + return fn; + } + else + { + return functional ? true : false; + } + } + + operator FunVPPtr() const + { + if (FP == kind) + { + return reinterpret_cast(fn); + } + else + { + return [](void* self) -> R + { + return static_cast(self)->functional(); + }; + } + } + + void* arg() const + { + if (FP == kind) + { + return nullptr; + } + else + { + return const_cast(this); + } + } + + operator FunctionType() const + { + if (FP == kind) + { + return fn; + } + else + { + return functional; + } + } + + /// Calling is safe without checking for nullptr. + /// If non-void, returns the default value. + /// In ISR context, where faults and exceptions must not + /// occurs, this saves the extra check for nullptr, + /// and allows the compiler to optimize out checks + /// in std::function which may not be ISR-safe or + /// cause linker errors, like l32r relocation errors + /// on the Xtensa ISA. + R IRAM_ATTR operator()() const + { + if (FP == kind) + { + if (fn) return fn(); + } + else + { + if (functional) return functional(); + } + return R(); + } + + protected: + union { + FunctionType functional; + FunPtr fn; + }; + enum { FUNC, FP } kind; + }; +#else + template + class DelegateImpl { + public: + using target_type = R(); + protected: + using FunPtr = target_type*; + using FunVPPtr = R(*)(void*); + public: + DelegateImpl() + { + fn = nullptr; + } + + DelegateImpl(std::nullptr_t) + { + fn = nullptr; + } + + DelegateImpl(const DelegateImpl& del) + { + fn = del.fn; + } + + DelegateImpl(DelegateImpl&& del) + { + fn = std::move(del.fn); + } + + DelegateImpl(FunPtr fn) + { + DelegateImpl::fn = fn; + } + + template DelegateImpl(F fn) + { + DelegateImpl::fn = std::forward(fn); + } + + DelegateImpl& operator=(const DelegateImpl& del) + { + if (this == &del) return *this; + fn = del.fn; + return *this; + } + + DelegateImpl& operator=(DelegateImpl&& del) + { + if (this == &del) return *this; + fn = std::move(del.fn); + return *this; + } + + DelegateImpl& operator=(FunPtr fn) + { + DelegateImpl::fn = fn; + return *this; + } + + inline DelegateImpl& IRAM_ATTR operator=(std::nullptr_t) ALWAYS_INLINE_ATTR + { + fn = nullptr; + return *this; + } + + inline IRAM_ATTR operator bool() const ALWAYS_INLINE_ATTR + { + return fn; + } + + operator FunVPPtr() const + { + return reinterpret_cast(fn); + } + + void* arg() const + { + return nullptr; + } + + /// Calling is safe without checking for nullptr. + /// If non-void, returns the default value. + /// In ISR context, where faults and exceptions must not + /// occurs, this saves the extra check for nullptr, + /// and allows the compiler to optimize out checks + /// in std::function which may not be ISR-safe or + /// cause linker errors, like l32r relocation errors + /// on the Xtensa ISA. + inline R IRAM_ATTR operator()() const ALWAYS_INLINE_ATTR + { + if (fn) return fn(); + return R(); + } + + protected: + FunPtr fn; + }; +#endif + + template + class Delegate : private detail::DelegatePImpl + { + public: + using target_type = R(P...); + protected: + using FunPtr = target_type*; + using FunAPtr = R(*)(A, P...); + using FunVPPtr = R(*)(void*, P...); +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + using FunctionType = std::function; +#endif + public: + using detail::DelegatePImpl::operator bool; + using detail::DelegatePImpl::arg; + using detail::DelegatePImpl::operator(); + + operator FunVPPtr() { return detail::DelegatePImpl::operator FunVPPtr(); } +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + operator FunctionType() { return detail::DelegatePImpl::operator FunctionType(); } +#endif + + Delegate() : detail::DelegatePImpl::DelegatePImpl() {} + + Delegate(std::nullptr_t) : detail::DelegatePImpl::DelegatePImpl(nullptr) {} + + Delegate(const Delegate& del) : detail::DelegatePImpl::DelegatePImpl( + static_cast&>(del)) {} + + Delegate(Delegate&& del) : detail::DelegatePImpl::DelegatePImpl( + std::move(static_cast&>(del))) {} + + Delegate(FunAPtr fnA, const A& obj) : detail::DelegatePImpl::DelegatePImpl(fnA, obj) {} + + Delegate(FunAPtr fnA, A&& obj) : detail::DelegatePImpl::DelegatePImpl(fnA, std::move(obj)) {} + + Delegate(FunPtr fn) : detail::DelegatePImpl::DelegatePImpl(fn) {} + + template Delegate(F functional) : detail::DelegatePImpl::DelegatePImpl(std::forward(functional)) {} + + Delegate& operator=(const Delegate& del) { + detail::DelegatePImpl::operator=(del); + return *this; + } + + Delegate& operator=(Delegate&& del) { + detail::DelegatePImpl::operator=(std::move(del)); + return *this; + } + + Delegate& operator=(FunPtr fn) { + detail::DelegatePImpl::operator=(fn); + return *this; + } + + inline Delegate& IRAM_ATTR operator=(std::nullptr_t) ALWAYS_INLINE_ATTR { + detail::DelegatePImpl::operator=(nullptr); + return *this; + } + }; + + template + class Delegate : private detail::DelegatePImpl + { + public: + using target_type = R(P...); + protected: + using FunPtr = target_type*; + using FunAPtr = R(*)(A*, P...); + using FunVPPtr = R(*)(void*, P...); +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + using FunctionType = std::function; +#endif + public: + using detail::DelegatePImpl::operator bool; + using detail::DelegatePImpl::operator(); + + operator FunVPPtr() const + { + if (detail::DelegatePImpl::FPA == detail::DelegatePImpl::kind) + { + return reinterpret_cast(detail::DelegatePImpl::fnA); + } + else + { + return detail::DelegatePImpl::operator FunVPPtr(); + } + } +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + operator FunctionType() { return detail::DelegatePImpl::operator FunctionType(); } +#endif + void* arg() const + { + if (detail::DelegatePImpl::FPA == detail::DelegatePImpl::kind) + { + return detail::DelegatePImpl::obj; + } + else + { + return detail::DelegatePImpl::arg(); + } + } + + Delegate() : detail::DelegatePImpl::DelegatePImpl() {} + + Delegate(std::nullptr_t) : detail::DelegatePImpl::DelegatePImpl(nullptr) {} + + Delegate(const Delegate& del) : detail::DelegatePImpl::DelegatePImpl( + static_cast&>(del)) {} + + Delegate(Delegate&& del) : detail::DelegatePImpl::DelegatePImpl( + std::move(static_cast&>(del))) {} + + Delegate(FunAPtr fnA, A* obj) : detail::DelegatePImpl::DelegatePImpl(fnA, obj) {} + + Delegate(FunPtr fn) : detail::DelegatePImpl::DelegatePImpl(fn) {} + + template Delegate(F functional) : detail::DelegatePImpl::DelegatePImpl(std::forward(functional)) {} + + Delegate& operator=(const Delegate& del) { + detail::DelegatePImpl::operator=(del); + return *this; + } + + Delegate& operator=(Delegate&& del) { + detail::DelegatePImpl::operator=(std::move(del)); + return *this; + } + + Delegate& operator=(FunPtr fn) { + detail::DelegatePImpl::operator=(fn); + return *this; + } + + inline Delegate& IRAM_ATTR operator=(std::nullptr_t) ALWAYS_INLINE_ATTR { + detail::DelegatePImpl::operator=(nullptr); + return *this; + } + }; + + template + class Delegate : private detail::DelegatePImpl + { + public: + using target_type = R(P...); + protected: + using FunPtr = target_type*; +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + using FunctionType = std::function; +#endif + using FunVPPtr = R(*)(void*, P...); + public: + using detail::DelegatePImpl::operator bool; + using detail::DelegatePImpl::arg; + using detail::DelegatePImpl::operator(); + + operator FunVPPtr() const { return detail::DelegatePImpl::operator FunVPPtr(); } +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + operator FunctionType() { return detail::DelegatePImpl::operator FunctionType(); } +#endif + + Delegate() : detail::DelegatePImpl::DelegatePImpl() {} + + Delegate(std::nullptr_t) : detail::DelegatePImpl::DelegatePImpl(nullptr) {} + + Delegate(const Delegate& del) : detail::DelegatePImpl::DelegatePImpl( + static_cast&>(del)) {} + + Delegate(Delegate&& del) : detail::DelegatePImpl::DelegatePImpl( + std::move(static_cast&>(del))) {} + + Delegate(FunPtr fn) : detail::DelegatePImpl::DelegatePImpl(fn) {} + + template Delegate(F functional) : detail::DelegatePImpl::DelegatePImpl(std::forward(functional)) {} + + Delegate& operator=(const Delegate& del) { + detail::DelegatePImpl::operator=(del); + return *this; + } + + Delegate& operator=(Delegate&& del) { + detail::DelegatePImpl::operator=(std::move(del)); + return *this; + } + + Delegate& operator=(FunPtr fn) { + detail::DelegatePImpl::operator=(fn); + return *this; + } + + inline Delegate& IRAM_ATTR operator=(std::nullptr_t) ALWAYS_INLINE_ATTR { + detail::DelegatePImpl::operator=(nullptr); + return *this; + } + }; + + template + class Delegate : private detail::DelegateImpl + { + public: + using target_type = R(); + protected: + using FunPtr = target_type*; + using FunAPtr = R(*)(A); + using FunVPPtr = R(*)(void*); +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + using FunctionType = std::function; +#endif + public: + using detail::DelegateImpl::operator bool; + using detail::DelegateImpl::arg; + using detail::DelegateImpl::operator(); + + operator FunVPPtr() { return detail::DelegateImpl::operator FunVPPtr(); } +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + operator FunctionType() { return detail::DelegateImpl::operator FunctionType(); } +#endif + + Delegate() : detail::DelegateImpl::DelegateImpl() {} + + Delegate(std::nullptr_t) : detail::DelegateImpl::DelegateImpl(nullptr) {} + + Delegate(const Delegate& del) : detail::DelegateImpl::DelegateImpl( + static_cast&>(del)) {} + + Delegate(Delegate&& del) : detail::DelegateImpl::DelegateImpl( + std::move(static_cast&>(del))) {} + + Delegate(FunAPtr fnA, const A& obj) : detail::DelegateImpl::DelegateImpl(fnA, obj) {} + + Delegate(FunAPtr fnA, A&& obj) : detail::DelegateImpl::DelegateImpl(fnA, std::move(obj)) {} + + Delegate(FunPtr fn) : detail::DelegateImpl::DelegateImpl(fn) {} + + template Delegate(F functional) : detail::DelegateImpl::DelegateImpl(std::forward(functional)) {} + + Delegate& operator=(const Delegate& del) { + detail::DelegateImpl::operator=(del); + return *this; + } + + Delegate& operator=(Delegate&& del) { + detail::DelegateImpl::operator=(std::move(del)); + return *this; + } + + Delegate& operator=(FunPtr fn) { + detail::DelegateImpl::operator=(fn); + return *this; + } + + inline Delegate& IRAM_ATTR operator=(std::nullptr_t) ALWAYS_INLINE_ATTR { + detail::DelegateImpl::operator=(nullptr); + return *this; + } + }; + + template + class Delegate : private detail::DelegateImpl + { + public: + using target_type = R(); + protected: + using FunPtr = target_type*; + using FunAPtr = R(*)(A*); + using FunVPPtr = R(*)(void*); +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + using FunctionType = std::function; +#endif + public: + using detail::DelegateImpl::operator bool; + using detail::DelegateImpl::operator(); + + operator FunVPPtr() const + { + if (detail::DelegateImpl::FPA == detail::DelegateImpl::kind) + { + return reinterpret_cast(detail::DelegateImpl::fnA); + } + else + { + return detail::DelegateImpl::operator FunVPPtr(); + } + } +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + operator FunctionType() { return detail::DelegateImpl::operator FunctionType(); } +#endif + void* arg() const + { + if (detail::DelegateImpl::FPA == detail::DelegateImpl::kind) + { + return detail::DelegateImpl::obj; + } + else + { + return detail::DelegateImpl::arg(); + } + } + + Delegate() : detail::DelegateImpl::DelegateImpl() {} + + Delegate(std::nullptr_t) : detail::DelegateImpl::DelegateImpl(nullptr) {} + + Delegate(const Delegate& del) : detail::DelegateImpl::DelegateImpl( + static_cast&>(del)) {} + + Delegate(Delegate&& del) : detail::DelegateImpl::DelegateImpl( + std::move(static_cast&>(del))) {} + + Delegate(FunAPtr fnA, A* obj) : detail::DelegateImpl::DelegateImpl(fnA, obj) {} + + Delegate(FunPtr fn) : detail::DelegateImpl::DelegateImpl(fn) {} + + template Delegate(F functional) : detail::DelegateImpl::DelegateImpl(std::forward(functional)) {} + + Delegate& operator=(const Delegate& del) { + detail::DelegateImpl::operator=(del); + return *this; + } + + Delegate& operator=(Delegate&& del) { + detail::DelegateImpl::operator=(std::move(del)); + return *this; + } + + Delegate& operator=(FunPtr fn) { + detail::DelegateImpl::operator=(fn); + return *this; + } + + inline Delegate& IRAM_ATTR operator=(std::nullptr_t) ALWAYS_INLINE_ATTR { + detail::DelegateImpl::operator=(nullptr); + return *this; + } + }; + + template + class Delegate : private detail::DelegateImpl + { + public: + using target_type = R(); + protected: + using FunPtr = target_type*; +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + using FunctionType = std::function; +#endif + using FunVPPtr = R(*)(void*); + public: + using detail::DelegateImpl::operator bool; + using detail::DelegateImpl::arg; + using detail::DelegateImpl::operator(); + + operator FunVPPtr() const { return detail::DelegateImpl::operator FunVPPtr(); } +#if !defined(ARDUINO) || defined(ESP8266) || defined(ESP32) + operator FunctionType() { return detail::DelegateImpl::operator FunctionType(); } +#endif + + Delegate() : detail::DelegateImpl::DelegateImpl() {} + + Delegate(std::nullptr_t) : detail::DelegateImpl::DelegateImpl(nullptr) {} + + Delegate(const Delegate& del) : detail::DelegateImpl::DelegateImpl( + static_cast&>(del)) {} + + Delegate(Delegate&& del) : detail::DelegateImpl::DelegateImpl( + std::move(static_cast&>(del))) {} + + Delegate(FunPtr fn) : detail::DelegateImpl::DelegateImpl(fn) {} + + template Delegate(F functional) : detail::DelegateImpl::DelegateImpl(std::forward(functional)) {} + + Delegate& operator=(const Delegate& del) { + detail::DelegateImpl::operator=(del); + return *this; + } + + Delegate& operator=(Delegate&& del) { + detail::DelegateImpl::operator=(std::move(del)); + return *this; + } + + Delegate& operator=(FunPtr fn) { + detail::DelegateImpl::operator=(fn); + return *this; + } + + inline Delegate& IRAM_ATTR operator=(std::nullptr_t) ALWAYS_INLINE_ATTR { + detail::DelegateImpl::operator=(nullptr); + return *this; + } + }; + } +} + +template class Delegate; +template class Delegate : public delegate::detail::Delegate +{ +public: + Delegate() : delegate::detail::Delegate::Delegate() {} + + Delegate(std::nullptr_t) : delegate::detail::Delegate::Delegate(nullptr) {} + + Delegate(const Delegate& del) : delegate::detail::Delegate::Delegate( + static_cast&>(del)) {} + + Delegate(Delegate&& del) : delegate::detail::Delegate::Delegate( + std::move(static_cast&>(del))) {} + + Delegate(typename delegate::detail::Delegate::FunAPtr fnA, const A& obj) : delegate::detail::Delegate::Delegate(fnA, obj) {} + + Delegate(typename delegate::detail::Delegate::FunAPtr fnA, A&& obj) : delegate::detail::Delegate::Delegate(fnA, std::move(obj)) {} + + Delegate(typename delegate::detail::Delegate::FunPtr fn) : delegate::detail::Delegate::Delegate(fn) {} + + template Delegate(F functional) : delegate::detail::Delegate::Delegate(std::forward(functional)) {} + + Delegate& operator=(const Delegate& del) { + delegate::detail::Delegate::operator=(del); + return *this; + } + + Delegate& operator=(Delegate&& del) { + delegate::detail::Delegate::operator=(std::move(del)); + return *this; + } + + Delegate& operator=(typename delegate::detail::Delegate::FunPtr fn) { + delegate::detail::Delegate::operator=(fn); + return *this; + } + + inline Delegate& IRAM_ATTR operator=(std::nullptr_t) ALWAYS_INLINE_ATTR { + delegate::detail::Delegate::operator=(nullptr); + return *this; + } +}; + +template class Delegate : public delegate::detail::Delegate +{ +public: + Delegate() : delegate::detail::Delegate::Delegate() {} + + Delegate(std::nullptr_t) : delegate::detail::Delegate::Delegate(nullptr) {} + + Delegate(const Delegate& del) : delegate::detail::Delegate::Delegate( + static_cast&>(del)) {} + + Delegate(Delegate&& del) : delegate::detail::Delegate::Delegate( + std::move(static_cast&>(del))) {} + + Delegate(typename delegate::detail::Delegate::FunPtr fn) : delegate::detail::Delegate::Delegate(fn) {} + + template Delegate(F functional) : delegate::detail::Delegate::Delegate(std::forward(functional)) {} + + Delegate& operator=(const Delegate& del) { + delegate::detail::Delegate::operator=(del); + return *this; + } + + Delegate& operator=(Delegate&& del) { + delegate::detail::Delegate::operator=(std::move(del)); + return *this; + } + + Delegate& operator=(typename delegate::detail::Delegate::FunPtr fn) { + delegate::detail::Delegate::operator=(fn); + return *this; + } + + inline Delegate& IRAM_ATTR operator=(std::nullptr_t) ALWAYS_INLINE_ATTR { + delegate::detail::Delegate::operator=(nullptr); + return *this; + } +}; + +#endif // __Delegate_h diff --git a/src/EspSoftwareSerial/src/circular_queue/MultiDelegate.h b/src/EspSoftwareSerial/src/circular_queue/MultiDelegate.h new file mode 100644 index 0000000..aee0741 --- /dev/null +++ b/src/EspSoftwareSerial/src/circular_queue/MultiDelegate.h @@ -0,0 +1,567 @@ +/* +MultiDelegate.h - A queue or event multiplexer based on the efficient Delegate +class +Copyright (c) 2019-2020 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __MULTIDELEGATE_H +#define __MULTIDELEGATE_H + +#include +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) +#include +#else +#include "circular_queue/ghostl.h" +#endif + +#if defined(ESP8266) +#include +using esp8266::InterruptLock; +#elif defined(ARDUINO) +class InterruptLock { +public: + InterruptLock() { + noInterrupts(); + } + ~InterruptLock() { + interrupts(); + } +}; +#else +#include +#endif + +namespace +{ + + template< typename Delegate, typename R, bool ISQUEUE = false, typename... P> + struct CallP + { + static R execute(Delegate& del, P... args) + { + return del(std::forward(args...)); + } + }; + + template< typename Delegate, bool ISQUEUE, typename... P> + struct CallP + { + static bool execute(Delegate& del, P... args) + { + del(std::forward(args...)); + return true; + } + }; + + template< typename Delegate, typename R, bool ISQUEUE = false> + struct Call + { + static R execute(Delegate& del) + { + return del(); + } + }; + + template< typename Delegate, bool ISQUEUE> + struct Call + { + static bool execute(Delegate& del) + { + del(); + return true; + } + }; + +} + +namespace delegate +{ + namespace detail + { + + template< typename Delegate, typename R, bool ISQUEUE = false, size_t QUEUE_CAPACITY = 32, typename... P> + class MultiDelegatePImpl + { + public: + MultiDelegatePImpl() = default; + ~MultiDelegatePImpl() + { + *this = nullptr; + } + + MultiDelegatePImpl(const MultiDelegatePImpl&) = delete; + MultiDelegatePImpl& operator=(const MultiDelegatePImpl&) = delete; + + MultiDelegatePImpl(MultiDelegatePImpl&& md) + { + first = md.first; + last = md.last; + unused = md.unused; + nodeCount = md.nodeCount; + md.first = nullptr; + md.last = nullptr; + md.unused = nullptr; + md.nodeCount = 0; + } + + MultiDelegatePImpl(const Delegate& del) + { + add(del); + } + + MultiDelegatePImpl(Delegate&& del) + { + add(std::move(del)); + } + + MultiDelegatePImpl& operator=(MultiDelegatePImpl&& md) + { + first = md.first; + last = md.last; + unused = md.unused; + nodeCount = md.nodeCount; + md.first = nullptr; + md.last = nullptr; + md.unused = nullptr; + md.nodeCount = 0; + return *this; + } + + MultiDelegatePImpl& operator=(std::nullptr_t) + { + if (last) + last->mNext = unused; + if (first) + unused = first; + while (unused) + { + auto to_delete = unused; + unused = unused->mNext; + delete(to_delete); + } + return *this; + } + + MultiDelegatePImpl& operator+=(const Delegate& del) + { + add(del); + return *this; + } + + MultiDelegatePImpl& operator+=(Delegate&& del) + { + add(std::move(del)); + return *this; + } + + protected: + struct Node_t + { + ~Node_t() + { + mDelegate = nullptr; // special overload in Delegate + } + Node_t* mNext = nullptr; + Delegate mDelegate; + }; + + Node_t* first = nullptr; + Node_t* last = nullptr; + Node_t* unused = nullptr; + size_t nodeCount = 0; + + // Returns a pointer to an unused Node_t, + // or if none are available allocates a new one, + // or nullptr if limit is reached + Node_t* IRAM_ATTR get_node_unsafe() + { + Node_t* result = nullptr; + // try to get an item from unused items list + if (unused) + { + result = unused; + unused = unused->mNext; + } + // if no unused items, and count not too high, allocate a new one + else if (nodeCount < QUEUE_CAPACITY) + { +#if defined(ESP8266) || defined(ESP32) + result = new (std::nothrow) Node_t; +#else + result = new Node_t; +#endif + if (result) + ++nodeCount; + } + return result; + } + + void recycle_node_unsafe(Node_t* node) + { + node->mDelegate = nullptr; // special overload in Delegate + node->mNext = unused; + unused = node; + } + +#ifndef ARDUINO + std::mutex mutex_unused; +#endif + public: + class iterator : public std::iterator + { + public: + Node_t* current = nullptr; + Node_t* prev = nullptr; + const Node_t* stop = nullptr; + + iterator(MultiDelegatePImpl& md) : current(md.first), stop(md.last) {} + iterator() = default; + iterator(const iterator&) = default; + iterator& operator=(const iterator&) = default; + iterator& operator=(iterator&&) = default; + operator bool() const + { + return current && stop; + } + bool operator==(const iterator& rhs) const + { + return current == rhs.current; + } + bool operator!=(const iterator& rhs) const + { + return !operator==(rhs); + } + Delegate& operator*() const + { + return current->mDelegate; + } + Delegate* operator->() const + { + return ¤t->mDelegate; + } + iterator& operator++() // prefix + { + if (current && stop != current) + { + prev = current; + current = current->mNext; + } + else + current = nullptr; // end + return *this; + } + iterator& operator++(int) // postfix + { + iterator tmp(*this); + operator++(); + return tmp; + } + }; + + iterator begin() + { + return iterator(*this); + } + iterator end() const + { + return iterator(); + } + + const Delegate* add(const Delegate& del) + { + return add(Delegate(del)); + } + + const Delegate* add(Delegate&& del) + { + if (!del) + return nullptr; + +#ifdef ARDUINO + InterruptLock lockAllInterruptsInThisScope; +#else + std::lock_guard lock(mutex_unused); +#endif + + Node_t* item = ISQUEUE ? get_node_unsafe() : +#if defined(ESP8266) || defined(ESP32) + new (std::nothrow) Node_t; +#else + new Node_t; +#endif + if (!item) + return nullptr; + + item->mDelegate = std::move(del); + item->mNext = nullptr; + + if (last) + last->mNext = item; + else + first = item; + last = item; + + return &item->mDelegate; + } + + iterator erase(iterator it) + { + if (!it) + return end(); +#ifdef ARDUINO + InterruptLock lockAllInterruptsInThisScope; +#else + std::lock_guard lock(mutex_unused); +#endif + auto to_recycle = it.current; + + if (last == it.current) + last = it.prev; + it.current = it.current->mNext; + if (it.prev) + { + it.prev->mNext = it.current; + } + else + { + first = it.current; + } + if (ISQUEUE) + recycle_node_unsafe(to_recycle); + else + delete to_recycle; + return it; + } + + bool erase(const Delegate* const del) + { + auto it = begin(); + while (it) + { + if (del == &(*it)) + { + erase(it); + return true; + } + ++it; + } + return false; + } + + operator bool() const + { + return first; + } + + R operator()(P... args) + { + auto it = begin(); + if (!it) + return {}; + + static std::atomic fence(false); + // prevent recursive calls +#if defined(ARDUINO) && !defined(ESP32) + if (fence.load()) return {}; + fence.store(true); +#else + if (fence.exchange(true)) return {}; +#endif + + R result; + do + { + result = CallP::execute(*it, args...); + if (result && ISQUEUE) + it = erase(it); + else + ++it; +#if defined(ESP8266) || defined(ESP32) + // running callbacks might last too long for watchdog etc. + optimistic_yield(10000); +#endif + } while (it); + + fence.store(false); + return result; + } + }; + + template< typename Delegate, typename R = void, bool ISQUEUE = false, size_t QUEUE_CAPACITY = 32> + class MultiDelegateImpl : public MultiDelegatePImpl + { + public: + using MultiDelegatePImpl::MultiDelegatePImpl; + + R operator()() + { + auto it = this->begin(); + if (!it) + return {}; + + static std::atomic fence(false); + // prevent recursive calls +#if defined(ARDUINO) && !defined(ESP32) + if (fence.load()) return {}; + fence.store(true); +#else + if (fence.exchange(true)) return {}; +#endif + + R result; + do + { + result = Call::execute(*it); + if (result && ISQUEUE) + it = this->erase(it); + else + ++it; +#if defined(ESP8266) || defined(ESP32) + // running callbacks might last too long for watchdog etc. + optimistic_yield(10000); +#endif + } while (it); + + fence.store(false); + return result; + } + }; + + template< typename Delegate, typename R, bool ISQUEUE, size_t QUEUE_CAPACITY, typename... P> class MultiDelegate; + + template< typename Delegate, typename R, bool ISQUEUE, size_t QUEUE_CAPACITY, typename... P> + class MultiDelegate : public MultiDelegatePImpl + { + public: + using MultiDelegatePImpl::MultiDelegatePImpl; + }; + + template< typename Delegate, typename R, bool ISQUEUE, size_t QUEUE_CAPACITY> + class MultiDelegate : public MultiDelegateImpl + { + public: + using MultiDelegateImpl::MultiDelegateImpl; + }; + + template< typename Delegate, bool ISQUEUE, size_t QUEUE_CAPACITY, typename... P> + class MultiDelegate : public MultiDelegatePImpl + { + public: + using MultiDelegatePImpl::MultiDelegatePImpl; + + void operator()(P... args) + { + auto it = this->begin(); + if (!it) + return; + + static std::atomic fence(false); + // prevent recursive calls +#if defined(ARDUINO) && !defined(ESP32) + if (fence.load()) return; + fence.store(true); +#else + if (fence.exchange(true)) return; +#endif + + do + { + CallP::execute(*it, args...); + if (ISQUEUE) + it = this->erase(it); + else + ++it; +#if defined(ESP8266) || defined(ESP32) + // running callbacks might last too long for watchdog etc. + optimistic_yield(10000); +#endif + } while (it); + + fence.store(false); + } + }; + + template< typename Delegate, bool ISQUEUE, size_t QUEUE_CAPACITY> + class MultiDelegate : public MultiDelegateImpl + { + public: + using MultiDelegateImpl::MultiDelegateImpl; + + void operator()() + { + auto it = this->begin(); + if (!it) + return; + + static std::atomic fence(false); + // prevent recursive calls +#if defined(ARDUINO) && !defined(ESP32) + if (fence.load()) return; + fence.store(true); +#else + if (fence.exchange(true)) return; +#endif + + do + { + Call::execute(*it); + if (ISQUEUE) + it = this->erase(it); + else + ++it; +#if defined(ESP8266) || defined(ESP32) + // running callbacks might last too long for watchdog etc. + optimistic_yield(10000); +#endif + } while (it); + + fence.store(false); + } + }; + + } + +} + +/** +The MultiDelegate class template can be specialized to either a queue or an event multiplexer. +It is designed to be used with Delegate, the efficient runtime wrapper for C function ptr and C++ std::function. +@tparam Delegate specifies the concrete type that MultiDelegate bases the queue or event multiplexer on. +@tparam ISQUEUE modifies the generated MultiDelegate class in subtle ways. In queue mode (ISQUEUE == true), + the value of QUEUE_CAPACITY enforces the maximum number of simultaneous items the queue can contain. + This is exploited to minimize the use of new and delete by reusing already allocated items, thus + reducing heap fragmentation. In event multiplexer mode (ISQUEUE = false), new and delete are + used for allocation of the event handler items. + If the result type of the function call operator of Delegate is void, calling a MultiDelegate queue + removes each item after calling it; a Multidelegate event multiplexer keeps event handlers until + explicitly removed. + If the result type of the function call operator of Delegate is non-void, in a MultiDelegate queue + the type-conversion to bool of that result determines if the item is immediately removed or kept + after each call: if true is returned, the item is removed. A Multidelegate event multiplexer keeps event + handlers until they are explicitly removed. +@tparam QUEUE_CAPACITY is only used if ISQUEUE == true. Then, it sets the maximum capacity that the queue dynamically + allocates from the heap. Unused items are not returned to the heap, but are managed by the MultiDelegate + instance during its own lifetime for efficiency. +*/ +template< typename Delegate, bool ISQUEUE = false, size_t QUEUE_CAPACITY = 32> +class MultiDelegate : public delegate::detail::MultiDelegate +{ +public: + using delegate::detail::MultiDelegate::MultiDelegate; +}; + +#endif // __MULTIDELEGATE_H diff --git a/src/EspSoftwareSerial/src/circular_queue/circular_queue.h b/src/EspSoftwareSerial/src/circular_queue/circular_queue.h new file mode 100644 index 0000000..3bd5e00 --- /dev/null +++ b/src/EspSoftwareSerial/src/circular_queue/circular_queue.h @@ -0,0 +1,384 @@ +/* +circular_queue.h - Implementation of a lock-free circular queue for EspSoftwareSerial. +Copyright (c) 2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __circular_queue_h +#define __circular_queue_h + +#ifdef ARDUINO +#include +#endif + +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) +#include +#include +#include +#include "Delegate.h" +using std::min; +#else +#include "ghostl.h" +#endif + +#if !defined(ESP32) && !defined(ESP8266) +#define IRAM_ATTR +#endif + +#if defined(__GNUC__) +#undef ALWAYS_INLINE_ATTR +#define ALWAYS_INLINE_ATTR __attribute__((always_inline)) +#else +#define ALWAYS_INLINE_ATTR +#endif + +/*! + @brief Instance class for a single-producer, single-consumer circular queue / ring buffer (FIFO). + This implementation is lock-free between producer and consumer for the available(), peek(), + pop(), and push() type functions. +*/ +template< typename T, typename ForEachArg = void > +class circular_queue +{ +public: + /*! + @brief Constructs a valid, but zero-capacity dummy queue. + */ + circular_queue() : m_bufSize(1) + { + m_inPos.store(0); + m_outPos.store(0); + } + /*! + @brief Constructs a queue of the given maximum capacity. + */ + circular_queue(const size_t capacity) : m_bufSize(capacity + 1), m_buffer(new T[m_bufSize]) + { + m_inPos.store(0); + m_outPos.store(0); + } + circular_queue(circular_queue&& cq) : + m_bufSize(cq.m_bufSize), m_buffer(cq.m_buffer), m_inPos(cq.m_inPos.load()), m_outPos(cq.m_outPos.load()) + {} + ~circular_queue() + { + m_buffer.reset(); + } + circular_queue(const circular_queue&) = delete; + circular_queue& operator=(circular_queue&& cq) + { + m_bufSize = cq.m_bufSize; + m_buffer = cq.m_buffer; + m_inPos.store(cq.m_inPos.load()); + m_outPos.store(cq.m_outPos.load()); + } + circular_queue& operator=(const circular_queue&) = delete; + + /*! + @brief Get the numer of elements the queue can hold at most. + */ + size_t capacity() const + { + return m_bufSize - 1; + } + + /*! + @brief Resize the queue. The available elements in the queue are preserved. + This is not lock-free and concurrent producer or consumer access + will lead to corruption. + @return True if the new capacity could accommodate the present elements in + the queue, otherwise nothing is done and false is returned. + */ + bool capacity(const size_t cap); + + /*! + @brief Discard all data in the queue. + */ + void flush() + { + m_outPos.store(m_inPos.load()); + } + + /*! + @brief Get a snapshot number of elements that can be retrieved by pop. + */ + size_t IRAM_ATTR available() const + { + int avail = static_cast(m_inPos.load() - m_outPos.load()); + if (avail < 0) avail += m_bufSize; + return avail; + } + + /*! + @brief Get the remaining free elementes for pushing. + */ + size_t IRAM_ATTR available_for_push() const + { + int avail = static_cast(m_outPos.load() - m_inPos.load()) - 1; + if (avail < 0) avail += m_bufSize; + return avail; + } + + /*! + @brief Peek at the next element pop will return without removing it from the queue. + @return An rvalue copy of the next element that can be popped. If the queue is empty, + return an rvalue copy of the element that is pending the next push. + */ + T peek() const + { + const auto outPos = m_outPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + return m_buffer[outPos]; + } + + /*! + @brief Peek at the next pending input value. + @return A reference to the next element that can be pushed. + */ + T& IRAM_ATTR pushpeek() + { + const auto inPos = m_inPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + return m_buffer[inPos]; + } + + /*! + @brief Release the next pending input value, accessible by pushpeek(), into the queue. + @return true if the queue accepted the value, false if the queue + was full. + */ + bool IRAM_ATTR push() + { + const auto inPos = m_inPos.load(std::memory_order_acquire); + const size_t next = (inPos + 1) % m_bufSize; + if (next == m_outPos.load(std::memory_order_relaxed)) { + return false; + } + std::atomic_thread_fence(std::memory_order_release); + m_inPos.store(next, std::memory_order_release); + return true; + } + + /*! + @brief Move the rvalue parameter into the queue. + @return true if the queue accepted the value, false if the queue + was full. + */ + bool IRAM_ATTR push(T&& val) + { + const auto inPos = m_inPos.load(std::memory_order_acquire); + const size_t next = (inPos + 1) % m_bufSize; + if (next == m_outPos.load(std::memory_order_relaxed)) { + return false; + } + m_buffer[inPos] = std::move(val); + std::atomic_thread_fence(std::memory_order_release); + m_inPos.store(next, std::memory_order_release); + return true; + } + + /*! + @brief Push a copy of the parameter into the queue. + @return true if the queue accepted the value, false if the queue + was full. + */ + inline bool IRAM_ATTR push(const T& val) ALWAYS_INLINE_ATTR + { + T v(val); + return push(std::move(v)); + } + +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) + /*! + @brief Push copies of multiple elements from a buffer into the queue, + in order, beginning at buffer's head. + @return The number of elements actually copied into the queue, counted + from the buffer head. + */ + size_t push_n(const T* buffer, size_t size); +#endif + + /*! + @brief Pop the next available element from the queue. + @return An rvalue copy of the popped element, or a default + value of type T if the queue is empty. + */ + T pop(); + +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) + /*! + @brief Pop multiple elements in ordered sequence from the queue to a buffer. + If buffer is nullptr, simply discards up to size elements from the queue. + @return The number of elements actually popped from the queue to + buffer. + */ + size_t pop_n(T* buffer, size_t size); +#endif + + /*! + @brief Iterate over and remove each available element from queue, + calling back fun with an rvalue reference of every single element. + */ +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) + void for_each(const Delegate& fun); +#else + void for_each(Delegate fun); +#endif + + /*! + @brief In reverse order, iterate over, pop and optionally requeue each available element from the queue, + calling back fun with a reference of every single element. + Requeuing is dependent on the return boolean of the callback function. If it + returns true, the requeue occurs. + */ +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) + bool for_each_rev_requeue(const Delegate& fun); +#else + bool for_each_rev_requeue(Delegate fun); +#endif + +protected: + size_t m_bufSize; +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) + std::unique_ptr m_buffer; +#else + std::unique_ptr m_buffer; +#endif + std::atomic m_inPos; + std::atomic m_outPos; +}; + +template< typename T, typename ForEachArg > +bool circular_queue::capacity(const size_t cap) +{ + if (cap + 1 == m_bufSize) return true; + else if (available() > cap) return false; + std::unique_ptr buffer(new T[cap + 1]); + const auto available = pop_n(buffer, cap); + m_buffer.reset(buffer); + m_bufSize = cap + 1; + m_inPos.store(available, std::memory_order_relaxed); + m_outPos.store(0, std::memory_order_relaxed); + return true; +} + +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) +template< typename T, typename ForEachArg > +size_t circular_queue::push_n(const T* buffer, size_t size) +{ + const auto inPos = m_inPos.load(std::memory_order_acquire); + const auto outPos = m_outPos.load(std::memory_order_relaxed); + + size_t blockSize = (outPos > inPos) ? outPos - 1 - inPos : (outPos == 0) ? m_bufSize - 1 - inPos : m_bufSize - inPos; + blockSize = min(size, blockSize); + if (!blockSize) return 0; + int next = (inPos + blockSize) % m_bufSize; + + auto dest = m_buffer.get() + inPos; + std::copy_n(std::make_move_iterator(buffer), blockSize, dest); + size = min(size - blockSize, outPos > 1 ? static_cast(outPos - next - 1) : 0); + next += size; + dest = m_buffer.get(); + std::copy_n(std::make_move_iterator(buffer + blockSize), size, dest); + + std::atomic_thread_fence(std::memory_order_release); + m_inPos.store(next, std::memory_order_release); + return blockSize + size; +} +#endif + +template< typename T, typename ForEachArg > +T circular_queue::pop() +{ + const auto outPos = m_outPos.load(std::memory_order_acquire); + if (m_inPos.load(std::memory_order_relaxed) == outPos) return {}; + + std::atomic_thread_fence(std::memory_order_acquire); + + auto val = std::move(m_buffer[outPos]); + + m_outPos.store((outPos + 1) % m_bufSize, std::memory_order_release); + return val; +} + +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) +template< typename T, typename ForEachArg > +size_t circular_queue::pop_n(T* buffer, size_t size) { + size_t avail = size = min(size, available()); + if (!avail) return 0; + const auto outPos = m_outPos.load(std::memory_order_acquire); + size_t n = min(avail, static_cast(m_bufSize - outPos)); + + std::atomic_thread_fence(std::memory_order_acquire); + + if (buffer) { + buffer = std::copy_n(std::make_move_iterator(m_buffer.get() + outPos), n, buffer); + avail -= n; + std::copy_n(std::make_move_iterator(m_buffer.get()), avail, buffer); + } + + m_outPos.store((outPos + size) % m_bufSize, std::memory_order_release); + return size; +} +#endif + +template< typename T, typename ForEachArg > +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) +void circular_queue::for_each(const Delegate& fun) +#else +void circular_queue::for_each(Delegate fun) +#endif +{ + auto outPos = m_outPos.load(std::memory_order_acquire); + const auto inPos = m_inPos.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + while (outPos != inPos) + { + fun(std::move(m_buffer[outPos])); + outPos = (outPos + 1) % m_bufSize; + m_outPos.store(outPos, std::memory_order_release); + } +} + +template< typename T, typename ForEachArg > +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) +bool circular_queue::for_each_rev_requeue(const Delegate& fun) +#else +bool circular_queue::for_each_rev_requeue(Delegate fun) +#endif +{ + auto inPos0 = circular_queue::m_inPos.load(std::memory_order_acquire); + auto outPos = circular_queue::m_outPos.load(std::memory_order_relaxed); + if (outPos == inPos0) return false; + auto pos = inPos0; + auto outPos1 = inPos0; + const auto posDecr = circular_queue::m_bufSize - 1; + std::atomic_thread_fence(std::memory_order_acquire); + do { + pos = (pos + posDecr) % circular_queue::m_bufSize; + T&& val = std::move(circular_queue::m_buffer[pos]); + if (fun(val)) + { + outPos1 = (outPos1 + posDecr) % circular_queue::m_bufSize; + if (outPos1 != pos) circular_queue::m_buffer[outPos1] = std::move(val); + } + } while (pos != outPos); + std::atomic_thread_fence(std::memory_order_release); + circular_queue::m_outPos.store(outPos1, std::memory_order_release); + return true; +} + +#endif // __circular_queue_h diff --git a/src/EspSoftwareSerial/src/circular_queue/circular_queue_mp.h b/src/EspSoftwareSerial/src/circular_queue/circular_queue_mp.h new file mode 100644 index 0000000..7d3b98e --- /dev/null +++ b/src/EspSoftwareSerial/src/circular_queue/circular_queue_mp.h @@ -0,0 +1,310 @@ +/* +circular_queue_mp.h - Implementation of a lock-free circular queue for EspSoftwareSerial. +Copyright (c) 2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __circular_queue_mp_h +#define __circular_queue_mp_h + +#include "circular_queue.h" + +#if defined(ESP8266) +#include +using esp8266::InterruptLock; +#endif + +/*! + @brief Instance class for a multi-producer, single-consumer circular queue / ring buffer (FIFO). + This implementation is lock-free between producers and consumer for the available(), peek(), + pop(), and push() type functions. +*/ +template< typename T, typename ForEachArg = void > +class circular_queue_mp : protected circular_queue +{ +public: + circular_queue_mp() : circular_queue() + { + m_inPos_mp.store(0); + m_concurrent_mp.store(0); + } + circular_queue_mp(const size_t capacity) : circular_queue(capacity) + { + m_inPos_mp.store(0); + m_concurrent_mp.store(0); + } + circular_queue_mp(circular_queue_mp&& cq) : circular_queue(std::move(cq)) + { + m_inPos_mp.store(cq.m_inPos_mp.load()); + m_concurrent_mp.store(cq.m_concurrent_mp.load()); + } + circular_queue_mp& operator=(circular_queue_mp&& cq) + { + circular_queue::operator=(std::move(cq)); + m_inPos_mp.store(cq.m_inPos_mp.load()); + m_concurrent_mp.store(cq.m_concurrent_mp.load()); + } + circular_queue_mp& operator=(const circular_queue_mp&) = delete; + + using circular_queue::capacity; + using circular_queue::flush; + using circular_queue::peek; + using circular_queue::pop; + using circular_queue::pop_n; + using circular_queue::for_each; + using circular_queue::for_each_rev_requeue; + + T& pushpeek() = delete; + bool push() = delete; + + inline size_t IRAM_ATTR available() const ALWAYS_INLINE_ATTR + { + return circular_queue::available(); + } + inline size_t IRAM_ATTR available_for_push() const ALWAYS_INLINE_ATTR + { + return circular_queue::available_for_push(); + } + + /*! + @brief Resize the queue. The available elements in the queue are preserved. + This is not lock-free and concurrent producer or consumer access + will lead to corruption. + @return True if the new capacity could accommodate the present elements in + the queue, otherwise nothing is done and false is returned. + */ + bool capacity(const size_t cap); + + /*! + @brief Move the rvalue parameter into the queue, guarded + for multiple concurrent producers. + @return true if the queue accepted the value, false if the queue + was full. + */ + bool push(T&& val); + + /*! + @brief Push a copy of the parameter into the queue, guarded + for multiple concurrent producers. + @return true if the queue accepted the value, false if the queue + was full. + */ + inline bool IRAM_ATTR push(const T& val) ALWAYS_INLINE_ATTR + { + T v(val); + return push(std::move(v)); + } + + /*! + @brief Push copies of multiple elements from a buffer into the queue, + in order, beginning at buffer's head. This is safe for + multiple producers. + @return The number of elements actually copied into the queue, counted + from the buffer head. + */ +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) + size_t push_n(const T* buffer, size_t size); +#endif + +protected: + std::atomic m_inPos_mp; + std::atomic m_concurrent_mp; +}; + +template< typename T, typename ForEachArg > +bool circular_queue_mp::capacity(const size_t cap) +{ + if (cap + 1 == circular_queue::m_bufSize) return true; + else if (!circular_queue::capacity(cap)) return false; + m_inPos_mp.store(circular_queue::m_inPos.load(std::memory_order_relaxed), + std::memory_order_relaxed); + m_concurrent_mp.store(0, std::memory_order_relaxed); + return true; +} + +template< typename T, typename ForEachArg > +bool IRAM_ATTR circular_queue_mp::push(T&& val) +{ + size_t inPos_mp; + size_t next; +#if !defined(ESP32) && defined(ARDUINO) + class InterruptLock { + public: + InterruptLock() { + noInterrupts(); + } + ~InterruptLock() { + interrupts(); + } + }; + { + InterruptLock lock; +#else + ++m_concurrent_mp; + do + { +#endif + inPos_mp = m_inPos_mp.load(std::memory_order_relaxed); + next = (inPos_mp + 1) % circular_queue::m_bufSize; + if (next == circular_queue::m_outPos.load(std::memory_order_relaxed)) { +#if !defined(ESP32) && defined(ARDUINO) + return false; + } + m_inPos_mp.store(next, std::memory_order_relaxed); + m_concurrent_mp.store(m_concurrent_mp.load(std::memory_order_relaxed) + 1, + std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_release); + } +#else + int concurrent_mp; + do + { + inPos_mp = m_inPos_mp.load(); + concurrent_mp = m_concurrent_mp.load(); + if (1 == concurrent_mp) + { + circular_queue::m_inPos.store(inPos_mp, std::memory_order_release); + } + } + while (!m_concurrent_mp.compare_exchange_weak(concurrent_mp, concurrent_mp - 1)); + return false; + } + } + while (!m_inPos_mp.compare_exchange_weak(inPos_mp, next)); +#endif + + circular_queue::m_buffer[inPos_mp] = std::move(val); + + std::atomic_thread_fence(std::memory_order_release); + +#if !defined(ESP32) && defined(ARDUINO) + { + InterruptLock lock; + if (1 == m_concurrent_mp.load(std::memory_order_relaxed)) + { + inPos_mp = m_inPos_mp.load(std::memory_order_relaxed); + circular_queue::m_inPos.store(inPos_mp, std::memory_order_relaxed); + } + m_concurrent_mp.store(m_concurrent_mp.load(std::memory_order_relaxed) - 1, + std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_release); + } +#else + int concurrent_mp; + do + { + inPos_mp = m_inPos_mp.load(); + concurrent_mp = m_concurrent_mp.load(); + if (1 == concurrent_mp) + { + circular_queue::m_inPos.store(inPos_mp, std::memory_order_release); + } + } + while (!m_concurrent_mp.compare_exchange_weak(concurrent_mp, concurrent_mp - 1)); +#endif + + return true; +} + +#if defined(ESP8266) || defined(ESP32) || !defined(ARDUINO) +template< typename T, typename ForEachArg > +size_t circular_queue_mp::push_n(const T* buffer, size_t size) +{ + const auto outPos = circular_queue::m_outPos.load(std::memory_order_relaxed); + size_t inPos_mp; + size_t next; + size_t blockSize; +#if !defined(ESP32) && defined(ARDUINO) + { + InterruptLock lock; +#else + ++m_concurrent_mp; + do + { +#endif + inPos_mp = m_inPos_mp.load(std::memory_order_relaxed); + blockSize = (outPos > inPos_mp) ? outPos - 1 - inPos_mp : (outPos == 0) ? circular_queue::m_bufSize - 1 - inPos_mp : circular_queue::m_bufSize - inPos_mp; + blockSize = min(size, blockSize); + if (!blockSize) + { +#if !defined(ESP32) && defined(ARDUINO) + return 0; + } + next = (inPos_mp + blockSize) % circular_queue::m_bufSize; + m_inPos_mp.store(next, std::memory_order_relaxed); + m_concurrent_mp.store(m_concurrent_mp.load(std::memory_order_relaxed) + 1, + std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_release); + } +#else + int concurrent_mp = m_concurrent_mp.load(); + do + { + inPos_mp = m_inPos_mp.load(); + concurrent_mp = m_concurrent_mp.load(); + if (1 == concurrent_mp) + { + circular_queue::m_inPos.store(inPos_mp, std::memory_order_release); + } + } + while (!m_concurrent_mp.compare_exchange_weak(concurrent_mp, concurrent_mp - 1)); + return false; + } + } + while (!m_inPos_mp.compare_exchange_weak(inPos_mp, next)); +#endif + + auto dest = circular_queue::m_buffer.get() + inPos_mp; + std::copy_n(std::make_move_iterator(buffer), blockSize, dest); + size = min(size - blockSize, outPos > 1 ? static_cast(outPos - next - 1) : 0); + next += size; + dest = circular_queue::m_buffer.get(); + std::copy_n(std::make_move_iterator(buffer + blockSize), size, dest); + + std::atomic_thread_fence(std::memory_order_release); + +#if !defined(ESP32) && defined(ARDUINO) + { + InterruptLock lock; + if (1 == m_concurrent_mp.load(std::memory_order_relaxed)) + { + inPos_mp = m_inPos_mp.load(std::memory_order_relaxed); + circular_queue::m_inPos.store(inPos_mp, std::memory_order_relaxed); + } + m_concurrent_mp.store(m_concurrent_mp.load(std::memory_order_relaxed) - 1, + std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_release); + } +#else + int concurrent_mp; + do + { + inPos_mp = m_inPos_mp.load(); + concurrent_mp = m_concurrent_mp.load(); + if (1 == concurrent_mp) + { + circular_queue::m_inPos.store(inPos_mp, std::memory_order_release); + } + } + while (!m_concurrent_mp.compare_exchange_weak(concurrent_mp, concurrent_mp - 1)); +#endif + + return blockSize + size; +} + +#endif + +#endif // __circular_queue_mp_h diff --git a/src/EspSoftwareSerial/src/circular_queue/ghostl.h b/src/EspSoftwareSerial/src/circular_queue/ghostl.h new file mode 100644 index 0000000..ba3f863 --- /dev/null +++ b/src/EspSoftwareSerial/src/circular_queue/ghostl.h @@ -0,0 +1,94 @@ +/* +ghostl.h - Implementation of a bare-bones, mostly no-op, C++ STL shell + that allows building some Arduino ESP8266/ESP32 + libraries on Aruduino AVR. +Copyright (c) 2019 Dirk O. Kaar. All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __ghostl_h +#define __ghostl_h + +#if defined(ARDUINO_ARCH_SAMD) +#include +#endif + +using size_t = decltype(sizeof(char)); + +namespace std +{ +#if !defined(ARDUINO_ARCH_SAMD) + typedef enum memory_order { + memory_order_relaxed, + memory_order_acquire, + memory_order_release, + memory_order_seq_cst + } memory_order; + template< typename T > class atomic { + private: + T value; + public: + atomic() {} + atomic(T desired) { value = desired; } + void store(T desired, std::memory_order = std::memory_order_seq_cst) volatile noexcept { value = desired; } + T load(std::memory_order = std::memory_order_seq_cst) const volatile noexcept { return value; } + }; + inline void atomic_thread_fence(std::memory_order order) noexcept {} + template< typename T > T&& move(T& t) noexcept { return static_cast(t); } +#endif + + template< typename T, size_t long N > struct array + { + T _M_elems[N]; + decltype(sizeof(0)) size() const { return N; } + T& operator[](decltype(sizeof(0)) i) { return _M_elems[i]; } + const T& operator[](decltype(sizeof(0)) i) const { return _M_elems[i]; } + }; + + template< typename T > class unique_ptr + { + public: + using pointer = T*; + unique_ptr() noexcept : ptr(nullptr) {} + unique_ptr(pointer p) : ptr(p) {} + pointer operator->() const noexcept { return ptr; } + T& operator[](decltype(sizeof(0)) i) const { return ptr[i]; } + void reset(pointer p = pointer()) noexcept + { + delete ptr; + ptr = p; + } + T& operator*() const { return *ptr; } + private: + pointer ptr; + }; + + template< typename T > using function = T*; + using nullptr_t = decltype(nullptr); + + template + struct identity { + typedef T type; + }; + + template + inline T&& forward(typename identity::type& t) noexcept + { + return static_cast::type&&>(t); + } +} + +#endif // __ghostl_h diff --git a/src/Software/assets/1698928110245.wav b/src/Software/assets/1698928110245.wav new file mode 100644 index 0000000..37ff478 Binary files /dev/null and b/src/Software/assets/1698928110245.wav differ diff --git a/src/Software/assets/external_comps/cn.kevinkun.BingMap/aiwebres/icon.png b/src/Software/assets/external_comps/cn.kevinkun.BingMap/aiwebres/icon.png new file mode 100644 index 0000000..ae6c795 Binary files /dev/null and b/src/Software/assets/external_comps/cn.kevinkun.BingMap/aiwebres/icon.png differ diff --git a/src/Software/assets/external_comps/cn.kevinkun.BingMap/assets/bingmap.html b/src/Software/assets/external_comps/cn.kevinkun.BingMap/assets/bingmap.html new file mode 100644 index 0000000..98429ab --- /dev/null +++ b/src/Software/assets/external_comps/cn.kevinkun.BingMap/assets/bingmap.html @@ -0,0 +1,489 @@ + + + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/src/Software/assets/external_comps/cn.kevinkun.BingMap/assets/pin1.png b/src/Software/assets/external_comps/cn.kevinkun.BingMap/assets/pin1.png new file mode 100644 index 0000000..52d2445 Binary files /dev/null and b/src/Software/assets/external_comps/cn.kevinkun.BingMap/assets/pin1.png differ diff --git a/src/Software/assets/external_comps/cn.kevinkun.BingMap/assets/pin2.png b/src/Software/assets/external_comps/cn.kevinkun.BingMap/assets/pin2.png new file mode 100644 index 0000000..e036c18 Binary files /dev/null and b/src/Software/assets/external_comps/cn.kevinkun.BingMap/assets/pin2.png differ diff --git a/src/Software/assets/external_comps/cn.kevinkun.BingMap/assets/pin3.png b/src/Software/assets/external_comps/cn.kevinkun.BingMap/assets/pin3.png new file mode 100644 index 0000000..7ce2d0c Binary files /dev/null and b/src/Software/assets/external_comps/cn.kevinkun.BingMap/assets/pin3.png differ diff --git a/src/Software/assets/external_comps/cn.kevinkun.BingMap/classes.jar b/src/Software/assets/external_comps/cn.kevinkun.BingMap/classes.jar new file mode 100644 index 0000000..4fcb2d7 Binary files /dev/null and b/src/Software/assets/external_comps/cn.kevinkun.BingMap/classes.jar differ diff --git a/src/Software/assets/external_comps/cn.kevinkun.BingMap/components.json b/src/Software/assets/external_comps/cn.kevinkun.BingMap/components.json new file mode 100644 index 0000000..d78bc50 --- /dev/null +++ b/src/Software/assets/external_comps/cn.kevinkun.BingMap/components.json @@ -0,0 +1 @@ +[{"categoryString":"EXTENSION","dateBuilt":"2023-02-27T21:02:34+0800","iconName":"aiwebres/icon.png","helpUrl":"https://www.kevinkun.cn/bingmap/","type":"cn.kevinkun.BingMap.BingMap","version":"1","helpString":"Bing map. visit here to get a key:https://learn.microsoft.com/en-us/bingmaps/getting-started/bing-maps-dev-center-help/getting-a-bing-maps-key","androidMinSdk":7,"nonVisible":"true","external":"true","showOnPalette":"true","events":[{"deprecated":"false","params":[{"type":"list","name":"location"}],"name":"MapClicked","description":""},{"deprecated":"false","params":[{"type":"list","name":"center"},{"type":"list","name":"bounds"},{"type":"text","name":"zoom"}],"name":"MapViewChanged","description":""},{"deprecated":"false","params":[{"type":"text","name":"id"},{"type":"list","name":"location"},{"type":"list","name":"point"}],"name":"PushpinClicked","description":""},{"deprecated":"false","params":[{"type":"text","name":"id"},{"type":"list","name":"location"},{"type":"list","name":"point"}],"name":"PushpinDragEnd","description":""},{"deprecated":"false","params":[{"type":"text","name":"id"},{"type":"list","name":"location"},{"type":"list","name":"point"}],"name":"PolylineClicked","description":""},{"deprecated":"false","params":[{"type":"text","name":"id"},{"type":"list","name":"location"},{"type":"list","name":"point"}],"name":"PolygonClicked","description":""},{"deprecated":"false","params":[{"type":"text","name":"action"}],"name":"ActionClicked","description":""},{"deprecated":"false","params":[{"type":"text","name":"address"}],"name":"GotAddress","description":""},{"deprecated":"false","params":[{"type":"any","name":"locations"}],"name":"GotLocations","description":""},{"deprecated":"false","params":[{"type":"text","name":"total"},{"type":"text","name":"index"},{"type":"text","name":"route"}],"name":"RouteUpdated","description":""},{"deprecated":"false","params":[{"type":"text","name":"message"}],"name":"OnError","description":""}],"methods":[{"deprecated":"false","params":[{"type":"any","name":"layout"}],"name":"Init","description":"load map in the layout"},{"deprecated":"false","params":[{"type":"text","name":"center"}],"name":"SetCenter","description":"pan the map to center"},{"deprecated":"false","params":[{"type":"number","name":"zoom"}],"name":"SetZoom","description":"set zoom"},{"deprecated":"false","params":[{"type":"text","name":"id"},{"type":"text","name":"location"},{"type":"text","name":"options"}],"name":"CreatePushpin","description":"id: unique id for pushpin.\n\nlocation:list like [lat,lng]\n\noptions:dictionary like {title: \u0027title\u0027,subTitle: \u0027subtitle\u0027,text: \u0027text\u0027, color: \u0027blue\u0027,icon: \u0027poi-custom.png\u0027, draggable: false }"},{"deprecated":"false","params":[{"type":"text","name":"id"},{"type":"text","name":"locations"},{"type":"text","name":"options"}],"name":"CreatePolyline","description":"id: unique id for polyline.\n\nlocations:list of list, like [[lat,lng],[lat,lng],...]\n\noptions:dictionary like {strokeColor: \u0027red\u0027,strokeThickness: 3, strokeDashArray: [10,10] }"},{"deprecated":"false","params":[{"type":"text","name":"id"},{"type":"text","name":"locations"},{"type":"text","name":"options"}],"name":"CreatePolygon","description":"id: unique id for polygon.\n\nlocations:list of list, like [[lat,lng],[lat,lng],...]\n\noptions \u003d { fillColor: \u0027#ff000022\u0027, strokeColor: \u0027#00ff00\u0027, strokeThickness: 4, strokeDashArray: [10,5] }"},{"deprecated":"false","params":[{"type":"text","name":"id"},{"type":"text","name":"center"},{"type":"number","name":"radius"},{"type":"number","name":"sides"},{"type":"number","name":"angle"},{"type":"text","name":"options"}],"name":"CreatePolygonRegular","description":"id: unique id for pushpin.\n\ncenter: list, like [lat,lng]\n\nradius: number\n\nangle: number, 0-360\n\nside: number\n\noptions: dictionary like { fillColor: \u0027#ff000022\u0027, strokeColor: \u0027#00ff00\u0027, strokeThickness: 4, strokeDashArray: [10,5] }"},{"deprecated":"false","params":[{"type":"text","name":"id"}],"name":"RemoveShape","description":"remove shape(pushpin, polyline, polygon) by id"},{"deprecated":"false","params":[],"name":"RemoveAllShape","description":"removal all shapes"},{"deprecated":"false","params":[{"type":"text","name":"location"},{"type":"text","name":"title"},{"type":"text","name":"decription"},{"type":"text","name":"actions"}],"name":"ShowInfobox","description":"show infobox at location"},{"deprecated":"false","params":[],"name":"HideInfobox","description":"hide the infobox"},{"deprecated":"false","params":[{"type":"text","name":"locations"},{"type":"text","name":"mode"}],"name":"CalculateRoute","description":"locations: list of list, like [[lat,lng],[lat,lng],...]\n\nmode:String, one of driving, transit, truck, or walking"},{"deprecated":"false","params":[],"name":"RemoveRoute","description":"remove the route"},{"deprecated":"false","params":[{"type":"text","name":"location"}],"name":"GetAddressFrom","description":"change location [lat,lng] to address"},{"deprecated":"false","params":[{"type":"text","name":"address"}],"name":"GetLocationsFrom","description":"change address to locations (maybe more than one location)"},{"deprecated":"false","params":[{"type":"list","name":"location1"},{"type":"list","name":"location2"}],"returnType":"number","name":"getDistance","description":""}],"blockProperties":[{"rw":"read-only","type":"number","deprecated":"false","name":"MinZoom","description":""},{"rw":"read-only","type":"text","deprecated":"false","name":"Center","description":""},{"rw":"read-only","type":"boolean","deprecated":"false","name":"EnablePanning","description":""},{"rw":"read-only","type":"boolean","deprecated":"false","name":"ShowLocateMeButton","description":""},{"rw":"read-only","type":"boolean","deprecated":"false","name":"EnableZooming","description":""},{"rw":"read-only","type":"boolean","deprecated":"false","name":"ShowZoomButtons","description":""},{"rw":"read-only","type":"number","deprecated":"false","name":"Zoom","description":""},{"rw":"read-only","type":"number","deprecated":"false","name":"MaxZoom","description":""},{"rw":"read-only","type":"boolean","deprecated":"false","name":"ShowScalebar","description":""},{"rw":"read-only","type":"text","deprecated":"false","name":"Key","description":""},{"rw":"read-only","type":"boolean","deprecated":"false","name":"ShowMapTypeSelector","description":""},{"rw":"read-only","type":"text","deprecated":"false","name":"MapType","description":""}],"properties":[{"defaultValue":"[39.92, 116.40]","editorArgs":[],"alwaysSend":false,"editorType":"string","name":"Center"},{"defaultValue":"15","editorArgs":[],"alwaysSend":false,"editorType":"non_negative_integer","name":"Zoom"},{"defaultValue":"","editorArgs":[],"alwaysSend":false,"editorType":"string","name":"Key"},{"defaultValue":"True","editorArgs":[],"alwaysSend":false,"editorType":"boolean","name":"EnablePanning"},{"defaultValue":"True","editorArgs":[],"alwaysSend":false,"editorType":"boolean","name":"EnableZooming"},{"defaultValue":"17","editorArgs":[],"alwaysSend":false,"editorType":"non_negative_integer","name":"MaxZoom"},{"defaultValue":"5","editorArgs":[],"alwaysSend":false,"editorType":"non_negative_integer","name":"MinZoom"},{"defaultValue":"True","editorArgs":[],"alwaysSend":false,"editorType":"boolean","name":"ShowLocateMeButton"},{"defaultValue":"True","editorArgs":[],"alwaysSend":false,"editorType":"boolean","name":"ShowMapTypeSelector"},{"defaultValue":"True","editorArgs":[],"alwaysSend":false,"editorType":"boolean","name":"ShowScalebar"},{"defaultValue":"True","editorArgs":[],"alwaysSend":false,"editorType":"boolean","name":"ShowZoomButtons"},{"defaultValue":"road","editorArgs":["road","aerial","streetside"],"alwaysSend":false,"editorType":"choices","name":"MapType"}],"name":"BingMap"}] \ No newline at end of file diff --git a/src/Software/assets/external_comps/cn.kevinkun.BingMap/files/AndroidRuntime.jar b/src/Software/assets/external_comps/cn.kevinkun.BingMap/files/AndroidRuntime.jar new file mode 100644 index 0000000..655b700 Binary files /dev/null and b/src/Software/assets/external_comps/cn.kevinkun.BingMap/files/AndroidRuntime.jar differ diff --git a/src/Software/assets/external_comps/cn.kevinkun.BingMap/files/component_build_infos.json b/src/Software/assets/external_comps/cn.kevinkun.BingMap/files/component_build_infos.json new file mode 100644 index 0000000..8b0a131 --- /dev/null +++ b/src/Software/assets/external_comps/cn.kevinkun.BingMap/files/component_build_infos.json @@ -0,0 +1 @@ +[{"permissions":["android.permission.ACCESS_FINE_LOCATION"],"conditionalPermissions":{},"conditionalBroadcastReceivers":{},"libraries":[],"native":[],"broadcastReceiver":[],"assets":["pin3.png","pin2.png","bingmap.html","pin1.png"],"activities":[],"broadcastReceivers":[],"type":"cn.kevinkun.BingMap.BingMap","androidMinSdk":["7"]}] \ No newline at end of file diff --git a/src/Software/assets/external_comps/com.sunny.tts/classes.jar b/src/Software/assets/external_comps/com.sunny.tts/classes.jar new file mode 100644 index 0000000..42f6d3b Binary files /dev/null and b/src/Software/assets/external_comps/com.sunny.tts/classes.jar differ diff --git a/src/Software/assets/external_comps/com.sunny.tts/components.json b/src/Software/assets/external_comps/com.sunny.tts/components.json new file mode 100644 index 0000000..318ea89 --- /dev/null +++ b/src/Software/assets/external_comps/com.sunny.tts/components.json @@ -0,0 +1 @@ +[{"categoryString":"EXTENSION","dateBuilt":"2023-09-21","nonVisible":"true","iconName":"https://res.cloudinary.com/andromedaviewflyvipul/image/upload/c_scale,h_20,w_20/v1571472765/ktvu4bapylsvnykoyhdm.png","methods":[{"deprecated":"false","name":"GetVoicesList","description":"Gets a list of voices in dictionary form","params":[]},{"deprecated":"false","name":"TextToSpeech","description":"Converts text to speech and saves audio file to output path","params":[{"name":"text","type":"text"},{"name":"outputhFile","type":"text"},{"name":"voiceName","type":"text"},{"name":"voiceGender","type":"text"},{"name":"voiceLang","type":"text"}]}],"blockProperties":[{"rw":"write-only","deprecated":"false","name":"ApiKey","description":"Set API key","type":"text"},{"rw":"write-only","deprecated":"false","name":"ResourceRegion","description":"Set resource region","type":"text"}],"helpUrl":"https://sunnythedeveloper.in/microsofttts-convert-text-to-audio-using-microsoft-texttospeech-api/","licenseName":"https://creativecommons.org/licenses/by-sa/4.0/","type":"com.sunny.tts.MicrosoftTTS","androidMinSdk":7,"versionName":"1","version":"4","external":"true","showOnPalette":"true","name":"MicrosoftTTS","helpString":"

Extension to convert Text to Speech using Microsoft Text-To-Speech API
Developed by Sunny Gupta<\/p>\n","events":[{"deprecated":"false","name":"GotVoicesList","description":"Event raised after gettting voices list. Each list element is a dictionary containing voice information.","params":[{"name":"reponseList","type":"list"}]},{"deprecated":"false","name":"GotError","description":"Event raised when error occurs","params":[{"name":"methodName","type":"text"},{"name":"errorMsg","type":"text"}]},{"deprecated":"false","name":"GotSpeechFile","description":"Event raised when text was successfully converted to audio and file was saved","params":[{"name":"outputFile","type":"text"}]}],"properties":[]}] \ No newline at end of file diff --git a/src/Software/assets/external_comps/com.sunny.tts/extension.properties b/src/Software/assets/external_comps/com.sunny.tts/extension.properties new file mode 100644 index 0000000..23bde9b --- /dev/null +++ b/src/Software/assets/external_comps/com.sunny.tts/extension.properties @@ -0,0 +1,2 @@ +type=external +rush-version=1.2.4 diff --git a/src/Software/assets/external_comps/com.sunny.tts/files/AndroidRuntime.jar b/src/Software/assets/external_comps/com.sunny.tts/files/AndroidRuntime.jar new file mode 100644 index 0000000..74da59d Binary files /dev/null and b/src/Software/assets/external_comps/com.sunny.tts/files/AndroidRuntime.jar differ diff --git a/src/Software/assets/external_comps/com.sunny.tts/files/component_build_infos.json b/src/Software/assets/external_comps/com.sunny.tts/files/component_build_infos.json new file mode 100644 index 0000000..8292728 --- /dev/null +++ b/src/Software/assets/external_comps/com.sunny.tts/files/component_build_infos.json @@ -0,0 +1 @@ +[{"assets":[],"activities":[],"permissions":["android.permission.INTERNET"],"type":"com.sunny.tts.MicrosoftTTS","androidMinSdk":[7]}] \ No newline at end of file diff --git a/src/Software/src/appinventor/ai_karlzj/ruangong/Screen1.bky b/src/Software/src/appinventor/ai_karlzj/ruangong/Screen1.bky new file mode 100644 index 0000000..d8da9b6 --- /dev/null +++ b/src/Software/src/appinventor/ai_karlzj/ruangong/Screen1.bky @@ -0,0 +1 @@ +播放间隔3距离emc0count0TEXT播报过程播报CheckedEQglobal countglobal 播放间隔文本语音转换器1当前前方距离global 距离100global count0global countglobal count1读取距离global TEXT蓝牙客户端1蓝牙客户端1global TEXTSPLITglobal TEXT,global 距离global TEXT325文本输入框_距离Textglobal 距离cm监测报警LTglobal 距离9.00文本语音转换器1距离过近global emcglobal emc1global emc0EQglobal emc5callingemc连接选中设备蓝牙客户端1列表选择框_蓝牙Selection列表选择框_蓝牙Selection蓝牙客户端1列表选择框_蓝牙Selection列表选择框_蓝牙EnabledFALSE计时器1TimerEnabledTRUE按钮_拨打电话calling按钮_关闭按钮_实时定位dingwei按钮2global 播放间隔文本输入框_计时Text计时器1GT蓝牙客户端10读取距离监测报警播报过程Screen1按钮_避障EnabledTRUE蓝牙客户端1列表选择框_蓝牙EnabledTRUE计时器1TimerEnabledFALSE列表选择框_蓝牙列表选择框_蓝牙Elements蓝牙客户端1AddressesAndNames列表选择框_蓝牙连接选中设备播报Textglobal count \ No newline at end of file diff --git a/src/Software/src/appinventor/ai_karlzj/ruangong/Screen1.bky.2023-10-23T14_34_53+0800.backup b/src/Software/src/appinventor/ai_karlzj/ruangong/Screen1.bky.2023-10-23T14_34_53+0800.backup new file mode 100644 index 0000000..3c4f3ce --- /dev/null +++ b/src/Software/src/appinventor/ai_karlzj/ruangong/Screen1.bky.2023-10-23T14_34_53+0800.backup @@ -0,0 +1,10 @@ + + + + 按钮1 + + + + + + \ No newline at end of file diff --git a/src/Software/src/appinventor/ai_karlzj/ruangong/Screen1.bky.2023-10-25T15_03_56+0800.backup b/src/Software/src/appinventor/ai_karlzj/ruangong/Screen1.bky.2023-10-25T15_03_56+0800.backup new file mode 100644 index 0000000..e42f47e --- /dev/null +++ b/src/Software/src/appinventor/ai_karlzj/ruangong/Screen1.bky.2023-10-25T15_03_56+0800.backup @@ -0,0 +1,41 @@ + + + + 按钮_避障 + Enabled + + + + 按钮_实时定位 + + + + + dingwei + + + + + + + + 按钮_拨打电话 + + + + + calling + + + + + + + + 按钮_关闭 + + + + + + \ No newline at end of file diff --git a/src/Software/src/appinventor/ai_karlzj/ruangong/Screen1.scm b/src/Software/src/appinventor/ai_karlzj/ruangong/Screen1.scm new file mode 100644 index 0000000..08cb060 --- /dev/null +++ b/src/Software/src/appinventor/ai_karlzj/ruangong/Screen1.scm @@ -0,0 +1,4 @@ +#| +$JSON +{"authURL":["app.gzjkw.net","ai2.appinventor.mit.edu"],"YaVersion":"224","Source":"Form","Properties":{"$Name":"Screen1","$Type":"Form","$Version":"30","ActionBar":"True","AlignHorizontal":"3","AppName":"ruangong","ShowListsAsJson":"False","Theme":"AppTheme.Light.DarkActionBar","Title":"\u907f\u969c\u76d1\u6d4b","Uuid":"0","$Components":[{"$Name":"\u6c34\u5e73\u5e03\u5c404","$Type":"HorizontalArrangement","$Version":"4","AlignHorizontal":"3","Height":"378","Width":"-2","Uuid":"-1446214067","$Components":[{"$Name":"\u6309\u94ae_\u907f\u969c","$Type":"Button","$Version":"7","Text":"\u907f\u969c","Uuid":"-644814633"},{"$Name":"\u6309\u94ae_\u5b9e\u65f6\u5b9a\u4f4d","$Type":"Button","$Version":"7","Text":"\u5b9e\u65f6\u5b9a\u4f4d","Uuid":"-18939228"},{"$Name":"\u6309\u94ae_\u62e8\u6253\u7535\u8bdd","$Type":"Button","$Version":"7","Text":"\u62e8\u6253\u7535\u8bdd","Uuid":"2062084651"},{"$Name":"\u5217\u8868\u9009\u62e9\u6846_\u84dd\u7259","$Type":"ListPicker","$Version":"9","Text":"\u8fde\u63a5\u8bbe\u5907","Uuid":"-1183153014"}]},{"$Name":"\u6c34\u5e73\u5e03\u5c403","$Type":"HorizontalArrangement","$Version":"4","AlignHorizontal":"3","AlignVertical":"2","Uuid":"-2055779801","$Components":[{"$Name":"\u6807\u7b7e_\u8ddd\u79bb","$Type":"Label","$Version":"5","Text":"\u8ddd\u79bb\uff1a","Uuid":"1009622951"},{"$Name":"\u6587\u672c\u8f93\u5165\u6846_\u8ddd\u79bb","$Type":"TextBox","$Version":"6","ReadOnly":"True","Uuid":"-2128440146"},{"$Name":"\u64ad\u62a5","$Type":"CheckBox","$Version":"2","Text":"\u81ea\u52a8\u64ad\u62a5","Uuid":"405481945"}]},{"$Name":"\u5782\u76f4\u5e03\u5c401","$Type":"VerticalArrangement","$Version":"4","AlignHorizontal":"3","AlignVertical":"3","Height":"-1040","Width":"-2","Uuid":"2081953051","$Components":[{"$Name":"\u6c34\u5e73\u5e03\u5c402","$Type":"HorizontalArrangement","$Version":"4","AlignHorizontal":"3","AlignVertical":"2","Uuid":"177806287","$Components":[{"$Name":"\u6807\u7b7e_\u8ba1\u65f6","$Type":"Label","$Version":"5","Text":"\u8ba1\u65f6\uff1a","Uuid":"-911991431"},{"$Name":"\u6587\u672c\u8f93\u5165\u6846_\u8ba1\u65f6","$Type":"TextBox","$Version":"6","NumbersOnly":"True","Text":"1","Uuid":"-152203493"},{"$Name":"\u6309\u94ae2","$Type":"Button","$Version":"7","Text":"\u786e\u5b9a","Uuid":"-444567807"}]},{"$Name":"\u6c34\u5e73\u5e03\u5c405","$Type":"HorizontalArrangement","$Version":"4","AlignHorizontal":"2","AlignVertical":"3","Width":"-2","Uuid":"-1161251788","$Components":[{"$Name":"\u6309\u94ae_\u5173\u95ed","$Type":"Button","$Version":"7","Text":"\u5173\u95ed","Uuid":"1124974886"}]}]},{"$Name":"\u84dd\u7259\u5ba2\u6237\u7aef1","$Type":"BluetoothClient","$Version":"8","Uuid":"-1413619977"},{"$Name":"\u8ba1\u65f6\u56681","$Type":"Clock","$Version":"4","Uuid":"530919694"},{"$Name":"\u6587\u672c\u8bed\u97f3\u8f6c\u6362\u56681","$Type":"TextToSpeech","$Version":"6","Uuid":"-1761536897"},{"$Name":"\u77ed\u4fe1\u6536\u53d1\u56681","$Type":"Texting","$Version":"5","Uuid":"-1268134884"}]}} +|# \ No newline at end of file diff --git a/src/Software/src/appinventor/ai_karlzj/ruangong/Screen1.scm.2023-10-23T14_24_48+0800.backup b/src/Software/src/appinventor/ai_karlzj/ruangong/Screen1.scm.2023-10-23T14_24_48+0800.backup new file mode 100644 index 0000000..e62ce22 --- /dev/null +++ b/src/Software/src/appinventor/ai_karlzj/ruangong/Screen1.scm.2023-10-23T14_24_48+0800.backup @@ -0,0 +1,4 @@ +#| +$JSON +{"authURL":["app.gzjkw.net"],"YaVersion":"172","Source":"Form","Properties":{"$Name":"Screen1","$Type":"Form","$Version":"23","AppName":"ruangong","Title":"Screen1","Uuid":"0","$Components":[{"$Name":"\u5217\u8868\u663e\u793a\u68461","$Type":"ListView","$Version":"5","Uuid":"1220671722"},{"$Name":"\u6309\u94ae1","$Type":"Button","$Version":"6","Text":"\u5173\u95ed","Uuid":"-644814633"}]}} +|# \ No newline at end of file diff --git a/src/Software/src/appinventor/ai_karlzj/ruangong/Screen1.scm.2023-10-25T15_02_46+0800.backup b/src/Software/src/appinventor/ai_karlzj/ruangong/Screen1.scm.2023-10-25T15_02_46+0800.backup new file mode 100644 index 0000000..1d8c731 --- /dev/null +++ b/src/Software/src/appinventor/ai_karlzj/ruangong/Screen1.scm.2023-10-25T15_02_46+0800.backup @@ -0,0 +1,4 @@ +#| +$JSON +{"authURL":["app.gzjkw.net"],"YaVersion":"172","Source":"Form","Properties":{"$Name":"Screen1","$Type":"Form","$Version":"23","AlignHorizontal":"3","AlignVertical":"2","AppName":"ruangong","Title":"Screen1","Uuid":"0","$Components":[{"$Name":"\u6c34\u5e73\u5e03\u5c401","$Type":"HorizontalArrangement","$Version":"3","AlignHorizontal":"3","AlignVertical":"2","Uuid":"177113749","$Components":[{"$Name":"\u6309\u94ae_\u907f\u969c","$Type":"Button","$Version":"6","Text":"\u907f\u969c","Uuid":"-644814633"},{"$Name":"\u6309\u94ae_\u5b9e\u65f6\u5b9a\u4f4d","$Type":"Button","$Version":"6","Text":"\u5b9e\u65f6\u5b9a\u4f4d","Uuid":"-18939228"},{"$Name":"\u6309\u94ae_\u62e8\u6253\u7535\u8bdd","$Type":"Button","$Version":"6","Text":"\u62e8\u6253\u7535\u8bdd","Uuid":"2062084651"}]},{"$Name":"\u5782\u76f4\u5e03\u5c401","$Type":"VerticalArrangement","$Version":"3","AlignHorizontal":"2","AlignVertical":"2","Uuid":"-1007398740","$Components":[{"$Name":"\u6c34\u5e73\u5e03\u5c402","$Type":"HorizontalArrangement","$Version":"3","Uuid":"177806287","$Components":[{"$Name":"\u6807\u7b7e_\u8ba1\u65f6","$Type":"Label","$Version":"4","Text":"\u8ba1\u65f6\uff1a","Uuid":"-911991431"},{"$Name":"\u6587\u672c\u8f93\u5165\u6846_\u8ba1\u65f6","$Type":"TextBox","$Version":"5","Uuid":"-152203493"}]},{"$Name":"\u6c34\u5e73\u5e03\u5c403","$Type":"HorizontalArrangement","$Version":"3","Uuid":"-2055779801","$Components":[{"$Name":"\u6807\u7b7e_\u8ddd\u79bb","$Type":"Label","$Version":"4","Text":"\u8ddd\u79bb\uff1a","Uuid":"1009622951"},{"$Name":"\u6587\u672c\u8f93\u5165\u6846_\u8ddd\u79bb","$Type":"TextBox","$Version":"5","Uuid":"-2128440146"}]},{"$Name":"\u6309\u94ae_\u5173\u95ed","$Type":"Button","$Version":"6","Text":"\u5173\u95ed","Uuid":"1124974886"}]}]}} +|# \ No newline at end of file diff --git a/src/Software/src/appinventor/ai_karlzj/ruangong/calling.bky b/src/Software/src/appinventor/ai_karlzj/ruangong/calling.bky new file mode 100644 index 0000000..4263733 --- /dev/null +++ b/src/Software/src/appinventor/ai_karlzj/ruangong/calling.bky @@ -0,0 +1 @@ +calling按钮_拨打电话EnabledTRUEEQUALemc电话拨号器_拨打电话PhoneNumber19968538899电话拨号器_拨打电话电话拨号器_拨打电话按钮_实时定位dingwei电话拨号器_拨打电话按钮_避障Screen1电话拨号器_拨打电话按钮_关闭列表选择框_电话列表NEQ列表选择框_电话列表Selection电话号码电话拨号器_拨打电话PhoneNumber列表选择框_电话列表Selection电话拨号器_拨打电话电话拨号器_拨打电话 \ No newline at end of file diff --git a/src/Software/src/appinventor/ai_karlzj/ruangong/calling.scm b/src/Software/src/appinventor/ai_karlzj/ruangong/calling.scm new file mode 100644 index 0000000..8d45ae0 --- /dev/null +++ b/src/Software/src/appinventor/ai_karlzj/ruangong/calling.scm @@ -0,0 +1,4 @@ +#| +$JSON +{"authURL":["app.gzjkw.net","ai2.appinventor.mit.edu"],"YaVersion":"224","Source":"Form","Properties":{"$Name":"calling","$Type":"Form","$Version":"30","ActionBar":"True","AlignHorizontal":"3","AppName":"ruangong","ShowListsAsJson":"False","Theme":"AppTheme.Light.DarkActionBar","Title":"\u62e8\u6253\u7535\u8bdd","Uuid":"0","$Components":[{"$Name":"\u6c34\u5e73\u5e03\u5c401","$Type":"HorizontalArrangement","$Version":"4","AlignHorizontal":"3","Height":"-1040","Width":"-2","Uuid":"497758912","$Components":[{"$Name":"\u6309\u94ae_\u907f\u969c","$Type":"Button","$Version":"7","Text":"\u907f\u969c","Uuid":"-1955423566"},{"$Name":"\u6309\u94ae_\u5b9e\u65f6\u5b9a\u4f4d","$Type":"Button","$Version":"7","Text":"\u5b9e\u65f6\u5b9a\u4f4d","Uuid":"479692292"},{"$Name":"\u6309\u94ae_\u62e8\u6253\u7535\u8bdd","$Type":"Button","$Version":"7","Text":"\u62e8\u6253\u7535\u8bdd","Uuid":"-1181431767"}]},{"$Name":"\u5782\u76f4\u5e03\u5c401","$Type":"VerticalArrangement","$Version":"4","AlignHorizontal":"3","AlignVertical":"2","Uuid":"-1614689866","$Components":[{"$Name":"\u5217\u8868\u9009\u62e9\u6846_\u7535\u8bdd\u5217\u8868","$Type":"ListPicker","$Version":"9","ElementsFromString":"123,19968538899","Text":"\u9009\u62e9\u7d27\u6025\u8054\u7cfb\u4eba","Uuid":"-1795360827"}]},{"$Name":"\u6c34\u5e73\u5e03\u5c402","$Type":"HorizontalArrangement","$Version":"4","AlignHorizontal":"2","AlignVertical":"3","Height":"-1040","Width":"-2","Uuid":"923044023","$Components":[{"$Name":"\u6309\u94ae_\u5173\u95ed","$Type":"Button","$Version":"7","Text":"\u5173\u95ed","Uuid":"2024754233"}]},{"$Name":"\u7535\u8bdd\u62e8\u53f7\u5668_\u62e8\u6253\u7535\u8bdd","$Type":"PhoneCall","$Version":"3","Uuid":"-1369827363"}]}} +|# \ No newline at end of file diff --git a/src/Software/src/appinventor/ai_karlzj/ruangong/dingwei.bky b/src/Software/src/appinventor/ai_karlzj/ruangong/dingwei.bky new file mode 100644 index 0000000..4ce2c6c --- /dev/null +++ b/src/Software/src/appinventor/ai_karlzj/ruangong/dingwei.bky @@ -0,0 +1 @@ +按钮_避障Screen1按钮_拨打电话calling按钮_关闭gps 117.3439568791239,39.11525785493998amap刷新Web客户端1Urlhttps://restapi.amap.com/v3/assistant/coordinate/convert?locations=global gps&coordsys=gps&output=json&key=e341d030386262503123cf0ace0082f9Web客户端1jsonWeb客户端1global jsonWeb客户端1responseContentglobal amaplocationsglobal jsonnot foundglobal amap117.35264243664,39.111479611546Web浏览框1https://restapi.amap.com/v3/staticmap?location=global amap&zoom=17&size=750*750&markers=mid,,A:global amap&key=e341d030386262503123cf0ace0082f9dingwei蓝牙客户端1列表选择框_蓝牙EnabledTRUE计时器1TimerEnabledTRUE位置传感器1EnabledTRUE位置传感器1EnabledTRUE列表选择框_蓝牙列表选择框_蓝牙Elements蓝牙客户端1AddressesAndNames列表选择框_蓝牙蓝牙客户端1列表选择框_蓝牙Selection列表选择框_蓝牙Selection蓝牙客户端1列表选择框_蓝牙Selection列表选择框_蓝牙EnabledFALSE计时器1TimerEnabledTRUE117.35044243664,39.116379611546117.35044243664,39.116379611546位置传感器1CurrentAddress计时器1短信收发器1PhoneNumber19968538899短信收发器1Message当前老人位置为global gps短信收发器1短信收发器1 \ No newline at end of file diff --git a/src/Software/src/appinventor/ai_karlzj/ruangong/dingwei.bky.2023-10-26T09_12_23+0800.backup b/src/Software/src/appinventor/ai_karlzj/ruangong/dingwei.bky.2023-10-26T09_12_23+0800.backup new file mode 100644 index 0000000..722cbce --- /dev/null +++ b/src/Software/src/appinventor/ai_karlzj/ruangong/dingwei.bky.2023-10-26T09_12_23+0800.backup @@ -0,0 +1,41 @@ + + + + 按钮_避障 + + + + + Screen1 + + + + + + + + 按钮_实时定位 + Enabled + + + + 按钮_拨打电话 + + + + + calling + + + + + + + + 按钮_关闭 + + + + + + \ No newline at end of file diff --git a/src/Software/src/appinventor/ai_karlzj/ruangong/dingwei.scm b/src/Software/src/appinventor/ai_karlzj/ruangong/dingwei.scm new file mode 100644 index 0000000..b30238a --- /dev/null +++ b/src/Software/src/appinventor/ai_karlzj/ruangong/dingwei.scm @@ -0,0 +1,4 @@ +#| +$JSON +{"authURL":["app.gzjkw.net","ai2.appinventor.mit.edu"],"YaVersion":"224","Source":"Form","Properties":{"$Name":"dingwei","$Type":"Form","$Version":"30","ActionBar":"True","AlignHorizontal":"3","AppName":"ruangong","ShowListsAsJson":"False","Theme":"AppTheme.Light.DarkActionBar","Title":"\u5b9e\u65f6\u5b9a\u4f4d","Uuid":"0","$Components":[{"$Name":"\u6c34\u5e73\u5e03\u5c401","$Type":"HorizontalArrangement","$Version":"4","AlignHorizontal":"3","AlignVertical":"2","Uuid":"-655869917","$Components":[{"$Name":"\u6309\u94ae_\u907f\u969c","$Type":"Button","$Version":"7","Text":"\u907f\u969c","Uuid":"1440681744"},{"$Name":"\u6309\u94ae_\u5b9e\u65f6\u5b9a\u4f4d","$Type":"Button","$Version":"7","Text":"\u5b9e\u65f6\u5b9a\u4f4d","Uuid":"652290387"},{"$Name":"\u6309\u94ae_\u62e8\u6253\u7535\u8bdd","$Type":"Button","$Version":"7","Text":"\u62e8\u6253\u7535\u8bdd","Uuid":"1956743508"},{"$Name":"\u5217\u8868\u9009\u62e9\u6846_\u84dd\u7259","$Type":"ListPicker","$Version":"9","Text":"\u8fde\u63a5\u8bbe\u5907","Uuid":"1464195969"}]},{"$Name":"\u6c34\u5e73\u5e03\u5c403","$Type":"HorizontalArrangement","$Version":"4","AlignHorizontal":"3","AlignVertical":"2","Height":"-1060","Width":"-2","Uuid":"1743176679","$Components":[{"$Name":"Web\u6d4f\u89c8\u68461","$Type":"WebViewer","$Version":"10","Uuid":"177751501"}]},{"$Name":"\u6c34\u5e73\u5e03\u5c402","$Type":"HorizontalArrangement","$Version":"4","AlignHorizontal":"3","AlignVertical":"2","Uuid":"785425026","$Components":[{"$Name":"\u5237\u65b0","$Type":"Button","$Version":"7","Text":"\u5237\u65b0","Uuid":"-990679099"}]},{"$Name":"\u6c34\u5e73\u5e03\u5c404","$Type":"HorizontalArrangement","$Version":"4","AlignHorizontal":"2","AlignVertical":"3","Height":"-1020","Width":"-2","Uuid":"1670022820","$Components":[{"$Name":"\u6309\u94ae_\u5173\u95ed","$Type":"Button","$Version":"7","Text":"\u5173\u95ed","Uuid":"-1949803666"}]},{"$Name":"\u84dd\u7259\u5ba2\u6237\u7aef1","$Type":"BluetoothClient","$Version":"8","Uuid":"985772490"},{"$Name":"\u8ba1\u65f6\u56681","$Type":"Clock","$Version":"4","TimerInterval":"40000","Uuid":"-415083639"},{"$Name":"\u4f4d\u7f6e\u4f20\u611f\u56681","$Type":"LocationSensor","$Version":"3","Uuid":"-939539378"},{"$Name":"Web\u5ba2\u6237\u7aef1","$Type":"Web","$Version":"8","Uuid":"-1721494346"},{"$Name":"\u77ed\u4fe1\u6536\u53d1\u56681","$Type":"Texting","$Version":"5","Uuid":"-459111995"}]}} +|# \ No newline at end of file diff --git a/src/Software/src/appinventor/ai_karlzj/ruangong/dingwei.scm.2023-10-26T09_12_57+0800.backup b/src/Software/src/appinventor/ai_karlzj/ruangong/dingwei.scm.2023-10-26T09_12_57+0800.backup new file mode 100644 index 0000000..edbd14b --- /dev/null +++ b/src/Software/src/appinventor/ai_karlzj/ruangong/dingwei.scm.2023-10-26T09_12_57+0800.backup @@ -0,0 +1,4 @@ +#| +$JSON +{"authURL":["app.gzjkw.net"],"YaVersion":"172","Source":"Form","Properties":{"$Name":"dingwei","$Type":"Form","$Version":"23","AlignHorizontal":"3","AlignVertical":"2","AppName":"ruangong","Title":"dingwei","Uuid":"0","$Components":[{"$Name":"\u6c34\u5e73\u5e03\u5c401","$Type":"HorizontalArrangement","$Version":"3","AlignHorizontal":"3","Uuid":"-655869917","$Components":[{"$Name":"\u6309\u94ae_\u907f\u969c","$Type":"Button","$Version":"6","Text":"\u907f\u969c","Uuid":"1440681744"},{"$Name":"\u6309\u94ae_\u5b9e\u65f6\u5b9a\u4f4d","$Type":"Button","$Version":"6","Text":"\u5b9e\u65f6\u5b9a\u4f4d","Uuid":"652290387"},{"$Name":"\u6309\u94ae_\u62e8\u6253\u7535\u8bdd","$Type":"Button","$Version":"6","Text":"\u62e8\u6253\u7535\u8bdd","Uuid":"1956743508"}]},{"$Name":"\u5782\u76f4\u5e03\u5c401","$Type":"VerticalArrangement","$Version":"3","AlignHorizontal":"2","Uuid":"525050555","$Components":[{"$Name":"\u6c34\u5e73\u5e03\u5c402","$Type":"HorizontalArrangement","$Version":"3","Uuid":"785425026","$Components":[{"$Name":"\u6807\u7b7e_\u8ba1\u65f6\u63d0\u793a","$Type":"Label","$Version":"4","Text":"\u8ba1\u65f6\uff1a","Uuid":"-1133475828"},{"$Name":"\u6587\u672c\u8f93\u5165\u6846_\u8ba1\u65f6","$Type":"TextBox","$Version":"5","Uuid":"2119337786"}]},{"$Name":"\u6309\u94ae_\u5173\u95ed","$Type":"Button","$Version":"6","Text":"\u5173\u95ed","Uuid":"139342436"}]}]}} +|# \ No newline at end of file diff --git a/src/Software/youngandroidproject/project.properties b/src/Software/youngandroidproject/project.properties new file mode 100644 index 0000000..f45ed65 --- /dev/null +++ b/src/Software/youngandroidproject/project.properties @@ -0,0 +1,19 @@ +# +#Tue Nov 14 13:34:00 UTC 2023 +source=../src +name=ruangong +defaultfilescope=App +main=appinventor.ai_dustinzrm.ruangong.Screen1 +color.accent=&HFFFF4081 +sizing=Responsive +assets=../assets +theme=AppTheme.Light.DarkActionBar +showlistsasjson=False +useslocation=False +aname=ruangong +actionbar=True +color.primary=&HFF3F51B5 +build=../build +versionname=1.0 +versioncode=1 +color.primary.dark=&HFF303F9F diff --git a/src/esp32/hardware/cores/esp32/Arduino.h b/src/esp32/hardware/cores/esp32/Arduino.h new file mode 100644 index 0000000..d0a7191 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/Arduino.h @@ -0,0 +1,225 @@ +/* + Arduino.h - Main include file for the Arduino SDK + Copyright (c) 2005-2013 Arduino Team. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef Arduino_h +#define Arduino_h + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "esp_arduino_version.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp32-hal.h" +#include "esp8266-compat.h" +#include "soc/gpio_reg.h" + +#include "stdlib_noniso.h" +#include "binary.h" + +#define PI 3.1415926535897932384626433832795 +#define HALF_PI 1.5707963267948966192313216916398 +#define TWO_PI 6.283185307179586476925286766559 +#define DEG_TO_RAD 0.017453292519943295769236907684886 +#define RAD_TO_DEG 57.295779513082320876798154814105 +#define EULER 2.718281828459045235360287471352 + +#define SERIAL 0x0 +#define DISPLAY 0x1 + +#define LSBFIRST 0 +#define MSBFIRST 1 + +//Interrupt Modes +#define RISING 0x01 +#define FALLING 0x02 +#define CHANGE 0x03 +#define ONLOW 0x04 +#define ONHIGH 0x05 +#define ONLOW_WE 0x0C +#define ONHIGH_WE 0x0D + +#define DEFAULT 1 +#define EXTERNAL 0 + +#ifndef __STRINGIFY +#define __STRINGIFY(a) #a +#endif + +// can't define max() / min() because of conflicts with C++ +#define _min(a,b) ((a)<(b)?(a):(b)) +#define _max(a,b) ((a)>(b)?(a):(b)) +#define _abs(x) ((x)>0?(x):-(x)) // abs() comes from STL +#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt))) +#define _round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) // round() comes from STL +#define radians(deg) ((deg)*DEG_TO_RAD) +#define degrees(rad) ((rad)*RAD_TO_DEG) +#define sq(x) ((x)*(x)) + +// ESP32xx runs FreeRTOS... disabling interrupts can lead to issues, such as Watchdog Timeout +#define sei() portENABLE_INTERRUPTS() +#define cli() portDISABLE_INTERRUPTS() +#define interrupts() sei() +#define noInterrupts() cli() + +#define clockCyclesPerMicrosecond() ( (long int)getCpuFrequencyMhz() ) +#define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() ) +#define microsecondsToClockCycles(a) ( (a) * clockCyclesPerMicrosecond() ) + +#define lowByte(w) ((uint8_t) ((w) & 0xff)) +#define highByte(w) ((uint8_t) ((w) >> 8)) + +#define bitRead(value, bit) (((value) >> (bit)) & 0x01) +#define bitSet(value, bit) ((value) |= (1UL << (bit))) +#define bitClear(value, bit) ((value) &= ~(1UL << (bit))) +#define bitToggle(value, bit) ((value) ^= (1UL << (bit))) +#define bitWrite(value, bit, bitvalue) ((bitvalue) ? bitSet(value, bit) : bitClear(value, bit)) + +// avr-libc defines _NOP() since 1.6.2 +#ifndef _NOP +#define _NOP() do { __asm__ volatile ("nop"); } while (0) +#endif + +#define bit(b) (1UL << (b)) +#define _BV(b) (1UL << (b)) + +#define digitalPinToTimer(pin) (0) +#define analogInPinToBit(P) (P) +#if SOC_GPIO_PIN_COUNT <= 32 +#define digitalPinToPort(pin) (0) +#define digitalPinToBitMask(pin) (1UL << digitalPinToGPIONumber(pin)) +#define portOutputRegister(port) ((volatile uint32_t*)GPIO_OUT_REG) +#define portInputRegister(port) ((volatile uint32_t*)GPIO_IN_REG) +#define portModeRegister(port) ((volatile uint32_t*)GPIO_ENABLE_REG) +#elif SOC_GPIO_PIN_COUNT <= 64 +#define digitalPinToPort(pin) ((digitalPinToGPIONumber(pin)>31)?1:0) +#define digitalPinToBitMask(pin) (1UL << (digitalPinToGPIONumber(pin)&31)) +#define portOutputRegister(port) ((volatile uint32_t*)((port)?GPIO_OUT1_REG:GPIO_OUT_REG)) +#define portInputRegister(port) ((volatile uint32_t*)((port)?GPIO_IN1_REG:GPIO_IN_REG)) +#define portModeRegister(port) ((volatile uint32_t*)((port)?GPIO_ENABLE1_REG:GPIO_ENABLE_REG)) +#else +#error SOC_GPIO_PIN_COUNT > 64 not implemented +#endif + +#define NOT_A_PIN -1 +#define NOT_A_PORT -1 +#define NOT_AN_INTERRUPT -1 +#define NOT_ON_TIMER 0 + +typedef bool boolean; +typedef uint8_t byte; +typedef unsigned int word; + +#ifdef __cplusplus +void setup(void); +void loop(void); + +// The default is using Real Hardware random number generator +// But when randomSeed() is called, it turns to Psedo random +// generator, exactly as done in Arduino mainstream +long random(long); +long random(long, long); +// Calling randomSeed() will make random() +// using pseudo random like in Arduino +void randomSeed(unsigned long); +// Allow the Application to decide if the random generator +// will use Real Hardware random generation (true - default) +// or Pseudo random generation (false) as in Arduino MainStream +void useRealRandomGenerator(bool useRandomHW); +#endif +long map(long, long, long, long, long); + +#ifdef __cplusplus +extern "C" { +#endif + +void init(void); +void initVariant(void); +void initArduino(void); + +unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout); +unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout); + +uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder); +void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val); + +#ifdef __cplusplus +} + +#include +#include + +#include "WCharacter.h" +#include "WString.h" +#include "Stream.h" +#include "Printable.h" +#include "Print.h" +#include "IPAddress.h" +#include "Client.h" +#include "Server.h" +#include "Udp.h" +#include "HardwareSerial.h" +#include "Esp.h" +#include "esp32/spiram.h" + +// Use float-compatible stl abs() and round(), we don't use Arduino macros to avoid issues with the C++ libraries +using std::abs; +using std::isinf; +using std::isnan; +using std::max; +using std::min; +using std::round; + +uint16_t makeWord(uint16_t w); +uint16_t makeWord(uint8_t h, uint8_t l); + +#define word(...) makeWord(__VA_ARGS__) + +size_t getArduinoLoopTaskStackSize(void); +#define SET_LOOP_TASK_STACK_SIZE(sz) size_t getArduinoLoopTaskStackSize() { return sz;} + +// allows user to bypass esp_spiram_test() +#define BYPASS_SPIRAM_TEST(bypass) bool testSPIRAM(void) { if (bypass) return true; else return esp_spiram_test(); } + +unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L); +unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L); + +extern "C" bool getLocalTime(struct tm * info, uint32_t ms = 5000); +extern "C" void configTime(long gmtOffset_sec, int daylightOffset_sec, + const char* server1, const char* server2 = nullptr, const char* server3 = nullptr); +extern "C" void configTzTime(const char* tz, + const char* server1, const char* server2 = nullptr, const char* server3 = nullptr); + +void setToneChannel(uint8_t channel = 0); +void tone(uint8_t _pin, unsigned int frequency, unsigned long duration = 0); +void noTone(uint8_t _pin); + +#endif /* __cplusplus */ + +#include "pins_arduino.h" +#include "io_pin_remap.h" + +#endif /* _ESP32_CORE_ARDUINO_H_ */ diff --git a/src/esp32/hardware/cores/esp32/Client.h b/src/esp32/hardware/cores/esp32/Client.h new file mode 100644 index 0000000..962cacc --- /dev/null +++ b/src/esp32/hardware/cores/esp32/Client.h @@ -0,0 +1,48 @@ +/* + Client.h - Base class that provides Client + Copyright (c) 2011 Adrian McEwen. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef client_h +#define client_h +#include "Print.h" +#include "Stream.h" +#include "IPAddress.h" + +class Client: public Stream +{ +public: + virtual int connect(IPAddress ip, uint16_t port) =0; + virtual int connect(const char *host, uint16_t port) =0; + virtual size_t write(uint8_t) =0; + virtual size_t write(const uint8_t *buf, size_t size) =0; + virtual int available() = 0; + virtual int read() = 0; + virtual int read(uint8_t *buf, size_t size) = 0; + virtual int peek() = 0; + virtual void flush() = 0; + virtual void stop() = 0; + virtual uint8_t connected() = 0; + virtual operator bool() = 0; +protected: + uint8_t* rawIPAddress(IPAddress& addr) + { + return addr.raw_address(); + } +}; + +#endif diff --git a/src/esp32/hardware/cores/esp32/Esp.cpp b/src/esp32/hardware/cores/esp32/Esp.cpp new file mode 100644 index 0000000..ee194a4 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/Esp.cpp @@ -0,0 +1,450 @@ +/* + Esp.cpp - ESP31B-specific APIs + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "Arduino.h" +#include "Esp.h" +#include "esp_sleep.h" +#include "esp_spi_flash.h" +#include +#include +#include +extern "C" { +#include "esp_ota_ops.h" +#include "esp_image_format.h" +} +#include + +#include "soc/spi_reg.h" +#include "esp_system.h" +#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+ +#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 +#include "esp32/rom/spi_flash.h" +#include "soc/efuse_reg.h" +#define ESP_FLASH_IMAGE_BASE 0x1000 // Flash offset containing flash size and spi mode +#elif CONFIG_IDF_TARGET_ESP32S2 +#include "esp32s2/rom/spi_flash.h" +#include "soc/efuse_reg.h" +#define ESP_FLASH_IMAGE_BASE 0x1000 +#elif CONFIG_IDF_TARGET_ESP32S3 +#include "esp32s3/rom/spi_flash.h" +#include "soc/efuse_reg.h" +#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32s3 is located at 0x0000 +#elif CONFIG_IDF_TARGET_ESP32C3 +#include "esp32c3/rom/spi_flash.h" +#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c3 is located at 0x0000 +#else +#error Target CONFIG_IDF_TARGET is not supported +#endif +#else // ESP32 Before IDF 4.0 +#include "rom/spi_flash.h" +#define ESP_FLASH_IMAGE_BASE 0x1000 +#endif + +// REG_SPI_BASE is not defined for S3/C3 ?? + +#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 + #ifndef REG_SPI_BASE + #define REG_SPI_BASE(i) (DR_REG_SPI1_BASE + (((i)>1) ? (((i)* 0x1000) + 0x20000) : (((~(i)) & 1)* 0x1000 ))) + #endif // REG_SPI_BASE +#endif // TARGET + +/** + * User-defined Literals + * usage: + * + * uint32_t = test = 10_MHz; // --> 10000000 + */ + +unsigned long long operator"" _kHz(unsigned long long x) +{ + return x * 1000; +} + +unsigned long long operator"" _MHz(unsigned long long x) +{ + return x * 1000 * 1000; +} + +unsigned long long operator"" _GHz(unsigned long long x) +{ + return x * 1000 * 1000 * 1000; +} + +unsigned long long operator"" _kBit(unsigned long long x) +{ + return x * 1024; +} + +unsigned long long operator"" _MBit(unsigned long long x) +{ + return x * 1024 * 1024; +} + +unsigned long long operator"" _GBit(unsigned long long x) +{ + return x * 1024 * 1024 * 1024; +} + +unsigned long long operator"" _kB(unsigned long long x) +{ + return x * 1024; +} + +unsigned long long operator"" _MB(unsigned long long x) +{ + return x * 1024 * 1024; +} + +unsigned long long operator"" _GB(unsigned long long x) +{ + return x * 1024 * 1024 * 1024; +} + + +EspClass ESP; + +void EspClass::deepSleep(uint32_t time_us) +{ + esp_deep_sleep(time_us); +} + +void EspClass::restart(void) +{ + esp_restart(); +} + +uint32_t EspClass::getHeapSize(void) +{ + multi_heap_info_t info; + heap_caps_get_info(&info, MALLOC_CAP_INTERNAL); + return info.total_free_bytes + info.total_allocated_bytes; +} + +uint32_t EspClass::getFreeHeap(void) +{ + return heap_caps_get_free_size(MALLOC_CAP_INTERNAL); +} + +uint32_t EspClass::getMinFreeHeap(void) +{ + return heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL); +} + +uint32_t EspClass::getMaxAllocHeap(void) +{ + return heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL); +} + +uint32_t EspClass::getPsramSize(void) +{ + if(psramFound()){ + multi_heap_info_t info; + heap_caps_get_info(&info, MALLOC_CAP_SPIRAM); + return info.total_free_bytes + info.total_allocated_bytes; + } + return 0; +} + +uint32_t EspClass::getFreePsram(void) +{ + if(psramFound()){ + return heap_caps_get_free_size(MALLOC_CAP_SPIRAM); + } + return 0; +} + +uint32_t EspClass::getMinFreePsram(void) +{ + if(psramFound()){ + return heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM); + } + return 0; +} + +uint32_t EspClass::getMaxAllocPsram(void) +{ + if(psramFound()){ + return heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM); + } + return 0; +} + +static uint32_t sketchSize(sketchSize_t response) { + esp_image_metadata_t data; + const esp_partition_t *running = esp_ota_get_running_partition(); + if (!running) return 0; + const esp_partition_pos_t running_pos = { + .offset = running->address, + .size = running->size, + }; + data.start_addr = running_pos.offset; + esp_image_verify(ESP_IMAGE_VERIFY, &running_pos, &data); + if (response) { + return running_pos.size - data.image_len; + } else { + return data.image_len; + } +} + +uint32_t EspClass::getSketchSize () { + return sketchSize(SKETCH_SIZE_TOTAL); +} + +String EspClass::getSketchMD5() +{ + static String result; + if (result.length()) { + return result; + } + uint32_t lengthLeft = getSketchSize(); + + const esp_partition_t *running = esp_ota_get_running_partition(); + if (!running) { + log_e("Partition could not be found"); + + return String(); + } + const size_t bufSize = SPI_FLASH_SEC_SIZE; + std::unique_ptr buf(new uint8_t[bufSize]); + uint32_t offset = 0; + if(!buf.get()) { + log_e("Not enough memory to allocate buffer"); + + return String(); + } + MD5Builder md5; + md5.begin(); + while( lengthLeft > 0) { + size_t readBytes = (lengthLeft < bufSize) ? lengthLeft : bufSize; + if (!ESP.flashRead(running->address + offset, reinterpret_cast(buf.get()), (readBytes + 3) & ~3)) { + log_e("Could not read buffer from flash"); + + return String(); + } + md5.add(buf.get(), readBytes); + lengthLeft -= readBytes; + offset += readBytes; + } + md5.calculate(); + result = md5.toString(); + return result; +} + +uint32_t EspClass::getFreeSketchSpace () { + const esp_partition_t* _partition = esp_ota_get_next_update_partition(NULL); + if(!_partition){ + return 0; + } + + return _partition->size; +} + +uint8_t EspClass::getChipRevision(void) +{ + esp_chip_info_t chip_info; + esp_chip_info(&chip_info); + return chip_info.revision; +} + +const char * EspClass::getChipModel(void) +{ +#if CONFIG_IDF_TARGET_ESP32 + uint32_t chip_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_VER_PKG); + uint32_t pkg_ver = chip_ver & 0x7; + switch (pkg_ver) { + case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ6 : + if (getChipRevision() == 3) + return "ESP32-D0WDQ6-V3"; + else + return "ESP32-D0WDQ6"; + case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ5 : + if (getChipRevision() == 3) + return "ESP32-D0WD-V3"; + else + return "ESP32-D0WD"; + case EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5 : + return "ESP32-D2WD"; + case EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2 : + return "ESP32-PICO-D2"; + case EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4 : + return "ESP32-PICO-D4"; + case EFUSE_RD_CHIP_VER_PKG_ESP32PICOV302 : + return "ESP32-PICO-V3-02"; + case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDR2V3 : + return "ESP32-D0WDR2-V3"; + default: + return "Unknown"; + } +#elif CONFIG_IDF_TARGET_ESP32S2 + uint32_t pkg_ver = REG_GET_FIELD(EFUSE_RD_MAC_SPI_SYS_3_REG, EFUSE_PKG_VERSION); + switch (pkg_ver) { + case 0: + return "ESP32-S2"; + case 1: + return "ESP32-S2FH16"; + case 2: + return "ESP32-S2FH32"; + default: + return "ESP32-S2 (Unknown)"; + } +#elif CONFIG_IDF_TARGET_ESP32S3 + return "ESP32-S3"; +#elif CONFIG_IDF_TARGET_ESP32C3 + return "ESP32-C3"; +#endif +} + +uint8_t EspClass::getChipCores(void) +{ + esp_chip_info_t chip_info; + esp_chip_info(&chip_info); + return chip_info.cores; +} + +const char * EspClass::getSdkVersion(void) +{ + return esp_get_idf_version(); +} + +uint32_t ESP_getFlashChipId(void) +{ + uint32_t id = g_rom_flashchip.device_id; + id = ((id & 0xff) << 16) | ((id >> 16) & 0xff) | (id & 0xff00); + return id; +} + +uint32_t EspClass::getFlashChipSize(void) +{ + uint32_t id = (ESP_getFlashChipId() >> 16) & 0xFF; + return 2 << (id - 1); +} + +uint32_t EspClass::getFlashChipSpeed(void) +{ + esp_image_header_t fhdr; + if(flashRead(ESP_FLASH_IMAGE_BASE, (uint32_t*)&fhdr, sizeof(esp_image_header_t)) && fhdr.magic != ESP_IMAGE_HEADER_MAGIC) { + return 0; + } + return magicFlashChipSpeed(fhdr.spi_speed); +} + +FlashMode_t EspClass::getFlashChipMode(void) +{ + #if CONFIG_IDF_TARGET_ESP32S2 + uint32_t spi_ctrl = REG_READ(PERIPHS_SPI_FLASH_CTRL); + #else + uint32_t spi_ctrl = REG_READ(SPI_CTRL_REG(0)); + #endif + /* Not all of the following constants are already defined in older versions of spi_reg.h, so do it manually for now*/ + if (spi_ctrl & BIT(24)) { //SPI_FREAD_QIO + return (FM_QIO); + } else if (spi_ctrl & BIT(20)) { //SPI_FREAD_QUAD + return (FM_QOUT); + } else if (spi_ctrl & BIT(23)) { //SPI_FREAD_DIO + return (FM_DIO); + } else if (spi_ctrl & BIT(14)) { // SPI_FREAD_DUAL + return (FM_DOUT); + } else if (spi_ctrl & BIT(13)) { //SPI_FASTRD_MODE + return (FM_FAST_READ); + } else { + return (FM_SLOW_READ); + } + return (FM_DOUT); +} + +uint32_t EspClass::magicFlashChipSize(uint8_t byte) +{ + switch(byte & 0x0F) { + case 0x0: // 8 MBit (1MB) + return (1_MB); + case 0x1: // 16 MBit (2MB) + return (2_MB); + case 0x2: // 32 MBit (4MB) + return (4_MB); + case 0x3: // 64 MBit (8MB) + return (8_MB); + case 0x4: // 128 MBit (16MB) + return (16_MB); + default: // fail? + return 0; + } +} + +uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) +{ + switch(byte & 0x0F) { + case 0x0: // 40 MHz + return (40_MHz); + case 0x1: // 26 MHz + return (26_MHz); + case 0x2: // 20 MHz + return (20_MHz); + case 0xf: // 80 MHz + return (80_MHz); + default: // fail? + return 0; + } +} + +FlashMode_t EspClass::magicFlashChipMode(uint8_t byte) +{ + FlashMode_t mode = (FlashMode_t) byte; + if(mode > FM_SLOW_READ) { + mode = FM_UNKNOWN; + } + return mode; +} + +bool EspClass::flashEraseSector(uint32_t sector) +{ + return spi_flash_erase_sector(sector) == ESP_OK; +} + +// Warning: These functions do not work with encrypted flash +bool EspClass::flashWrite(uint32_t offset, uint32_t *data, size_t size) +{ + return spi_flash_write(offset, (uint32_t*) data, size) == ESP_OK; +} + +bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size) +{ + return spi_flash_read(offset, (uint32_t*) data, size) == ESP_OK; +} + +bool EspClass::partitionEraseRange(const esp_partition_t *partition, uint32_t offset, size_t size) +{ + return esp_partition_erase_range(partition, offset, size) == ESP_OK; +} + +bool EspClass::partitionWrite(const esp_partition_t *partition, uint32_t offset, uint32_t *data, size_t size) +{ + return esp_partition_write(partition, offset, data, size) == ESP_OK; +} + +bool EspClass::partitionRead(const esp_partition_t *partition, uint32_t offset, uint32_t *data, size_t size) +{ + return esp_partition_read(partition, offset, data, size) == ESP_OK; +} + +uint64_t EspClass::getEfuseMac(void) +{ + uint64_t _chipmacid = 0LL; + esp_efuse_mac_get_default((uint8_t*) (&_chipmacid)); + return _chipmacid; +} diff --git a/src/esp32/hardware/cores/esp32/Esp.h b/src/esp32/hardware/cores/esp32/Esp.h new file mode 100644 index 0000000..34ddb3b --- /dev/null +++ b/src/esp32/hardware/cores/esp32/Esp.h @@ -0,0 +1,119 @@ +/* + Esp.h - ESP31B-specific APIs + Copyright (c) 2015 Ivan Grokhotkov. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ESP_H +#define ESP_H + +#include +#include +#include + +/** + * AVR macros for WDT managment + */ +typedef enum { + WDTO_0MS = 0, //!< WDTO_0MS + WDTO_15MS = 15, //!< WDTO_15MS + WDTO_30MS = 30, //!< WDTO_30MS + WDTO_60MS = 60, //!< WDTO_60MS + WDTO_120MS = 120, //!< WDTO_120MS + WDTO_250MS = 250, //!< WDTO_250MS + WDTO_500MS = 500, //!< WDTO_500MS + WDTO_1S = 1000,//!< WDTO_1S + WDTO_2S = 2000,//!< WDTO_2S + WDTO_4S = 4000,//!< WDTO_4S + WDTO_8S = 8000 //!< WDTO_8S +} WDTO_t; + + +typedef enum { + FM_QIO = 0x00, + FM_QOUT = 0x01, + FM_DIO = 0x02, + FM_DOUT = 0x03, + FM_FAST_READ = 0x04, + FM_SLOW_READ = 0x05, + FM_UNKNOWN = 0xff +} FlashMode_t; + +typedef enum { + SKETCH_SIZE_TOTAL = 0, + SKETCH_SIZE_FREE = 1 +} sketchSize_t; + +class EspClass +{ +public: + EspClass() {} + ~EspClass() {} + void restart(); + + //Internal RAM + uint32_t getHeapSize(); //total heap size + uint32_t getFreeHeap(); //available heap + uint32_t getMinFreeHeap(); //lowest level of free heap since boot + uint32_t getMaxAllocHeap(); //largest block of heap that can be allocated at once + + //SPI RAM + uint32_t getPsramSize(); + uint32_t getFreePsram(); + uint32_t getMinFreePsram(); + uint32_t getMaxAllocPsram(); + + uint8_t getChipRevision(); + const char * getChipModel(); + uint8_t getChipCores(); + uint32_t getCpuFreqMHz(){ return getCpuFrequencyMhz(); } + inline uint32_t getCycleCount() __attribute__((always_inline)); + const char * getSdkVersion(); + + void deepSleep(uint32_t time_us); + + uint32_t getFlashChipSize(); + uint32_t getFlashChipSpeed(); + FlashMode_t getFlashChipMode(); + + uint32_t magicFlashChipSize(uint8_t byte); + uint32_t magicFlashChipSpeed(uint8_t byte); + FlashMode_t magicFlashChipMode(uint8_t byte); + + uint32_t getSketchSize(); + String getSketchMD5(); + uint32_t getFreeSketchSpace(); + + bool flashEraseSector(uint32_t sector); + bool flashWrite(uint32_t offset, uint32_t *data, size_t size); + bool flashRead(uint32_t offset, uint32_t *data, size_t size); + + bool partitionEraseRange(const esp_partition_t *partition, uint32_t offset, size_t size); + bool partitionWrite(const esp_partition_t *partition, uint32_t offset, uint32_t *data, size_t size); + bool partitionRead(const esp_partition_t *partition, uint32_t offset, uint32_t *data, size_t size); + + uint64_t getEfuseMac(); + +}; + +uint32_t ARDUINO_ISR_ATTR EspClass::getCycleCount() +{ + return cpu_hal_get_cycle_count(); +} + +extern EspClass ESP; + +#endif //ESP_H diff --git a/src/esp32/hardware/cores/esp32/FirmwareMSC.cpp b/src/esp32/hardware/cores/esp32/FirmwareMSC.cpp new file mode 100644 index 0000000..e869cbe --- /dev/null +++ b/src/esp32/hardware/cores/esp32/FirmwareMSC.cpp @@ -0,0 +1,424 @@ +// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "FirmwareMSC.h" + +#if CONFIG_TINYUSB_MSC_ENABLED + +#include +#include "esp_partition.h" +#include "esp_ota_ops.h" +#include "esp32-hal.h" +#include "pins_arduino.h" +#include "firmware_msc_fat.h" + +#ifndef USB_FW_MSC_VENDOR_ID +#define USB_FW_MSC_VENDOR_ID "ESP32" //max 8 chars +#endif +#ifndef USB_FW_MSC_PRODUCT_ID +#define USB_FW_MSC_PRODUCT_ID "Firmware MSC"//max 16 chars +#endif +#ifndef USB_FW_MSC_PRODUCT_REVISION +#define USB_FW_MSC_PRODUCT_REVISION "1.0" //max 4 chars +#endif +#ifndef USB_FW_MSC_VOLUME_NAME +#define USB_FW_MSC_VOLUME_NAME "ESP32-FWMSC" //max 11 chars +#endif +#ifndef USB_FW_MSC_SERIAL_NUMBER +#define USB_FW_MSC_SERIAL_NUMBER 0x00000000 +#endif + +ESP_EVENT_DEFINE_BASE(ARDUINO_FIRMWARE_MSC_EVENTS); +esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait); +esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg); + +//General Variables +static uint8_t * msc_ram_disk = NULL; +static fat_boot_sector_t * msc_boot = NULL; +static uint8_t * msc_table = NULL; +static uint16_t msc_table_sectors = 0; +static uint16_t msc_total_sectors = 0; +static bool mcs_is_fat16 = false; + +//Firmware Read +static const esp_partition_t* msc_run_partition = NULL; +static uint16_t fw_start_sector = 0; +static uint16_t fw_end_sector = 0; +static size_t fw_size = 0; +static fat_dir_entry_t * fw_entry = NULL; + +//Firmware Write +typedef enum { + MSC_UPDATE_IDLE, + MSC_UPDATE_STARTING, + MSC_UPDATE_RUNNING, + MSC_UPDATE_END +} msc_update_state_t; + +static const esp_partition_t* msc_ota_partition = NULL; +static msc_update_state_t msc_update_state = MSC_UPDATE_IDLE; +static uint16_t msc_update_start_sector = 0; +static uint32_t msc_update_bytes_written = 0; +static fat_dir_entry_t * msc_update_entry = NULL; + +static uint32_t get_firmware_size(const esp_partition_t* partition){ + esp_image_metadata_t data; + const esp_partition_pos_t running_pos = { + .offset = partition->address, + .size = partition->size, + }; + data.start_addr = running_pos.offset; + esp_image_verify(ESP_IMAGE_VERIFY, &running_pos, &data); + return data.image_len; +} + +//Get number of sectors required based on the size of the firmware and OTA partition +static size_t msc_update_get_required_disk_sectors(){ + size_t data_sectors = 16; + size_t total_sectors = 0; + msc_run_partition = esp_ota_get_running_partition(); + msc_ota_partition = esp_ota_get_next_update_partition(NULL); + if(msc_run_partition){ + fw_size = get_firmware_size(msc_run_partition); + data_sectors += FAT_SIZE_TO_SECTORS(fw_size); + log_d("APP size: %u (%u sectors)", fw_size, FAT_SIZE_TO_SECTORS(fw_size)); + } else { + log_w("APP partition not found. Reading disabled"); + } + if(msc_ota_partition){ + data_sectors += FAT_SIZE_TO_SECTORS(msc_ota_partition->size); + log_d("OTA size: %u (%u sectors)", msc_ota_partition->size, FAT_SIZE_TO_SECTORS(msc_ota_partition->size)); + } else { + log_w("OTA partition not found. Writing disabled"); + } + msc_table_sectors = fat_sectors_per_alloc_table(data_sectors, false); + total_sectors = data_sectors + msc_table_sectors + 2; + if(total_sectors > 0xFF4){ + log_d("USING FAT16"); + mcs_is_fat16 = true; + total_sectors -= msc_table_sectors; + msc_table_sectors = fat_sectors_per_alloc_table(data_sectors, true); + total_sectors += msc_table_sectors; + } else { + log_d("USING FAT12"); + mcs_is_fat16 = false; + } + log_d("FAT sector size: %u", DISK_SECTOR_SIZE); + log_d("FAT data sectors: %u", data_sectors); + log_d("FAT table sectors: %u", msc_table_sectors); + log_d("FAT total sectors: %u (%uKB)", total_sectors, (total_sectors * DISK_SECTOR_SIZE) / 1024); + return total_sectors; +} + +//setup the ramdisk and add the firmware download file +static bool msc_update_setup_disk(const char * volume_label, uint32_t serial_number){ + msc_total_sectors = msc_update_get_required_disk_sectors(); + uint8_t ram_sectors = msc_table_sectors + 2; + msc_ram_disk = (uint8_t*)calloc(ram_sectors, DISK_SECTOR_SIZE); + if(!msc_ram_disk){ + log_e("Failed to allocate RAM Disk: %u bytes", ram_sectors * DISK_SECTOR_SIZE); + return false; + } + fw_start_sector = ram_sectors; + fw_end_sector = fw_start_sector; + msc_boot = fat_add_boot_sector(msc_ram_disk, msc_total_sectors, msc_table_sectors, fat_file_system_type(mcs_is_fat16), volume_label, serial_number); + msc_table = fat_add_table(msc_ram_disk, msc_boot, mcs_is_fat16); + //fat_dir_entry_t * label = fat_add_label(msc_ram_disk, volume_label); + if(msc_run_partition){ + fw_entry = fat_add_root_file(msc_ram_disk, 0, "FIRMWARE", "BIN", fw_size, 2, mcs_is_fat16); + fw_end_sector = FAT_SIZE_TO_SECTORS(fw_size) + fw_start_sector; + } + return true; +} + +static void msc_update_delete_disk(){ + fw_entry = NULL; + fw_size = 0; + fw_end_sector = 0; + fw_start_sector = 0; + msc_table = NULL; + msc_boot = NULL; + msc_table_sectors = 0; + msc_total_sectors = 0; + msc_run_partition = NULL; + msc_ota_partition = NULL; + msc_update_state = MSC_UPDATE_IDLE; + msc_update_start_sector = 0; + msc_update_bytes_written = 0; + msc_update_entry = NULL; + free(msc_ram_disk); + msc_ram_disk = NULL; +} + +//filter out entries to only include BINs in the root folder +static fat_dir_entry_t * msc_update_get_root_bin_entry(uint8_t index){ + fat_dir_entry_t * entry = (fat_dir_entry_t *)(msc_ram_disk + ((msc_boot->sectors_per_alloc_table+1) * DISK_SECTOR_SIZE) + (index * sizeof(fat_dir_entry_t))); + fat_lfn_entry_t * lfn = (fat_lfn_entry_t*)entry; + + //empty entry + if(entry->file_magic == 0){ + return NULL; + } + //long file name + if(lfn->attr == 0x0F && lfn->type == 0x00 && lfn->first_cluster == 0x0000){ + return NULL; + } + //only files marked as archives + if(entry->file_attr != FAT_FILE_ATTR_ARCHIVE){ + return NULL; + } + //deleted + if(entry->file_magic == 0xE5 || entry->file_magic == 0x05){ + return NULL; + } + //not bins + if(memcmp("BIN", entry->file_extension, 3)){ + return NULL; + } + return entry; +} + +//get an empty bin (the host will add an entry for file about to be written with size of zero) +static fat_dir_entry_t * msc_update_find_new_bin(){ + for(uint8_t i=16; i;){ + i--; + fat_dir_entry_t * entry = msc_update_get_root_bin_entry(i); + if(entry && entry->file_size == 0){ + return entry; + } + } + return NULL; +} + +//get a bin starting from particular sector +static fat_dir_entry_t * msc_update_find_bin(uint16_t sector){ + for(uint8_t i=16; i; ){ + i--; + fat_dir_entry_t * entry = msc_update_get_root_bin_entry(i); + if(entry && entry->data_start_sector == (sector - msc_boot->sectors_per_alloc_table)){ + return entry; + } + } + return NULL; +} + +//write the new data and erase the flash blocks when necessary +static esp_err_t msc_update_write(const esp_partition_t *partition, uint32_t offset, void *data, size_t size){ + esp_err_t err = ESP_OK; + if((offset & (SPI_FLASH_SEC_SIZE-1)) == 0){ + err = esp_partition_erase_range(partition, offset, SPI_FLASH_SEC_SIZE); + log_v("ERASE[0x%08X]: %s", offset, (err != ESP_OK)?"FAIL":"OK"); + if(err != ESP_OK){ + return err; + } + } + return esp_partition_write(partition, offset, data, size); +} + +//called when error was encountered while updating +static void msc_update_error(){ + log_e("UPDATE_ERROR: %u", msc_update_bytes_written); + arduino_firmware_msc_event_data_t p; + p.error.size = msc_update_bytes_written; + arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_ERROR_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY); + msc_update_state = MSC_UPDATE_IDLE; + msc_update_entry = NULL; + msc_update_bytes_written = 0; + msc_update_start_sector = 0; +} + +//called when all firmware bytes have been received +static void msc_update_end(){ + log_d("UPDATE_END: %u", msc_update_entry->file_size); + msc_update_state = MSC_UPDATE_END; + size_t ota_size = get_firmware_size(msc_ota_partition); + if(ota_size != msc_update_entry->file_size){ + log_e("OTA SIZE MISMATCH %u != %u", ota_size, msc_update_entry->file_size); + msc_update_error(); + return; + } + if(!ota_size || esp_ota_set_boot_partition(msc_ota_partition) != ESP_OK){ + log_e("ENABLING OTA PARTITION FAILED"); + msc_update_error(); + return; + } + arduino_firmware_msc_event_data_t p; + p.end.size = msc_update_entry->file_size; + arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_END_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY); +} + +static int32_t msc_write(uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize){ + //log_d("lba: %u, offset: %u, bufsize: %u", lba, offset, bufsize); + if(lba < fw_start_sector){ + //write to sectors that are in RAM + memcpy(msc_ram_disk + (lba * DISK_SECTOR_SIZE) + offset, buffer, bufsize); + if(msc_ota_partition && lba == (fw_start_sector - 1)){ + //monitor the root folder table + if(msc_update_state <= MSC_UPDATE_RUNNING){ + fat_dir_entry_t * update_entry = msc_update_find_new_bin(); + if(update_entry) { + if(msc_update_entry) { + log_v("REPLACING ENTRY"); + } else { + log_v("ASSIGNING ENTRY"); + } + if(msc_update_state <= MSC_UPDATE_STARTING){ + msc_update_state = MSC_UPDATE_STARTING; + msc_update_bytes_written = 0; + msc_update_start_sector = 0; + } + msc_update_entry = update_entry; + } else if(msc_update_state == MSC_UPDATE_RUNNING){ + if(!msc_update_entry && msc_update_start_sector){ + msc_update_entry = msc_update_find_bin(msc_update_start_sector); + } + if(msc_update_entry && msc_update_bytes_written >= msc_update_entry->file_size){ + msc_update_end(); + } + } + } + } + } else if(msc_ota_partition && lba >= msc_update_start_sector){ + //handle writes to the region where the new firmware will be uploaded + arduino_firmware_msc_event_data_t p; + if(msc_update_state <= MSC_UPDATE_STARTING && buffer[0] == 0xE9){ + msc_update_state = MSC_UPDATE_RUNNING; + msc_update_start_sector = lba; + msc_update_bytes_written = 0; + log_d("UPDATE_START: %u (0x%02X)", lba, lba - msc_boot->sectors_per_alloc_table); + arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_START_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY); + if(msc_update_write(msc_ota_partition, ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, buffer, bufsize) == ESP_OK){ + log_v("UPDATE_WRITE: %u %u", ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, bufsize); + msc_update_bytes_written = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset + bufsize; + p.write.offset = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset; + p.write.size = bufsize; + arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_WRITE_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY); + } else { + msc_update_error(); + return 0; + } + } else if(msc_update_state == MSC_UPDATE_RUNNING){ + if(msc_update_entry && msc_update_entry->file_size && msc_update_bytes_written < msc_update_entry->file_size && (msc_update_bytes_written + bufsize) >= msc_update_entry->file_size){ + bufsize = msc_update_entry->file_size - msc_update_bytes_written; + } + if(msc_update_write(msc_ota_partition, ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, buffer, bufsize) == ESP_OK){ + log_v("UPDATE_WRITE: %u %u", ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset, bufsize); + msc_update_bytes_written = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset + bufsize; + p.write.offset = ((lba - msc_update_start_sector) * DISK_SECTOR_SIZE) + offset; + p.write.size = bufsize; + arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_WRITE_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY); + if(msc_update_entry && msc_update_entry->file_size && msc_update_bytes_written >= msc_update_entry->file_size){ + msc_update_end(); + } + } else { + msc_update_error(); + return 0; + } + } + } + return bufsize; +} + +static int32_t msc_read(uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize){ + //log_d("lba: %u, offset: %u, bufsize: %u", lba, offset, bufsize); + if(lba < fw_start_sector){ + memcpy(buffer, msc_ram_disk + (lba * DISK_SECTOR_SIZE) + offset, bufsize); + } else if(msc_run_partition && lba < fw_end_sector){ + //read the currently running firmware + if(esp_partition_read(msc_run_partition, ((lba - fw_start_sector) * DISK_SECTOR_SIZE) + offset, buffer, bufsize) != ESP_OK){ + return 0; + } + } else { + memset(buffer, 0, bufsize); + } + return bufsize; +} + +static bool msc_start_stop(uint8_t power_condition, bool start, bool load_eject){ + //log_d("power: %u, start: %u, eject: %u", power_condition, start, load_eject); + arduino_firmware_msc_event_data_t p; + p.power.power_condition = power_condition; + p.power.start = start; + p.power.load_eject = load_eject; + arduino_usb_event_post(ARDUINO_FIRMWARE_MSC_EVENTS, ARDUINO_FIRMWARE_MSC_POWER_EVENT, &p, sizeof(arduino_firmware_msc_event_data_t), portMAX_DELAY); + return true; +} + +static volatile TaskHandle_t msc_task_handle = NULL; +static void msc_task(void *pvParameters){ + for (;;) { + if(msc_update_state == MSC_UPDATE_END){ + delay(100); + esp_restart(); + } + delay(100); + } + msc_task_handle = NULL; + vTaskDelete(NULL); +} + +FirmwareMSC::FirmwareMSC():msc(){} + +FirmwareMSC::~FirmwareMSC(){ + end(); +} + +bool FirmwareMSC::begin(){ + if(msc_ram_disk){ + return true; + } + + if(!msc_update_setup_disk(USB_FW_MSC_VOLUME_NAME, USB_FW_MSC_SERIAL_NUMBER)){ + return false; + } + + if(!msc_task_handle){ + xTaskCreateUniversal(msc_task, "msc_disk", 1024, NULL, 2, (TaskHandle_t*)&msc_task_handle, 0); + if(!msc_task_handle){ + msc_update_delete_disk(); + return false; + } + } + + msc.vendorID(USB_FW_MSC_VENDOR_ID); + msc.productID(USB_FW_MSC_PRODUCT_ID); + msc.productRevision(USB_FW_MSC_PRODUCT_REVISION); + msc.onStartStop(msc_start_stop); + msc.onRead(msc_read); + msc.onWrite(msc_write); + msc.mediaPresent(true); + msc.begin(msc_boot->fat12_sector_num, DISK_SECTOR_SIZE); + return true; +} + +void FirmwareMSC::end(){ + msc.end(); + if(msc_task_handle){ + vTaskDelete(msc_task_handle); + msc_task_handle = NULL; + } + msc_update_delete_disk(); +} + +void FirmwareMSC::onEvent(esp_event_handler_t callback){ + onEvent(ARDUINO_FIRMWARE_MSC_ANY_EVENT, callback); +} +void FirmwareMSC::onEvent(arduino_firmware_msc_event_t event, esp_event_handler_t callback){ + arduino_usb_event_handler_register_with(ARDUINO_FIRMWARE_MSC_EVENTS, event, callback, this); +} + +#if ARDUINO_USB_MSC_ON_BOOT +FirmwareMSC MSC_Update; +#endif + +#endif /* CONFIG_USB_MSC_ENABLED */ diff --git a/src/esp32/hardware/cores/esp32/FirmwareMSC.h b/src/esp32/hardware/cores/esp32/FirmwareMSC.h new file mode 100644 index 0000000..570feac --- /dev/null +++ b/src/esp32/hardware/cores/esp32/FirmwareMSC.h @@ -0,0 +1,70 @@ +// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#include +#include "USBMSC.h" + +#if CONFIG_TINYUSB_MSC_ENABLED + +#include "esp_event.h" + +ESP_EVENT_DECLARE_BASE(ARDUINO_FIRMWARE_MSC_EVENTS); + +typedef enum { + ARDUINO_FIRMWARE_MSC_ANY_EVENT = ESP_EVENT_ANY_ID, + ARDUINO_FIRMWARE_MSC_START_EVENT = 0, + ARDUINO_FIRMWARE_MSC_WRITE_EVENT, + ARDUINO_FIRMWARE_MSC_END_EVENT, + ARDUINO_FIRMWARE_MSC_ERROR_EVENT, + ARDUINO_FIRMWARE_MSC_POWER_EVENT, + ARDUINO_FIRMWARE_MSC_MAX_EVENT, +} arduino_firmware_msc_event_t; + +typedef union { + struct { + size_t offset; + size_t size; + } write; + struct { + uint8_t power_condition; + bool start; + bool load_eject; + } power; + struct { + size_t size; + } end; + struct { + size_t size; + } error; +} arduino_firmware_msc_event_data_t; + +class FirmwareMSC { +private: + USBMSC msc; + +public: + FirmwareMSC(); + ~FirmwareMSC(); + bool begin(); + void end(); + void onEvent(esp_event_handler_t callback); + void onEvent(arduino_firmware_msc_event_t event, esp_event_handler_t callback); +}; + +#if ARDUINO_USB_MSC_ON_BOOT +extern FirmwareMSC MSC_Update; +#endif + +#endif /* CONFIG_TINYUSB_MSC_ENABLED */ diff --git a/src/esp32/hardware/cores/esp32/FunctionalInterrupt.cpp b/src/esp32/hardware/cores/esp32/FunctionalInterrupt.cpp new file mode 100644 index 0000000..b278332 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/FunctionalInterrupt.cpp @@ -0,0 +1,44 @@ +/* + * FunctionalInterrupt.cpp + * + * Created on: 8 jul. 2018 + * Author: Herman + */ + +#include "FunctionalInterrupt.h" +#include "Arduino.h" + +typedef void (*voidFuncPtr)(void); +typedef void (*voidFuncPtrArg)(void*); + +extern "C" +{ + extern void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtrArg userFunc, void * arg, int intr_type, bool functional); +} + +void ARDUINO_ISR_ATTR interruptFunctional(void* arg) +{ + InterruptArgStructure* localArg = (InterruptArgStructure*)arg; + if (localArg->interruptFunction) + { + localArg->interruptFunction(); + } +} + +void attachInterrupt(uint8_t pin, std::function intRoutine, int mode) +{ + // use the local interrupt routine which takes the ArgStructure as argument + __attachInterruptFunctionalArg (digitalPinToGPIONumber(pin), (voidFuncPtrArg)interruptFunctional, new InterruptArgStructure{intRoutine}, mode, true); +} + +extern "C" +{ + void cleanupFunctional(void* arg) + { + delete (InterruptArgStructure*)arg; + } +} + + + + diff --git a/src/esp32/hardware/cores/esp32/FunctionalInterrupt.h b/src/esp32/hardware/cores/esp32/FunctionalInterrupt.h new file mode 100644 index 0000000..a6d083b --- /dev/null +++ b/src/esp32/hardware/cores/esp32/FunctionalInterrupt.h @@ -0,0 +1,22 @@ +/* + * FunctionalInterrupt.h + * + * Created on: 8 jul. 2018 + * Author: Herman + */ + +#ifndef CORE_CORE_FUNCTIONALINTERRUPT_H_ +#define CORE_CORE_FUNCTIONALINTERRUPT_H_ + +#include +#include + +struct InterruptArgStructure { + std::function interruptFunction; +}; + +// The extra set of parentheses here prevents macros defined +// in io_pin_remap.h from applying to this declaration. +void (attachInterrupt)(uint8_t pin, std::function intRoutine, int mode); + +#endif /* CORE_CORE_FUNCTIONALINTERRUPT_H_ */ diff --git a/src/esp32/hardware/cores/esp32/HWCDC.cpp b/src/esp32/hardware/cores/esp32/HWCDC.cpp new file mode 100644 index 0000000..5dcc1b4 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/HWCDC.cpp @@ -0,0 +1,411 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "USB.h" +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + +#include "esp32-hal.h" +#include "HWCDC.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/ringbuf.h" +#include "esp_intr_alloc.h" +#include "soc/periph_defs.h" +#include "hal/usb_serial_jtag_ll.h" + +ESP_EVENT_DEFINE_BASE(ARDUINO_HW_CDC_EVENTS); + +static RingbufHandle_t tx_ring_buf = NULL; +static xQueueHandle rx_queue = NULL; +static uint8_t rx_data_buf[64] = {0}; +static intr_handle_t intr_handle = NULL; +static volatile bool initial_empty = false; +static xSemaphoreHandle tx_lock = NULL; + +// workaround for when USB CDC is not connected +static uint32_t tx_timeout_ms = 0; +static bool tx_timeout_change_request = false; + +static esp_event_loop_handle_t arduino_hw_cdc_event_loop_handle = NULL; + +static esp_err_t arduino_hw_cdc_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, BaseType_t *task_unblocked){ + if(arduino_hw_cdc_event_loop_handle == NULL){ + return ESP_FAIL; + } + return esp_event_isr_post_to(arduino_hw_cdc_event_loop_handle, event_base, event_id, event_data, event_data_size, task_unblocked); +} + +static esp_err_t arduino_hw_cdc_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg){ + if (!arduino_hw_cdc_event_loop_handle) { + esp_event_loop_args_t event_task_args = { + .queue_size = 5, + .task_name = "arduino_hw_cdc_events", + .task_priority = 5, + .task_stack_size = 2048, + .task_core_id = tskNO_AFFINITY + }; + if (esp_event_loop_create(&event_task_args, &arduino_hw_cdc_event_loop_handle) != ESP_OK) { + log_e("esp_event_loop_create failed"); + } + } + if(arduino_hw_cdc_event_loop_handle == NULL){ + return ESP_FAIL; + } + return esp_event_handler_register_with(arduino_hw_cdc_event_loop_handle, event_base, event_id, event_handler, event_handler_arg); +} + +static void hw_cdc_isr_handler(void *arg) { + portBASE_TYPE xTaskWoken = 0; + uint32_t usbjtag_intr_status = 0; + arduino_hw_cdc_event_data_t event = {0}; + usbjtag_intr_status = usb_serial_jtag_ll_get_intsts_mask(); + + if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY) { + // Interrupt tells us the host picked up the data we sent. + if (usb_serial_jtag_ll_txfifo_writable() == 1) { + // We disable the interrupt here so that the interrupt won't be triggered if there is no data to send. + usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + if(!initial_empty){ + initial_empty = true; + // First time USB is plugged and the application has not explicitly set TX Timeout, set it to default 100ms. + // Otherwise, USB is still unplugged and the timeout will be kept as Zero in order to avoid any delay in the + // application whenever it uses write() and the TX Queue gets full. + if (!tx_timeout_change_request) { + tx_timeout_ms = 100; + } + //send event? + //ets_printf("CONNECTED\n"); + arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_CONNECTED_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken); + } + size_t queued_size; + uint8_t *queued_buff = (uint8_t *)xRingbufferReceiveUpToFromISR(tx_ring_buf, &queued_size, 64); + // If the hardware fifo is avaliable, write in it. Otherwise, do nothing. + if (queued_buff != NULL) { //Although tx_queued_bytes may be larger than 0. We may have interrupt before xRingbufferSend() was called. + //Copy the queued buffer into the TX FIFO + usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + usb_serial_jtag_ll_write_txfifo(queued_buff, queued_size); + usb_serial_jtag_ll_txfifo_flush(); + vRingbufferReturnItemFromISR(tx_ring_buf, queued_buff, &xTaskWoken); + usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + //send event? + //ets_printf("TX:%u\n", queued_size); + event.tx.len = queued_size; + arduino_hw_cdc_event_post(ARDUINO_HW_CDC_EVENTS, ARDUINO_HW_CDC_TX_EVENT, &event, sizeof(arduino_hw_cdc_event_data_t), &xTaskWoken); + } + } else { + usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + } + } + + if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT) { + // read rx buffer(max length is 64), and send avaliable data to ringbuffer. + // Ensure the rx buffer size is larger than RX_MAX_SIZE. + usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT); + uint32_t rx_fifo_len = usb_serial_jtag_ll_read_rxfifo(rx_data_buf, 64); + uint32_t i=0; + for(i=0; i size){ + space = size; + } + // Non-Blocking method, Sending data to ringbuffer, and handle the data in ISR. + if(xRingbufferSend(tx_ring_buf, (void*) (buffer), space, 0) != pdTRUE){ + size = 0; + } else { + to_send -= space; + so_far += space; + // Now trigger the ISR to read data from the ring buffer. + usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + + while(to_send){ + if(max_size > to_send){ + max_size = to_send; + } + // Blocking method, Sending data to ringbuffer, and handle the data in ISR. + if(xRingbufferSend(tx_ring_buf, (void*) (buffer+so_far), max_size, tx_timeout_ms / portTICK_PERIOD_MS) != pdTRUE){ + size = so_far; + break; + } + so_far += max_size; + to_send -= max_size; + // Now trigger the ISR to read data from the ring buffer. + usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + } + } + xSemaphoreGive(tx_lock); + return size; +} + +size_t HWCDC::write(uint8_t c) +{ + return write(&c, 1); +} + +void HWCDC::flush(void) +{ + if(tx_ring_buf == NULL || tx_lock == NULL){ + return; + } + if(xSemaphoreTake(tx_lock, tx_timeout_ms / portTICK_PERIOD_MS) != pdPASS){ + return; + } + UBaseType_t uxItemsWaiting = 0; + vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting); + if(uxItemsWaiting){ + // Now trigger the ISR to read data from the ring buffer. + usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY); + } + while(uxItemsWaiting){ + delay(5); + vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting); + } + xSemaphoreGive(tx_lock); +} + +/* + * READING +*/ + +size_t HWCDC::setRxBufferSize(size_t rx_queue_len){ + if(rx_queue){ + vQueueDelete(rx_queue); + rx_queue = NULL; + } + if(!rx_queue_len){ + return 0; + } + rx_queue = xQueueCreate(rx_queue_len, sizeof(uint8_t)); + if(!rx_queue){ + return 0; + } + return rx_queue_len; +} + +int HWCDC::available(void) +{ + if(rx_queue == NULL){ + return -1; + } + return uxQueueMessagesWaiting(rx_queue); +} + +int HWCDC::peek(void) +{ + if(rx_queue == NULL){ + return -1; + } + uint8_t c; + if(xQueuePeek(rx_queue, &c, 0)) { + return c; + } + return -1; +} + +int HWCDC::read(void) +{ + if(rx_queue == NULL){ + return -1; + } + uint8_t c = 0; + if(xQueueReceive(rx_queue, &c, 0)) { + return c; + } + return -1; +} + +size_t HWCDC::read(uint8_t *buffer, size_t size) +{ + if(rx_queue == NULL){ + return -1; + } + uint8_t c = 0; + size_t count = 0; + while(count < size && xQueueReceive(rx_queue, &c, 0)){ + buffer[count++] = c; + } + return count; +} + +/* + * DEBUG +*/ + +void HWCDC::setDebugOutput(bool en) +{ + if(en) { + uartSetDebug(NULL); + ets_install_putc1((void (*)(char)) &cdc0_write_char); + } else { + ets_install_putc1(NULL); + } +} + +#if ARDUINO_USB_MODE +#if ARDUINO_USB_CDC_ON_BOOT//Serial used for USB CDC +HWCDC Serial; +#else +HWCDC USBSerial; +#endif +#endif + +#endif /* CONFIG_TINYUSB_CDC_ENABLED */ diff --git a/src/esp32/hardware/cores/esp32/HWCDC.h b/src/esp32/hardware/cores/esp32/HWCDC.h new file mode 100644 index 0000000..5878ad0 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/HWCDC.h @@ -0,0 +1,109 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include "sdkconfig.h" +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + +#include +#include "esp_event.h" +#include "Stream.h" + +ESP_EVENT_DECLARE_BASE(ARDUINO_HW_CDC_EVENTS); + +typedef enum { + ARDUINO_HW_CDC_ANY_EVENT = ESP_EVENT_ANY_ID, + ARDUINO_HW_CDC_CONNECTED_EVENT = 0, + ARDUINO_HW_CDC_BUS_RESET_EVENT, + ARDUINO_HW_CDC_RX_EVENT, + ARDUINO_HW_CDC_TX_EVENT, + ARDUINO_HW_CDC_MAX_EVENT, +} arduino_hw_cdc_event_t; + +typedef union { + struct { + size_t len; + } rx; + struct { + size_t len; + } tx; +} arduino_hw_cdc_event_data_t; + +class HWCDC: public Stream +{ +public: + HWCDC(); + ~HWCDC(); + + void onEvent(esp_event_handler_t callback); + void onEvent(arduino_hw_cdc_event_t event, esp_event_handler_t callback); + + size_t setRxBufferSize(size_t); + size_t setTxBufferSize(size_t); + void setTxTimeoutMs(uint32_t timeout); + void begin(unsigned long baud=0); + void end(); + + int available(void); + int availableForWrite(void); + int peek(void); + int read(void); + size_t read(uint8_t *buffer, size_t size); + size_t write(uint8_t); + size_t write(const uint8_t *buffer, size_t size); + void flush(void); + + inline size_t read(char * buffer, size_t size) + { + return read((uint8_t*) buffer, size); + } + inline size_t write(const char * buffer, size_t size) + { + return write((uint8_t*) buffer, size); + } + inline size_t write(const char * s) + { + return write((uint8_t*) s, strlen(s)); + } + inline size_t write(unsigned long n) + { + return write((uint8_t) n); + } + inline size_t write(long n) + { + return write((uint8_t) n); + } + inline size_t write(unsigned int n) + { + return write((uint8_t) n); + } + inline size_t write(int n) + { + return write((uint8_t) n); + } + operator bool() const; + void setDebugOutput(bool); + uint32_t baudRate(){return 115200;} + +}; + +#if ARDUINO_USB_MODE +#if ARDUINO_USB_CDC_ON_BOOT//Serial used for USB CDC +extern HWCDC Serial; +#else +extern HWCDC USBSerial; +#endif +#endif + +#endif /* CONFIG_IDF_TARGET_ESP32C3 */ diff --git a/src/esp32/hardware/cores/esp32/HardwareSerial.cpp b/src/esp32/hardware/cores/esp32/HardwareSerial.cpp new file mode 100644 index 0000000..4655cf2 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/HardwareSerial.cpp @@ -0,0 +1,623 @@ +#include +#include +#include +#include + +#include "pins_arduino.h" +#include "io_pin_remap.h" +#include "HardwareSerial.h" +#include "soc/soc_caps.h" +#include "driver/uart.h" +#include "freertos/queue.h" + +#ifndef ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE +#define ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE 2048 +#endif + +#ifndef ARDUINO_SERIAL_EVENT_TASK_PRIORITY +#define ARDUINO_SERIAL_EVENT_TASK_PRIORITY (configMAX_PRIORITIES-1) +#endif + +#ifndef ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE +#define ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE -1 +#endif + +#ifndef SOC_RX0 +#if CONFIG_IDF_TARGET_ESP32 +#define SOC_RX0 3 +#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 +#define SOC_RX0 44 +#elif CONFIG_IDF_TARGET_ESP32C3 +#define SOC_RX0 20 +#endif +#endif + +#ifndef SOC_TX0 +#if CONFIG_IDF_TARGET_ESP32 +#define SOC_TX0 1 +#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 +#define SOC_TX0 43 +#elif CONFIG_IDF_TARGET_ESP32C3 +#define SOC_TX0 21 +#endif +#endif + +void serialEvent(void) __attribute__((weak)); +void serialEvent(void) {} + +#if SOC_UART_NUM > 1 + +#ifndef RX1 +#if CONFIG_IDF_TARGET_ESP32 +#define RX1 9 +#elif CONFIG_IDF_TARGET_ESP32S2 +#define RX1 18 +#elif CONFIG_IDF_TARGET_ESP32C3 +#define RX1 18 +#elif CONFIG_IDF_TARGET_ESP32S3 +#define RX1 15 +#endif +#endif + +#ifndef TX1 +#if CONFIG_IDF_TARGET_ESP32 +#define TX1 10 +#elif CONFIG_IDF_TARGET_ESP32S2 +#define TX1 17 +#elif CONFIG_IDF_TARGET_ESP32C3 +#define TX1 19 +#elif CONFIG_IDF_TARGET_ESP32S3 +#define TX1 16 +#endif +#endif + +void serialEvent1(void) __attribute__((weak)); +void serialEvent1(void) {} +#endif /* SOC_UART_NUM > 1 */ + +#if SOC_UART_NUM > 2 +#ifndef RX2 +#if CONFIG_IDF_TARGET_ESP32 +#define RX2 16 +#elif CONFIG_IDF_TARGET_ESP32S3 +#define RX2 19 +#endif +#endif + +#ifndef TX2 +#if CONFIG_IDF_TARGET_ESP32 +#define TX2 17 +#elif CONFIG_IDF_TARGET_ESP32S3 +#define TX2 20 +#endif +#endif + +void serialEvent2(void) __attribute__((weak)); +void serialEvent2(void) {} +#endif /* SOC_UART_NUM > 2 */ + +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL) +#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC +HardwareSerial Serial0(0); +#else +HardwareSerial Serial(0); +#endif +#if SOC_UART_NUM > 1 +HardwareSerial Serial1(1); +#endif +#if SOC_UART_NUM > 2 +HardwareSerial Serial2(2); +#endif + +void serialEventRun(void) +{ +#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC + if(Serial0.available()) serialEvent(); +#else + if(Serial.available()) serialEvent(); +#endif +#if SOC_UART_NUM > 1 + if(Serial1.available()) serialEvent1(); +#endif +#if SOC_UART_NUM > 2 + if(Serial2.available()) serialEvent2(); +#endif +} +#endif + +#if !CONFIG_DISABLE_HAL_LOCKS +#define HSERIAL_MUTEX_LOCK() do {} while (xSemaphoreTake(_lock, portMAX_DELAY) != pdPASS) +#define HSERIAL_MUTEX_UNLOCK() xSemaphoreGive(_lock) +#else +#define HSERIAL_MUTEX_LOCK() +#define HSERIAL_MUTEX_UNLOCK() +#endif + +HardwareSerial::HardwareSerial(int uart_nr) : +_uart_nr(uart_nr), +_uart(NULL), +_rxBufferSize(256), +_txBufferSize(0), +_onReceiveCB(NULL), +_onReceiveErrorCB(NULL), +_onReceiveTimeout(false), +_rxTimeout(2), +_rxFIFOFull(0), +_eventTask(NULL) +#if !CONFIG_DISABLE_HAL_LOCKS + ,_lock(NULL) +#endif +,_rxPin(-1) +,_txPin(-1) +,_ctsPin(-1) +,_rtsPin(-1) +{ +#if !CONFIG_DISABLE_HAL_LOCKS + if(_lock == NULL){ + _lock = xSemaphoreCreateMutex(); + if(_lock == NULL){ + log_e("xSemaphoreCreateMutex failed"); + return; + } + } +#endif +} + +HardwareSerial::~HardwareSerial() +{ + end(); +#if !CONFIG_DISABLE_HAL_LOCKS + if(_lock != NULL){ + vSemaphoreDelete(_lock); + } +#endif +} + + +void HardwareSerial::_createEventTask(void *args) +{ + // Creating UART event Task + xTaskCreateUniversal(_uartEventTask, "uart_event_task", ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE, this, ARDUINO_SERIAL_EVENT_TASK_PRIORITY, &_eventTask, ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE); + if (_eventTask == NULL) { + log_e(" -- UART%d Event Task not Created!", _uart_nr); + } +} + +void HardwareSerial::_destroyEventTask(void) +{ + if (_eventTask != NULL) { + vTaskDelete(_eventTask); + _eventTask = NULL; + } +} + +void HardwareSerial::onReceiveError(OnReceiveErrorCb function) +{ + HSERIAL_MUTEX_LOCK(); + // function may be NULL to cancel onReceive() from its respective task + _onReceiveErrorCB = function; + // this can be called after Serial.begin(), therefore it shall create the event task + if (function != NULL && _uart != NULL && _eventTask == NULL) { + _createEventTask(this); + } + HSERIAL_MUTEX_UNLOCK(); +} + +void HardwareSerial::onReceive(OnReceiveCb function, bool onlyOnTimeout) +{ + HSERIAL_MUTEX_LOCK(); + // function may be NULL to cancel onReceive() from its respective task + _onReceiveCB = function; + + // setting the callback to NULL will just disable it + if (_onReceiveCB != NULL) { + // When Rx timeout is Zero (disabled), there is only one possible option that is callback when FIFO reaches 120 bytes + _onReceiveTimeout = _rxTimeout > 0 ? onlyOnTimeout : false; + + // in case that onReceive() shall work only with RX Timeout, FIFO shall be high + // this is a work around for an IDF issue with events and low FIFO Full value (< 3) + if (_onReceiveTimeout) { + uartSetRxFIFOFull(_uart, 120); + log_w("OnReceive is set to Timeout only, thus FIFO Full is now 120 bytes."); + } + + // this method can be called after Serial.begin(), therefore it shall create the event task + if (_uart != NULL && _eventTask == NULL) { + _createEventTask(this); // Create event task + } + } + HSERIAL_MUTEX_UNLOCK(); +} + +// This function allow the user to define how many bytes will trigger an Interrupt that will copy RX FIFO to the internal RX Ringbuffer +// ISR will also move data from FIFO to RX Ringbuffer after a RX Timeout defined in HardwareSerial::setRxTimeout(uint8_t symbols_timeout) +// A low value of FIFO Full bytes will consume more CPU time within the ISR +// A high value of FIFO Full bytes will make the application wait longer to have byte available for the Stkech in a streaming scenario +// Both RX FIFO Full and RX Timeout may affect when onReceive() will be called +bool HardwareSerial::setRxFIFOFull(uint8_t fifoBytes) +{ + HSERIAL_MUTEX_LOCK(); + // in case that onReceive() shall work only with RX Timeout, FIFO shall be high + // this is a work around for an IDF issue with events and low FIFO Full value (< 3) + if (_onReceiveCB != NULL && _onReceiveTimeout) { + fifoBytes = 120; + log_w("OnReceive is set to Timeout only, thus FIFO Full is now 120 bytes."); + } + bool retCode = uartSetRxFIFOFull(_uart, fifoBytes); // Set new timeout + if (fifoBytes > 0 && fifoBytes < SOC_UART_FIFO_LEN - 1) _rxFIFOFull = fifoBytes; + HSERIAL_MUTEX_UNLOCK(); + return retCode; +} + +// timout is calculates in time to receive UART symbols at the UART baudrate. +// the estimation is about 11 bits per symbol (SERIAL_8N1) +bool HardwareSerial::setRxTimeout(uint8_t symbols_timeout) +{ + HSERIAL_MUTEX_LOCK(); + + // Zero disables timeout, thus, onReceive callback will only be called when RX FIFO reaches 120 bytes + // Any non-zero value will activate onReceive callback based on UART baudrate with about 11 bits per symbol + _rxTimeout = symbols_timeout; + if (!symbols_timeout) _onReceiveTimeout = false; // only when RX timeout is disabled, we also must disable this flag + + bool retCode = uartSetRxTimeout(_uart, _rxTimeout); // Set new timeout + + HSERIAL_MUTEX_UNLOCK(); + return retCode; +} + +void HardwareSerial::eventQueueReset() +{ + QueueHandle_t uartEventQueue = NULL; + if (_uart == NULL) { + return; + } + uartGetEventQueue(_uart, &uartEventQueue); + if (uartEventQueue != NULL) { + xQueueReset(uartEventQueue); + } +} + +void HardwareSerial::_uartEventTask(void *args) +{ + HardwareSerial *uart = (HardwareSerial *)args; + uart_event_t event; + QueueHandle_t uartEventQueue = NULL; + uartGetEventQueue(uart->_uart, &uartEventQueue); + if (uartEventQueue != NULL) { + for(;;) { + //Waiting for UART event. + if(xQueueReceive(uartEventQueue, (void * )&event, (portTickType)portMAX_DELAY)) { + hardwareSerial_error_t currentErr = UART_NO_ERROR; + switch(event.type) { + case UART_DATA: + if(uart->_onReceiveCB && uart->available() > 0 && + ((uart->_onReceiveTimeout && event.timeout_flag) || !uart->_onReceiveTimeout) ) + uart->_onReceiveCB(); + break; + case UART_FIFO_OVF: + log_w("UART%d FIFO Overflow. Consider adding Hardware Flow Control to your Application.", uart->_uart_nr); + currentErr = UART_FIFO_OVF_ERROR; + break; + case UART_BUFFER_FULL: + log_w("UART%d Buffer Full. Consider increasing your buffer size of your Application.", uart->_uart_nr); + currentErr = UART_BUFFER_FULL_ERROR; + break; + case UART_BREAK: + log_w("UART%d RX break.", uart->_uart_nr); + currentErr = UART_BREAK_ERROR; + break; + case UART_PARITY_ERR: + log_w("UART%d parity error.", uart->_uart_nr); + currentErr = UART_PARITY_ERROR; + break; + case UART_FRAME_ERR: + log_w("UART%d frame error.", uart->_uart_nr); + currentErr = UART_FRAME_ERROR; + break; + default: + log_w("UART%d unknown event type %d.", uart->_uart_nr, event.type); + break; + } + if (currentErr != UART_NO_ERROR) { + if(uart->_onReceiveErrorCB) uart->_onReceiveErrorCB(currentErr); + } + } + } + } + vTaskDelete(NULL); +} + +void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert, unsigned long timeout_ms, uint8_t rxfifo_full_thrhd) +{ + if(0 > _uart_nr || _uart_nr >= SOC_UART_NUM) { + log_e("Serial number is invalid, please use numers from 0 to %u", SOC_UART_NUM - 1); + return; + } + +#if !CONFIG_DISABLE_HAL_LOCKS + if(_lock == NULL){ + log_e("MUTEX Lock failed. Can't begin."); + return; + } +#endif + + HSERIAL_MUTEX_LOCK(); + // First Time or after end() --> set default Pins + if (!uartIsDriverInstalled(_uart)) { + switch (_uart_nr) { + case UART_NUM_0: + if (rxPin < 0 && txPin < 0) { + rxPin = SOC_RX0; + txPin = SOC_TX0; + } + break; +#if SOC_UART_NUM > 1 // may save some flash bytes... + case UART_NUM_1: + if (rxPin < 0 && txPin < 0) { + rxPin = RX1; + txPin = TX1; + } + break; +#endif +#if SOC_UART_NUM > 2 // may save some flash bytes... + case UART_NUM_2: + if (rxPin < 0 && txPin < 0) { + rxPin = RX2; + txPin = TX2; + } + break; +#endif + } + } + + // map logical pins to GPIO numbers + rxPin = digitalPinToGPIONumber(rxPin); + txPin = digitalPinToGPIONumber(txPin); + + if(_uart) { + // in this case it is a begin() over a previous begin() - maybe to change baud rate + // thus do not disable debug output + end(false); + } + + // IDF UART driver keeps Pin setting on restarting. Negative Pin number will keep it unmodified. + _uart = uartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, _rxBufferSize, _txBufferSize, invert, rxfifo_full_thrhd); + if (!baud) { + // using baud rate as zero, forces it to try to detect the current baud rate in place + uartStartDetectBaudrate(_uart); + time_t startMillis = millis(); + unsigned long detectedBaudRate = 0; + while(millis() - startMillis < timeout_ms && !(detectedBaudRate = uartDetectBaudrate(_uart))) { + yield(); + } + + end(false); + + if(detectedBaudRate) { + delay(100); // Give some time... + _uart = uartBegin(_uart_nr, detectedBaudRate, config, rxPin, txPin, _rxBufferSize, _txBufferSize, invert, rxfifo_full_thrhd); + } else { + log_e("Could not detect baudrate. Serial data at the port must be present within the timeout for detection to be possible"); + _uart = NULL; + } + } + // create a task to deal with Serial Events when, for example, calling begin() twice to change the baudrate, + // or when setting the callback before calling begin() + if (_uart != NULL && (_onReceiveCB != NULL || _onReceiveErrorCB != NULL) && _eventTask == NULL) { + _createEventTask(this); + } + + // Set UART RX timeout + uartSetRxTimeout(_uart, _rxTimeout); + + // Set UART FIFO Full depending on the baud rate. + // Lower baud rates will force to emulate byte-by-byte reading + // Higher baud rates will keep IDF default of 120 bytes for FIFO FULL Interrupt + // It can also be changed by the application at any time + if (!_rxFIFOFull) { // it has not being changed before calling begin() + // set a default FIFO Full value for the IDF driver + uint8_t fifoFull = 1; + if (baud > 57600 || (_onReceiveCB != NULL && _onReceiveTimeout)) { + fifoFull = 120; + } + uartSetRxFIFOFull(_uart, fifoFull); + _rxFIFOFull = fifoFull; + } + + _rxPin = rxPin; + _txPin = txPin; + + HSERIAL_MUTEX_UNLOCK(); +} + +void HardwareSerial::updateBaudRate(unsigned long baud) +{ + uartSetBaudRate(_uart, baud); +} + +void HardwareSerial::end(bool fullyTerminate) +{ + // default Serial.end() will completely disable HardwareSerial, + // including any tasks or debug message channel (log_x()) - but not for IDF log messages! + if(fullyTerminate) { + _onReceiveCB = NULL; + _onReceiveErrorCB = NULL; + if (uartGetDebug() == _uart_nr) { + uartSetDebug(0); + } + + _rxFIFOFull = 0; + + uartDetachPins(_uart, _rxPin, _txPin, _ctsPin, _rtsPin); + _rxPin = _txPin = _ctsPin = _rtsPin = -1; + + } + delay(10); + uartEnd(_uart); + _uart = 0; + _destroyEventTask(); +} + +void HardwareSerial::setDebugOutput(bool en) +{ + if(_uart == 0) { + return; + } + if(en) { + uartSetDebug(_uart); + } else { + if(uartGetDebug() == _uart_nr) { + uartSetDebug(NULL); + } + } +} + +int HardwareSerial::available(void) +{ + return uartAvailable(_uart); +} +int HardwareSerial::availableForWrite(void) +{ + return uartAvailableForWrite(_uart); +} + +int HardwareSerial::peek(void) +{ + if (available()) { + return uartPeek(_uart); + } + return -1; +} + +int HardwareSerial::read(void) +{ + uint8_t c = 0; + if (uartReadBytes(_uart, &c, 1, 0) == 1) { + return c; + } else { + return -1; + } +} + +// read characters into buffer +// terminates if size characters have been read, or no further are pending +// returns the number of characters placed in the buffer +// the buffer is NOT null terminated. +size_t HardwareSerial::read(uint8_t *buffer, size_t size) +{ + return uartReadBytes(_uart, buffer, size, 0); +} + +// Overrides Stream::readBytes() to be faster using IDF +size_t HardwareSerial::readBytes(uint8_t *buffer, size_t length) +{ + return uartReadBytes(_uart, buffer, length, (uint32_t)getTimeout()); +} + +void HardwareSerial::flush(void) +{ + uartFlush(_uart); +} + +void HardwareSerial::flush(bool txOnly) +{ + uartFlushTxOnly(_uart, txOnly); +} + +size_t HardwareSerial::write(uint8_t c) +{ + uartWrite(_uart, c); + return 1; +} + +size_t HardwareSerial::write(const uint8_t *buffer, size_t size) +{ + uartWriteBuf(_uart, buffer, size); + return size; +} +uint32_t HardwareSerial::baudRate() + +{ + return uartGetBaudRate(_uart); +} +HardwareSerial::operator bool() const +{ + return uartIsDriverInstalled(_uart); +} + +void HardwareSerial::setRxInvert(bool invert) +{ + uartSetRxInvert(_uart, invert); +} + +// negative Pin value will keep it unmodified +bool HardwareSerial::setPins(int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin) +{ + if(_uart == NULL) { + log_e("setPins() shall be called after begin() - nothing done\n"); + return false; + } + + // map logical pins to GPIO numbers + rxPin = digitalPinToGPIONumber(rxPin); + txPin = digitalPinToGPIONumber(txPin); + ctsPin = digitalPinToGPIONumber(ctsPin); + rtsPin = digitalPinToGPIONumber(rtsPin); + + // uartSetPins() checks if pins are valid for each function and for the SoC + bool retCode = uartSetPins(_uart, rxPin, txPin, ctsPin, rtsPin); + if (retCode) { + _txPin = _txPin >= 0 ? txPin : _txPin; + _rxPin = _rxPin >= 0 ? rxPin : _rxPin; + _rtsPin = _rtsPin >= 0 ? rtsPin : _rtsPin; + _ctsPin = _ctsPin >= 0 ? ctsPin : _ctsPin; + } else { + log_e("Error when setting Serial port Pins. Invalid Pin.\n"); + } + return retCode; +} + +// Enables or disables Hardware Flow Control using RTS and/or CTS pins (must use setAllPins() before) +bool HardwareSerial::setHwFlowCtrlMode(uint8_t mode, uint8_t threshold) +{ + return uartSetHwFlowCtrlMode(_uart, mode, threshold); +} + +// Sets the uart mode in the esp32 uart for use with RS485 modes (HwFlowCtrl must be disabled and RTS pin set) +bool HardwareSerial::setMode(uint8_t mode) +{ + return uartSetMode(_uart, mode); +} + +size_t HardwareSerial::setRxBufferSize(size_t new_size) { + + if (_uart) { + log_e("RX Buffer can't be resized when Serial is already running.\n"); + return 0; + } + + if (new_size <= SOC_UART_FIFO_LEN) { + log_e("RX Buffer must be higher than %d.\n", SOC_UART_FIFO_LEN); // ESP32, S2, S3 and C3 means higher than 128 + return 0; + } + + _rxBufferSize = new_size; + return _rxBufferSize; +} + +size_t HardwareSerial::setTxBufferSize(size_t new_size) { + + if (_uart) { + log_e("TX Buffer can't be resized when Serial is already running.\n"); + return 0; + } + + if (new_size <= SOC_UART_FIFO_LEN) { + log_e("TX Buffer must be higher than %d.\n", SOC_UART_FIFO_LEN); // ESP32, S2, S3 and C3 means higher than 128 + return 0; + } + + _txBufferSize = new_size; + return _txBufferSize; +} diff --git a/src/esp32/hardware/cores/esp32/HardwareSerial.h b/src/esp32/hardware/cores/esp32/HardwareSerial.h new file mode 100644 index 0000000..d6516f0 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/HardwareSerial.h @@ -0,0 +1,216 @@ +/* + HardwareSerial.h - Hardware serial library for Wiring + Copyright (c) 2006 Nicholas Zambetti. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Modified 28 September 2010 by Mark Sproul + Modified 14 August 2012 by Alarus + Modified 3 December 2013 by Matthijs Kooijman + Modified 18 December 2014 by Ivan Grokhotkov (esp8266 platform support) + Modified 31 March 2015 by Markus Sattler (rewrite the code for UART0 + UART1 support in ESP8266) + Modified 25 April 2015 by Thomas Flayols (add configuration different from 8N1 in ESP8266) + Modified 13 October 2018 by Jeroen Döll (add baudrate detection) + Baudrate detection example usage (detection on Serial1): + void setup() { + Serial.begin(115200); + delay(100); + Serial.println(); + + Serial1.begin(0, SERIAL_8N1, -1, -1, true, 11000UL); // Passing 0 for baudrate to detect it, the last parameter is a timeout in ms + + unsigned long detectedBaudRate = Serial1.baudRate(); + if(detectedBaudRate) { + Serial.printf("Detected baudrate is %lu\n", detectedBaudRate); + } else { + Serial.println("No baudrate detected, Serial1 will not work!"); + } + } + + Pay attention: the baudrate returned by baudRate() may be rounded, eg 115200 returns 115201 + */ + +#ifndef HardwareSerial_h +#define HardwareSerial_h + +#include +#include +#include "Stream.h" +#include "esp32-hal.h" +#include "soc/soc_caps.h" +#include "HWCDC.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" + +typedef enum { + UART_NO_ERROR, + UART_BREAK_ERROR, + UART_BUFFER_FULL_ERROR, + UART_FIFO_OVF_ERROR, + UART_FRAME_ERROR, + UART_PARITY_ERROR +} hardwareSerial_error_t; + +typedef std::function OnReceiveCb; +typedef std::function OnReceiveErrorCb; + +class HardwareSerial: public Stream +{ +public: + HardwareSerial(int uart_nr); + ~HardwareSerial(); + + // setRxTimeout sets the timeout after which onReceive callback will be called (after receiving data, it waits for this time of UART rx inactivity to call the callback fnc) + // param symbols_timeout defines a timeout threshold in uart symbol periods. Setting 0 symbol timeout disables the callback call by timeout. + // Maximum timeout setting is calculacted automatically by IDF. If set above the maximum, it is ignored and an error is printed on Serial0 (check console). + // Examples: Maximum for 11 bits symbol is 92 (SERIAL_8N2, SERIAL_8E1, SERIAL_8O1, etc), Maximum for 10 bits symbol is 101 (SERIAL_8N1). + // For example symbols_timeout=1 defines a timeout equal to transmission time of one symbol (~11 bit) on current baudrate. + // For a baudrate of 9600, SERIAL_8N1 (10 bit symbol) and symbols_timeout = 3, the timeout would be 3 / (9600 / 10) = 3.125 ms + bool setRxTimeout(uint8_t symbols_timeout); + + // setRxFIFOFull(uint8_t fifoBytes) will set the number of bytes that will trigger UART_INTR_RXFIFO_FULL interrupt and fill up RxRingBuffer + // This affects some functions such as Serial::available() and Serial.read() because, in a UART flow of receiving data, Serial internal + // RxRingBuffer will be filled only after these number of bytes arrive or a RX Timeout happens. + // This parameter can be set to 1 in order to receive byte by byte, but it will also consume more CPU time as the ISR will be activates often. + bool setRxFIFOFull(uint8_t fifoBytes); + + // onReceive will setup a callback that will be called whenever an UART interruption occurs (UART_INTR_RXFIFO_FULL or UART_INTR_RXFIFO_TOUT) + // UART_INTR_RXFIFO_FULL interrupt triggers at UART_FULL_THRESH_DEFAULT bytes received (defined as 120 bytes by default in IDF) + // UART_INTR_RXFIFO_TOUT interrupt triggers at UART_TOUT_THRESH_DEFAULT symbols passed without any reception (defined as 10 symbos by default in IDF) + // onlyOnTimeout parameter will define how onReceive will behave: + // Default: true -- The callback will only be called when RX Timeout happens. + // Whole stream of bytes will be ready for being read on the callback function at once. + // This option may lead to Rx Overflow depending on the Rx Buffer Size and number of bytes received in the streaming + // false -- The callback will be called when FIFO reaches 120 bytes and also on RX Timeout. + // The stream of incommig bytes will be "split" into blocks of 120 bytes on each callback. + // This option avoid any sort of Rx Overflow, but leaves the UART packet reassembling work to the Application. + void onReceive(OnReceiveCb function, bool onlyOnTimeout = false); + + // onReceive will be called on error events (see hardwareSerial_error_t) + void onReceiveError(OnReceiveErrorCb function); + + // eventQueueReset clears all events in the queue (the events that trigger onReceive and onReceiveError) - maybe usefull in some use cases + void eventQueueReset(); + + void begin(unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1, bool invert=false, unsigned long timeout_ms = 20000UL, uint8_t rxfifo_full_thrhd = 112); + void end(bool fullyTerminate = true); + void updateBaudRate(unsigned long baud); + int available(void); + int availableForWrite(void); + int peek(void); + int read(void); + size_t read(uint8_t *buffer, size_t size); + inline size_t read(char * buffer, size_t size) + { + return read((uint8_t*) buffer, size); + } + // Overrides Stream::readBytes() to be faster using IDF + size_t readBytes(uint8_t *buffer, size_t length); + size_t readBytes(char *buffer, size_t length) + { + return readBytes((uint8_t *) buffer, length); + } + void flush(void); + void flush( bool txOnly); + size_t write(uint8_t); + size_t write(const uint8_t *buffer, size_t size); + inline size_t write(const char * buffer, size_t size) + { + return write((uint8_t*) buffer, size); + } + inline size_t write(const char * s) + { + return write((uint8_t*) s, strlen(s)); + } + inline size_t write(unsigned long n) + { + return write((uint8_t) n); + } + inline size_t write(long n) + { + return write((uint8_t) n); + } + inline size_t write(unsigned int n) + { + return write((uint8_t) n); + } + inline size_t write(int n) + { + return write((uint8_t) n); + } + uint32_t baudRate(); + operator bool() const; + + void setDebugOutput(bool); + + void setRxInvert(bool); + + // Negative Pin Number will keep it unmodified, thus this function can set individual pins + // SetPins shall be called after Serial begin() + bool setPins(int8_t rxPin, int8_t txPin, int8_t ctsPin = -1, int8_t rtsPin = -1); + // Enables or disables Hardware Flow Control using RTS and/or CTS pins (must use setAllPins() before) + bool setHwFlowCtrlMode(uint8_t mode = HW_FLOWCTRL_CTS_RTS, uint8_t threshold = 64); // 64 is half FIFO Length + // Used to set RS485 modes such as UART_MODE_RS485_HALF_DUPLEX for Auto RTS function on ESP32 + bool setMode(uint8_t mode); + size_t setRxBufferSize(size_t new_size); + size_t setTxBufferSize(size_t new_size); + +protected: + int _uart_nr; + uart_t* _uart; + size_t _rxBufferSize; + size_t _txBufferSize; + OnReceiveCb _onReceiveCB; + OnReceiveErrorCb _onReceiveErrorCB; + // _onReceive and _rxTimeout have be consistent when timeout is disabled + bool _onReceiveTimeout; + uint8_t _rxTimeout, _rxFIFOFull; + TaskHandle_t _eventTask; +#if !CONFIG_DISABLE_HAL_LOCKS + SemaphoreHandle_t _lock; +#endif + int8_t _rxPin, _txPin, _ctsPin, _rtsPin; + + void _createEventTask(void *args); + void _destroyEventTask(void); + static void _uartEventTask(void *args); +}; + +extern void serialEventRun(void) __attribute__((weak)); + +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL) +#ifndef ARDUINO_USB_CDC_ON_BOOT +#define ARDUINO_USB_CDC_ON_BOOT 0 +#endif +#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC +#if !ARDUINO_USB_MODE +#include "USB.h" +#include "USBCDC.h" +#endif +extern HardwareSerial Serial0; +#else +extern HardwareSerial Serial; +#endif +#if SOC_UART_NUM > 1 +extern HardwareSerial Serial1; +#endif +#if SOC_UART_NUM > 2 +extern HardwareSerial Serial2; +#endif +#endif + +#endif // HardwareSerial_h diff --git a/src/esp32/hardware/cores/esp32/base64.cpp b/src/esp32/hardware/cores/esp32/base64.cpp new file mode 100644 index 0000000..1fc144e --- /dev/null +++ b/src/esp32/hardware/cores/esp32/base64.cpp @@ -0,0 +1,64 @@ +/** + * base64.cpp + * + * Created on: 09.12.2015 + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the ESP31B core for Arduino. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "Arduino.h" +extern "C" { +#include "libb64/cdecode.h" +#include "libb64/cencode.h" +} +#include "base64.h" + +/** + * convert input data to base64 + * @param data const uint8_t * + * @param length size_t + * @return String + */ +String base64::encode(const uint8_t * data, size_t length) +{ + size_t size = base64_encode_expected_len(length) + 1; + char * buffer = (char *) malloc(size); + if(buffer) { + base64_encodestate _state; + base64_init_encodestate(&_state); + int len = base64_encode_block((const char *) &data[0], length, &buffer[0], &_state); + len = base64_encode_blockend((buffer + len), &_state); + + String base64 = String(buffer); + free(buffer); + return base64; + } + return String("-FAIL-"); +} + +/** + * convert input data to base64 + * @param text const String& + * @return String + */ +String base64::encode(const String& text) +{ + return base64::encode((uint8_t *) text.c_str(), text.length()); +} + diff --git a/src/esp32/hardware/cores/esp32/base64.h b/src/esp32/hardware/cores/esp32/base64.h new file mode 100644 index 0000000..9709581 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/base64.h @@ -0,0 +1,13 @@ +#ifndef CORE_BASE64_H_ +#define CORE_BASE64_H_ + +class base64 +{ +public: + static String encode(const uint8_t * data, size_t length); + static String encode(const String& text); +private: +}; + + +#endif /* CORE_BASE64_H_ */ diff --git a/src/esp32/hardware/cores/esp32/binary.h b/src/esp32/hardware/cores/esp32/binary.h new file mode 100644 index 0000000..c2f189d --- /dev/null +++ b/src/esp32/hardware/cores/esp32/binary.h @@ -0,0 +1,534 @@ +/* + binary.h - Definitions for binary constants + Copyright (c) 2006 David A. Mellis. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef Binary_h +#define Binary_h + +#define B0 0 +#define B00 0 +#define B000 0 +#define B0000 0 +#define B00000 0 +#define B000000 0 +#define B0000000 0 +#define B00000000 0 +#define B1 1 +#define B01 1 +#define B001 1 +#define B0001 1 +#define B00001 1 +#define B000001 1 +#define B0000001 1 +#define B00000001 1 +#define B10 2 +#define B010 2 +#define B0010 2 +#define B00010 2 +#define B000010 2 +#define B0000010 2 +#define B00000010 2 +#define B11 3 +#define B011 3 +#define B0011 3 +#define B00011 3 +#define B000011 3 +#define B0000011 3 +#define B00000011 3 +#define B100 4 +#define B0100 4 +#define B00100 4 +#define B000100 4 +#define B0000100 4 +#define B00000100 4 +#define B101 5 +#define B0101 5 +#define B00101 5 +#define B000101 5 +#define B0000101 5 +#define B00000101 5 +#define B110 6 +#define B0110 6 +#define B00110 6 +#define B000110 6 +#define B0000110 6 +#define B00000110 6 +#define B111 7 +#define B0111 7 +#define B00111 7 +#define B000111 7 +#define B0000111 7 +#define B00000111 7 +#define B1000 8 +#define B01000 8 +#define B001000 8 +#define B0001000 8 +#define B00001000 8 +#define B1001 9 +#define B01001 9 +#define B001001 9 +#define B0001001 9 +#define B00001001 9 +#define B1010 10 +#define B01010 10 +#define B001010 10 +#define B0001010 10 +#define B00001010 10 +#define B1011 11 +#define B01011 11 +#define B001011 11 +#define B0001011 11 +#define B00001011 11 +#define B1100 12 +#define B01100 12 +#define B001100 12 +#define B0001100 12 +#define B00001100 12 +#define B1101 13 +#define B01101 13 +#define B001101 13 +#define B0001101 13 +#define B00001101 13 +#define B1110 14 +#define B01110 14 +#define B001110 14 +#define B0001110 14 +#define B00001110 14 +#define B1111 15 +#define B01111 15 +#define B001111 15 +#define B0001111 15 +#define B00001111 15 +#define B10000 16 +#define B010000 16 +#define B0010000 16 +#define B00010000 16 +#define B10001 17 +#define B010001 17 +#define B0010001 17 +#define B00010001 17 +#define B10010 18 +#define B010010 18 +#define B0010010 18 +#define B00010010 18 +#define B10011 19 +#define B010011 19 +#define B0010011 19 +#define B00010011 19 +#define B10100 20 +#define B010100 20 +#define B0010100 20 +#define B00010100 20 +#define B10101 21 +#define B010101 21 +#define B0010101 21 +#define B00010101 21 +#define B10110 22 +#define B010110 22 +#define B0010110 22 +#define B00010110 22 +#define B10111 23 +#define B010111 23 +#define B0010111 23 +#define B00010111 23 +#define B11000 24 +#define B011000 24 +#define B0011000 24 +#define B00011000 24 +#define B11001 25 +#define B011001 25 +#define B0011001 25 +#define B00011001 25 +#define B11010 26 +#define B011010 26 +#define B0011010 26 +#define B00011010 26 +#define B11011 27 +#define B011011 27 +#define B0011011 27 +#define B00011011 27 +#define B11100 28 +#define B011100 28 +#define B0011100 28 +#define B00011100 28 +#define B11101 29 +#define B011101 29 +#define B0011101 29 +#define B00011101 29 +#define B11110 30 +#define B011110 30 +#define B0011110 30 +#define B00011110 30 +#define B11111 31 +#define B011111 31 +#define B0011111 31 +#define B00011111 31 +#define B100000 32 +#define B0100000 32 +#define B00100000 32 +#define B100001 33 +#define B0100001 33 +#define B00100001 33 +#define B100010 34 +#define B0100010 34 +#define B00100010 34 +#define B100011 35 +#define B0100011 35 +#define B00100011 35 +#define B100100 36 +#define B0100100 36 +#define B00100100 36 +#define B100101 37 +#define B0100101 37 +#define B00100101 37 +#define B100110 38 +#define B0100110 38 +#define B00100110 38 +#define B100111 39 +#define B0100111 39 +#define B00100111 39 +#define B101000 40 +#define B0101000 40 +#define B00101000 40 +#define B101001 41 +#define B0101001 41 +#define B00101001 41 +#define B101010 42 +#define B0101010 42 +#define B00101010 42 +#define B101011 43 +#define B0101011 43 +#define B00101011 43 +#define B101100 44 +#define B0101100 44 +#define B00101100 44 +#define B101101 45 +#define B0101101 45 +#define B00101101 45 +#define B101110 46 +#define B0101110 46 +#define B00101110 46 +#define B101111 47 +#define B0101111 47 +#define B00101111 47 +#define B110000 48 +#define B0110000 48 +#define B00110000 48 +#define B110001 49 +#define B0110001 49 +#define B00110001 49 +#define B110010 50 +#define B0110010 50 +#define B00110010 50 +#define B110011 51 +#define B0110011 51 +#define B00110011 51 +#define B110100 52 +#define B0110100 52 +#define B00110100 52 +#define B110101 53 +#define B0110101 53 +#define B00110101 53 +#define B110110 54 +#define B0110110 54 +#define B00110110 54 +#define B110111 55 +#define B0110111 55 +#define B00110111 55 +#define B111000 56 +#define B0111000 56 +#define B00111000 56 +#define B111001 57 +#define B0111001 57 +#define B00111001 57 +#define B111010 58 +#define B0111010 58 +#define B00111010 58 +#define B111011 59 +#define B0111011 59 +#define B00111011 59 +#define B111100 60 +#define B0111100 60 +#define B00111100 60 +#define B111101 61 +#define B0111101 61 +#define B00111101 61 +#define B111110 62 +#define B0111110 62 +#define B00111110 62 +#define B111111 63 +#define B0111111 63 +#define B00111111 63 +#define B1000000 64 +#define B01000000 64 +#define B1000001 65 +#define B01000001 65 +#define B1000010 66 +#define B01000010 66 +#define B1000011 67 +#define B01000011 67 +#define B1000100 68 +#define B01000100 68 +#define B1000101 69 +#define B01000101 69 +#define B1000110 70 +#define B01000110 70 +#define B1000111 71 +#define B01000111 71 +#define B1001000 72 +#define B01001000 72 +#define B1001001 73 +#define B01001001 73 +#define B1001010 74 +#define B01001010 74 +#define B1001011 75 +#define B01001011 75 +#define B1001100 76 +#define B01001100 76 +#define B1001101 77 +#define B01001101 77 +#define B1001110 78 +#define B01001110 78 +#define B1001111 79 +#define B01001111 79 +#define B1010000 80 +#define B01010000 80 +#define B1010001 81 +#define B01010001 81 +#define B1010010 82 +#define B01010010 82 +#define B1010011 83 +#define B01010011 83 +#define B1010100 84 +#define B01010100 84 +#define B1010101 85 +#define B01010101 85 +#define B1010110 86 +#define B01010110 86 +#define B1010111 87 +#define B01010111 87 +#define B1011000 88 +#define B01011000 88 +#define B1011001 89 +#define B01011001 89 +#define B1011010 90 +#define B01011010 90 +#define B1011011 91 +#define B01011011 91 +#define B1011100 92 +#define B01011100 92 +#define B1011101 93 +#define B01011101 93 +#define B1011110 94 +#define B01011110 94 +#define B1011111 95 +#define B01011111 95 +#define B1100000 96 +#define B01100000 96 +#define B1100001 97 +#define B01100001 97 +#define B1100010 98 +#define B01100010 98 +#define B1100011 99 +#define B01100011 99 +#define B1100100 100 +#define B01100100 100 +#define B1100101 101 +#define B01100101 101 +#define B1100110 102 +#define B01100110 102 +#define B1100111 103 +#define B01100111 103 +#define B1101000 104 +#define B01101000 104 +#define B1101001 105 +#define B01101001 105 +#define B1101010 106 +#define B01101010 106 +#define B1101011 107 +#define B01101011 107 +#define B1101100 108 +#define B01101100 108 +#define B1101101 109 +#define B01101101 109 +#define B1101110 110 +#define B01101110 110 +#define B1101111 111 +#define B01101111 111 +#define B1110000 112 +#define B01110000 112 +#define B1110001 113 +#define B01110001 113 +#define B1110010 114 +#define B01110010 114 +#define B1110011 115 +#define B01110011 115 +#define B1110100 116 +#define B01110100 116 +#define B1110101 117 +#define B01110101 117 +#define B1110110 118 +#define B01110110 118 +#define B1110111 119 +#define B01110111 119 +#define B1111000 120 +#define B01111000 120 +#define B1111001 121 +#define B01111001 121 +#define B1111010 122 +#define B01111010 122 +#define B1111011 123 +#define B01111011 123 +#define B1111100 124 +#define B01111100 124 +#define B1111101 125 +#define B01111101 125 +#define B1111110 126 +#define B01111110 126 +#define B1111111 127 +#define B01111111 127 +#define B10000000 128 +#define B10000001 129 +#define B10000010 130 +#define B10000011 131 +#define B10000100 132 +#define B10000101 133 +#define B10000110 134 +#define B10000111 135 +#define B10001000 136 +#define B10001001 137 +#define B10001010 138 +#define B10001011 139 +#define B10001100 140 +#define B10001101 141 +#define B10001110 142 +#define B10001111 143 +#define B10010000 144 +#define B10010001 145 +#define B10010010 146 +#define B10010011 147 +#define B10010100 148 +#define B10010101 149 +#define B10010110 150 +#define B10010111 151 +#define B10011000 152 +#define B10011001 153 +#define B10011010 154 +#define B10011011 155 +#define B10011100 156 +#define B10011101 157 +#define B10011110 158 +#define B10011111 159 +#define B10100000 160 +#define B10100001 161 +#define B10100010 162 +#define B10100011 163 +#define B10100100 164 +#define B10100101 165 +#define B10100110 166 +#define B10100111 167 +#define B10101000 168 +#define B10101001 169 +#define B10101010 170 +#define B10101011 171 +#define B10101100 172 +#define B10101101 173 +#define B10101110 174 +#define B10101111 175 +#define B10110000 176 +#define B10110001 177 +#define B10110010 178 +#define B10110011 179 +#define B10110100 180 +#define B10110101 181 +#define B10110110 182 +#define B10110111 183 +#define B10111000 184 +#define B10111001 185 +#define B10111010 186 +#define B10111011 187 +#define B10111100 188 +#define B10111101 189 +#define B10111110 190 +#define B10111111 191 +#define B11000000 192 +#define B11000001 193 +#define B11000010 194 +#define B11000011 195 +#define B11000100 196 +#define B11000101 197 +#define B11000110 198 +#define B11000111 199 +#define B11001000 200 +#define B11001001 201 +#define B11001010 202 +#define B11001011 203 +#define B11001100 204 +#define B11001101 205 +#define B11001110 206 +#define B11001111 207 +#define B11010000 208 +#define B11010001 209 +#define B11010010 210 +#define B11010011 211 +#define B11010100 212 +#define B11010101 213 +#define B11010110 214 +#define B11010111 215 +#define B11011000 216 +#define B11011001 217 +#define B11011010 218 +#define B11011011 219 +#define B11011100 220 +#define B11011101 221 +#define B11011110 222 +#define B11011111 223 +#define B11100000 224 +#define B11100001 225 +#define B11100010 226 +#define B11100011 227 +#define B11100100 228 +#define B11100101 229 +#define B11100110 230 +#define B11100111 231 +#define B11101000 232 +#define B11101001 233 +#define B11101010 234 +#define B11101011 235 +#define B11101100 236 +#define B11101101 237 +#define B11101110 238 +#define B11101111 239 +#define B11110000 240 +#define B11110001 241 +#define B11110010 242 +#define B11110011 243 +#define B11110100 244 +#define B11110101 245 +#define B11110110 246 +#define B11110111 247 +#define B11111000 248 +#define B11111001 249 +#define B11111010 250 +#define B11111011 251 +#define B11111100 252 +#define B11111101 253 +#define B11111110 254 +#define B11111111 255 + +#endif diff --git a/src/esp32/hardware/cores/esp32/cbuf.cpp b/src/esp32/hardware/cores/esp32/cbuf.cpp new file mode 100644 index 0000000..ef7370a --- /dev/null +++ b/src/esp32/hardware/cores/esp32/cbuf.cpp @@ -0,0 +1,196 @@ +/* + cbuf.cpp - Circular buffer implementation + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "cbuf.h" + +cbuf::cbuf(size_t size) : + next(NULL), _size(size+1), _buf(new char[size+1]), _bufend(_buf + size + 1), _begin(_buf), _end(_begin) +{ +} + +cbuf::~cbuf() +{ + delete[] _buf; +} + +size_t cbuf::resizeAdd(size_t addSize) +{ + return resize(_size + addSize); +} + +size_t cbuf::resize(size_t newSize) +{ + + size_t bytes_available = available(); + newSize += 1; + // not lose any data + // if data can be lost use remove or flush before resize + if((newSize < bytes_available) || (newSize == _size)) { + return _size; + } + + char *newbuf = new char[newSize]; + char *oldbuf = _buf; + + if(!newbuf) { + return _size; + } + + if(_buf) { + read(newbuf, bytes_available); + memset((newbuf + bytes_available), 0x00, (newSize - bytes_available)); + } + + _begin = newbuf; + _end = newbuf + bytes_available; + _bufend = newbuf + newSize; + _size = newSize; + + _buf = newbuf; + delete[] oldbuf; + + return _size; +} + +size_t cbuf::available() const +{ + if(_end >= _begin) { + return _end - _begin; + } + return _size - (_begin - _end); +} + +size_t cbuf::size() +{ + return _size; +} + +size_t cbuf::room() const +{ + if(_end >= _begin) { + return _size - (_end - _begin) - 1; + } + return _begin - _end - 1; +} + +int cbuf::peek() +{ + if(empty()) { + return -1; + } + + return static_cast(*_begin); +} + +size_t cbuf::peek(char *dst, size_t size) +{ + size_t bytes_available = available(); + size_t size_to_read = (size < bytes_available) ? size : bytes_available; + size_t size_read = size_to_read; + char * begin = _begin; + if(_end < _begin && size_to_read > (size_t) (_bufend - _begin)) { + size_t top_size = _bufend - _begin; + memcpy(dst, _begin, top_size); + begin = _buf; + size_to_read -= top_size; + dst += top_size; + } + memcpy(dst, begin, size_to_read); + return size_read; +} + +int cbuf::read() +{ + if(empty()) { + return -1; + } + + char result = *_begin; + _begin = wrap_if_bufend(_begin + 1); + return static_cast(result); +} + +size_t cbuf::read(char* dst, size_t size) +{ + size_t bytes_available = available(); + size_t size_to_read = (size < bytes_available) ? size : bytes_available; + size_t size_read = size_to_read; + if(_end < _begin && size_to_read > (size_t) (_bufend - _begin)) { + size_t top_size = _bufend - _begin; + memcpy(dst, _begin, top_size); + _begin = _buf; + size_to_read -= top_size; + dst += top_size; + } + memcpy(dst, _begin, size_to_read); + _begin = wrap_if_bufend(_begin + size_to_read); + return size_read; +} + +size_t cbuf::write(char c) +{ + if(full()) { + return 0; + } + + *_end = c; + _end = wrap_if_bufend(_end + 1); + return 1; +} + +size_t cbuf::write(const char* src, size_t size) +{ + size_t bytes_available = room(); + size_t size_to_write = (size < bytes_available) ? size : bytes_available; + size_t size_written = size_to_write; + if(_end >= _begin && size_to_write > (size_t) (_bufend - _end)) { + size_t top_size = _bufend - _end; + memcpy(_end, src, top_size); + _end = _buf; + size_to_write -= top_size; + src += top_size; + } + memcpy(_end, src, size_to_write); + _end = wrap_if_bufend(_end + size_to_write); + return size_written; +} + +void cbuf::flush() +{ + _begin = _buf; + _end = _buf; +} + +size_t cbuf::remove(size_t size) +{ + size_t bytes_available = available(); + if(size >= bytes_available) { + flush(); + return 0; + } + size_t size_to_remove = (size < bytes_available) ? size : bytes_available; + if(_end < _begin && size_to_remove > (size_t) (_bufend - _begin)) { + size_t top_size = _bufend - _begin; + _begin = _buf; + size_to_remove -= top_size; + } + _begin = wrap_if_bufend(_begin + size_to_remove); + return available(); +} diff --git a/src/esp32/hardware/cores/esp32/cbuf.h b/src/esp32/hardware/cores/esp32/cbuf.h new file mode 100644 index 0000000..490352e --- /dev/null +++ b/src/esp32/hardware/cores/esp32/cbuf.h @@ -0,0 +1,79 @@ +/* + cbuf.h - Circular buffer implementation + Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __cbuf_h +#define __cbuf_h + +#include +#include +#include + +class cbuf +{ +public: + cbuf(size_t size); + ~cbuf(); + + size_t resizeAdd(size_t addSize); + size_t resize(size_t newSize); + size_t available() const; + size_t size(); + + size_t room() const; + + inline bool empty() const + { + return _begin == _end; + } + + inline bool full() const + { + return wrap_if_bufend(_end + 1) == _begin; + } + + int peek(); + size_t peek(char *dst, size_t size); + + int read(); + size_t read(char* dst, size_t size); + + size_t write(char c); + size_t write(const char* src, size_t size); + + void flush(); + size_t remove(size_t size); + + cbuf *next; + +protected: + inline char* wrap_if_bufend(char* ptr) const + { + return (ptr == _bufend) ? _buf : ptr; + } + + size_t _size; + char* _buf; + const char* _bufend; + char* _begin; + char* _end; + +}; + +#endif//__cbuf_h diff --git a/src/esp32/hardware/cores/esp32/core_version.h b/src/esp32/hardware/cores/esp32/core_version.h new file mode 100644 index 0000000..abfed69 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/core_version.h @@ -0,0 +1,4 @@ +#define ARDUINO_ESP32_GIT_VER 0x725146d1 +#define ARDUINO_ESP32_GIT_DESC 2.0.12 +#define ARDUINO_ESP32_RELEASE_2_0_12 +#define ARDUINO_ESP32_RELEASE "2_0_12" diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-adc.c b/src/esp32/hardware/cores/esp32/esp32-hal-adc.c new file mode 100644 index 0000000..0fe73d1 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-adc.c @@ -0,0 +1,281 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp32-hal-adc.h" +#include "driver/adc.h" +#include "esp_adc_cal.h" + +#if SOC_DAC_SUPPORTED //ESP32, ESP32S2 +#include "soc/dac_channel.h" +#include "soc/sens_reg.h" +#include "soc/rtc_io_reg.h" +#endif + +#define DEFAULT_VREF 1100 + +static uint8_t __analogAttenuation = 3;//11db +static uint8_t __analogWidth = ADC_WIDTH_MAX - 1; //3 for ESP32/ESP32C3; 4 for ESP32S2 +static uint8_t __analogReturnedWidth = SOC_ADC_MAX_BITWIDTH; //12 for ESP32/ESP32C3; 13 for ESP32S2 +static uint8_t __analogClockDiv = 1; +static adc_attenuation_t __pin_attenuation[SOC_GPIO_PIN_COUNT]; + +static uint16_t __analogVRef = 0; +#if CONFIG_IDF_TARGET_ESP32 +static uint8_t __analogVRefPin = 0; +#endif + +static inline uint16_t mapResolution(uint16_t value) +{ + uint8_t from = __analogWidth + 9; + if (from == __analogReturnedWidth) { + return value; + } + if (from > __analogReturnedWidth) { + return value >> (from - __analogReturnedWidth); + } + return value << (__analogReturnedWidth - from); +} + +void __analogSetClockDiv(uint8_t clockDiv){ + if(!clockDiv){ + clockDiv = 1; + } + __analogClockDiv = clockDiv; +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 + adc_set_clk_div(__analogClockDiv); +#endif +} + +void __analogSetAttenuation(adc_attenuation_t attenuation) +{ + __analogAttenuation = attenuation & 3; +} + +#if CONFIG_IDF_TARGET_ESP32 +void __analogSetWidth(uint8_t bits){ + if(bits < 9){ + bits = 9; + } else if(bits > 12){ + bits = 12; + } + __analogWidth = bits - 9; + adc1_config_width(__analogWidth); +} +#endif + +void __analogInit(){ + static bool initialized = false; + if(initialized){ + return; + } + initialized = true; + __analogSetClockDiv(__analogClockDiv); +#if CONFIG_IDF_TARGET_ESP32 + __analogSetWidth(__analogWidth + 9);//in bits +#endif + for(int i=0; i 3){ + return ; + } + if(channel > (SOC_ADC_MAX_CHANNEL_NUM - 1)){ + adc2_config_channel_atten(channel - SOC_ADC_MAX_CHANNEL_NUM, attenuation); + } else { + adc1_config_channel_atten(channel, attenuation); + } + __analogInit(); + if((__pin_attenuation[pin] != ADC_ATTENDB_MAX) || (attenuation != __analogAttenuation)){ + __pin_attenuation[pin] = attenuation; + } +} + +bool __adcAttachPin(uint8_t pin){ + int8_t channel = digitalPinToAnalogChannel(pin); + if(channel < 0){ + log_e("Pin %u is not ADC pin!", pin); + return false; + } + __analogInit(); + int8_t pad = digitalPinToTouchChannel(pin); + if(pad >= 0){ +#if CONFIG_IDF_TARGET_ESP32 + uint32_t touch = READ_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG); + if(touch & (1 << pad)){ + touch &= ~((1 << (pad + SENS_TOUCH_PAD_OUTEN2_S)) + | (1 << (pad + SENS_TOUCH_PAD_OUTEN1_S)) + | (1 << (pad + SENS_TOUCH_PAD_WORKEN_S))); + WRITE_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG, touch); + } +#endif + } +#if SOC_DAC_SUPPORTED + else if(pin == DAC_CHANNEL_1_GPIO_NUM){ + CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_XPD_DAC | RTC_IO_PDAC1_DAC_XPD_FORCE);//stop dac1 + } else if(pin == DAC_CHANNEL_2_GPIO_NUM){ + CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_XPD_DAC | RTC_IO_PDAC2_DAC_XPD_FORCE);//stop dac2 + } +#endif + + pinMode(pin, ANALOG); + __analogSetPinAttenuation(pin, (__pin_attenuation[pin] != ADC_ATTENDB_MAX)?__pin_attenuation[pin]:__analogAttenuation); + return true; +} + +void __analogReadResolution(uint8_t bits) +{ + if(!bits || bits > 16){ + return; + } + __analogReturnedWidth = bits; +#if CONFIG_IDF_TARGET_ESP32 + __analogSetWidth(bits); // hadware from 9 to 12 +#endif +} + +uint16_t __analogRead(uint8_t pin) +{ + int8_t channel = digitalPinToAnalogChannel(pin); + int value = 0; + esp_err_t r = ESP_OK; + if(channel < 0){ + log_e("Pin %u is not ADC pin!", pin); + return value; + } + __adcAttachPin(pin); + if(channel > (SOC_ADC_MAX_CHANNEL_NUM - 1)){ + channel -= SOC_ADC_MAX_CHANNEL_NUM; + r = adc2_get_raw( channel, __analogWidth, &value); + if ( r == ESP_OK ) { + return mapResolution(value); + } else if ( r == ESP_ERR_INVALID_STATE ) { + log_e("GPIO%u: %s: ADC2 not initialized yet.", pin, esp_err_to_name(r)); + } else if ( r == ESP_ERR_TIMEOUT ) { + log_e("GPIO%u: %s: ADC2 is in use by Wi-Fi. Please see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html#adc-limitations for more info", pin, esp_err_to_name(r)); + } else { + log_e("GPIO%u: %s", pin, esp_err_to_name(r)); + } + } else { + value = adc1_get_raw(channel); + return mapResolution(value); + } + return mapResolution(value); +} + +uint32_t __analogReadMilliVolts(uint8_t pin){ + int8_t channel = digitalPinToAnalogChannel(pin); + if(channel < 0){ + log_e("Pin %u is not ADC pin!", pin); + return 0; + } + + if(!__analogVRef){ + if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) == ESP_OK) { + log_d("eFuse Two Point: Supported"); + __analogVRef = DEFAULT_VREF; + } + if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_VREF) == ESP_OK) { + log_d("eFuse Vref: Supported"); + __analogVRef = DEFAULT_VREF; + } + if(!__analogVRef){ + __analogVRef = DEFAULT_VREF; + + #if CONFIG_IDF_TARGET_ESP32 + if(__analogVRefPin){ + esp_adc_cal_characteristics_t chars; + if(adc_vref_to_gpio(ADC_UNIT_2, __analogVRefPin) == ESP_OK){ + __analogVRef = __analogRead(__analogVRefPin); + esp_adc_cal_characterize(1, __analogAttenuation, __analogWidth, DEFAULT_VREF, &chars); + __analogVRef = esp_adc_cal_raw_to_voltage(__analogVRef, &chars); + log_d("Vref to GPIO%u: %u", __analogVRefPin, __analogVRef); + } + } + #endif + } + } + uint8_t unit = 1; + if(channel > (SOC_ADC_MAX_CHANNEL_NUM - 1)){ + unit = 2; + } + + uint16_t adc_reading = __analogRead(pin); + + uint8_t atten = __analogAttenuation; + if (__pin_attenuation[pin] != ADC_ATTENDB_MAX){ + atten = __pin_attenuation[pin]; + } + + esp_adc_cal_characteristics_t chars = {}; + esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, __analogWidth, __analogVRef, &chars); + + static bool print_chars_info = true; + if(print_chars_info) + { + if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) { + log_i("ADC%u: Characterized using Two Point Value: %u\n", unit, chars.vref); + } + else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) { + log_i("ADC%u: Characterized using eFuse Vref: %u\n", unit, chars.vref); + } + #if CONFIG_IDF_TARGET_ESP32 + else if(__analogVRef != DEFAULT_VREF){ + log_i("ADC%u: Characterized using Vref to GPIO%u: %u\n", unit, __analogVRefPin, chars.vref); + } + #endif + else { + log_i("ADC%u: Characterized using Default Vref: %u\n", unit, chars.vref); + } + print_chars_info = false; + } + return esp_adc_cal_raw_to_voltage((uint32_t)adc_reading, &chars); +} + +#if CONFIG_IDF_TARGET_ESP32 + +void __analogSetVRefPin(uint8_t pin){ + if(pin <25 || pin > 27){ + pin = 0; + } + __analogVRefPin = pin; +} + +int __hallRead() //hall sensor using idf read +{ + pinMode(36, ANALOG); + pinMode(39, ANALOG); + __analogSetWidth(12); + return hall_sensor_read(); +} +#endif + +extern uint16_t analogRead(uint8_t pin) __attribute__ ((weak, alias("__analogRead"))); +extern uint32_t analogReadMilliVolts(uint8_t pin) __attribute__ ((weak, alias("__analogReadMilliVolts"))); +extern void analogReadResolution(uint8_t bits) __attribute__ ((weak, alias("__analogReadResolution"))); +extern void analogSetClockDiv(uint8_t clockDiv) __attribute__ ((weak, alias("__analogSetClockDiv"))); +extern void analogSetAttenuation(adc_attenuation_t attenuation) __attribute__ ((weak, alias("__analogSetAttenuation"))); +extern void analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation) __attribute__ ((weak, alias("__analogSetPinAttenuation"))); + +extern bool adcAttachPin(uint8_t pin) __attribute__ ((weak, alias("__adcAttachPin"))); + +#if CONFIG_IDF_TARGET_ESP32 +extern void analogSetVRefPin(uint8_t pin) __attribute__ ((weak, alias("__analogSetVRefPin"))); +extern void analogSetWidth(uint8_t bits) __attribute__ ((weak, alias("__analogSetWidth"))); +extern int hallRead() __attribute__ ((weak, alias("__hallRead"))); +#endif diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-adc.h b/src/esp32/hardware/cores/esp32/esp32-hal-adc.h new file mode 100644 index 0000000..1b094d8 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-adc.h @@ -0,0 +1,104 @@ +/* + Arduino.h - Main include file for the Arduino SDK + Copyright (c) 2005-2013 Arduino Team. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MAIN_ESP32_HAL_ADC_H_ +#define MAIN_ESP32_HAL_ADC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp32-hal.h" + +typedef enum { + ADC_0db, + ADC_2_5db, + ADC_6db, + ADC_11db, + ADC_ATTENDB_MAX +} adc_attenuation_t; + +/* + * Get ADC value for pin + * */ +uint16_t analogRead(uint8_t pin); + +/* + * Get MilliVolts value for pin + * */ +uint32_t analogReadMilliVolts(uint8_t pin); + +/* + * Set the resolution of analogRead return values. Default is 12 bits (range from 0 to 4096). + * If between 9 and 12, it will equal the set hardware resolution, else value will be shifted. + * Range is 1 - 16 + * + * Note: compatibility with Arduino SAM + */ +void analogReadResolution(uint8_t bits); + +/* + * Set the divider for the ADC clock. + * Default is 1 + * Range is 1 - 255 + * */ +void analogSetClockDiv(uint8_t clockDiv); + +/* + * Set the attenuation for all channels + * Default is 11db + * */ +void analogSetAttenuation(adc_attenuation_t attenuation); + +/* + * Set the attenuation for particular pin + * Default is 11db + * */ +void analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation); + +/* + * Attach pin to ADC (will also clear any other analog mode that could be on) + * */ +bool adcAttachPin(uint8_t pin); + +#if CONFIG_IDF_TARGET_ESP32 +/* + * Sets the sample bits and read resolution + * Default is 12bit (0 - 4095) + * Range is 9 - 12 + * */ +void analogSetWidth(uint8_t bits); + +/* + * Set pin to use for ADC calibration if the esp is not already calibrated (25, 26 or 27) + * */ +void analogSetVRefPin(uint8_t pin); + +/* + * Get value for HALL sensor (without LNA) + * connected to pins 36(SVP) and 39(SVN) + * */ +int hallRead(); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* MAIN_ESP32_HAL_ADC_H_ */ diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-bt.c b/src/esp32/hardware/cores/esp32/esp32-hal-bt.c new file mode 100644 index 0000000..b4c381a --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-bt.c @@ -0,0 +1,105 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp32-hal-bt.h" + +#ifdef CONFIG_BT_ENABLED + +#if CONFIG_IDF_TARGET_ESP32 +bool btInUse(){ return true; } +#else +// user may want to change it to free resources +__attribute__((weak)) bool btInUse(){ return true; } +#endif + +#include "esp_bt.h" + +#ifdef CONFIG_BTDM_CONTROLLER_MODE_BTDM +#define BT_MODE ESP_BT_MODE_BTDM +#elif defined(CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY) +#define BT_MODE ESP_BT_MODE_CLASSIC_BT +#else +#define BT_MODE ESP_BT_MODE_BLE +#endif + +bool btStarted(){ + return (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED); +} + +bool btStart(){ + esp_bt_controller_config_t cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + if(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED){ + return true; + } + if(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE){ + esp_bt_controller_init(&cfg); + while(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE){} + } + if(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED){ + if (esp_bt_controller_enable(BT_MODE)) { + log_e("BT Enable failed"); + return false; + } + } + if(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED){ + return true; + } + log_e("BT Start failed"); + return false; +} + +bool btStop(){ + if(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE){ + return true; + } + if(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED){ + if (esp_bt_controller_disable()) { + log_e("BT Disable failed"); + return false; + } + while(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED); + } + if(esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED){ + if (esp_bt_controller_deinit()) { + log_e("BT deint failed"); + return false; + } + vTaskDelay(1); + if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_IDLE) { + return false; + } + return true; + } + log_e("BT Stop failed"); + return false; +} + +#else // CONFIG_BT_ENABLED +bool btStarted() +{ + return false; +} + +bool btStart() +{ + return false; +} + +bool btStop() +{ + return false; +} + +#endif // CONFIG_BT_ENABLED + diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-bt.h b/src/esp32/hardware/cores/esp32/esp32-hal-bt.h new file mode 100644 index 0000000..56222da --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-bt.h @@ -0,0 +1,32 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _ESP32_ESP32_HAL_BT_H_ +#define _ESP32_ESP32_HAL_BT_H_ + +#include "esp32-hal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool btStarted(); +bool btStart(); +bool btStop(); + +#ifdef __cplusplus +} +#endif + +#endif /* _ESP32_ESP32_HAL_BT_H_ */ diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-cpu.c b/src/esp32/hardware/cores/esp32/esp32-hal-cpu.c new file mode 100644 index 0000000..5ece7fb --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-cpu.c @@ -0,0 +1,262 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/task.h" +#include "esp_attr.h" +#include "esp_log.h" +#include "soc/rtc.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/apb_ctrl_reg.h" +#include "soc/efuse_reg.h" +#include "esp32-hal.h" +#include "esp32-hal-cpu.h" + +#include "esp_system.h" +#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+ +#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 +#include "freertos/xtensa_timer.h" +#include "esp32/rom/rtc.h" +#elif CONFIG_IDF_TARGET_ESP32S2 +#include "freertos/xtensa_timer.h" +#include "esp32s2/rom/rtc.h" +#elif CONFIG_IDF_TARGET_ESP32S3 +#include "freertos/xtensa_timer.h" +#include "esp32s3/rom/rtc.h" +#elif CONFIG_IDF_TARGET_ESP32C3 +#include "esp32c3/rom/rtc.h" +#else +#error Target CONFIG_IDF_TARGET is not supported +#endif +#else // ESP32 Before IDF 4.0 +#include "rom/rtc.h" +#endif + +typedef struct apb_change_cb_s { + struct apb_change_cb_s * prev; + struct apb_change_cb_s * next; + void * arg; + apb_change_cb_t cb; +} apb_change_t; + + +static apb_change_t * apb_change_callbacks = NULL; +static xSemaphoreHandle apb_change_lock = NULL; + +static void initApbChangeCallback(){ + static volatile bool initialized = false; + if(!initialized){ + initialized = true; + apb_change_lock = xSemaphoreCreateMutex(); + if(!apb_change_lock){ + initialized = false; + } + } +} + +static void triggerApbChangeCallback(apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){ + initApbChangeCallback(); + xSemaphoreTake(apb_change_lock, portMAX_DELAY); + apb_change_t * r = apb_change_callbacks; + if( r != NULL ){ + if(ev_type == APB_BEFORE_CHANGE ) + while(r != NULL){ + r->cb(r->arg, ev_type, old_apb, new_apb); + r=r->next; + } + else { // run backwards through chain + while(r->next != NULL) r = r->next; // find first added + while( r != NULL){ + r->cb(r->arg, ev_type, old_apb, new_apb); + r=r->prev; + } + } + } + xSemaphoreGive(apb_change_lock); +} + +bool addApbChangeCallback(void * arg, apb_change_cb_t cb){ + initApbChangeCallback(); + apb_change_t * c = (apb_change_t*)malloc(sizeof(apb_change_t)); + if(!c){ + log_e("Callback Object Malloc Failed"); + return false; + } + c->next = NULL; + c->prev = NULL; + c->arg = arg; + c->cb = cb; + xSemaphoreTake(apb_change_lock, portMAX_DELAY); + if(apb_change_callbacks == NULL){ + apb_change_callbacks = c; + } else { + apb_change_t * r = apb_change_callbacks; + // look for duplicate callbacks + while( (r != NULL ) && !((r->cb == cb) && ( r->arg == arg))) r = r->next; + if (r) { + log_e("duplicate func=%8p arg=%8p",c->cb,c->arg); + free(c); + xSemaphoreGive(apb_change_lock); + return false; + } + else { + c->next = apb_change_callbacks; + apb_change_callbacks-> prev = c; + apb_change_callbacks = c; + } + } + xSemaphoreGive(apb_change_lock); + return true; +} + +bool removeApbChangeCallback(void * arg, apb_change_cb_t cb){ + initApbChangeCallback(); + xSemaphoreTake(apb_change_lock, portMAX_DELAY); + apb_change_t * r = apb_change_callbacks; + // look for matching callback + while( (r != NULL ) && !((r->cb == cb) && ( r->arg == arg))) r = r->next; + if ( r == NULL ) { + log_e("not found func=%8p arg=%8p",cb,arg); + xSemaphoreGive(apb_change_lock); + return false; + } + else { + // patch links + if(r->prev) r->prev->next = r->next; + else { // this is first link + apb_change_callbacks = r->next; + } + if(r->next) r->next->prev = r->prev; + free(r); + } + xSemaphoreGive(apb_change_lock); + return true; +} + +static uint32_t calculateApb(rtc_cpu_freq_config_t * conf){ +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + return APB_CLK_FREQ; +#else + if(conf->freq_mhz >= 80){ + return 80 * MHZ; + } + return (conf->source_freq_mhz * MHZ) / conf->div; +#endif +} + +void esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us); //private in IDF + +bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz){ + rtc_cpu_freq_config_t conf, cconf; + uint32_t capb, apb; + //Get XTAL Frequency and calculate min CPU MHz + rtc_xtal_freq_t xtal = rtc_clk_xtal_freq_get(); +#if CONFIG_IDF_TARGET_ESP32 + if(xtal > RTC_XTAL_FREQ_AUTO){ + if(xtal < RTC_XTAL_FREQ_40M) { + if(cpu_freq_mhz <= xtal && cpu_freq_mhz != xtal && cpu_freq_mhz != (xtal/2)){ + log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2); + return false; + } + } else if(cpu_freq_mhz <= xtal && cpu_freq_mhz != xtal && cpu_freq_mhz != (xtal/2) && cpu_freq_mhz != (xtal/4)){ + log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2, xtal/4); + return false; + } + } +#endif + if(cpu_freq_mhz > xtal && cpu_freq_mhz != 240 && cpu_freq_mhz != 160 && cpu_freq_mhz != 80){ + if(xtal >= RTC_XTAL_FREQ_40M){ + log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2, xtal/4); + } else { + log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u and %u MHz", cpu_freq_mhz, xtal, xtal/2); + } + return false; + } +#if CONFIG_IDF_TARGET_ESP32 + //check if cpu supports the frequency + if(cpu_freq_mhz == 240){ + //Check if ESP32 is rated for a CPU frequency of 160MHz only + if (REG_GET_BIT(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_CPU_FREQ_RATED) && + REG_GET_BIT(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_CPU_FREQ_LOW)) { + log_e("Can not switch to 240 MHz! Chip CPU frequency rated for 160MHz."); + cpu_freq_mhz = 160; + } + } +#endif + //Get current CPU clock configuration + rtc_clk_cpu_freq_get_config(&cconf); + //return if frequency has not changed + if(cconf.freq_mhz == cpu_freq_mhz){ + return true; + } + //Get configuration for the new CPU frequency + if(!rtc_clk_cpu_freq_mhz_to_config(cpu_freq_mhz, &conf)){ + log_e("CPU clock could not be set to %u MHz", cpu_freq_mhz); + return false; + } + //Current APB + capb = calculateApb(&cconf); + //New APB + apb = calculateApb(&conf); + + //Call peripheral functions before the APB change + if(apb_change_callbacks){ + triggerApbChangeCallback(APB_BEFORE_CHANGE, capb, apb); + } + //Make the frequency change + rtc_clk_cpu_freq_set_config_fast(&conf); + if(capb != apb){ + //Update REF_TICK (uncomment if REF_TICK is different than 1MHz) + //if(conf.freq_mhz < 80){ + // ESP_REG(APB_CTRL_XTAL_TICK_CONF_REG) = conf.freq_mhz / (REF_CLK_FREQ / MHZ) - 1; + // } + //Update APB Freq REG + rtc_clk_apb_freq_update(apb); + //Update esp_timer divisor + esp_timer_impl_update_apb_freq(apb / MHZ); + } + //Update FreeRTOS Tick Divisor +#if CONFIG_IDF_TARGET_ESP32C3 + +#elif CONFIG_IDF_TARGET_ESP32S3 + +#else + uint32_t fcpu = (conf.freq_mhz >= 80)?(conf.freq_mhz * MHZ):(apb); + _xt_tick_divisor = fcpu / XT_TICK_PER_SEC; +#endif + //Call peripheral functions after the APB change + if(apb_change_callbacks){ + triggerApbChangeCallback(APB_AFTER_CHANGE, capb, apb); + } + log_d("%s: %u / %u = %u Mhz, APB: %u Hz", (conf.source == RTC_CPU_FREQ_SRC_PLL)?"PLL":((conf.source == RTC_CPU_FREQ_SRC_APLL)?"APLL":((conf.source == RTC_CPU_FREQ_SRC_XTAL)?"XTAL":"8M")), conf.source_freq_mhz, conf.div, conf.freq_mhz, apb); + return true; +} + +uint32_t getCpuFrequencyMhz(){ + rtc_cpu_freq_config_t conf; + rtc_clk_cpu_freq_get_config(&conf); + return conf.freq_mhz; +} + +uint32_t getXtalFrequencyMhz(){ + return rtc_clk_xtal_freq_get(); +} + +uint32_t getApbFrequency(){ + rtc_cpu_freq_config_t conf; + rtc_clk_cpu_freq_get_config(&conf); + return calculateApb(&conf); +} diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-cpu.h b/src/esp32/hardware/cores/esp32/esp32-hal-cpu.h new file mode 100644 index 0000000..646b598 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-cpu.h @@ -0,0 +1,48 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _ESP32_HAL_CPU_H_ +#define _ESP32_HAL_CPU_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +typedef enum { APB_BEFORE_CHANGE, APB_AFTER_CHANGE } apb_change_ev_t; + +typedef void (* apb_change_cb_t)(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb); + +bool addApbChangeCallback(void * arg, apb_change_cb_t cb); +bool removeApbChangeCallback(void * arg, apb_change_cb_t cb); + +//function takes the following frequencies as valid values: +// 240, 160, 80 <<< For all XTAL types +// 40, 20, 10 <<< For 40MHz XTAL +// 26, 13 <<< For 26MHz XTAL +// 24, 12 <<< For 24MHz XTAL +bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz); + +uint32_t getCpuFrequencyMhz(); // In MHz +uint32_t getXtalFrequencyMhz(); // In MHz +uint32_t getApbFrequency(); // In Hz + +#ifdef __cplusplus +} +#endif + +#endif /* _ESP32_HAL_CPU_H_ */ diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-dac.c b/src/esp32/hardware/cores/esp32/esp32-hal-dac.c new file mode 100644 index 0000000..db1bb74 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-dac.c @@ -0,0 +1,49 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp32-hal.h" +#include "soc/soc_caps.h" + +#ifndef SOC_DAC_SUPPORTED +#define NODAC +#else +#include "soc/dac_channel.h" +#include "driver/dac_common.h" + +void ARDUINO_ISR_ATTR __dacWrite(uint8_t pin, uint8_t value) +{ + if(pin < DAC_CHANNEL_1_GPIO_NUM || pin > DAC_CHANNEL_2_GPIO_NUM){ + return;//not dac pin + } + + uint8_t channel = pin - DAC_CHANNEL_1_GPIO_NUM; + dac_output_enable(channel); + dac_output_voltage(channel, value); + +} + +void ARDUINO_ISR_ATTR __dacDisable(uint8_t pin) +{ + if(pin < DAC_CHANNEL_1_GPIO_NUM || pin > DAC_CHANNEL_2_GPIO_NUM){ + return;//not dac pin + } + + uint8_t channel = pin - DAC_CHANNEL_1_GPIO_NUM; + dac_output_disable(channel); +} + +extern void dacWrite(uint8_t pin, uint8_t value) __attribute__ ((weak, alias("__dacWrite"))); +extern void dacDisable(uint8_t pin) __attribute__ ((weak, alias("__dacDisable"))); + +#endif diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-dac.h b/src/esp32/hardware/cores/esp32/esp32-hal-dac.h new file mode 100644 index 0000000..cafab07 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-dac.h @@ -0,0 +1,37 @@ +/* + Arduino.h - Main include file for the Arduino SDK + Copyright (c) 2005-2013 Arduino Team. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MAIN_ESP32_HAL_DAC_H_ +#define MAIN_ESP32_HAL_DAC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp32-hal.h" +#include "driver/gpio.h" + +void dacWrite(uint8_t pin, uint8_t value); +void dacDisable(uint8_t pin); + +#ifdef __cplusplus +} +#endif + +#endif /* MAIN_ESP32_HAL_DAC_H_ */ diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-gpio.c b/src/esp32/hardware/cores/esp32/esp32-hal-gpio.c new file mode 100644 index 0000000..eaaef7f --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-gpio.c @@ -0,0 +1,236 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp32-hal-gpio.h" +#include "hal/gpio_hal.h" +#include "soc/soc_caps.h" + +// It fixes lack of pin definition for S3 and for any future SoC +// this function works for ESP32, ESP32-S2 and ESP32-S3 - including the C3, it will return -1 for any pin +#if SOC_TOUCH_SENSOR_NUM > 0 +#include "soc/touch_sensor_periph.h" + +int8_t digitalPinToTouchChannel(uint8_t pin) +{ + int8_t ret = -1; + if (pin < SOC_GPIO_PIN_COUNT) { + for (uint8_t i = 0; i < SOC_TOUCH_SENSOR_NUM; i++) { + if (touch_sensor_channel_io_map[i] == pin) { + ret = i; + break; + } + } + } + return ret; +} +#else +// No Touch Sensor available +int8_t digitalPinToTouchChannel(uint8_t pin) +{ + return -1; +} +#endif + +#ifdef SOC_ADC_SUPPORTED +#include "soc/adc_periph.h" + +int8_t digitalPinToAnalogChannel(uint8_t pin) +{ + uint8_t channel = 0; + if (pin < SOC_GPIO_PIN_COUNT) { + for (uint8_t i = 0; i < SOC_ADC_PERIPH_NUM; i++) { + for (uint8_t j = 0; j < SOC_ADC_MAX_CHANNEL_NUM; j++) { + if (adc_channel_io_map[i][j] == pin) { + return channel; + } + channel++; + } + } + } + return -1; +} + +int8_t analogChannelToDigitalPin(uint8_t channel) +{ + if (channel >= (SOC_ADC_PERIPH_NUM * SOC_ADC_MAX_CHANNEL_NUM)) { + return -1; + } + uint8_t adc_unit = (channel / SOC_ADC_MAX_CHANNEL_NUM); + uint8_t adc_chan = (channel % SOC_ADC_MAX_CHANNEL_NUM); + return adc_channel_io_map[adc_unit][adc_chan]; +} +#else +// No Analog channels availible +int8_t analogChannelToDigitalPin(uint8_t channel) +{ + return -1; +} +#endif + +typedef void (*voidFuncPtr)(void); +typedef void (*voidFuncPtrArg)(void*); +typedef struct { + voidFuncPtr fn; + void* arg; + bool functional; +} InterruptHandle_t; +static InterruptHandle_t __pinInterruptHandlers[SOC_GPIO_PIN_COUNT] = {0,}; + +#include "driver/rtc_io.h" + +extern void ARDUINO_ISR_ATTR __pinMode(uint8_t pin, uint8_t mode) +{ +#ifdef RGB_BUILTIN + if (pin == RGB_BUILTIN){ + __pinMode(RGB_BUILTIN-SOC_GPIO_PIN_COUNT, mode); + return; + } +#endif + + if (!GPIO_IS_VALID_GPIO(pin)) { + log_e("Invalid pin selected"); + return; + } + + gpio_hal_context_t gpiohal; + gpiohal.dev = GPIO_LL_GET_HW(GPIO_PORT_0); + + gpio_config_t conf = { + .pin_bit_mask = (1ULL<pin[pin].int_type /*!< GPIO interrupt type - previously set */ + }; + if (mode < 0x20) {//io + conf.mode = mode & (INPUT | OUTPUT); + if (mode & OPEN_DRAIN) { + conf.mode |= GPIO_MODE_DEF_OD; + } + if (mode & PULLUP) { + conf.pull_up_en = GPIO_PULLUP_ENABLE; + } + if (mode & PULLDOWN) { + conf.pull_down_en = GPIO_PULLDOWN_ENABLE; + } + } + if(gpio_config(&conf) != ESP_OK) + { + log_e("GPIO config failed"); + return; + } +} + +extern void ARDUINO_ISR_ATTR __digitalWrite(uint8_t pin, uint8_t val) +{ + #ifdef RGB_BUILTIN + if(pin == RGB_BUILTIN){ + //use RMT to set all channels on/off + const uint8_t comm_val = val != 0 ? RGB_BRIGHTNESS : 0; + neopixelWrite(RGB_BUILTIN, comm_val, comm_val, comm_val); + return; + } + #endif + gpio_set_level((gpio_num_t)pin, val); +} + +extern int ARDUINO_ISR_ATTR __digitalRead(uint8_t pin) +{ + return gpio_get_level((gpio_num_t)pin); +} + +static void ARDUINO_ISR_ATTR __onPinInterrupt(void * arg) { + InterruptHandle_t * isr = (InterruptHandle_t*)arg; + if(isr->fn) { + if(isr->arg){ + ((voidFuncPtrArg)isr->fn)(isr->arg); + } else { + isr->fn(); + } + } +} + +extern void cleanupFunctional(void* arg); + +extern void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtrArg userFunc, void * arg, int intr_type, bool functional) +{ + static bool interrupt_initialized = false; + + // makes sure that pin -1 (255) will never work -- this follows Arduino standard + if (pin >= SOC_GPIO_PIN_COUNT) return; + + if(!interrupt_initialized) { + esp_err_t err = gpio_install_isr_service((int)ARDUINO_ISR_FLAG); + interrupt_initialized = (err == ESP_OK) || (err == ESP_ERR_INVALID_STATE); + } + if(!interrupt_initialized) { + log_e("GPIO ISR Service Failed To Start"); + return; + } + + // if new attach without detach remove old info + if (__pinInterruptHandlers[pin].functional && __pinInterruptHandlers[pin].arg) + { + cleanupFunctional(__pinInterruptHandlers[pin].arg); + } + __pinInterruptHandlers[pin].fn = (voidFuncPtr)userFunc; + __pinInterruptHandlers[pin].arg = arg; + __pinInterruptHandlers[pin].functional = functional; + + gpio_set_intr_type((gpio_num_t)pin, (gpio_int_type_t)(intr_type & 0x7)); + if(intr_type & 0x8){ + gpio_wakeup_enable((gpio_num_t)pin, (gpio_int_type_t)(intr_type & 0x7)); + } + gpio_isr_handler_add((gpio_num_t)pin, __onPinInterrupt, &__pinInterruptHandlers[pin]); + + + //FIX interrupts on peripherals outputs (eg. LEDC,...) + //Enable input in GPIO register + gpio_hal_context_t gpiohal; + gpiohal.dev = GPIO_LL_GET_HW(GPIO_PORT_0); + gpio_hal_input_enable(&gpiohal, pin); +} + +extern void __attachInterruptArg(uint8_t pin, voidFuncPtrArg userFunc, void * arg, int intr_type) +{ + __attachInterruptFunctionalArg(pin, userFunc, arg, intr_type, false); +} + +extern void __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, int intr_type) { + __attachInterruptFunctionalArg(pin, (voidFuncPtrArg)userFunc, NULL, intr_type, false); +} + +extern void __detachInterrupt(uint8_t pin) +{ + gpio_isr_handler_remove((gpio_num_t)pin); //remove handle and disable isr for pin + gpio_wakeup_disable((gpio_num_t)pin); + + if (__pinInterruptHandlers[pin].functional && __pinInterruptHandlers[pin].arg) + { + cleanupFunctional(__pinInterruptHandlers[pin].arg); + } + __pinInterruptHandlers[pin].fn = NULL; + __pinInterruptHandlers[pin].arg = NULL; + __pinInterruptHandlers[pin].functional = false; + + gpio_set_intr_type((gpio_num_t)pin, GPIO_INTR_DISABLE); +} + + +extern void pinMode(uint8_t pin, uint8_t mode) __attribute__ ((weak, alias("__pinMode"))); +extern void digitalWrite(uint8_t pin, uint8_t val) __attribute__ ((weak, alias("__digitalWrite"))); +extern int digitalRead(uint8_t pin) __attribute__ ((weak, alias("__digitalRead"))); +extern void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode) __attribute__ ((weak, alias("__attachInterrupt"))); +extern void attachInterruptArg(uint8_t pin, voidFuncPtrArg handler, void * arg, int mode) __attribute__ ((weak, alias("__attachInterruptArg"))); +extern void detachInterrupt(uint8_t pin) __attribute__ ((weak, alias("__detachInterrupt"))); diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-gpio.h b/src/esp32/hardware/cores/esp32/esp32-hal-gpio.h new file mode 100644 index 0000000..69b5517 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-gpio.h @@ -0,0 +1,90 @@ +/* + Arduino.h - Main include file for the Arduino SDK + Copyright (c) 2005-2013 Arduino Team. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MAIN_ESP32_HAL_GPIO_H_ +#define MAIN_ESP32_HAL_GPIO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp32-hal.h" +#include "soc/soc_caps.h" +#include "pins_arduino.h" + +#if (CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3) +#define NUM_OUPUT_PINS 46 +#define PIN_DAC1 17 +#define PIN_DAC2 18 +#else +#define NUM_OUPUT_PINS 34 +#define PIN_DAC1 25 +#define PIN_DAC2 26 +#endif + +#define LOW 0x0 +#define HIGH 0x1 + +//GPIO FUNCTIONS +#define INPUT 0x01 +// Changed OUTPUT from 0x02 to behave the same as Arduino pinMode(pin,OUTPUT) +// where you can read the state of pin even when it is set as OUTPUT +#define OUTPUT 0x03 +#define PULLUP 0x04 +#define INPUT_PULLUP 0x05 +#define PULLDOWN 0x08 +#define INPUT_PULLDOWN 0x09 +#define OPEN_DRAIN 0x10 +#define OUTPUT_OPEN_DRAIN 0x13 +#define ANALOG 0xC0 + +//Interrupt Modes +#define DISABLED 0x00 +#define RISING 0x01 +#define FALLING 0x02 +#define CHANGE 0x03 +#define ONLOW 0x04 +#define ONHIGH 0x05 +#define ONLOW_WE 0x0C +#define ONHIGH_WE 0x0D + + +#define digitalPinIsValid(pin) GPIO_IS_VALID_GPIO(pin) +#define digitalPinCanOutput(pin) GPIO_IS_VALID_OUTPUT_GPIO(pin) + +#define digitalPinToRtcPin(pin) ((RTC_GPIO_IS_VALID_GPIO(pin))?rtc_io_number_get(pin):-1) +#define digitalPinToDacChannel(pin) (((pin) == DAC_CHANNEL_1_GPIO_NUM)?0:((pin) == DAC_CHANNEL_2_GPIO_NUM)?1:-1) + +void pinMode(uint8_t pin, uint8_t mode); +void digitalWrite(uint8_t pin, uint8_t val); +int digitalRead(uint8_t pin); + +void attachInterrupt(uint8_t pin, void (*)(void), int mode); +void attachInterruptArg(uint8_t pin, void (*)(void*), void * arg, int mode); +void detachInterrupt(uint8_t pin); + +int8_t digitalPinToTouchChannel(uint8_t pin); +int8_t digitalPinToAnalogChannel(uint8_t pin); +int8_t analogChannelToDigitalPin(uint8_t channel); + +#ifdef __cplusplus +} +#endif + +#endif /* MAIN_ESP32_HAL_GPIO_H_ */ diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-i2c-slave.c b/src/esp32/hardware/cores/esp32/esp32-hal-i2c-slave.c new file mode 100644 index 0000000..16dc050 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-i2c-slave.c @@ -0,0 +1,841 @@ +// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sdkconfig.h" +#include "esp_attr.h" +#include "rom/gpio.h" +#include "soc/gpio_sig_map.h" +#include "hal/gpio_types.h" +#include "driver/gpio.h" +#include "esp_err.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "freertos/ringbuf.h" + +#include "esp_intr_alloc.h" +#include "driver/periph_ctrl.h" +#include "soc/i2c_reg.h" +#include "soc/i2c_struct.h" +#include "hal/i2c_ll.h" +#include "esp32-hal-log.h" +#include "esp32-hal-i2c-slave.h" + +#define I2C_SLAVE_USE_RX_QUEUE 0 // 1: Queue, 0: RingBuffer + +#if SOC_I2C_NUM > 1 +#define I2C_SCL_IDX(p) ((p==0)?I2CEXT0_SCL_OUT_IDX:((p==1)?I2CEXT1_SCL_OUT_IDX:0)) +#define I2C_SDA_IDX(p) ((p==0)?I2CEXT0_SDA_OUT_IDX:((p==1)?I2CEXT1_SDA_OUT_IDX:0)) +#else +#define I2C_SCL_IDX(p) I2CEXT0_SCL_OUT_IDX +#define I2C_SDA_IDX(p) I2CEXT0_SDA_OUT_IDX +#endif + +#if CONFIG_IDF_TARGET_ESP32 + #define I2C_TXFIFO_WM_INT_ENA I2C_TXFIFO_EMPTY_INT_ENA + #define I2C_RXFIFO_WM_INT_ENA I2C_RXFIFO_FULL_INT_ENA +#endif + +enum { + I2C_SLAVE_EVT_RX, I2C_SLAVE_EVT_TX +}; + +typedef struct i2c_slave_struct_t { + i2c_dev_t * dev; + uint8_t num; + int8_t sda; + int8_t scl; + i2c_slave_request_cb_t request_callback; + i2c_slave_receive_cb_t receive_callback; + void * arg; + intr_handle_t intr_handle; + TaskHandle_t task_handle; + xQueueHandle event_queue; +#if I2C_SLAVE_USE_RX_QUEUE + xQueueHandle rx_queue; +#else + RingbufHandle_t rx_ring_buf; +#endif + xQueueHandle tx_queue; + uint32_t rx_data_count; +#if !CONFIG_DISABLE_HAL_LOCKS + xSemaphoreHandle lock; +#endif +} i2c_slave_struct_t; + +typedef union { + struct { + uint32_t event : 2; + uint32_t stop : 1; + uint32_t param : 29; + }; + uint32_t val; +} i2c_slave_queue_event_t; + +static i2c_slave_struct_t _i2c_bus_array[SOC_I2C_NUM] = { + { &I2C0, 0, -1, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 +#if !CONFIG_DISABLE_HAL_LOCKS + , NULL +#endif + }, +#if SOC_I2C_NUM > 1 + { &I2C1, 1, -1, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 +#if !CONFIG_DISABLE_HAL_LOCKS + , NULL +#endif + } +#endif +}; + +#if CONFIG_DISABLE_HAL_LOCKS +#define I2C_SLAVE_MUTEX_LOCK() +#define I2C_SLAVE_MUTEX_UNLOCK() +#else +#define I2C_SLAVE_MUTEX_LOCK() if(i2c->lock){xSemaphoreTake(i2c->lock, portMAX_DELAY);} +#define I2C_SLAVE_MUTEX_UNLOCK() if(i2c->lock){xSemaphoreGive(i2c->lock);} +#endif + +//-------------------------------------- HAL_LL (Missing Functions) ------------------------------------------------ +typedef enum { + I2C_STRETCH_CAUSE_MASTER_READ, + I2C_STRETCH_CAUSE_TX_FIFO_EMPTY, + I2C_STRETCH_CAUSE_RX_FIFO_FULL, + I2C_STRETCH_CAUSE_MAX +} i2c_stretch_cause_t; + +static inline i2c_stretch_cause_t i2c_ll_stretch_cause(i2c_dev_t *hw) +{ +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + return hw->sr.stretch_cause; +#elif CONFIG_IDF_TARGET_ESP32S2 + return hw->status_reg.stretch_cause; +#else + return I2C_STRETCH_CAUSE_MAX; +#endif +} + +static inline void i2c_ll_set_stretch(i2c_dev_t *hw, uint16_t time) +{ +#ifndef CONFIG_IDF_TARGET_ESP32 + typeof(hw->scl_stretch_conf) scl_stretch_conf; + scl_stretch_conf.val = 0; + scl_stretch_conf.slave_scl_stretch_en = (time > 0); + scl_stretch_conf.stretch_protect_num = time; + scl_stretch_conf.slave_scl_stretch_clr = 1; + hw->scl_stretch_conf.val = scl_stretch_conf.val; + if(time > 0){ + //enable interrupt + hw->int_ena.val |= I2C_SLAVE_STRETCH_INT_ENA; + } else { + //disable interrupt + hw->int_ena.val &= (~I2C_SLAVE_STRETCH_INT_ENA); + } +#endif +} + +static inline void i2c_ll_stretch_clr(i2c_dev_t *hw) +{ +#ifndef CONFIG_IDF_TARGET_ESP32 + hw->scl_stretch_conf.slave_scl_stretch_clr = 1; +#endif +} + +static inline bool i2c_ll_slave_addressed(i2c_dev_t *hw) +{ +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + return hw->sr.slave_addressed; +#else + return hw->status_reg.slave_addressed; +#endif +} + +static inline bool i2c_ll_slave_rw(i2c_dev_t *hw)//not exposed by hal_ll +{ +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + return hw->sr.slave_rw; +#else + return hw->status_reg.slave_rw; +#endif +} + +//-------------------------------------- PRIVATE (Function Prototypes) ------------------------------------------------ +static void i2c_slave_free_resources(i2c_slave_struct_t * i2c); +static void i2c_slave_delay_us(uint64_t us); +static void i2c_slave_gpio_mode(int8_t pin, gpio_mode_t mode); +static bool i2c_slave_check_line_state(int8_t sda, int8_t scl); +static bool i2c_slave_attach_gpio(i2c_slave_struct_t * i2c, int8_t sda, int8_t scl); +static bool i2c_slave_detach_gpio(i2c_slave_struct_t * i2c); +static bool i2c_slave_set_frequency(i2c_slave_struct_t * i2c, uint32_t clk_speed); +static bool i2c_slave_send_event(i2c_slave_struct_t * i2c, i2c_slave_queue_event_t* event); +static bool i2c_slave_handle_tx_fifo_empty(i2c_slave_struct_t * i2c); +static bool i2c_slave_handle_rx_fifo_full(i2c_slave_struct_t * i2c, uint32_t len); +static size_t i2c_slave_read_rx(i2c_slave_struct_t * i2c, uint8_t * data, size_t len); +static void i2c_slave_isr_handler(void* arg); +static void i2c_slave_task(void *pv_args); + + +//===================================================================================================================== +//-------------------------------------- Public Functions ------------------------------------------------------------- +//===================================================================================================================== + +esp_err_t i2cSlaveAttachCallbacks(uint8_t num, i2c_slave_request_cb_t request_callback, i2c_slave_receive_cb_t receive_callback, void * arg){ + if(num >= SOC_I2C_NUM){ + log_e("Invalid port num: %u", num); + return ESP_ERR_INVALID_ARG; + } + i2c_slave_struct_t * i2c = &_i2c_bus_array[num]; + I2C_SLAVE_MUTEX_LOCK(); + i2c->request_callback = request_callback; + i2c->receive_callback = receive_callback; + i2c->arg = arg; + I2C_SLAVE_MUTEX_UNLOCK(); + return ESP_OK; +} + +esp_err_t i2cSlaveInit(uint8_t num, int sda, int scl, uint16_t slaveID, uint32_t frequency, size_t rx_len, size_t tx_len) { + if(num >= SOC_I2C_NUM){ + log_e("Invalid port num: %u", num); + return ESP_ERR_INVALID_ARG; + } + + if (sda < 0 || scl < 0) { + log_e("invalid pins sda=%d, scl=%d", sda, scl); + return ESP_ERR_INVALID_ARG; + } + + if(!frequency){ + frequency = 100000; + } else if(frequency > 1000000){ + frequency = 1000000; + } + + log_i("Initialising I2C Slave: sda=%d scl=%d freq=%d, addr=0x%x", sda, scl, frequency, slaveID); + + i2c_slave_struct_t * i2c = &_i2c_bus_array[num]; + esp_err_t ret = ESP_OK; + +#if !CONFIG_DISABLE_HAL_LOCKS + if(!i2c->lock){ + i2c->lock = xSemaphoreCreateMutex(); + if (i2c->lock == NULL) { + log_e("RX queue create failed"); + return ESP_ERR_NO_MEM; + } + } +#endif + + I2C_SLAVE_MUTEX_LOCK(); + i2c_slave_free_resources(i2c); + +#if I2C_SLAVE_USE_RX_QUEUE + i2c->rx_queue = xQueueCreate(rx_len, sizeof(uint8_t)); + if (i2c->rx_queue == NULL) { + log_e("RX queue create failed"); + ret = ESP_ERR_NO_MEM; + goto fail; + } +#else + i2c->rx_ring_buf = xRingbufferCreate(rx_len, RINGBUF_TYPE_BYTEBUF); + if (i2c->rx_ring_buf == NULL) { + log_e("RX RingBuf create failed"); + ret = ESP_ERR_NO_MEM; + goto fail; + } +#endif + + i2c->tx_queue = xQueueCreate(tx_len, sizeof(uint8_t)); + if (i2c->tx_queue == NULL) { + log_e("TX queue create failed"); + ret = ESP_ERR_NO_MEM; + goto fail; + } + + i2c->event_queue = xQueueCreate(16, sizeof(i2c_slave_queue_event_t)); + if (i2c->event_queue == NULL) { + log_e("Event queue create failed"); + ret = ESP_ERR_NO_MEM; + goto fail; + } + + xTaskCreate(i2c_slave_task, "i2c_slave_task", 4096, i2c, 20, &i2c->task_handle); + if(i2c->task_handle == NULL){ + log_e("Event thread create failed"); + ret = ESP_ERR_NO_MEM; + goto fail; + } + + if (frequency == 0) { + frequency = 100000L; + } + frequency = (frequency * 5) / 4; + + if (i2c->num == 0) { + periph_module_enable(PERIPH_I2C0_MODULE); +#if SOC_I2C_NUM > 1 + } else { + periph_module_enable(PERIPH_I2C1_MODULE); +#endif + } + + i2c_ll_slave_init(i2c->dev); + i2c_ll_set_fifo_mode(i2c->dev, true); + i2c_ll_set_slave_addr(i2c->dev, slaveID, false); + i2c_ll_set_tout(i2c->dev, I2C_LL_MAX_TIMEOUT); + i2c_slave_set_frequency(i2c, frequency); + + if (!i2c_slave_check_line_state(sda, scl)) { + log_e("bad pin state"); + ret = ESP_FAIL; + goto fail; + } + + i2c_slave_attach_gpio(i2c, sda, scl); + + if (i2c_ll_is_bus_busy(i2c->dev)) { + log_w("Bus busy, reinit"); + ret = ESP_FAIL; + goto fail; + } + + i2c_ll_disable_intr_mask(i2c->dev, I2C_LL_INTR_MASK); + i2c_ll_clr_intsts_mask(i2c->dev, I2C_LL_INTR_MASK); + i2c_ll_set_fifo_mode(i2c->dev, true); + + if (!i2c->intr_handle) { + uint32_t flags = ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED; + if(i2c->num == 0) { + ret = esp_intr_alloc(ETS_I2C_EXT0_INTR_SOURCE, flags, &i2c_slave_isr_handler, i2c, &i2c->intr_handle); +#if SOC_I2C_NUM > 1 + } else { + ret = esp_intr_alloc(ETS_I2C_EXT1_INTR_SOURCE, flags, &i2c_slave_isr_handler, i2c, &i2c->intr_handle); +#endif + } + + if (ret != ESP_OK) { + log_e("install interrupt handler Failed=%d", ret); + goto fail; + } + } + + i2c_ll_txfifo_rst(i2c->dev); + i2c_ll_rxfifo_rst(i2c->dev); + i2c_ll_slave_enable_rx_it(i2c->dev); + i2c_ll_set_stretch(i2c->dev, 0x3FF); + i2c_ll_update(i2c->dev); + I2C_SLAVE_MUTEX_UNLOCK(); + return ret; + +fail: + i2c_slave_free_resources(i2c); + I2C_SLAVE_MUTEX_UNLOCK(); + return ret; +} + +esp_err_t i2cSlaveDeinit(uint8_t num){ + if(num >= SOC_I2C_NUM){ + log_e("Invalid port num: %u", num); + return ESP_ERR_INVALID_ARG; + } + + i2c_slave_struct_t * i2c = &_i2c_bus_array[num]; +#if !CONFIG_DISABLE_HAL_LOCKS + if(!i2c->lock){ + log_e("Lock is not initialized! Did you call i2c_slave_init()?"); + return ESP_ERR_NO_MEM; + } +#endif + I2C_SLAVE_MUTEX_LOCK(); + i2c_slave_free_resources(i2c); + I2C_SLAVE_MUTEX_UNLOCK(); + return ESP_OK; +} + +size_t i2cSlaveWrite(uint8_t num, const uint8_t *buf, uint32_t len, uint32_t timeout_ms) { + if(num >= SOC_I2C_NUM){ + log_e("Invalid port num: %u", num); + return 0; + } + size_t to_queue = 0, to_fifo = 0; + i2c_slave_struct_t * i2c = &_i2c_bus_array[num]; +#if !CONFIG_DISABLE_HAL_LOCKS + if(!i2c->lock){ + log_e("Lock is not initialized! Did you call i2c_slave_init()?"); + return ESP_ERR_NO_MEM; + } +#endif + if(!i2c->tx_queue){ + return 0; + } + I2C_SLAVE_MUTEX_LOCK(); +#if CONFIG_IDF_TARGET_ESP32 + i2c_ll_slave_disable_tx_it(i2c->dev); + if (i2c_ll_get_txfifo_len(i2c->dev) < SOC_I2C_FIFO_LEN) { + i2c_ll_txfifo_rst(i2c->dev); + } +#endif + to_fifo = i2c_ll_get_txfifo_len(i2c->dev); + if(len < to_fifo){ + to_fifo = len; + } + i2c_ll_write_txfifo(i2c->dev, (uint8_t*)buf, to_fifo); + buf += to_fifo; + len -= to_fifo; + //reset tx_queue + xQueueReset(i2c->tx_queue); + //write the rest of the bytes to the queue + if(len){ + to_queue = uxQueueSpacesAvailable(i2c->tx_queue); + if(len < to_queue){ + to_queue = len; + } + for (size_t i = 0; i < to_queue; i++) { + if (xQueueSend(i2c->tx_queue, &buf[i], timeout_ms / portTICK_RATE_MS) != pdTRUE) { + xQueueReset(i2c->tx_queue); + to_queue = 0; + break; + } + } + //no need to enable TX_EMPTY if tx_queue is empty + if(to_queue){ + i2c_ll_slave_enable_tx_it(i2c->dev); + } + } + I2C_SLAVE_MUTEX_UNLOCK(); + return to_queue + to_fifo; +} + +//===================================================================================================================== +//-------------------------------------- Private Functions ------------------------------------------------------------ +//===================================================================================================================== + +static void i2c_slave_free_resources(i2c_slave_struct_t * i2c){ + i2c_slave_detach_gpio(i2c); + i2c_ll_set_slave_addr(i2c->dev, 0, false); + i2c_ll_disable_intr_mask(i2c->dev, I2C_LL_INTR_MASK); + i2c_ll_clr_intsts_mask(i2c->dev, I2C_LL_INTR_MASK); + + if (i2c->intr_handle) { + esp_intr_free(i2c->intr_handle); + i2c->intr_handle = NULL; + } + + if(i2c->task_handle){ + vTaskDelete(i2c->task_handle); + i2c->task_handle = NULL; + } + +#if I2C_SLAVE_USE_RX_QUEUE + if (i2c->rx_queue) { + vQueueDelete(i2c->rx_queue); + i2c->rx_queue = NULL; + } +#else + if (i2c->rx_ring_buf) { + vRingbufferDelete(i2c->rx_ring_buf); + i2c->rx_ring_buf = NULL; + } +#endif + + if (i2c->tx_queue) { + vQueueDelete(i2c->tx_queue); + i2c->tx_queue = NULL; + } + + if (i2c->event_queue) { + vQueueDelete(i2c->event_queue); + i2c->event_queue = NULL; + } + + i2c->rx_data_count = 0; +} + +static bool i2c_slave_set_frequency(i2c_slave_struct_t * i2c, uint32_t clk_speed) +{ + if (i2c == NULL) { + log_e("no control buffer"); + return false; + } + if(clk_speed > 1100000UL){ + clk_speed = 1100000UL; + } + + // Adjust Fifo thresholds based on frequency + uint32_t a = (clk_speed / 50000L) + 2; + log_d("Fifo thresholds: rx_fifo_full = %d, tx_fifo_empty = %d", SOC_I2C_FIFO_LEN - a, a); + + i2c_clk_cal_t clk_cal; +#if SOC_I2C_SUPPORT_APB + i2c_ll_cal_bus_clk(APB_CLK_FREQ, clk_speed, &clk_cal); + i2c_ll_set_source_clk(i2c->dev, I2C_SCLK_APB); /*!< I2C source clock from APB, 80M*/ +#elif SOC_I2C_SUPPORT_XTAL + i2c_ll_cal_bus_clk(XTAL_CLK_FREQ, clk_speed, &clk_cal); + i2c_ll_set_source_clk(i2c->dev, I2C_SCLK_XTAL); /*!< I2C source clock from XTAL, 40M */ +#endif + i2c_ll_set_txfifo_empty_thr(i2c->dev, a); + i2c_ll_set_rxfifo_full_thr(i2c->dev, SOC_I2C_FIFO_LEN - a); + i2c_ll_set_bus_timing(i2c->dev, &clk_cal); + i2c_ll_set_filter(i2c->dev, 3); + return true; +} + +static void i2c_slave_delay_us(uint64_t us) +{ + uint64_t m = esp_timer_get_time(); + if (us) { + uint64_t e = (m + us); + if (m > e) { //overflow + while ((uint64_t)esp_timer_get_time() > e); + } + while ((uint64_t)esp_timer_get_time() < e); + } +} + +static void i2c_slave_gpio_mode(int8_t pin, gpio_mode_t mode) +{ + gpio_config_t conf = { + .pin_bit_mask = 1LL << pin, + .mode = mode, + .pull_up_en = GPIO_PULLUP_ENABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE + }; + gpio_config(&conf); +} + +static bool i2c_slave_check_line_state(int8_t sda, int8_t scl) +{ + if (sda < 0 || scl < 0) { + return false;//return false since there is nothing to do + } + // if the bus is not 'clear' try the cycling SCL until SDA goes High or 9 cycles + gpio_set_level(sda, 1); + gpio_set_level(scl, 1); + i2c_slave_gpio_mode(sda, GPIO_MODE_INPUT | GPIO_MODE_DEF_OD); + i2c_slave_gpio_mode(scl, GPIO_MODE_INPUT | GPIO_MODE_DEF_OD); + gpio_set_level(scl, 1); + + if (!gpio_get_level(sda) || !gpio_get_level(scl)) { // bus in busy state + log_w("invalid state sda(%d)=%d, scl(%d)=%d", sda, gpio_get_level(sda), scl, gpio_get_level(scl)); + for (uint8_t a=0; a<9; a++) { + i2c_slave_delay_us(5); + if (gpio_get_level(sda) && gpio_get_level(scl)) { // bus recovered + log_w("Recovered after %d Cycles",a); + gpio_set_level(sda,0); // start + i2c_slave_delay_us(5); + for (uint8_t a=0;a<9; a++) { + gpio_set_level(scl,1); + i2c_slave_delay_us(5); + gpio_set_level(scl,0); + i2c_slave_delay_us(5); + } + gpio_set_level(scl,1); + i2c_slave_delay_us(5); + gpio_set_level(sda,1); // stop + break; + } + gpio_set_level(scl, 0); + i2c_slave_delay_us(5); + gpio_set_level(scl, 1); + } + } + + if (!gpio_get_level(sda) || !gpio_get_level(scl)) { // bus in busy state + log_e("Bus Invalid State, Can't init sda=%d, scl=%d",gpio_get_level(sda),gpio_get_level(scl)); + return false; // bus is busy + } + return true; +} + +static bool i2c_slave_attach_gpio(i2c_slave_struct_t * i2c, int8_t sda, int8_t scl) +{ + if (i2c == NULL) { + log_e("no control block"); + return false; + } + + if ((sda < 0)||( scl < 0)) { + log_e("bad pins sda=%d, scl=%d",sda,scl); + return false; + } + + i2c->scl = scl; + gpio_set_level(scl, 1); + i2c_slave_gpio_mode(scl, GPIO_MODE_INPUT_OUTPUT_OD); + gpio_matrix_out(scl, I2C_SCL_IDX(i2c->num), false, false); + gpio_matrix_in(scl, I2C_SCL_IDX(i2c->num), false); + + i2c->sda = sda; + gpio_set_level(sda, 1); + i2c_slave_gpio_mode(sda, GPIO_MODE_INPUT_OUTPUT_OD); + gpio_matrix_out(sda, I2C_SDA_IDX(i2c->num), false, false); + gpio_matrix_in(sda, I2C_SDA_IDX(i2c->num), false); + + return true; +} + +static bool i2c_slave_detach_gpio(i2c_slave_struct_t * i2c) +{ + if (i2c == NULL) { + log_e("no control Block"); + return false; + } + if (i2c->scl >= 0) { + gpio_matrix_out(i2c->scl, 0x100, false, false); + gpio_matrix_in(0x30, I2C_SCL_IDX(i2c->num), false); + i2c_slave_gpio_mode(i2c->scl, GPIO_MODE_INPUT); + i2c->scl = -1; // un attached + } + if (i2c->sda >= 0) { + gpio_matrix_out(i2c->sda, 0x100, false, false); + gpio_matrix_in(0x30, I2C_SDA_IDX(i2c->num), false); + i2c_slave_gpio_mode(i2c->sda, GPIO_MODE_INPUT); + i2c->sda = -1; // un attached + } + return true; +} + +static bool i2c_slave_send_event(i2c_slave_struct_t * i2c, i2c_slave_queue_event_t* event) +{ + bool pxHigherPriorityTaskWoken = false; + if(i2c->event_queue) { + if(xQueueSendFromISR(i2c->event_queue, event, (BaseType_t * const)&pxHigherPriorityTaskWoken) != pdTRUE){ + //log_e("event_queue_full"); + } + } + return pxHigherPriorityTaskWoken; +} + +static bool i2c_slave_handle_tx_fifo_empty(i2c_slave_struct_t * i2c) +{ + bool pxHigherPriorityTaskWoken = false; + uint32_t d = 0, moveCnt = i2c_ll_get_txfifo_len(i2c->dev); + while (moveCnt > 0) { // read tx queue until Fifo is full or queue is empty + if(xQueueReceiveFromISR(i2c->tx_queue, &d, (BaseType_t * const)&pxHigherPriorityTaskWoken) == pdTRUE){ + i2c_ll_write_txfifo(i2c->dev, (uint8_t*)&d, 1); + moveCnt--; + } else { + i2c_ll_slave_disable_tx_it(i2c->dev); + break; + } + } + return pxHigherPriorityTaskWoken; +} + +static bool i2c_slave_handle_rx_fifo_full(i2c_slave_struct_t * i2c, uint32_t len) +{ +#if I2C_SLAVE_USE_RX_QUEUE + uint32_t d = 0; +#else + uint8_t data[SOC_I2C_FIFO_LEN]; +#endif + bool pxHigherPriorityTaskWoken = false; +#if I2C_SLAVE_USE_RX_QUEUE + while (len > 0) { + i2c_ll_read_rxfifo(i2c->dev, (uint8_t*)&d, 1); + if(xQueueSendFromISR(i2c->rx_queue, &d, (BaseType_t * const)&pxHigherPriorityTaskWoken) != pdTRUE){ + log_e("rx_queue_full"); + } else { + i2c->rx_data_count++; + } + if (--len == 0) { + len = i2c_ll_get_rxfifo_cnt(i2c->dev); + } +#else + if(len){ + i2c_ll_read_rxfifo(i2c->dev, data, len); + if(xRingbufferSendFromISR(i2c->rx_ring_buf, (void*) data, len, (BaseType_t * const)&pxHigherPriorityTaskWoken) != pdTRUE){ + log_e("rx_ring_buf_full"); + } else { + i2c->rx_data_count += len; + } +#endif + } + return pxHigherPriorityTaskWoken; +} + +static void i2c_slave_isr_handler(void* arg) +{ + bool pxHigherPriorityTaskWoken = false; + i2c_slave_struct_t * i2c = (i2c_slave_struct_t *) arg; // recover data + + uint32_t activeInt = i2c_ll_get_intsts_mask(i2c->dev); + i2c_ll_clr_intsts_mask(i2c->dev, activeInt); + uint8_t rx_fifo_len = i2c_ll_get_rxfifo_cnt(i2c->dev); + bool slave_rw = i2c_ll_slave_rw(i2c->dev); + + if(activeInt & I2C_RXFIFO_WM_INT_ENA){ // RX FiFo Full + pxHigherPriorityTaskWoken |= i2c_slave_handle_rx_fifo_full(i2c, rx_fifo_len); + i2c_ll_slave_enable_rx_it(i2c->dev);//is this necessary? + } + + if(activeInt & I2C_TRANS_COMPLETE_INT_ENA){ // STOP + if(rx_fifo_len){ //READ RX FIFO + pxHigherPriorityTaskWoken |= i2c_slave_handle_rx_fifo_full(i2c, rx_fifo_len); + } + if(i2c->rx_data_count){ //WRITE or RepeatedStart + //SEND RX Event + i2c_slave_queue_event_t event; + event.event = I2C_SLAVE_EVT_RX; + event.stop = !slave_rw; + event.param = i2c->rx_data_count; + pxHigherPriorityTaskWoken |= i2c_slave_send_event(i2c, &event); + //Zero RX count + i2c->rx_data_count = 0; + } + if(slave_rw){ // READ +#if CONFIG_IDF_TARGET_ESP32 + if(i2c->dev->status_reg.scl_main_state_last == 6){ + //SEND TX Event + i2c_slave_queue_event_t event; + event.event = I2C_SLAVE_EVT_TX; + pxHigherPriorityTaskWoken |= i2c_slave_send_event(i2c, &event); + } +#else + //reset TX data + i2c_ll_txfifo_rst(i2c->dev); + uint8_t d; + while (xQueueReceiveFromISR(i2c->tx_queue, &d, (BaseType_t * const)&pxHigherPriorityTaskWoken) == pdTRUE) ;//flush partial write +#endif + } + } + +#ifndef CONFIG_IDF_TARGET_ESP32 + if(activeInt & I2C_SLAVE_STRETCH_INT_ENA){ // STRETCH + i2c_stretch_cause_t cause = i2c_ll_stretch_cause(i2c->dev); + if(cause == I2C_STRETCH_CAUSE_MASTER_READ){ + //on C3 RX data dissapears with repeated start, so we need to get it here + if(rx_fifo_len){ + pxHigherPriorityTaskWoken |= i2c_slave_handle_rx_fifo_full(i2c, rx_fifo_len); + } + //SEND TX Event + i2c_slave_queue_event_t event; + event.event = I2C_SLAVE_EVT_TX; + pxHigherPriorityTaskWoken |= i2c_slave_send_event(i2c, &event); + //will clear after execution + } else if(cause == I2C_STRETCH_CAUSE_TX_FIFO_EMPTY){ + pxHigherPriorityTaskWoken |= i2c_slave_handle_tx_fifo_empty(i2c); + i2c_ll_stretch_clr(i2c->dev); + } else if(cause == I2C_STRETCH_CAUSE_RX_FIFO_FULL){ + pxHigherPriorityTaskWoken |= i2c_slave_handle_rx_fifo_full(i2c, rx_fifo_len); + i2c_ll_stretch_clr(i2c->dev); + } + } +#endif + + if(activeInt & I2C_TXFIFO_WM_INT_ENA){ // TX FiFo Empty + pxHigherPriorityTaskWoken |= i2c_slave_handle_tx_fifo_empty(i2c); + } + + if(pxHigherPriorityTaskWoken){ + portYIELD_FROM_ISR(); + } +} + +static size_t i2c_slave_read_rx(i2c_slave_struct_t * i2c, uint8_t * data, size_t len){ + if(!len){ + return 0; + } +#if I2C_SLAVE_USE_RX_QUEUE + uint8_t d = 0; + BaseType_t res = pdTRUE; + for(size_t i=0; irx_queue, &data[i], 0); + } else { + res = xQueueReceive(i2c->rx_queue, &d, 0); + } + if (res != pdTRUE) { + log_e("Read Queue(%u) Failed", i); + len = i; + break; + } + } + return (data)?len:0; +#else + size_t dlen = 0, + to_read = len, + so_far = 0, + available = 0; + uint8_t * rx_data = NULL; + + vRingbufferGetInfo(i2c->rx_ring_buf, NULL, NULL, NULL, NULL, &available); + if(available < to_read){ + log_e("Less available than requested. %u < %u", available, len); + to_read = available; + } + + while(to_read){ + dlen = 0; + rx_data = (uint8_t *)xRingbufferReceiveUpTo(i2c->rx_ring_buf, &dlen, 0, to_read); + if(!rx_data){ + log_e("Receive %u Failed", to_read); + return so_far; + } + if(data){ + memcpy(data+so_far, rx_data, dlen); + } + vRingbufferReturnItem(i2c->rx_ring_buf, rx_data); + so_far+=dlen; + to_read-=dlen; + } + return (data)?so_far:0; +#endif +} + +static void i2c_slave_task(void *pv_args) +{ + i2c_slave_struct_t * i2c = (i2c_slave_struct_t *)pv_args; + i2c_slave_queue_event_t event; + size_t len = 0; + bool stop = false; + uint8_t * data = NULL; + for(;;){ + if(xQueueReceive(i2c->event_queue, &event, portMAX_DELAY) == pdTRUE){ + // Write + if(event.event == I2C_SLAVE_EVT_RX){ + len = event.param; + stop = event.stop; + data = (len > 0)?(uint8_t*)malloc(len):NULL; + + if(len && data == NULL){ + log_e("Malloc (%u) Failed", len); + } + len = i2c_slave_read_rx(i2c, data, len); + if(i2c->receive_callback){ + i2c->receive_callback(i2c->num, data, len, stop, i2c->arg); + } + free(data); + + // Read + } else if(event.event == I2C_SLAVE_EVT_TX){ + if(i2c->request_callback){ + i2c->request_callback(i2c->num, i2c->arg); + } + i2c_ll_stretch_clr(i2c->dev); + } + } + } + vTaskDelete(NULL); +} diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-i2c-slave.h b/src/esp32/hardware/cores/esp32/esp32-hal-i2c-slave.h new file mode 100644 index 0000000..ceed8b1 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-i2c-slave.h @@ -0,0 +1,35 @@ +// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "stdint.h" +#include "stddef.h" +#include "esp_err.h" + +typedef void (*i2c_slave_request_cb_t) (uint8_t num, void * arg); +typedef void (*i2c_slave_receive_cb_t) (uint8_t num, uint8_t * data, size_t len, bool stop, void * arg); +esp_err_t i2cSlaveAttachCallbacks(uint8_t num, i2c_slave_request_cb_t request_callback, i2c_slave_receive_cb_t receive_callback, void * arg); + +esp_err_t i2cSlaveInit(uint8_t num, int sda, int scl, uint16_t slaveID, uint32_t frequency, size_t rx_len, size_t tx_len); +esp_err_t i2cSlaveDeinit(uint8_t num); +size_t i2cSlaveWrite(uint8_t num, const uint8_t *buf, uint32_t len, uint32_t timeout_ms); + +#ifdef __cplusplus +} +#endif diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-i2c.c b/src/esp32/hardware/cores/esp32/esp32-hal-i2c.c new file mode 100644 index 0000000..4538a7e --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-i2c.c @@ -0,0 +1,343 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp32-hal-i2c.h" +#include "esp32-hal.h" +#if !CONFIG_DISABLE_HAL_LOCKS +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#endif +#include "esp_attr.h" +#include "esp_system.h" +#include "soc/soc_caps.h" +#include "soc/i2c_periph.h" +#include "hal/i2c_hal.h" +#include "hal/i2c_ll.h" +#include "driver/i2c.h" + +typedef volatile struct { + bool initialized; + uint32_t frequency; +#if !CONFIG_DISABLE_HAL_LOCKS + SemaphoreHandle_t lock; +#endif +} i2c_bus_t; + +static i2c_bus_t bus[SOC_I2C_NUM]; + +bool i2cIsInit(uint8_t i2c_num){ + if(i2c_num >= SOC_I2C_NUM){ + return false; + } + return bus[i2c_num].initialized; +} + +esp_err_t i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t frequency){ + if(i2c_num >= SOC_I2C_NUM){ + return ESP_ERR_INVALID_ARG; + } +#if !CONFIG_DISABLE_HAL_LOCKS + if(bus[i2c_num].lock == NULL){ + bus[i2c_num].lock = xSemaphoreCreateMutex(); + if(bus[i2c_num].lock == NULL){ + log_e("xSemaphoreCreateMutex failed"); + return ESP_ERR_NO_MEM; + } + } + //acquire lock + if(xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE){ + log_e("could not acquire lock"); + return ESP_FAIL; + } +#endif + if(bus[i2c_num].initialized){ + log_e("bus is already initialized"); + return ESP_FAIL; + } + + if(!frequency){ + frequency = 100000UL; + } else if(frequency > 1000000UL){ + frequency = 1000000UL; + } + log_i("Initialising I2C Master: sda=%d scl=%d freq=%d", sda, scl, frequency); + + i2c_config_t conf = { }; + conf.mode = I2C_MODE_MASTER; + conf.scl_io_num = (gpio_num_t)scl; + conf.sda_io_num = (gpio_num_t)sda; + conf.scl_pullup_en = GPIO_PULLUP_ENABLE; + conf.sda_pullup_en = GPIO_PULLUP_ENABLE; + conf.master.clk_speed = frequency; + conf.clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL; //Any one clock source that is available for the specified frequency may be choosen + + esp_err_t ret = i2c_param_config((i2c_port_t)i2c_num, &conf); + if (ret != ESP_OK) { + log_e("i2c_param_config failed"); + } else { + ret = i2c_driver_install((i2c_port_t)i2c_num, conf.mode, 0, 0, 0); + if (ret != ESP_OK) { + log_e("i2c_driver_install failed"); + } else { + bus[i2c_num].initialized = true; + bus[i2c_num].frequency = frequency; + //Clock Stretching Timeout: 20b:esp32, 5b:esp32-c3, 24b:esp32-s2 + i2c_set_timeout((i2c_port_t)i2c_num, I2C_LL_MAX_TIMEOUT); + } + } +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(bus[i2c_num].lock); +#endif + return ret; +} + +esp_err_t i2cDeinit(uint8_t i2c_num){ + esp_err_t err = ESP_FAIL; + if(i2c_num >= SOC_I2C_NUM){ + return ESP_ERR_INVALID_ARG; + } +#if !CONFIG_DISABLE_HAL_LOCKS + //acquire lock + if(bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE){ + log_e("could not acquire lock"); + return err; + } +#endif + if(!bus[i2c_num].initialized){ + log_e("bus is not initialized"); + } else { + err = i2c_driver_delete((i2c_port_t)i2c_num); + if(err == ESP_OK){ + bus[i2c_num].initialized = false; + } + } +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(bus[i2c_num].lock); +#endif + return err; +} + +esp_err_t i2cWrite(uint8_t i2c_num, uint16_t address, const uint8_t* buff, size_t size, uint32_t timeOutMillis){ + esp_err_t ret = ESP_FAIL; + i2c_cmd_handle_t cmd = NULL; + if(i2c_num >= SOC_I2C_NUM){ + return ESP_ERR_INVALID_ARG; + } +#if !CONFIG_DISABLE_HAL_LOCKS + //acquire lock + if(bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE){ + log_e("could not acquire lock"); + return ret; + } +#endif + if(!bus[i2c_num].initialized){ + log_e("bus is not initialized"); + goto end; + } + + //short implementation does not support zero size writes (example when scanning) PR in IDF? + //ret = i2c_master_write_to_device((i2c_port_t)i2c_num, address, buff, size, timeOutMillis / portTICK_RATE_MS); + + ret = ESP_OK; + uint8_t cmd_buff[I2C_LINK_RECOMMENDED_SIZE(1)] = { 0 }; + cmd = i2c_cmd_link_create_static(cmd_buff, I2C_LINK_RECOMMENDED_SIZE(1)); + ret = i2c_master_start(cmd); + if (ret != ESP_OK) { + goto end; + } + ret = i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, true); + if (ret != ESP_OK) { + goto end; + } + if(size){ + ret = i2c_master_write(cmd, buff, size, true); + if (ret != ESP_OK) { + goto end; + } + } + ret = i2c_master_stop(cmd); + if (ret != ESP_OK) { + goto end; + } + ret = i2c_master_cmd_begin((i2c_port_t)i2c_num, cmd, timeOutMillis / portTICK_RATE_MS); + +end: + if(cmd != NULL){ + i2c_cmd_link_delete_static(cmd); + } +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(bus[i2c_num].lock); +#endif + return ret; +} + +esp_err_t i2cRead(uint8_t i2c_num, uint16_t address, uint8_t* buff, size_t size, uint32_t timeOutMillis, size_t *readCount){ + esp_err_t ret = ESP_FAIL; + if(i2c_num >= SOC_I2C_NUM){ + return ESP_ERR_INVALID_ARG; + } +#if !CONFIG_DISABLE_HAL_LOCKS + //acquire lock + if(bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE){ + log_e("could not acquire lock"); + return ret; + } +#endif + if(!bus[i2c_num].initialized){ + log_e("bus is not initialized"); + } else { + ret = i2c_master_read_from_device((i2c_port_t)i2c_num, address, buff, size, timeOutMillis / portTICK_RATE_MS); + if(ret == ESP_OK){ + *readCount = size; + } else { + *readCount = 0; + } + } +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(bus[i2c_num].lock); +#endif + return ret; +} + +esp_err_t i2cWriteReadNonStop(uint8_t i2c_num, uint16_t address, const uint8_t* wbuff, size_t wsize, uint8_t* rbuff, size_t rsize, uint32_t timeOutMillis, size_t *readCount){ + esp_err_t ret = ESP_FAIL; + if(i2c_num >= SOC_I2C_NUM){ + return ESP_ERR_INVALID_ARG; + } +#if !CONFIG_DISABLE_HAL_LOCKS + //acquire lock + if(bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE){ + log_e("could not acquire lock"); + return ret; + } +#endif + if(!bus[i2c_num].initialized){ + log_e("bus is not initialized"); + } else { + ret = i2c_master_write_read_device((i2c_port_t)i2c_num, address, wbuff, wsize, rbuff, rsize, timeOutMillis / portTICK_RATE_MS); + if(ret == ESP_OK){ + *readCount = rsize; + } else { + *readCount = 0; + } + } +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(bus[i2c_num].lock); +#endif + return ret; +} + +esp_err_t i2cSetClock(uint8_t i2c_num, uint32_t frequency){ + esp_err_t ret = ESP_FAIL; + if(i2c_num >= SOC_I2C_NUM){ + return ESP_ERR_INVALID_ARG; + } +#if !CONFIG_DISABLE_HAL_LOCKS + //acquire lock + if(bus[i2c_num].lock == NULL || xSemaphoreTake(bus[i2c_num].lock, portMAX_DELAY) != pdTRUE){ + log_e("could not acquire lock"); + return ret; + } +#endif + if(!bus[i2c_num].initialized){ + log_e("bus is not initialized"); + goto end; + } + if(bus[i2c_num].frequency == frequency){ + ret = ESP_OK; + goto end; + } + if(!frequency){ + frequency = 100000UL; + } else if(frequency > 1000000UL){ + frequency = 1000000UL; + } + // Freq limitation when using different clock sources + #define I2C_CLK_LIMIT_REF_TICK (1 * 1000 * 1000 / 20) /*!< Limited by REF_TICK, no more than REF_TICK/20*/ + #define I2C_CLK_LIMIT_APB (80 * 1000 * 1000 / 20) /*!< Limited by APB, no more than APB/20*/ + #define I2C_CLK_LIMIT_RTC (20 * 1000 * 1000 / 20) /*!< Limited by RTC, no more than RTC/20*/ + #define I2C_CLK_LIMIT_XTAL (40 * 1000 * 1000 / 20) /*!< Limited by RTC, no more than XTAL/20*/ + + typedef struct { + uint8_t character; /*!< I2C source clock characteristic */ + uint32_t clk_freq; /*!< I2C source clock frequency */ + } i2c_clk_alloc_t; + + // i2c clock characteristic, The order is the same as i2c_sclk_t. + static i2c_clk_alloc_t i2c_clk_alloc[I2C_SCLK_MAX] = { + {0, 0}, + #if SOC_I2C_SUPPORT_APB + {0, I2C_CLK_LIMIT_APB}, /*!< I2C APB clock characteristic*/ + #endif + #if SOC_I2C_SUPPORT_XTAL + {0, I2C_CLK_LIMIT_XTAL}, /*!< I2C XTAL characteristic*/ + #endif + #if SOC_I2C_SUPPORT_RTC + {I2C_SCLK_SRC_FLAG_LIGHT_SLEEP | I2C_SCLK_SRC_FLAG_AWARE_DFS, I2C_CLK_LIMIT_RTC}, /*!< I2C 20M RTC characteristic*/ + #endif + #if SOC_I2C_SUPPORT_REF_TICK + {I2C_SCLK_SRC_FLAG_AWARE_DFS, I2C_CLK_LIMIT_REF_TICK}, /*!< I2C REF_TICK characteristic*/ + #endif + }; + + i2c_sclk_t src_clk = I2C_SCLK_DEFAULT; + ret = ESP_OK; + for (i2c_sclk_t clk = I2C_SCLK_DEFAULT + 1; clk < I2C_SCLK_MAX; clk++) { +#if CONFIG_IDF_TARGET_ESP32S3 + if (clk == I2C_SCLK_RTC) { // RTC clock for s3 is unaccessable now. + continue; + } +#endif + if (frequency <= i2c_clk_alloc[clk].clk_freq) { + src_clk = clk; + break; + } + } + if(src_clk == I2C_SCLK_MAX){ + log_e("clock source could not be selected"); + ret = ESP_FAIL; + } else { + i2c_hal_context_t hal; + hal.dev = I2C_LL_GET_HW(i2c_num); + i2c_hal_set_bus_timing(&(hal), frequency, src_clk); + bus[i2c_num].frequency = frequency; + //Clock Stretching Timeout: 20b:esp32, 5b:esp32-c3, 24b:esp32-s2 + i2c_set_timeout((i2c_port_t)i2c_num, I2C_LL_MAX_TIMEOUT); + } + +end: +#if !CONFIG_DISABLE_HAL_LOCKS + //release lock + xSemaphoreGive(bus[i2c_num].lock); +#endif + return ret; +} + +esp_err_t i2cGetClock(uint8_t i2c_num, uint32_t * frequency){ + if(i2c_num >= SOC_I2C_NUM){ + return ESP_ERR_INVALID_ARG; + } + if(!bus[i2c_num].initialized){ + log_e("bus is not initialized"); + return ESP_FAIL; + } + *frequency = bus[i2c_num].frequency; + return ESP_OK; +} diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-i2c.h b/src/esp32/hardware/cores/esp32/esp32-hal-i2c.h new file mode 100644 index 0000000..3fa889f --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-i2c.h @@ -0,0 +1,41 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// modified Nov 2017 by Chuck Todd to support Interrupt Driven I/O +// modified Nov 2021 by Hristo Gochkov to support ESP-IDF API + +#ifndef _ESP32_HAL_I2C_H_ +#define _ESP32_HAL_I2C_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +esp_err_t i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t clk_speed); +esp_err_t i2cDeinit(uint8_t i2c_num); +esp_err_t i2cSetClock(uint8_t i2c_num, uint32_t frequency); +esp_err_t i2cGetClock(uint8_t i2c_num, uint32_t * frequency); +esp_err_t i2cWrite(uint8_t i2c_num, uint16_t address, const uint8_t* buff, size_t size, uint32_t timeOutMillis); +esp_err_t i2cRead(uint8_t i2c_num, uint16_t address, uint8_t* buff, size_t size, uint32_t timeOutMillis, size_t *readCount); +esp_err_t i2cWriteReadNonStop(uint8_t i2c_num, uint16_t address, const uint8_t* wbuff, size_t wsize, uint8_t* rbuff, size_t rsize, uint32_t timeOutMillis, size_t *readCount); +bool i2cIsInit(uint8_t i2c_num); + +#ifdef __cplusplus +} +#endif + +#endif /* _ESP32_HAL_I2C_H_ */ diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-ledc.c b/src/esp32/hardware/cores/esp32/esp32-hal-ledc.c new file mode 100644 index 0000000..db14f04 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-ledc.c @@ -0,0 +1,272 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp32-hal.h" +#include "soc/soc_caps.h" +#include "driver/ledc.h" + +#ifdef SOC_LEDC_SUPPORT_HS_MODE +#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM<<1) +#else +#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM) +#endif + +//Use XTAL clock if possible to avoid timer frequency error when setting APB clock < 80 Mhz +//Need to be fixed in ESP-IDF +#ifdef SOC_LEDC_SUPPORT_XTAL_CLOCK +#define LEDC_DEFAULT_CLK LEDC_USE_XTAL_CLK +#else +#define LEDC_DEFAULT_CLK LEDC_AUTO_CLK +#endif + +#define LEDC_MAX_BIT_WIDTH SOC_LEDC_TIMER_BIT_WIDE_NUM + +/* + * LEDC Chan to Group/Channel/Timer Mapping +** ledc: 0 => Group: 0, Channel: 0, Timer: 0 +** ledc: 1 => Group: 0, Channel: 1, Timer: 0 +** ledc: 2 => Group: 0, Channel: 2, Timer: 1 +** ledc: 3 => Group: 0, Channel: 3, Timer: 1 +** ledc: 4 => Group: 0, Channel: 4, Timer: 2 +** ledc: 5 => Group: 0, Channel: 5, Timer: 2 +** ledc: 6 => Group: 0, Channel: 6, Timer: 3 +** ledc: 7 => Group: 0, Channel: 7, Timer: 3 +** ledc: 8 => Group: 1, Channel: 0, Timer: 0 +** ledc: 9 => Group: 1, Channel: 1, Timer: 0 +** ledc: 10 => Group: 1, Channel: 2, Timer: 1 +** ledc: 11 => Group: 1, Channel: 3, Timer: 1 +** ledc: 12 => Group: 1, Channel: 4, Timer: 2 +** ledc: 13 => Group: 1, Channel: 5, Timer: 2 +** ledc: 14 => Group: 1, Channel: 6, Timer: 3 +** ledc: 15 => Group: 1, Channel: 7, Timer: 3 +*/ + +uint8_t channels_resolution[LEDC_CHANNELS] = {0}; + +uint32_t ledcSetup(uint8_t chan, uint32_t freq, uint8_t bit_num) +{ + if(chan >= LEDC_CHANNELS || bit_num > LEDC_MAX_BIT_WIDTH){ + log_e("No more LEDC channels available! (maximum %u) or bit width too big (maximum %u)", LEDC_CHANNELS, LEDC_MAX_BIT_WIDTH); + return 0; + } + + uint8_t group=(chan/8), timer=((chan/2)%4); + + ledc_timer_config_t ledc_timer = { + .speed_mode = group, + .timer_num = timer, + .duty_resolution = bit_num, + .freq_hz = freq, + .clk_cfg = LEDC_DEFAULT_CLK + }; + if(ledc_timer_config(&ledc_timer) != ESP_OK) + { + log_e("ledc setup failed!"); + return 0; + } + channels_resolution[chan] = bit_num; + return ledc_get_freq(group,timer); +} + +void ledcWrite(uint8_t chan, uint32_t duty) +{ + if(chan >= LEDC_CHANNELS){ + return; + } + uint8_t group=(chan/8), channel=(chan%8); + + //Fixing if all bits in resolution is set = LEDC FULL ON + uint32_t max_duty = (1 << channels_resolution[chan]) - 1; + + if((duty == max_duty) && (max_duty != 1)){ + duty = max_duty + 1; + } + + ledc_set_duty(group, channel, duty); + ledc_update_duty(group, channel); +} + +uint32_t ledcRead(uint8_t chan) +{ + if(chan >= LEDC_CHANNELS){ + return 0; + } + uint8_t group=(chan/8), channel=(chan%8); + return ledc_get_duty(group,channel); +} + +uint32_t ledcReadFreq(uint8_t chan) +{ + if(!ledcRead(chan)){ + return 0; + } + uint8_t group=(chan/8), timer=((chan/2)%4); + return ledc_get_freq(group,timer); +} + +uint32_t ledcWriteTone(uint8_t chan, uint32_t freq) +{ + if(chan >= LEDC_CHANNELS){ + return 0; + } + if(!freq){ + ledcWrite(chan, 0); + return 0; + } + + uint8_t group=(chan/8), timer=((chan/2)%4); + + ledc_timer_config_t ledc_timer = { + .speed_mode = group, + .timer_num = timer, + .duty_resolution = 10, + .freq_hz = freq, + .clk_cfg = LEDC_DEFAULT_CLK + }; + + if(ledc_timer_config(&ledc_timer) != ESP_OK) + { + log_e("ledcSetup failed!"); + return 0; + } + channels_resolution[chan] = 10; + + uint32_t res_freq = ledc_get_freq(group,timer); + ledcWrite(chan, 0x1FF); + return res_freq; +} + +uint32_t ledcWriteNote(uint8_t chan, note_t note, uint8_t octave){ + const uint16_t noteFrequencyBase[12] = { + // C C# D Eb E F F# G G# A Bb B + 4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645, 7040, 7459, 7902 + }; + + if(octave > 8 || note >= NOTE_MAX){ + return 0; + } + uint32_t noteFreq = (uint32_t)noteFrequencyBase[note] / (uint32_t)(1 << (8-octave)); + return ledcWriteTone(chan, noteFreq); +} + +void ledcAttachPin(uint8_t pin, uint8_t chan) +{ + if(chan >= LEDC_CHANNELS){ + return; + } + uint8_t group=(chan/8), channel=(chan%8), timer=((chan/2)%4); + uint32_t duty = ledc_get_duty(group,channel); + + ledc_channel_config_t ledc_channel = { + .speed_mode = group, + .channel = channel, + .timer_sel = timer, + .intr_type = LEDC_INTR_DISABLE, + .gpio_num = pin, + .duty = duty, + .hpoint = 0 + }; + ledc_channel_config(&ledc_channel); +} + +void ledcDetachPin(uint8_t pin) +{ + pinMatrixOutDetach(pin, false, false); +} + +uint32_t ledcChangeFrequency(uint8_t chan, uint32_t freq, uint8_t bit_num) +{ + if(chan >= LEDC_CHANNELS || bit_num > LEDC_MAX_BIT_WIDTH){ + log_e("LEDC channel not available! (maximum %u) or bit width too big (maximum %u)", LEDC_CHANNELS, LEDC_MAX_BIT_WIDTH); + return 0; + } + uint8_t group=(chan/8), timer=((chan/2)%4); + + ledc_timer_config_t ledc_timer = { + .speed_mode = group, + .timer_num = timer, + .duty_resolution = bit_num, + .freq_hz = freq, + .clk_cfg = LEDC_DEFAULT_CLK + }; + + if(ledc_timer_config(&ledc_timer) != ESP_OK) + { + log_e("ledcChangeFrequency failed!"); + return 0; + } + channels_resolution[chan] = bit_num; + return ledc_get_freq(group,timer); +} + +static int8_t pin_to_channel[SOC_GPIO_PIN_COUNT] = { 0 }; +static int cnt_channel = LEDC_CHANNELS; +static uint8_t analog_resolution = 8; +static int analog_frequency = 1000; +void analogWrite(uint8_t pin, int value) { + // Use ledc hardware for internal pins + if (pin < SOC_GPIO_PIN_COUNT) { + int8_t channel = -1; + if (pin_to_channel[pin] == 0) { + if (!cnt_channel) { + log_e("No more analogWrite channels available! You can have maximum %u", LEDC_CHANNELS); + return; + } + cnt_channel--; + channel = cnt_channel; + } else { + channel = analogGetChannel(pin); + } + log_v("GPIO %d - Using Channel %d, Value = %d", pin, channel, value); + if(ledcSetup(channel, analog_frequency, analog_resolution) == 0){ + log_e("analogWrite setup failed (freq = %u, resolution = %u). Try setting different resolution or frequency"); + return; + } + ledcAttachPin(pin, channel); + pin_to_channel[pin] = channel + 1; + ledcWrite(channel, value); + } +} + +int8_t analogGetChannel(uint8_t pin) { + return pin_to_channel[pin] - 1; +} + +void analogWriteFrequency(uint32_t freq) { + if (cnt_channel != LEDC_CHANNELS) { + for (int channel = LEDC_CHANNELS - 1; channel >= cnt_channel; channel--) { + if (ledcChangeFrequency(channel, freq, analog_resolution) == 0){ + log_e("analogWrite frequency cant be set due to selected resolution! Try to adjust resolution first"); + return; + } + } + } + analog_frequency = freq; +} + +void analogWriteResolution(uint8_t bits) { + if(bits > LEDC_MAX_BIT_WIDTH) { + log_w("analogWrite resolution width too big! Setting to maximum %u bits)", LEDC_MAX_BIT_WIDTH); + bits = LEDC_MAX_BIT_WIDTH; + } + if (cnt_channel != LEDC_CHANNELS) { + for (int channel = LEDC_CHANNELS - 1; channel >= cnt_channel; channel--) { + if (ledcChangeFrequency(channel, analog_frequency, bits) == 0){ + log_e("analogWrite resolution cant be set due to selected frequency! Try to adjust frequency first"); + return; + } + } + } + analog_resolution = bits; +} diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-ledc.h b/src/esp32/hardware/cores/esp32/esp32-hal-ledc.h new file mode 100644 index 0000000..4b8bc7d --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-ledc.h @@ -0,0 +1,45 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _ESP32_HAL_LEDC_H_ +#define _ESP32_HAL_LEDC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef enum { + NOTE_C, NOTE_Cs, NOTE_D, NOTE_Eb, NOTE_E, NOTE_F, NOTE_Fs, NOTE_G, NOTE_Gs, NOTE_A, NOTE_Bb, NOTE_B, NOTE_MAX +} note_t; + +//channel 0-15 resolution 1-16bits freq limits depend on resolution +uint32_t ledcSetup(uint8_t channel, uint32_t freq, uint8_t resolution_bits); +void ledcWrite(uint8_t channel, uint32_t duty); +uint32_t ledcWriteTone(uint8_t channel, uint32_t freq); +uint32_t ledcWriteNote(uint8_t channel, note_t note, uint8_t octave); +uint32_t ledcRead(uint8_t channel); +uint32_t ledcReadFreq(uint8_t channel); +void ledcAttachPin(uint8_t pin, uint8_t channel); +void ledcDetachPin(uint8_t pin); +uint32_t ledcChangeFrequency(uint8_t channel, uint32_t freq, uint8_t resolution_bits); + + +#ifdef __cplusplus +} +#endif + +#endif /* _ESP32_HAL_LEDC_H_ */ diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-log.h b/src/esp32/hardware/cores/esp32/esp32-hal-log.h new file mode 100644 index 0000000..3198387 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-log.h @@ -0,0 +1,226 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef __ARDUHAL_LOG_H__ +#define __ARDUHAL_LOG_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "sdkconfig.h" +#include "esp_timer.h" + +#define ARDUHAL_LOG_LEVEL_NONE (0) +#define ARDUHAL_LOG_LEVEL_ERROR (1) +#define ARDUHAL_LOG_LEVEL_WARN (2) +#define ARDUHAL_LOG_LEVEL_INFO (3) +#define ARDUHAL_LOG_LEVEL_DEBUG (4) +#define ARDUHAL_LOG_LEVEL_VERBOSE (5) + +#ifndef CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL +#define CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL ARDUHAL_LOG_LEVEL_NONE +#endif + +#ifndef CORE_DEBUG_LEVEL +#define ARDUHAL_LOG_LEVEL CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL +#else +#define ARDUHAL_LOG_LEVEL CORE_DEBUG_LEVEL +#ifdef USE_ESP_IDF_LOG +#ifndef LOG_LOCAL_LEVEL +#define LOG_LOCAL_LEVEL CORE_DEBUG_LEVEL +#endif +#endif +#endif + +#ifndef CONFIG_ARDUHAL_LOG_COLORS +#define CONFIG_ARDUHAL_LOG_COLORS 0 +#endif + +#if CONFIG_ARDUHAL_LOG_COLORS +#define ARDUHAL_LOG_COLOR_BLACK "30" +#define ARDUHAL_LOG_COLOR_RED "31" //ERROR +#define ARDUHAL_LOG_COLOR_GREEN "32" //INFO +#define ARDUHAL_LOG_COLOR_YELLOW "33" //WARNING +#define ARDUHAL_LOG_COLOR_BLUE "34" +#define ARDUHAL_LOG_COLOR_MAGENTA "35" +#define ARDUHAL_LOG_COLOR_CYAN "36" //DEBUG +#define ARDUHAL_LOG_COLOR_GRAY "37" //VERBOSE +#define ARDUHAL_LOG_COLOR_WHITE "38" + +#define ARDUHAL_LOG_COLOR(COLOR) "\033[0;" COLOR "m" +#define ARDUHAL_LOG_BOLD(COLOR) "\033[1;" COLOR "m" +#define ARDUHAL_LOG_RESET_COLOR "\033[0m" + +#define ARDUHAL_LOG_COLOR_E ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_RED) +#define ARDUHAL_LOG_COLOR_W ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_YELLOW) +#define ARDUHAL_LOG_COLOR_I ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_GREEN) +#define ARDUHAL_LOG_COLOR_D ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_CYAN) +#define ARDUHAL_LOG_COLOR_V ARDUHAL_LOG_COLOR(ARDUHAL_LOG_COLOR_GRAY) +#define ARDUHAL_LOG_COLOR_PRINT(letter) log_printf(ARDUHAL_LOG_COLOR_ ## letter) +#define ARDUHAL_LOG_COLOR_PRINT_END log_printf(ARDUHAL_LOG_RESET_COLOR) +#else +#define ARDUHAL_LOG_COLOR_E +#define ARDUHAL_LOG_COLOR_W +#define ARDUHAL_LOG_COLOR_I +#define ARDUHAL_LOG_COLOR_D +#define ARDUHAL_LOG_COLOR_V +#define ARDUHAL_LOG_RESET_COLOR +#define ARDUHAL_LOG_COLOR_PRINT(letter) +#define ARDUHAL_LOG_COLOR_PRINT_END +#endif + + + +const char * pathToFileName(const char * path); +int log_printf(const char *fmt, ...); +void log_print_buf(const uint8_t *b, size_t len); + +#define ARDUHAL_SHORT_LOG_FORMAT(letter, format) ARDUHAL_LOG_COLOR_ ## letter format ARDUHAL_LOG_RESET_COLOR "\r\n" +#define ARDUHAL_LOG_FORMAT(letter, format) ARDUHAL_LOG_COLOR_ ## letter "[%6u][" #letter "][%s:%u] %s(): " format ARDUHAL_LOG_RESET_COLOR "\r\n", (unsigned long) (esp_timer_get_time() / 1000ULL), pathToFileName(__FILE__), __LINE__, __FUNCTION__ + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE +#ifndef USE_ESP_IDF_LOG +#define log_v(format, ...) log_printf(ARDUHAL_LOG_FORMAT(V, format), ##__VA_ARGS__) +#define isr_log_v(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(V, format), ##__VA_ARGS__) +#define log_buf_v(b,l) do{ARDUHAL_LOG_COLOR_PRINT(V);log_print_buf(b,l);ARDUHAL_LOG_COLOR_PRINT_END;}while(0) +#else +#define log_v(format, ...) do {ESP_LOG_LEVEL_LOCAL(ESP_LOG_VERBOSE, TAG, format, ##__VA_ARGS__);}while(0) +#define isr_log_v(format, ...) do {ets_printf(LOG_FORMAT(V, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0) +#define log_buf_v(b,l) do {ESP_LOG_BUFFER_HEXDUMP(TAG, b, l, ESP_LOG_VERBOSE);}while(0) +#endif +#else +#define log_v(format, ...) do {} while(0) +#define isr_log_v(format, ...) do {} while(0) +#define log_buf_v(b,l) do {} while(0) +#endif + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG +#ifndef USE_ESP_IDF_LOG +#define log_d(format, ...) log_printf(ARDUHAL_LOG_FORMAT(D, format), ##__VA_ARGS__) +#define isr_log_d(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(D, format), ##__VA_ARGS__) +#define log_buf_d(b,l) do{ARDUHAL_LOG_COLOR_PRINT(D);log_print_buf(b,l);ARDUHAL_LOG_COLOR_PRINT_END;}while(0) +#else +#define log_d(format, ...) do {ESP_LOG_LEVEL_LOCAL(ESP_LOG_DEBUG, TAG, format, ##__VA_ARGS__);}while(0) +#define isr_log_d(format, ...) do {ets_printf(LOG_FORMAT(D, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0) +#define log_buf_d(b,l) do {ESP_LOG_BUFFER_HEXDUMP(TAG, b, l, ESP_LOG_DEBUG);}while(0) +#endif +#else +#define log_d(format, ...) do {} while(0) +#define isr_log_d(format, ...) do {} while(0) +#define log_buf_d(b,l) do {} while(0) +#endif + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO +#ifndef USE_ESP_IDF_LOG +#define log_i(format, ...) log_printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__) +#define isr_log_i(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__) +#define log_buf_i(b,l) do{ARDUHAL_LOG_COLOR_PRINT(I);log_print_buf(b,l);ARDUHAL_LOG_COLOR_PRINT_END;}while(0) +#else +#define log_i(format, ...) do {ESP_LOG_LEVEL_LOCAL(ESP_LOG_INFO, TAG, format, ##__VA_ARGS__);}while(0) +#define isr_log_i(format, ...) do {ets_printf(LOG_FORMAT(I, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0) +#define log_buf_i(b,l) do {ESP_LOG_BUFFER_HEXDUMP(TAG, b, l, ESP_LOG_INFO);}while(0) +#endif +#else +#define log_i(format, ...) do {} while(0) +#define isr_log_i(format, ...) do {} while(0) +#define log_buf_i(b,l) do {} while(0) +#endif + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_WARN +#ifndef USE_ESP_IDF_LOG +#define log_w(format, ...) log_printf(ARDUHAL_LOG_FORMAT(W, format), ##__VA_ARGS__) +#define isr_log_w(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(W, format), ##__VA_ARGS__) +#define log_buf_w(b,l) do{ARDUHAL_LOG_COLOR_PRINT(W);log_print_buf(b,l);ARDUHAL_LOG_COLOR_PRINT_END;}while(0) +#else +#define log_w(format, ...) do {ESP_LOG_LEVEL_LOCAL(ESP_LOG_WARN, TAG, format, ##__VA_ARGS__);}while(0) +#define isr_log_w(format, ...) do {ets_printf(LOG_FORMAT(W, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0) +#define log_buf_w(b,l) do {ESP_LOG_BUFFER_HEXDUMP(TAG, b, l, ESP_LOG_WARN);}while(0) +#endif +#else +#define log_w(format, ...) do {} while(0) +#define isr_log_w(format, ...) do {} while(0) +#define log_buf_w(b,l) do {} while(0) +#endif + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR +#ifndef USE_ESP_IDF_LOG +#define log_e(format, ...) log_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__) +#define isr_log_e(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__) +#define log_buf_e(b,l) do{ARDUHAL_LOG_COLOR_PRINT(E);log_print_buf(b,l);ARDUHAL_LOG_COLOR_PRINT_END;}while(0) +#else +#define log_e(format, ...) do {ESP_LOG_LEVEL_LOCAL(ESP_LOG_ERROR, TAG, format, ##__VA_ARGS__);}while(0) +#define isr_log_e(format, ...) do {ets_printf(LOG_FORMAT(E, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0) +#define log_buf_e(b,l) do {ESP_LOG_BUFFER_HEXDUMP(TAG, b, l, ESP_LOG_ERROR);}while(0) +#endif +#else +#define log_e(format, ...) do {} while(0) +#define isr_log_e(format, ...) do {} while(0) +#define log_buf_e(b,l) do {} while(0) +#endif + +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_NONE +#ifndef USE_ESP_IDF_LOG +#define log_n(format, ...) log_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__) +#define isr_log_n(format, ...) ets_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__) +#define log_buf_n(b,l) do{ARDUHAL_LOG_COLOR_PRINT(E);log_print_buf(b,l);ARDUHAL_LOG_COLOR_PRINT_END;}while(0) +#else +#define log_n(format, ...) do {ESP_LOG_LEVEL_LOCAL(ESP_LOG_ERROR, TAG, format, ##__VA_ARGS__);}while(0) +#define isr_log_n(format, ...) do {ets_printf(LOG_FORMAT(E, format), esp_log_timestamp(), TAG, ##__VA_ARGS__);}while(0) +#define log_buf_n(b,l) do {ESP_LOG_BUFFER_HEXDUMP(TAG, b, l, ESP_LOG_ERROR);}while(0) +#endif +#else +#define log_n(format, ...) do {} while(0) +#define isr_log_n(format, ...) do {} while(0) +#define log_buf_n(b,l) do {} while(0) +#endif + +#include "esp_log.h" + +#ifdef USE_ESP_IDF_LOG +//#ifndef TAG +//#define TAG "ARDUINO" +//#endif +//#define log_n(format, ...) myLog(ESP_LOG_NONE, format, ##__VA_ARGS__) +#else +#ifdef CONFIG_ARDUHAL_ESP_LOG +#undef ESP_LOGE +#undef ESP_LOGW +#undef ESP_LOGI +#undef ESP_LOGD +#undef ESP_LOGV +#undef ESP_EARLY_LOGE +#undef ESP_EARLY_LOGW +#undef ESP_EARLY_LOGI +#undef ESP_EARLY_LOGD +#undef ESP_EARLY_LOGV + +#define ESP_LOGE(tag, format, ...) log_e("[%s] " format, tag, ##__VA_ARGS__) +#define ESP_LOGW(tag, format, ...) log_w("[%s] " format, tag, ##__VA_ARGS__) +#define ESP_LOGI(tag, format, ...) log_i("[%s] " format, tag, ##__VA_ARGS__) +#define ESP_LOGD(tag, format, ...) log_d("[%s] " format, tag, ##__VA_ARGS__) +#define ESP_LOGV(tag, format, ...) log_v("[%s] " format, tag, ##__VA_ARGS__) +#define ESP_EARLY_LOGE(tag, format, ...) isr_log_e("[%s] " format, tag, ##__VA_ARGS__) +#define ESP_EARLY_LOGW(tag, format, ...) isr_log_w("[%s] " format, tag, ##__VA_ARGS__) +#define ESP_EARLY_LOGI(tag, format, ...) isr_log_i("[%s] " format, tag, ##__VA_ARGS__) +#define ESP_EARLY_LOGD(tag, format, ...) isr_log_d("[%s] " format, tag, ##__VA_ARGS__) +#define ESP_EARLY_LOGV(tag, format, ...) isr_log_v("[%s] " format, tag, ##__VA_ARGS__) +#endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_LOGGING_H__ */ diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-matrix.c b/src/esp32/hardware/cores/esp32/esp32-hal-matrix.c new file mode 100644 index 0000000..3a82f31 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-matrix.c @@ -0,0 +1,62 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp32-hal-matrix.h" +#include "esp_attr.h" + +#include "esp_system.h" +#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+ +#include "soc/gpio_pins.h" +#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 +#elif CONFIG_IDF_TARGET_ESP32S2 +#include "esp32s2/rom/gpio.h" +#elif CONFIG_IDF_TARGET_ESP32S3 +#include "esp32s3/rom/gpio.h" +#elif CONFIG_IDF_TARGET_ESP32C3 +#include "esp32c3/rom/gpio.h" +#else +#error Target CONFIG_IDF_TARGET is not supported +#endif +#else // ESP32 Before IDF 4.0 +#include "rom/gpio.h" +#define GPIO_MATRIX_CONST_ZERO_INPUT GPIO_FUNC_IN_LOW +#define GPIO_MATRIX_CONST_ONE_INPUT GPIO_FUNC_IN_HIGH +#endif + + +void ARDUINO_ISR_ATTR pinMatrixOutAttach(uint8_t pin, uint8_t function, bool invertOut, bool invertEnable) +{ + gpio_matrix_out(pin, function, invertOut, invertEnable); +} + +void ARDUINO_ISR_ATTR pinMatrixOutDetach(uint8_t pin, bool invertOut, bool invertEnable) +{ + gpio_matrix_out(pin, SIG_GPIO_OUT_IDX, invertOut, invertEnable); +} + +void ARDUINO_ISR_ATTR pinMatrixInAttach(uint8_t pin, uint8_t signal, bool inverted) +{ + gpio_matrix_in(pin, signal, inverted); +} + +void ARDUINO_ISR_ATTR pinMatrixInDetach(uint8_t signal, bool high, bool inverted) +{ + gpio_matrix_in(high?GPIO_MATRIX_CONST_ONE_INPUT:GPIO_MATRIX_CONST_ZERO_INPUT, signal, inverted); +} +/* +void ARDUINO_ISR_ATTR intrMatrixAttach(uint32_t source, uint32_t inum){ + intr_matrix_set(PRO_CPU_NUM, source, inum); +} +*/ + diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-matrix.h b/src/esp32/hardware/cores/esp32/esp32-hal-matrix.h new file mode 100644 index 0000000..3bc9049 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-matrix.h @@ -0,0 +1,35 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _ESP32_HAL_MATRIX_H_ +#define _ESP32_HAL_MATRIX_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp32-hal.h" +#include "soc/gpio_sig_map.h" + +void pinMatrixOutAttach(uint8_t pin, uint8_t function, bool invertOut, bool invertEnable); +void pinMatrixOutDetach(uint8_t pin, bool invertOut, bool invertEnable); +void pinMatrixInAttach(uint8_t pin, uint8_t signal, bool inverted); +void pinMatrixInDetach(uint8_t signal, bool high, bool inverted); + +#ifdef __cplusplus +} +#endif + +#endif /* COMPONENTS_ARDUHAL_INCLUDE_ESP32_HAL_MATRIX_H_ */ diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-misc.c b/src/esp32/hardware/cores/esp32/esp32-hal-misc.c new file mode 100644 index 0000000..835cce6 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-misc.c @@ -0,0 +1,290 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_attr.h" +#include "nvs_flash.h" +#include "nvs.h" +#include "esp_partition.h" +#include "esp_log.h" +#include "esp_timer.h" +#ifdef CONFIG_APP_ROLLBACK_ENABLE +#include "esp_ota_ops.h" +#endif //CONFIG_APP_ROLLBACK_ENABLE +#ifdef CONFIG_BT_ENABLED +#include "esp_bt.h" +#endif //CONFIG_BT_ENABLED +#include +#include "soc/rtc.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/apb_ctrl_reg.h" +#include "esp_task_wdt.h" +#include "esp32-hal.h" + +#include "esp_system.h" +#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+ +#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 +#include "esp32/rom/rtc.h" +#elif CONFIG_IDF_TARGET_ESP32S2 +#include "esp32s2/rom/rtc.h" +#include "driver/temp_sensor.h" +#elif CONFIG_IDF_TARGET_ESP32S3 +#include "esp32s3/rom/rtc.h" +#include "driver/temp_sensor.h" +#elif CONFIG_IDF_TARGET_ESP32C3 +#include "esp32c3/rom/rtc.h" +#include "driver/temp_sensor.h" +#else +#error Target CONFIG_IDF_TARGET is not supported +#endif +#else // ESP32 Before IDF 4.0 +#include "rom/rtc.h" +#endif + +//Undocumented!!! Get chip temperature in Farenheit +//Source: https://github.com/pcbreflux/espressif/blob/master/esp32/arduino/sketchbook/ESP32_int_temp_sensor/ESP32_int_temp_sensor.ino +#ifdef CONFIG_IDF_TARGET_ESP32 +uint8_t temprature_sens_read(); + +float temperatureRead() +{ + return (temprature_sens_read() - 32) / 1.8; +} +#else +float temperatureRead() +{ + float result = NAN; + temp_sensor_config_t tsens = TSENS_CONFIG_DEFAULT(); + temp_sensor_set_config(tsens); + temp_sensor_start(); + temp_sensor_read_celsius(&result); + temp_sensor_stop(); + return result; +} +#endif + +void __yield() +{ + vPortYield(); +} + +void yield() __attribute__ ((weak, alias("__yield"))); + +#if CONFIG_AUTOSTART_ARDUINO + +extern TaskHandle_t loopTaskHandle; +extern bool loopTaskWDTEnabled; + +void enableLoopWDT(){ + if(loopTaskHandle != NULL){ + if(esp_task_wdt_add(loopTaskHandle) != ESP_OK){ + log_e("Failed to add loop task to WDT"); + } else { + loopTaskWDTEnabled = true; + } + } +} + +void disableLoopWDT(){ + if(loopTaskHandle != NULL && loopTaskWDTEnabled){ + loopTaskWDTEnabled = false; + if(esp_task_wdt_delete(loopTaskHandle) != ESP_OK){ + log_e("Failed to remove loop task from WDT"); + } + } +} + +void feedLoopWDT(){ + esp_err_t err = esp_task_wdt_reset(); + if(err != ESP_OK){ + log_e("Failed to feed WDT! Error: %d", err); + } +} +#endif + +void enableCore0WDT(){ + TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0); + if(idle_0 == NULL || esp_task_wdt_add(idle_0) != ESP_OK){ + log_e("Failed to add Core 0 IDLE task to WDT"); + } +} + +void disableCore0WDT(){ + TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0); + if(idle_0 == NULL || esp_task_wdt_delete(idle_0) != ESP_OK){ + log_e("Failed to remove Core 0 IDLE task from WDT"); + } +} + +#ifndef CONFIG_FREERTOS_UNICORE +void enableCore1WDT(){ + TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCPU(1); + if(idle_1 == NULL || esp_task_wdt_add(idle_1) != ESP_OK){ + log_e("Failed to add Core 1 IDLE task to WDT"); + } +} + +void disableCore1WDT(){ + TaskHandle_t idle_1 = xTaskGetIdleTaskHandleForCPU(1); + if(idle_1 == NULL || esp_task_wdt_delete(idle_1) != ESP_OK){ + log_e("Failed to remove Core 1 IDLE task from WDT"); + } +} +#endif + +BaseType_t xTaskCreateUniversal( TaskFunction_t pxTaskCode, + const char * const pcName, + const uint32_t usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask, + const BaseType_t xCoreID ){ +#ifndef CONFIG_FREERTOS_UNICORE + if(xCoreID >= 0 && xCoreID < 2) { + return xTaskCreatePinnedToCore(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask, xCoreID); + } else { +#endif + return xTaskCreate(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask); +#ifndef CONFIG_FREERTOS_UNICORE + } +#endif +} + +unsigned long ARDUINO_ISR_ATTR micros() +{ + return (unsigned long) (esp_timer_get_time()); +} + +unsigned long ARDUINO_ISR_ATTR millis() +{ + return (unsigned long) (esp_timer_get_time() / 1000ULL); +} + +void delay(uint32_t ms) +{ + vTaskDelay(ms / portTICK_PERIOD_MS); +} + +void ARDUINO_ISR_ATTR delayMicroseconds(uint32_t us) +{ + uint64_t m = (uint64_t)esp_timer_get_time(); + if(us){ + uint64_t e = (m + us); + if(m > e){ //overflow + while((uint64_t)esp_timer_get_time() > e){ + NOP(); + } + } + while((uint64_t)esp_timer_get_time() < e){ + NOP(); + } + } +} + +void initVariant() __attribute__((weak)); +void initVariant() {} + +void init() __attribute__((weak)); +void init() {} + +#ifdef CONFIG_APP_ROLLBACK_ENABLE +bool verifyOta() __attribute__((weak)); +bool verifyOta() { return true; } + +bool verifyRollbackLater() __attribute__((weak)); +bool verifyRollbackLater() { return false; } +#endif + +#ifdef CONFIG_BT_ENABLED +#if CONFIG_IDF_TARGET_ESP32 +//overwritten in esp32-hal-bt.c +bool btInUse() __attribute__((weak)); +bool btInUse(){ return false; } +#else +//from esp32-hal-bt.c +extern bool btInUse(); +#endif +#endif + +void initArduino() +{ +#ifdef CONFIG_APP_ROLLBACK_ENABLE + if(!verifyRollbackLater()){ + const esp_partition_t *running = esp_ota_get_running_partition(); + esp_ota_img_states_t ota_state; + if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) { + if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) { + if (verifyOta()) { + esp_ota_mark_app_valid_cancel_rollback(); + } else { + log_e("OTA verification failed! Start rollback to the previous version ..."); + esp_ota_mark_app_invalid_rollback_and_reboot(); + } + } + } + } +#endif + //init proper ref tick value for PLL (uncomment if REF_TICK is different than 1MHz) + //ESP_REG(APB_CTRL_PLL_TICK_CONF_REG) = APB_CLK_FREQ / REF_CLK_FREQ - 1; +#ifdef F_CPU + setCpuFrequencyMhz(F_CPU/1000000); +#endif +#if CONFIG_SPIRAM_SUPPORT || CONFIG_SPIRAM + psramInit(); +#endif + esp_log_level_set("*", CONFIG_LOG_DEFAULT_LEVEL); + esp_err_t err = nvs_flash_init(); + if(err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND){ + const esp_partition_t* partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL); + if (partition != NULL) { + err = esp_partition_erase_range(partition, 0, partition->size); + if(!err){ + err = nvs_flash_init(); + } else { + log_e("Failed to format the broken NVS partition!"); + } + } else { + log_e("Could not find NVS partition"); + } + } + if(err) { + log_e("Failed to initialize NVS! Error: %u", err); + } +#ifdef CONFIG_BT_ENABLED + if(!btInUse()){ + esp_bt_controller_mem_release(ESP_BT_MODE_BTDM); + } +#endif + init(); + initVariant(); +} + +//used by hal log +const char * ARDUINO_ISR_ATTR pathToFileName(const char * path) +{ + size_t i = 0; + size_t pos = 0; + char * p = (char *)path; + while(*p){ + i++; + if(*p == '/' || *p == '\\'){ + pos = i; + } + p++; + } + return path+pos; +} + diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-psram.c b/src/esp32/hardware/cores/esp32/esp32-hal-psram.c new file mode 100644 index 0000000..424499e --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-psram.c @@ -0,0 +1,148 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp32-hal.h" + +#if CONFIG_SPIRAM_SUPPORT || CONFIG_SPIRAM +#include "soc/efuse_reg.h" +#include "esp_heap_caps.h" + +#include "esp_system.h" +#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+ +#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 +#include "esp32/spiram.h" +#elif CONFIG_IDF_TARGET_ESP32S2 +#include "esp32s2/spiram.h" +#include "esp32s2/rom/cache.h" +#elif CONFIG_IDF_TARGET_ESP32S3 +#include "esp32s3/spiram.h" +#include "esp32s3/rom/cache.h" +#else +#error Target CONFIG_IDF_TARGET is not supported +#endif +#else // ESP32 Before IDF 4.0 +#include "esp_spiram.h" +#endif + +static volatile bool spiramDetected = false; +static volatile bool spiramFailed = false; + +//allows user to bypass SPI RAM test routine +__attribute__((weak)) bool testSPIRAM(void) +{ + return esp_spiram_test(); +} + + +bool psramInit(){ + if (spiramDetected) { + return true; + } +#ifndef CONFIG_SPIRAM_BOOT_INIT + if (spiramFailed) { + return false; + } +#if CONFIG_IDF_TARGET_ESP32 + uint32_t chip_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_VER_PKG); + uint32_t pkg_ver = chip_ver & 0x7; + if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5 || pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2) { + spiramFailed = true; + log_w("PSRAM not supported!"); + return false; + } +#elif CONFIG_IDF_TARGET_ESP32S2 + extern void esp_config_data_cache_mode(void); + esp_config_data_cache_mode(); + Cache_Enable_DCache(0); +#endif + if (esp_spiram_init() != ESP_OK) { + spiramFailed = true; + log_w("PSRAM init failed!"); +#if CONFIG_IDF_TARGET_ESP32 + if (pkg_ver != EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4) { + pinMatrixOutDetach(16, false, false); + pinMatrixOutDetach(17, false, false); + } +#endif + return false; + } + esp_spiram_init_cache(); + //testSPIRAM() allows user to bypass SPI RAM test routine + if (!testSPIRAM()) { + spiramFailed = true; + log_e("PSRAM test failed!"); + return false; + } + if (esp_spiram_add_to_heapalloc() != ESP_OK) { + spiramFailed = true; + log_e("PSRAM could not be added to the heap!"); + return false; + } +#if CONFIG_SPIRAM_USE_MALLOC && !CONFIG_ARDUINO_ISR_IRAM + heap_caps_malloc_extmem_enable(CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL); +#endif +#endif /* CONFIG_SPIRAM_BOOT_INIT */ + log_i("PSRAM enabled"); + spiramDetected = true; + return true; +} + +bool ARDUINO_ISR_ATTR psramFound(){ + return spiramDetected; +} + +void ARDUINO_ISR_ATTR *ps_malloc(size_t size){ + if(!spiramDetected){ + return NULL; + } + return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); +} + +void ARDUINO_ISR_ATTR *ps_calloc(size_t n, size_t size){ + if(!spiramDetected){ + return NULL; + } + return heap_caps_calloc(n, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); +} + +void ARDUINO_ISR_ATTR *ps_realloc(void *ptr, size_t size){ + if(!spiramDetected){ + return NULL; + } + return heap_caps_realloc(ptr, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); +} + +#else + +bool psramInit(){ + return false; +} + +bool ARDUINO_ISR_ATTR psramFound(){ + return false; +} + +void ARDUINO_ISR_ATTR *ps_malloc(size_t size){ + return NULL; +} + +void ARDUINO_ISR_ATTR *ps_calloc(size_t n, size_t size){ + return NULL; +} + +void ARDUINO_ISR_ATTR *ps_realloc(void *ptr, size_t size){ + return NULL; +} + +#endif diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-psram.h b/src/esp32/hardware/cores/esp32/esp32-hal-psram.h new file mode 100644 index 0000000..0ba6763 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-psram.h @@ -0,0 +1,44 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _ESP32_HAL_PSRAM_H_ +#define _ESP32_HAL_PSRAM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sdkconfig.h" + +#ifndef BOARD_HAS_PSRAM +#ifdef CONFIG_SPIRAM_SUPPORT +#undef CONFIG_SPIRAM_SUPPORT +#endif +#ifdef CONFIG_SPIRAM +#undef CONFIG_SPIRAM +#endif +#endif + +bool psramInit(); +bool psramFound(); + +void *ps_malloc(size_t size); +void *ps_calloc(size_t n, size_t size); +void *ps_realloc(void *ptr, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* _ESP32_HAL_PSRAM_H_ */ diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-rgb-led.c b/src/esp32/hardware/cores/esp32/esp32-hal-rgb-led.c new file mode 100644 index 0000000..61558b8 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-rgb-led.c @@ -0,0 +1,47 @@ +#include "esp32-hal-rgb-led.h" + + +void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val){ + rmt_data_t led_data[24]; + static rmt_obj_t* rmt_send = NULL; + static bool initialized = false; + + uint8_t _pin = pin; +#ifdef RGB_BUILTIN + if(pin == RGB_BUILTIN){ + _pin = RGB_BUILTIN-SOC_GPIO_PIN_COUNT; + } +#endif + + if(!initialized){ + if((rmt_send = rmtInit(_pin, RMT_TX_MODE, RMT_MEM_64)) == NULL){ + log_e("RGB LED driver initialization failed!"); + rmt_send = NULL; + return; + } + rmtSetTick(rmt_send, 100); + initialized = true; + } + + int color[] = {green_val, red_val, blue_val}; // Color coding is in order GREEN, RED, BLUE + int i = 0; + for(int col=0; col<3; col++ ){ + for(int bit=0; bit<8; bit++){ + if((color[col] & (1<<(7-bit)))){ + // HIGH bit + led_data[i].level0 = 1; // T1H + led_data[i].duration0 = 8; // 0.8us + led_data[i].level1 = 0; // T1L + led_data[i].duration1 = 4; // 0.4us + }else{ + // LOW bit + led_data[i].level0 = 1; // T0H + led_data[i].duration0 = 4; // 0.4us + led_data[i].level1 = 0; // T0L + led_data[i].duration1 = 8; // 0.8us + } + i++; + } + } + rmtWrite(rmt_send, led_data, 24); +} diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-rgb-led.h b/src/esp32/hardware/cores/esp32/esp32-hal-rgb-led.h new file mode 100644 index 0000000..f3539a2 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-rgb-led.h @@ -0,0 +1,20 @@ +#ifndef MAIN_ESP32_HAL_RGB_LED_H_ +#define MAIN_ESP32_HAL_RGB_LED_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp32-hal.h" + +#ifndef RGB_BRIGHTNESS + #define RGB_BRIGHTNESS 64 +#endif + +void neopixelWrite(uint8_t pin, uint8_t red_val, uint8_t green_val, uint8_t blue_val); + +#ifdef __cplusplus +} +#endif + +#endif /* MAIN_ESP32_HAL_RGB_LED_H_ */ \ No newline at end of file diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-rmt.c b/src/esp32/hardware/cores/esp32/esp32-hal-rmt.c new file mode 100644 index 0000000..309cd13 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-rmt.c @@ -0,0 +1,634 @@ +// Copyright 2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp32-hal.h" +#include "driver/rmt.h" + +/** + * Internal macros + */ + +#define MAX_CHANNELS (SOC_RMT_GROUPS * SOC_RMT_CHANNELS_PER_GROUP) + +#define RMT_TX_CH_START (0) +#define RMT_TX_CH_END (SOC_RMT_TX_CANDIDATES_PER_GROUP - 1) +#define RMT_RX_CH_START (SOC_RMT_CHANNELS_PER_GROUP - SOC_RMT_TX_CANDIDATES_PER_GROUP) +#define RMT_RX_CH_END (SOC_RMT_CHANNELS_PER_GROUP - 1) + +#define _LIMIT(a,b) (a>b?b:a) + +#if CONFIG_DISABLE_HAL_LOCKS +# define RMT_MUTEX_LOCK(channel) +# define RMT_MUTEX_UNLOCK(channel) +#else +# define RMT_MUTEX_LOCK(channel) do {} while (xSemaphoreTake(g_rmt_objlocks[channel], portMAX_DELAY) != pdPASS) +# define RMT_MUTEX_UNLOCK(channel) xSemaphoreGive(g_rmt_objlocks[channel]) +#endif /* CONFIG_DISABLE_HAL_LOCKS */ + +//#define _RMT_INTERNAL_DEBUG +#ifdef _RMT_INTERNAL_DEBUG +# define DEBUG_INTERRUPT_START(pin) digitalWrite(pin, 1); +# define DEBUG_INTERRUPT_END(pin) digitalWrite(pin, 0); +#else +# define DEBUG_INTERRUPT_START(pin) +# define DEBUG_INTERRUPT_END(pin) +#endif /* _RMT_INTERNAL_DEBUG */ + +#define RMT_DEFAULT_ARD_CONFIG_TX(gpio, channel_id, buffers) \ + { \ + .rmt_mode = RMT_MODE_TX, \ + .channel = channel_id, \ + .gpio_num = gpio, \ + .clk_div = 1, \ + .mem_block_num = buffers, \ + .flags = 0, \ + .tx_config = { \ + .carrier_level = RMT_CARRIER_LEVEL_HIGH, \ + .idle_level = RMT_IDLE_LEVEL_LOW, \ + .carrier_duty_percent = 50, \ + .carrier_en = false, \ + .loop_en = false, \ + .idle_output_en = true, \ + } \ + } + +#define RMT_DEFAULT_ARD_CONFIG_RX(gpio, channel_id, buffers) \ + { \ + .rmt_mode = RMT_MODE_RX, \ + .channel = channel_id, \ + .gpio_num = gpio, \ + .clk_div = 1, \ + .mem_block_num = buffers, \ + .flags = 0, \ + .rx_config = { \ + .idle_threshold = 0x80, \ + .filter_ticks_thresh = 100, \ + .filter_en = false, \ + } \ + } + + + + +/** + * Typedefs for internal stuctures, enums + */ +struct rmt_obj_s +{ + bool allocated; + EventGroupHandle_t events; + uint32_t channel; + uint32_t buffers; + uint32_t data_size; + uint32_t* data_ptr; + rmt_rx_data_cb_t cb; + void * arg; + TaskHandle_t rxTaskHandle; + bool rx_completed; + bool tx_not_rx; +}; + +/** + * Internal variables for channel descriptors + */ +static xSemaphoreHandle g_rmt_objlocks[MAX_CHANNELS] = { + NULL, NULL, NULL, NULL, +#if MAX_CHANNELS > 4 + NULL, NULL, NULL, NULL +#endif +}; + +static rmt_obj_t g_rmt_objects[MAX_CHANNELS] = { + { false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true}, + { false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true}, + { false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true}, + { false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true}, +#if MAX_CHANNELS > 4 + { false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true}, + { false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true}, + { false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true}, + { false, NULL, 0, 0, 0, NULL, NULL, NULL, NULL, true, true}, +#endif +}; + +/** + * Internal variables for driver data + */ +static xSemaphoreHandle g_rmt_block_lock = NULL; + +/** + * Internal method (private) declarations + */ + +static rmt_obj_t* _rmtAllocate(int pin, int from, int size) +{ + int i; + // setup how many buffers shall we use + g_rmt_objects[from].buffers = size; + + for (i=0; ichannel; + +RMT_MUTEX_LOCK(channel); + rmt_get_tx_loop_mode(channel, &loop_en); + rmt_get_clk_div(channel, &div_cnt); + rmt_get_mem_block_num(channel, &memNum); + rmt_get_mem_pd(channel, &lowPowerMode); + rmt_get_memory_owner(channel, &owner); + rmt_get_rx_idle_thresh(channel, &idleThreshold); + rmt_get_status(channel, &status); + rmt_get_source_clk(channel, &srcClk); + + log_d("Status for RMT channel %d", channel); + log_d("- Loop enabled: %d", loop_en); + log_d("- Clock divisor: %d", div_cnt); + log_d("- Number of memory blocks: %d", memNum); + log_d("- Low power mode: %d", lowPowerMode); + log_d("- Memory owner: %s", owner==RMT_MEM_OWNER_TX?"TX":"RX"); + log_d("- Idle threshold: %d", idleThreshold); + log_d("- Status: %d", status); + log_d("- Source clock: %s", srcClk==RMT_BASECLK_APB?"APB (80MHz)":"1MHz"); +RMT_MUTEX_UNLOCK(channel); +} + +static void _rmtRxTask(void *args) { + rmt_obj_t *rmt = (rmt_obj_t *) args; + RingbufHandle_t rb = NULL; + size_t rmt_len = 0; + rmt_item32_t *data = NULL; + + if (!rmt) { + log_e(" -- Inavalid Argument"); + goto err; + } + + int channel = rmt->channel; + rmt_get_ringbuf_handle(channel, &rb); + if (!rb) { + log_e(" -- Failed to get RMT ringbuffer handle"); + goto err; + } + + for(;;) { + data = (rmt_item32_t *) xRingbufferReceive(rb, &rmt_len, portMAX_DELAY); + if (data) { + log_d(" -- Got %d bytes on RX Ringbuffer - CH %d", rmt_len, rmt->channel); + rmt->rx_completed = true; // used in rmtReceiveCompleted() + // callback + if (rmt->cb) { + (rmt->cb)((uint32_t *)data, rmt_len / sizeof(rmt_item32_t), rmt->arg); + } else { + // stop RX -- will force a correct call with a callback pointer / new rmtReadData() / rmtReadAsync() + rmt_rx_stop(channel); + } + // Async Read -- copy data to caller + if (rmt->data_ptr && rmt->data_size) { + uint32_t data_size = rmt->data_size; + uint32_t read_len = rmt_len / sizeof(rmt_item32_t); + if (read_len < rmt->data_size) data_size = read_len; + rmt_item32_t *p = (rmt_item32_t *)rmt->data_ptr; + for (uint32_t i = 0; i < data_size; i++) { + p[i] = data[i]; + } + } + // set events + if (rmt->events) { + xEventGroupSetBits(rmt->events, RMT_FLAG_RX_DONE); + } + vRingbufferReturnItem(rb, (void *) data); + } // xRingbufferReceive + } // for(;;) + +err: + vTaskDelete(NULL); +} + + +static bool _rmtCreateRxTask(rmt_obj_t* rmt) +{ + if (!rmt) { + return false; + } + if (rmt->rxTaskHandle) { // Task already created + return false; + } + + xTaskCreate(_rmtRxTask, "rmt_rx_task", 4096, rmt, 20, &rmt->rxTaskHandle); + + if(rmt->rxTaskHandle == NULL){ + log_e("RMT RX Task create failed"); + return false; + } + return true; +} + +// Helper function to test if an RMT channel is correctly assigned to TX or RX, issuing an error message if necessary +// Also test RMT pointer for NULL and returns false in case it is NULL +// return true when it is correctly assigned, false otherwise +static bool _rmtCheckTXnotRX(rmt_obj_t* rmt, bool tx_not_rx) +{ + if (!rmt) { // also returns false on NULL + return false; + } + + if (rmt->tx_not_rx == tx_not_rx) { // matches expected RX/TX channel + return true; + } + + if (tx_not_rx) { // expected TX channel + log_e("Can't write on a RX RMT Channel"); + } else{ // expected RX channel + log_e("Can't read on a TX RMT Channel"); + } + return false; // missmatched +} + +/** + * Public method definitions + */ + +bool rmtSetCarrier(rmt_obj_t* rmt, bool carrier_en, bool carrier_level, uint32_t low, uint32_t high) +{ + if (!_rmtCheckTXnotRX(rmt, RMT_TX_MODE) || low > 0xFFFF || high > 0xFFFF) { + return false; + } + size_t channel = rmt->channel; + + RMT_MUTEX_LOCK(channel); + rmt_set_tx_carrier(channel, carrier_en, high, low, carrier_level); + RMT_MUTEX_UNLOCK(channel); + return true; +} + +bool rmtSetFilter(rmt_obj_t* rmt, bool filter_en, uint32_t filter_level) +{ + if (!_rmtCheckTXnotRX(rmt, RMT_RX_MODE) || filter_level > 0xFF) { + return false; + } + size_t channel = rmt->channel; + + RMT_MUTEX_LOCK(channel); + rmt_set_rx_filter(channel, filter_en, filter_level); + RMT_MUTEX_UNLOCK(channel); + return true; +} + +bool rmtSetRxThreshold(rmt_obj_t* rmt, uint32_t value) +{ + if (!_rmtCheckTXnotRX(rmt, RMT_RX_MODE) || value > 0xFFFF) { + return false; + } + size_t channel = rmt->channel; + + RMT_MUTEX_LOCK(channel); + rmt_set_rx_idle_thresh(channel, value); + RMT_MUTEX_UNLOCK(channel); + return true; +} + + +bool rmtDeinit(rmt_obj_t *rmt) +{ + if (!rmt) { + return false; + } + + // sanity check + if (rmt != &(g_rmt_objects[rmt->channel])) { + return false; + } + + RMT_MUTEX_LOCK(rmt->channel); + // force stopping rmt processing + if (rmt->tx_not_rx) { + rmt_tx_stop(rmt->channel); + } else { + rmt_rx_stop(rmt->channel); + if(rmt->rxTaskHandle){ + vTaskDelete(rmt->rxTaskHandle); + rmt->rxTaskHandle = NULL; + } + } + + rmt_driver_uninstall(rmt->channel); + + size_t from = rmt->channel; + size_t to = rmt->buffers + rmt->channel; + size_t i; + + for (i = from; i < to; i++) { + g_rmt_objects[i].allocated = false; + } + + g_rmt_objects[from].channel = 0; + g_rmt_objects[from].buffers = 0; + RMT_MUTEX_UNLOCK(rmt->channel); + +#if !CONFIG_DISABLE_HAL_LOCKS + if(g_rmt_objlocks[from] != NULL) { + vSemaphoreDelete(g_rmt_objlocks[from]); + g_rmt_objlocks[from] = NULL; + } +#endif + + return true; +} + +bool rmtLoop(rmt_obj_t* rmt, rmt_data_t* data, size_t size) +{ + if (!_rmtCheckTXnotRX(rmt, RMT_TX_MODE)) { + return false; + } + int channel = rmt->channel; + RMT_MUTEX_LOCK(channel); + rmt_tx_stop(channel); + rmt_set_tx_loop_mode(channel, true); + rmt_write_items(channel, (const rmt_item32_t *)data, size, false); + RMT_MUTEX_UNLOCK(channel); + return true; +} + +bool rmtWrite(rmt_obj_t* rmt, rmt_data_t* data, size_t size) +{ + if (!_rmtCheckTXnotRX(rmt, RMT_TX_MODE)) { + return false; + } + int channel = rmt->channel; + RMT_MUTEX_LOCK(channel); + rmt_tx_stop(channel); + rmt_set_tx_loop_mode(channel, false); + rmt_write_items(channel, (const rmt_item32_t *)data, size, false); + RMT_MUTEX_UNLOCK(channel); + return true; +} + +bool rmtWriteBlocking(rmt_obj_t* rmt, rmt_data_t* data, size_t size) +{ + if (!_rmtCheckTXnotRX(rmt, RMT_TX_MODE)) { + return false; + } + int channel = rmt->channel; + RMT_MUTEX_LOCK(channel); + rmt_tx_stop(channel); + rmt_set_tx_loop_mode(channel, false); + rmt_write_items(channel, (const rmt_item32_t *)data, size, true); + RMT_MUTEX_UNLOCK(channel); + return true; +} + +bool rmtReadData(rmt_obj_t* rmt, uint32_t* data, size_t size) +{ + if (!_rmtCheckTXnotRX(rmt, RMT_RX_MODE)) { + return false; + } + rmtReadAsync(rmt, (rmt_data_t*) data, size, NULL, false, 0); + return true; +} + + +bool rmtBeginReceive(rmt_obj_t* rmt) +{ + if (!_rmtCheckTXnotRX(rmt, RMT_RX_MODE)) { + return false; + } + int channel = rmt->channel; + + RMT_MUTEX_LOCK(channel); + rmt_set_memory_owner(channel, RMT_MEM_OWNER_RX); + rmt_rx_start(channel, true); + rmt->rx_completed = false; + RMT_MUTEX_UNLOCK(channel); + return true; +} + +bool rmtReceiveCompleted(rmt_obj_t* rmt) +{ + if (!rmt) { + return false; + } + + return rmt->rx_completed; +} + +bool rmtRead(rmt_obj_t* rmt, rmt_rx_data_cb_t cb, void * arg) +{ + if (!_rmtCheckTXnotRX(rmt, RMT_RX_MODE)) { + return false; + } + int channel = rmt->channel; + + rmt->arg = arg; + rmt->cb = cb; + + RMT_MUTEX_LOCK(channel); + // cb as NULL is a way to cancel the callback process + if (cb == NULL) { + rmt_rx_stop(channel); + return true; + } + // Start a read process but now with a call back function + rmt_set_memory_owner(channel, RMT_MEM_OWNER_RX); + rmt_rx_start(channel, true); + rmt->rx_completed = false; + _rmtCreateRxTask(rmt); + RMT_MUTEX_UNLOCK(channel); + return true; +} + +bool rmtEnd(rmt_obj_t* rmt) +{ + if (!rmt) { + return false; + } + int channel = rmt->channel; + + RMT_MUTEX_LOCK(channel); + if (rmt->tx_not_rx) { + rmt_tx_stop(channel); + } else { + rmt_rx_stop(channel); + rmt->rx_completed = true; + } + RMT_MUTEX_UNLOCK(channel); + return true; +} + +bool rmtReadAsync(rmt_obj_t* rmt, rmt_data_t* data, size_t size, void* eventFlag, bool waitForData, uint32_t timeout) +{ + if (!_rmtCheckTXnotRX(rmt, RMT_RX_MODE)) { + return false; + } + int channel = rmt->channel; + + // No limit on size with IDF ;-) + //if (g_rmt_objects[channel].buffers < size/SOC_RMT_MEM_WORDS_PER_CHANNEL) { + // return false; + //} + + RMT_MUTEX_LOCK(channel); + if (eventFlag) { + xEventGroupClearBits(eventFlag, RMT_FLAGS_ALL); + } + // if NULL, no problems - rmtReadAsync works as a plain rmtReadData() + rmt->events = eventFlag; + + // if NULL, no problems - task will take care of it + rmt->data_ptr = (uint32_t*)data; + rmt->data_size = size; + + // Start a read process + rmt_set_memory_owner(channel, RMT_MEM_OWNER_RX); + rmt_rx_start(channel, true); + rmt->rx_completed = false; + _rmtCreateRxTask(rmt); + RMT_MUTEX_UNLOCK(channel); + + // wait for data if requested so + if (waitForData && eventFlag) { + xEventGroupWaitBits(eventFlag, RMT_FLAGS_ALL, + pdTRUE /* clear on exit */, pdFALSE /* wait for all bits */, timeout); + } + return true; +} + +float rmtSetTick(rmt_obj_t* rmt, float tick) +{ + if (!rmt) { + return false; + } + size_t channel = rmt->channel; + + RMT_MUTEX_LOCK(channel); + // RMT_BASECLK_REF (1MHz) is not supported in IDF upon Programmming Guide + // Only APB works + rmt_set_source_clk(channel, RMT_BASECLK_APB); + int apb_div = _LIMIT(tick/12.5f, 256); + float apb_tick = 12.5f * apb_div; + + rmt_set_clk_div(channel, apb_div & 0xFF); + RMT_MUTEX_UNLOCK(channel); + return apb_tick; +} + +rmt_obj_t* rmtInit(int pin, bool tx_not_rx, rmt_reserve_memsize_t memsize) +{ + uint32_t buffers = memsize; + rmt_obj_t* rmt = NULL; + uint32_t i = 0; + uint32_t j = 0; + + // create common block mutex for protecting allocs from multiple threads + if (!g_rmt_block_lock) { + g_rmt_block_lock = xSemaphoreCreateMutex(); + } + // lock + while (xSemaphoreTake(g_rmt_block_lock, portMAX_DELAY) != pdPASS) {} + + // Some SoC may have fixed channel numbers for TX and RX - example: ESP32C3 + uint8_t ch_start, ch_end; + if (tx_not_rx) { + ch_start = RMT_TX_CH_START; + ch_end = RMT_TX_CH_END; + } else { + ch_start = RMT_RX_CH_START; + ch_end = RMT_RX_CH_END; + } + for (i=ch_start; i<=ch_end; i++) { + for (j=0; j MAX_CHANNELS || j != buffers) { + xSemaphoreGive(g_rmt_block_lock); + log_e("rmInit Failed - not enough channels"); + return NULL; + } + + // A suitable channel has been found, it has to block its resources in our internal data strucuture + size_t channel = i; + rmt = _rmtAllocate(pin, i, buffers); + + xSemaphoreGive(g_rmt_block_lock); + + rmt->buffers = buffers; + rmt->channel = channel; + rmt->arg = NULL; + rmt->cb = NULL; + rmt->data_ptr = NULL; + rmt->data_size = 0; + rmt->rx_completed = false; + rmt->events = NULL; + rmt->tx_not_rx = tx_not_rx; + +#if !CONFIG_DISABLE_HAL_LOCKS + if(g_rmt_objlocks[channel] == NULL) { + g_rmt_objlocks[channel] = xSemaphoreCreateMutex(); + if(g_rmt_objlocks[channel] == NULL) { + return NULL; + } + } +#endif + + RMT_MUTEX_LOCK(channel); + esp_err_t esp_err_code = ESP_OK; + + if (tx_not_rx) { + rmt_config_t config = RMT_DEFAULT_ARD_CONFIG_TX(pin, channel, buffers); + esp_err_code = rmt_config(&config); + if (esp_err_code == ESP_OK) + esp_err_code = rmt_driver_install(channel, 0, 0); + log_d(" -- %s RMT - CH %d - %d RAM Blocks - pin %d", tx_not_rx?"TX":"RX", channel, buffers, pin); + } else { + rmt_config_t config = RMT_DEFAULT_ARD_CONFIG_RX(pin, channel, buffers); + esp_err_code = rmt_config(&config); + if (esp_err_code == ESP_OK) + esp_err_code = rmt_driver_install(channel, 1024, 0); + if (esp_err_code == ESP_OK) + esp_err_code = rmt_set_memory_owner(channel, RMT_MEM_OWNER_RX); + log_d(" -- %s RMT - CH %d - %d RAM Blocks - pin %d", tx_not_rx?"TX":"RX", channel, buffers, pin); + } + + RMT_MUTEX_UNLOCK(channel); + + if (esp_err_code == ESP_OK) { + return rmt; + } else { + log_e("RMT failed to initilize."); + return NULL; + } +} diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-rmt.h b/src/esp32/hardware/cores/esp32/esp32-hal-rmt.h new file mode 100644 index 0000000..201fe59 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-rmt.h @@ -0,0 +1,169 @@ +// Copyright 2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef MAIN_ESP32_HAL_RMT_H_ +#define MAIN_ESP32_HAL_RMT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// notification flags +#define RMT_FLAG_TX_DONE (1) +#define RMT_FLAG_RX_DONE (2) +#define RMT_FLAG_ERROR (4) +#define RMT_FLAGS_ALL (RMT_FLAG_TX_DONE | RMT_FLAG_RX_DONE | RMT_FLAG_ERROR) + +#define RMT_TX_MODE true +#define RMT_RX_MODE false + +struct rmt_obj_s; + +typedef enum { + RMT_MEM_64 = 1, + RMT_MEM_128 = 2, + RMT_MEM_192 = 3, + RMT_MEM_256 = 4, + RMT_MEM_320 = 5, + RMT_MEM_384 = 6, + RMT_MEM_448 = 7, + RMT_MEM_512 = 8, +} rmt_reserve_memsize_t; + +typedef struct rmt_obj_s rmt_obj_t; + +typedef void (*rmt_rx_data_cb_t)(uint32_t *data, size_t len, void *arg); + +typedef struct { + union { + struct { + uint32_t duration0 :15; + uint32_t level0 :1; + uint32_t duration1 :15; + uint32_t level1 :1; + }; + uint32_t val; + }; +} rmt_data_t; + + +/** +* Prints object information +* +*/ +void _rmtDumpStatus(rmt_obj_t* rmt); + +/** +* Initialize the object +* +*/ +rmt_obj_t* rmtInit(int pin, bool tx_not_rx, rmt_reserve_memsize_t memsize); + +/** +* Sets the clock/divider of timebase the nearest tick to the supplied value in nanoseconds +* return the real actual tick value in ns +*/ +float rmtSetTick(rmt_obj_t* rmt, float tick); + +/** +* Sending data in one-go mode or continual mode +* (more data being send while updating buffers in interrupts) +* Non-Blocking mode - returns right after executing +*/ +bool rmtWrite(rmt_obj_t* rmt, rmt_data_t* data, size_t size); + +/** +* Sending data in one-go mode or continual mode +* (more data being send while updating buffers in interrupts) +* Blocking mode - only returns when data has been sent +*/ +bool rmtWriteBlocking(rmt_obj_t* rmt, rmt_data_t* data, size_t size); + +/** +* Loop data up to the reserved memsize continuously +* +*/ +bool rmtLoop(rmt_obj_t* rmt, rmt_data_t* data, size_t size); + +/** +* Initiates async receive, event flag indicates data received +* +*/ +bool rmtReadAsync(rmt_obj_t* rmt, rmt_data_t* data, size_t size, void* eventFlag, bool waitForData, uint32_t timeout); + +/** +* Initiates async receive with automatic buffering +* and callback with data from ISR +* +*/ +bool rmtRead(rmt_obj_t* rmt, rmt_rx_data_cb_t cb, void * arg); + +/*** + * Ends async receive started with rmtRead(); but does not + * rmtDeInit(). + */ +bool rmtEnd(rmt_obj_t* rmt); + +/* Additional interface */ + +/** +* Start reception +* +*/ +bool rmtBeginReceive(rmt_obj_t* rmt); + +/** +* Checks if reception completes +* +*/ +bool rmtReceiveCompleted(rmt_obj_t* rmt); + +/** +* Reads the data for particular channel +* +*/ +bool rmtReadData(rmt_obj_t* rmt, uint32_t* data, size_t size); + +/** + * Setting threshold for Rx completed + */ +bool rmtSetRxThreshold(rmt_obj_t* rmt, uint32_t value); + +/** + * Setting carrier + */ +bool rmtSetCarrier(rmt_obj_t* rmt, bool carrier_en, bool carrier_level, uint32_t low, uint32_t high); + +/** + * Setting input filter + */ +bool rmtSetFilter(rmt_obj_t* rmt, bool filter_en, uint32_t filter_level); + +/** + * Deinitialize the driver + */ +bool rmtDeinit(rmt_obj_t *rmt); + +// TODO: +// * uninstall interrupt when all channels are deinit +// * send only-conti mode with circular-buffer +// * put sanity checks to some macro or inlines +// * doxy comments +// * error reporting + +#ifdef __cplusplus +} +#endif + +#endif /* MAIN_ESP32_HAL_RMT_H_ */ diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-sigmadelta.c b/src/esp32/hardware/cores/esp32/esp32-hal-sigmadelta.c new file mode 100644 index 0000000..82ba1a2 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-sigmadelta.c @@ -0,0 +1,89 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + + +#include "esp32-hal.h" +#include "soc/soc_caps.h" +#include "driver/sigmadelta.h" + +static uint8_t duty_set[SOC_SIGMADELTA_CHANNEL_NUM] = {0}; +static uint32_t prescaler_set[SOC_SIGMADELTA_CHANNEL_NUM] = {0}; + +static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){ + if(old_apb == new_apb){ + return; + } + uint32_t iarg = (uint32_t)arg; + uint8_t channel = iarg; + if(ev_type == APB_AFTER_CHANGE){ + old_apb /= 1000000; + new_apb /= 1000000; + uint32_t old_prescale = prescaler_set[channel] + 1; + uint32_t new_prescale = ((new_apb * old_prescale) / old_apb) - 1; + sigmadelta_set_prescale(channel,new_prescale); + prescaler_set[channel] = new_prescale; + } +} + +uint32_t sigmaDeltaSetup(uint8_t pin, uint8_t channel, uint32_t freq) //chan 0-x according to SOC, freq 1220-312500 +{ + if(channel >= SOC_SIGMADELTA_CHANNEL_NUM){ + return 0; + } + + uint32_t apb_freq = getApbFrequency(); + uint32_t prescale = (apb_freq/(freq*256)) - 1; + if(prescale > 0xFF) { + prescale = 0xFF; + } + + sigmadelta_config_t sigmadelta_cfg = { + .channel = channel, + .sigmadelta_prescale = prescale, + .sigmadelta_duty = 0, + .sigmadelta_gpio = pin, + }; + sigmadelta_config(&sigmadelta_cfg); + + prescaler_set[channel] = prescale; + uint32_t iarg = channel; + addApbChangeCallback((void*)iarg, _on_apb_change); + + return apb_freq/((prescale + 1) * 256); +} + +void sigmaDeltaWrite(uint8_t channel, uint8_t duty) //chan 0-x according to SOC duty 8 bit +{ + if(channel >= SOC_SIGMADELTA_CHANNEL_NUM){ + return; + } + duty -= 128; + + sigmadelta_set_duty(channel,duty); + duty_set[channel] = duty; +} + +uint8_t sigmaDeltaRead(uint8_t channel) //chan 0-x according to SOC +{ + if(channel >= SOC_SIGMADELTA_CHANNEL_NUM){ + return 0; + } + return duty_set[channel]+128; +} + +void sigmaDeltaDetachPin(uint8_t pin) +{ + pinMatrixOutDetach(pin, false, false); +} \ No newline at end of file diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-sigmadelta.h b/src/esp32/hardware/cores/esp32/esp32-hal-sigmadelta.h new file mode 100644 index 0000000..0a526e8 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-sigmadelta.h @@ -0,0 +1,36 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _ESP32_HAL_SD_H_ +#define _ESP32_HAL_SD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +//channel 0-7 freq 1220-312500 duty 0-255 +uint32_t sigmaDeltaSetup(uint8_t pin, uint8_t channel, uint32_t freq); +void sigmaDeltaWrite(uint8_t channel, uint8_t duty); +uint8_t sigmaDeltaRead(uint8_t channel); +void sigmaDeltaDetachPin(uint8_t pin); + + +#ifdef __cplusplus +} +#endif + +#endif /* _ESP32_HAL_SD_H_ */ diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-spi.c b/src/esp32/hardware/cores/esp32/esp32-hal-spi.c new file mode 100644 index 0000000..43ea98b --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-spi.c @@ -0,0 +1,1560 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp32-hal-spi.h" +#include "esp32-hal.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_attr.h" +#include "soc/spi_reg.h" +#include "soc/spi_struct.h" +#include "soc/io_mux_reg.h" +#include "soc/gpio_sig_map.h" +#include "soc/rtc.h" +#include "driver/periph_ctrl.h" + +#include "esp_system.h" +#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+ +#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 +#include "soc/dport_reg.h" +#include "esp32/rom/ets_sys.h" +#include "esp32/rom/gpio.h" +#include "esp_intr_alloc.h" +#elif CONFIG_IDF_TARGET_ESP32S2 +#include "soc/dport_reg.h" +#include "esp32s2/rom/ets_sys.h" +#include "esp32s2/rom/gpio.h" +#include "esp_intr_alloc.h" +#elif CONFIG_IDF_TARGET_ESP32S3 +#include "soc/dport_reg.h" +#include "esp32s3/rom/ets_sys.h" +#include "esp32s3/rom/gpio.h" +#include "esp_intr_alloc.h" +#elif CONFIG_IDF_TARGET_ESP32C3 +#include "esp32c3/rom/ets_sys.h" +#include "esp32c3/rom/gpio.h" +#include "esp_intr_alloc.h" +#else +#error Target CONFIG_IDF_TARGET is not supported +#endif +#else // ESP32 Before IDF 4.0 +#include "rom/ets_sys.h" +#include "rom/gpio.h" +#include "esp_intr.h" +#endif + +struct spi_struct_t { + spi_dev_t * dev; +#if !CONFIG_DISABLE_HAL_LOCKS + xSemaphoreHandle lock; +#endif + uint8_t num; +}; + +#if CONFIG_IDF_TARGET_ESP32S2 +// ESP32S2 +#define SPI_COUNT (3) + +#define SPI_CLK_IDX(p) ((p==0)?SPICLK_OUT_MUX_IDX:((p==1)?FSPICLK_OUT_MUX_IDX:((p==2)?SPI3_CLK_OUT_MUX_IDX:0))) +#define SPI_MISO_IDX(p) ((p==0)?SPIQ_OUT_IDX:((p==1)?FSPIQ_OUT_IDX:((p==2)?SPI3_Q_OUT_IDX:0))) +#define SPI_MOSI_IDX(p) ((p==0)?SPID_IN_IDX:((p==1)?FSPID_IN_IDX:((p==2)?SPI3_D_IN_IDX:0))) + +#define SPI_SPI_SS_IDX(n) ((n==0)?SPICS0_OUT_IDX:((n==1)?SPICS1_OUT_IDX:0)) +#define SPI_HSPI_SS_IDX(n) ((n==0)?SPI3_CS0_OUT_IDX:((n==1)?SPI3_CS1_OUT_IDX:((n==2)?SPI3_CS2_OUT_IDX:SPI3_CS0_OUT_IDX))) +#define SPI_FSPI_SS_IDX(n) ((n==0)?FSPICS0_OUT_IDX:((n==1)?FSPICS1_OUT_IDX:((n==2)?FSPICS2_OUT_IDX:FSPICS0_OUT_IDX))) +#define SPI_SS_IDX(p, n) ((p==0)?SPI_SPI_SS_IDX(n):((p==1)?SPI_SPI_SS_IDX(n):((p==2)?SPI_HSPI_SS_IDX(n):0))) + +#elif CONFIG_IDF_TARGET_ESP32S3 +// ESP32S3 +#define SPI_COUNT (2) + +#define SPI_CLK_IDX(p) ((p==0)?FSPICLK_OUT_IDX:((p==1)?SPI3_CLK_OUT_IDX:0)) +#define SPI_MISO_IDX(p) ((p==0)?FSPIQ_OUT_IDX:((p==1)?SPI3_Q_OUT_IDX:0)) +#define SPI_MOSI_IDX(p) ((p==0)?FSPID_IN_IDX:((p==1)?SPI3_D_IN_IDX:0)) + +#define SPI_HSPI_SS_IDX(n) ((n==0)?SPI3_CS0_OUT_IDX:((n==1)?SPI3_CS1_OUT_IDX:0)) +#define SPI_FSPI_SS_IDX(n) ((n==0)?FSPICS0_OUT_IDX:((n==1)?FSPICS1_OUT_IDX:0)) +#define SPI_SS_IDX(p, n) ((p==0)?SPI_FSPI_SS_IDX(n):((p==1)?SPI_HSPI_SS_IDX(n):0)) + +#elif CONFIG_IDF_TARGET_ESP32C3 +// ESP32C3 +#define SPI_COUNT (1) + +#define SPI_CLK_IDX(p) FSPICLK_OUT_IDX +#define SPI_MISO_IDX(p) FSPIQ_OUT_IDX +#define SPI_MOSI_IDX(p) FSPID_IN_IDX + +#define SPI_SPI_SS_IDX(n) ((n==0)?FSPICS0_OUT_IDX:((n==1)?FSPICS1_OUT_IDX:((n==2)?FSPICS2_OUT_IDX:FSPICS0_OUT_IDX))) +#define SPI_SS_IDX(p, n) SPI_SPI_SS_IDX(n) + +#else +// ESP32 +#define SPI_COUNT (4) + +#define SPI_CLK_IDX(p) ((p==0)?SPICLK_OUT_IDX:((p==1)?SPICLK_OUT_IDX:((p==2)?HSPICLK_OUT_IDX:((p==3)?VSPICLK_OUT_IDX:0)))) +#define SPI_MISO_IDX(p) ((p==0)?SPIQ_OUT_IDX:((p==1)?SPIQ_OUT_IDX:((p==2)?HSPIQ_OUT_IDX:((p==3)?VSPIQ_OUT_IDX:0)))) +#define SPI_MOSI_IDX(p) ((p==0)?SPID_IN_IDX:((p==1)?SPID_IN_IDX:((p==2)?HSPID_IN_IDX:((p==3)?VSPID_IN_IDX:0)))) + +#define SPI_SPI_SS_IDX(n) ((n==0)?SPICS0_OUT_IDX:((n==1)?SPICS1_OUT_IDX:((n==2)?SPICS2_OUT_IDX:SPICS0_OUT_IDX))) +#define SPI_HSPI_SS_IDX(n) ((n==0)?HSPICS0_OUT_IDX:((n==1)?HSPICS1_OUT_IDX:((n==2)?HSPICS2_OUT_IDX:HSPICS0_OUT_IDX))) +#define SPI_VSPI_SS_IDX(n) ((n==0)?VSPICS0_OUT_IDX:((n==1)?VSPICS1_OUT_IDX:((n==2)?VSPICS2_OUT_IDX:VSPICS0_OUT_IDX))) +#define SPI_SS_IDX(p, n) ((p==0)?SPI_SPI_SS_IDX(n):((p==1)?SPI_SPI_SS_IDX(n):((p==2)?SPI_HSPI_SS_IDX(n):((p==3)?SPI_VSPI_SS_IDX(n):0)))) + +#endif + +#if CONFIG_DISABLE_HAL_LOCKS +#define SPI_MUTEX_LOCK() +#define SPI_MUTEX_UNLOCK() + +static spi_t _spi_bus_array[] = { +#if CONFIG_IDF_TARGET_ESP32S2 + {(volatile spi_dev_t *)(DR_REG_SPI1_BASE), 0}, + {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), 1}, + {(volatile spi_dev_t *)(DR_REG_SPI3_BASE), 2} +#elif CONFIG_IDF_TARGET_ESP32S3 + {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), 0}, + {(volatile spi_dev_t *)(DR_REG_SPI3_BASE), 1} +#elif CONFIG_IDF_TARGET_ESP32C3 + {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), 0} +#else + {(volatile spi_dev_t *)(DR_REG_SPI0_BASE), 0}, + {(volatile spi_dev_t *)(DR_REG_SPI1_BASE), 1}, + {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), 2}, + {(volatile spi_dev_t *)(DR_REG_SPI3_BASE), 3} +#endif +}; +#else +#define SPI_MUTEX_LOCK() do {} while (xSemaphoreTake(spi->lock, portMAX_DELAY) != pdPASS) +#define SPI_MUTEX_UNLOCK() xSemaphoreGive(spi->lock) + +static spi_t _spi_bus_array[] = { +#if CONFIG_IDF_TARGET_ESP32S2 + {(volatile spi_dev_t *)(DR_REG_SPI1_BASE), NULL, 0}, + {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), NULL, 1}, + {(volatile spi_dev_t *)(DR_REG_SPI3_BASE), NULL, 2} +#elif CONFIG_IDF_TARGET_ESP32S3 + {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), NULL, 0}, + {(volatile spi_dev_t *)(DR_REG_SPI3_BASE), NULL, 1} +#elif CONFIG_IDF_TARGET_ESP32C3 + {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), NULL, 0} +#else + {(volatile spi_dev_t *)(DR_REG_SPI0_BASE), NULL, 0}, + {(volatile spi_dev_t *)(DR_REG_SPI1_BASE), NULL, 1}, + {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), NULL, 2}, + {(volatile spi_dev_t *)(DR_REG_SPI3_BASE), NULL, 3} +#endif +}; +#endif + +void spiAttachSCK(spi_t * spi, int8_t sck) +{ + if(!spi) { + return; + } + if(sck < 0) { +#if CONFIG_IDF_TARGET_ESP32S2 + if(spi->num == FSPI) { + sck = 36; + } else { + log_e("HSPI Does not have default pins on ESP32S2!"); + return; + } +#elif CONFIG_IDF_TARGET_ESP32S3 + if(spi->num == FSPI) { + sck = 12; + } else { + log_e("HSPI Does not have default pins on ESP32S3!"); + return; + } +#elif CONFIG_IDF_TARGET_ESP32 + if(spi->num == HSPI) { + sck = 14; + } else if(spi->num == VSPI) { + sck = 18; + } else { + sck = 6; + } +#elif CONFIG_IDF_TARGET_ESP32C3 + log_e("SPI Does not have default pins on ESP32C3!"); + return; +#endif + } + pinMode(sck, OUTPUT); + pinMatrixOutAttach(sck, SPI_CLK_IDX(spi->num), false, false); +} + +void spiAttachMISO(spi_t * spi, int8_t miso) +{ + if(!spi) { + return; + } + if(miso < 0) { +#if CONFIG_IDF_TARGET_ESP32S2 + if(spi->num == FSPI) { + miso = 37; + } else { + log_e("HSPI Does not have default pins on ESP32S2!"); + return; + } +#elif CONFIG_IDF_TARGET_ESP32S3 + if(spi->num == FSPI) { + miso = 13; + } else { + log_e("HSPI Does not have default pins on ESP32S3!"); + return; + } +#elif CONFIG_IDF_TARGET_ESP32 + if(spi->num == HSPI) { + miso = 12; + } else if(spi->num == VSPI) { + miso = 19; + } else { + miso = 7; + } +#elif CONFIG_IDF_TARGET_ESP32C3 + log_e("SPI Does not have default pins on ESP32C3!"); + return; +#endif + } + SPI_MUTEX_LOCK(); + pinMode(miso, INPUT); + pinMatrixInAttach(miso, SPI_MISO_IDX(spi->num), false); + SPI_MUTEX_UNLOCK(); +} + +void spiAttachMOSI(spi_t * spi, int8_t mosi) +{ + if(!spi) { + return; + } + if(mosi < 0) { +#if CONFIG_IDF_TARGET_ESP32S2 + if(spi->num == FSPI) { + mosi = 35; + } else { + log_e("HSPI Does not have default pins on ESP32S2!"); + return; + } +#elif CONFIG_IDF_TARGET_ESP32S3 + if(spi->num == FSPI) { + mosi = 11; + } else { + log_e("HSPI Does not have default pins on ESP32S3!"); + return; + } +#elif CONFIG_IDF_TARGET_ESP32 + if(spi->num == HSPI) { + mosi = 13; + } else if(spi->num == VSPI) { + mosi = 23; + } else { + mosi = 8; + } +#elif CONFIG_IDF_TARGET_ESP32C3 + log_e("SPI Does not have default pins on ESP32C3!"); + return; +#endif + } + pinMode(mosi, OUTPUT); + pinMatrixOutAttach(mosi, SPI_MOSI_IDX(spi->num), false, false); +} + +void spiDetachSCK(spi_t * spi, int8_t sck) +{ + if(!spi) { + return; + } + if(sck < 0) { +#if CONFIG_IDF_TARGET_ESP32S2 + if(spi->num == FSPI) { + sck = 36; + } else { + log_e("HSPI Does not have default pins on ESP32S2!"); + return; + } +#elif CONFIG_IDF_TARGET_ESP32S3 + if(spi->num == FSPI) { + sck = 12; + } else { + log_e("HSPI Does not have default pins on ESP32S3!"); + return; + } +#elif CONFIG_IDF_TARGET_ESP32 + if(spi->num == HSPI) { + sck = 14; + } else if(spi->num == VSPI) { + sck = 18; + } else { + sck = 6; + } +#elif CONFIG_IDF_TARGET_ESP32C3 + log_e("SPI Does not have default pins on ESP32C3!"); + return; +#endif + } + pinMatrixOutDetach(sck, false, false); + pinMode(sck, INPUT); +} + +void spiDetachMISO(spi_t * spi, int8_t miso) +{ + if(!spi) { + return; + } + if(miso < 0) { +#if CONFIG_IDF_TARGET_ESP32S2 + if(spi->num == FSPI) { + miso = 37; + } else { + log_e("HSPI Does not have default pins on ESP32S2!"); + return; + } +#elif CONFIG_IDF_TARGET_ESP32S3 + if(spi->num == FSPI) { + miso = 13; + } else { + log_e("HSPI Does not have default pins on ESP32S3!"); + return; + } +#elif CONFIG_IDF_TARGET_ESP32 + if(spi->num == HSPI) { + miso = 12; + } else if(spi->num == VSPI) { + miso = 19; + } else { + miso = 7; + } +#elif CONFIG_IDF_TARGET_ESP32C3 + log_e("SPI Does not have default pins on ESP32C3!"); + return; +#endif + } + pinMatrixInDetach(SPI_MISO_IDX(spi->num), false, false); + pinMode(miso, INPUT); +} + +void spiDetachMOSI(spi_t * spi, int8_t mosi) +{ + if(!spi) { + return; + } + if(mosi < 0) { +#if CONFIG_IDF_TARGET_ESP32S2 + if(spi->num == FSPI) { + mosi = 35; + } else { + log_e("HSPI Does not have default pins on ESP32S2!"); + return; + } +#elif CONFIG_IDF_TARGET_ESP32S3 + if(spi->num == FSPI) { + mosi = 11; + } else { + log_e("HSPI Does not have default pins on ESP32S3!"); + return; + } +#elif CONFIG_IDF_TARGET_ESP32 + if(spi->num == HSPI) { + mosi = 13; + } else if(spi->num == VSPI) { + mosi = 23; + } else { + mosi = 8; + } +#elif CONFIG_IDF_TARGET_ESP32C3 + log_e("SPI Does not have default pins on ESP32C3!"); + return; +#endif + } + pinMatrixOutDetach(mosi, false, false); + pinMode(mosi, INPUT); +} + +void spiAttachSS(spi_t * spi, uint8_t cs_num, int8_t ss) +{ + if(!spi) { + return; + } + if(cs_num > 2) { + return; + } + if(ss < 0) { + cs_num = 0; +#if CONFIG_IDF_TARGET_ESP32S2 + if(spi->num == FSPI) { + ss = 34; + } else { + log_e("HSPI Does not have default pins on ESP32S2!"); + return; + } +#elif CONFIG_IDF_TARGET_ESP32S3 + if(spi->num == FSPI) { + ss = 10; + } else { + log_e("HSPI Does not have default pins on ESP32S3!"); + return; + } +#elif CONFIG_IDF_TARGET_ESP32 + if(spi->num == HSPI) { + ss = 15; + } else if(spi->num == VSPI) { + ss = 5; + } else { + ss = 11; + } +#elif CONFIG_IDF_TARGET_ESP32C3 + log_e("SPI Does not have default pins on ESP32C3!"); + return; +#endif + } + pinMode(ss, OUTPUT); + pinMatrixOutAttach(ss, SPI_SS_IDX(spi->num, cs_num), false, false); + spiEnableSSPins(spi, (1 << cs_num)); +} + +void spiDetachSS(spi_t * spi, int8_t ss) +{ + if(!spi) { + return; + } + if(ss < 0) { +#if CONFIG_IDF_TARGET_ESP32S2 + if(spi->num == FSPI) { + ss = 34; + } else { + log_e("HSPI Does not have default pins on ESP32S2!"); + return; + } +#elif CONFIG_IDF_TARGET_ESP32S3 + if(spi->num == FSPI) { + ss = 10; + } else { + log_e("HSPI Does not have default pins on ESP32S3!"); + return; + } +#elif CONFIG_IDF_TARGET_ESP32 + if(spi->num == HSPI) { + ss = 15; + } else if(spi->num == VSPI) { + ss = 5; + } else { + ss = 11; + } +#elif CONFIG_IDF_TARGET_ESP32C3 + log_e("SPI Does not have default pins on ESP32C3!"); + return; +#endif + } + pinMatrixOutDetach(ss, false, false); + pinMode(ss, INPUT); +} + +void spiEnableSSPins(spi_t * spi, uint8_t cs_mask) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 + spi->dev->misc.val &= ~(cs_mask & SPI_CS_MASK_ALL); +#else + spi->dev->pin.val &= ~(cs_mask & SPI_CS_MASK_ALL); +#endif + SPI_MUTEX_UNLOCK(); +} + +void spiDisableSSPins(spi_t * spi, uint8_t cs_mask) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 + spi->dev->misc.val |= (cs_mask & SPI_CS_MASK_ALL); +#else + spi->dev->pin.val |= (cs_mask & SPI_CS_MASK_ALL); +#endif + SPI_MUTEX_UNLOCK(); +} + +void spiSSEnable(spi_t * spi) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + spi->dev->user.cs_setup = 1; + spi->dev->user.cs_hold = 1; + SPI_MUTEX_UNLOCK(); +} + +void spiSSDisable(spi_t * spi) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + spi->dev->user.cs_setup = 0; + spi->dev->user.cs_hold = 0; + SPI_MUTEX_UNLOCK(); +} + +void spiSSSet(spi_t * spi) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 + spi->dev->misc.cs_keep_active = 1; +#else + spi->dev->pin.cs_keep_active = 1; +#endif + SPI_MUTEX_UNLOCK(); +} + +void spiSSClear(spi_t * spi) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 + spi->dev->misc.cs_keep_active = 0; +#else + spi->dev->pin.cs_keep_active = 0; +#endif + SPI_MUTEX_UNLOCK(); +} + +uint32_t spiGetClockDiv(spi_t * spi) +{ + if(!spi) { + return 0; + } + return spi->dev->clock.val; +} + +void spiSetClockDiv(spi_t * spi, uint32_t clockDiv) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + spi->dev->clock.val = clockDiv; + SPI_MUTEX_UNLOCK(); +} + +uint8_t spiGetDataMode(spi_t * spi) +{ + if(!spi) { + return 0; + } +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 + bool idleEdge = spi->dev->misc.ck_idle_edge; +#else + bool idleEdge = spi->dev->pin.ck_idle_edge; +#endif + bool outEdge = spi->dev->user.ck_out_edge; + if(idleEdge) { + if(outEdge) { + return SPI_MODE2; + } + return SPI_MODE3; + } + if(outEdge) { + return SPI_MODE1; + } + return SPI_MODE0; +} + +void spiSetDataMode(spi_t * spi, uint8_t dataMode) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + switch (dataMode) { + case SPI_MODE1: +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 + spi->dev->misc.ck_idle_edge = 0; +#else + spi->dev->pin.ck_idle_edge = 0; +#endif + spi->dev->user.ck_out_edge = 1; + break; + case SPI_MODE2: +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 + spi->dev->misc.ck_idle_edge = 1; +#else + spi->dev->pin.ck_idle_edge = 1; +#endif + spi->dev->user.ck_out_edge = 1; + break; + case SPI_MODE3: +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 + spi->dev->misc.ck_idle_edge = 1; +#else + spi->dev->pin.ck_idle_edge = 1; +#endif + spi->dev->user.ck_out_edge = 0; + break; + case SPI_MODE0: + default: +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 + spi->dev->misc.ck_idle_edge = 0; +#else + spi->dev->pin.ck_idle_edge = 0; +#endif + spi->dev->user.ck_out_edge = 0; + break; + } + SPI_MUTEX_UNLOCK(); +} + +uint8_t spiGetBitOrder(spi_t * spi) +{ + if(!spi) { + return 0; + } + return (spi->dev->ctrl.wr_bit_order | spi->dev->ctrl.rd_bit_order) == 0; +} + +void spiSetBitOrder(spi_t * spi, uint8_t bitOrder) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + if (SPI_MSBFIRST == bitOrder) { + spi->dev->ctrl.wr_bit_order = 0; + spi->dev->ctrl.rd_bit_order = 0; + } else if (SPI_LSBFIRST == bitOrder) { + spi->dev->ctrl.wr_bit_order = 1; + spi->dev->ctrl.rd_bit_order = 1; + } + SPI_MUTEX_UNLOCK(); +} + +static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb) +{ + spi_t * spi = (spi_t *)arg; + if(ev_type == APB_BEFORE_CHANGE){ + SPI_MUTEX_LOCK(); + while(spi->dev->cmd.usr); + } else { + spi->dev->clock.val = spiFrequencyToClockDiv(old_apb / ((spi->dev->clock.clkdiv_pre + 1) * (spi->dev->clock.clkcnt_n + 1))); + SPI_MUTEX_UNLOCK(); + } +} + +static void spiInitBus(spi_t * spi) +{ +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 + spi->dev->slave.trans_done = 0; +#endif + spi->dev->slave.val = 0; +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 + spi->dev->misc.val = 0; +#else + spi->dev->pin.val = 0; +#endif + spi->dev->user.val = 0; + spi->dev->user1.val = 0; + spi->dev->ctrl.val = 0; +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 + spi->dev->ctrl1.val = 0; + spi->dev->ctrl2.val = 0; +#else + spi->dev->clk_gate.val = 0; + spi->dev->dma_conf.val = 0; + spi->dev->dma_conf.rx_afifo_rst = 1; + spi->dev->dma_conf.buf_afifo_rst = 1; +#endif + spi->dev->clock.val = 0; +} + +void spiStopBus(spi_t * spi) +{ + if(!spi) { + return; + } + + removeApbChangeCallback(spi, _on_apb_change); + + SPI_MUTEX_LOCK(); + spiInitBus(spi); + SPI_MUTEX_UNLOCK(); +} + +spi_t * spiStartBus(uint8_t spi_num, uint32_t clockDiv, uint8_t dataMode, uint8_t bitOrder) +{ + if(spi_num >= SPI_COUNT){ + return NULL; + } + + spi_t * spi = &_spi_bus_array[spi_num]; + +#if !CONFIG_DISABLE_HAL_LOCKS + if(spi->lock == NULL){ + spi->lock = xSemaphoreCreateMutex(); + if(spi->lock == NULL) { + return NULL; + } + } +#endif + +#if CONFIG_IDF_TARGET_ESP32S2 + if(spi_num == FSPI) { + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI2_CLK_EN); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI2_RST); + } else if(spi_num == HSPI) { + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI3_CLK_EN); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI3_RST); + } else { + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI01_CLK_EN); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI01_RST); + } +#elif CONFIG_IDF_TARGET_ESP32S3 + if(spi_num == FSPI) { + periph_module_reset( PERIPH_SPI2_MODULE ); + periph_module_enable( PERIPH_SPI2_MODULE ); + } else if(spi_num == HSPI) { + periph_module_reset( PERIPH_SPI3_MODULE ); + periph_module_enable( PERIPH_SPI3_MODULE ); + } +#elif CONFIG_IDF_TARGET_ESP32 + if(spi_num == HSPI) { + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI2_CLK_EN); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI2_RST); + } else if(spi_num == VSPI) { + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI3_CLK_EN); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI3_RST); + } else { + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI01_CLK_EN); + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI01_RST); + } +#elif CONFIG_IDF_TARGET_ESP32C3 + periph_module_reset( PERIPH_SPI2_MODULE ); + periph_module_enable( PERIPH_SPI2_MODULE ); +#endif + + SPI_MUTEX_LOCK(); + spiInitBus(spi); +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + spi->dev->clk_gate.clk_en = 1; + spi->dev->clk_gate.mst_clk_sel = 1; + spi->dev->clk_gate.mst_clk_active = 1; + spi->dev->dma_conf.tx_seg_trans_clr_en = 1; + spi->dev->dma_conf.rx_seg_trans_clr_en = 1; + spi->dev->dma_conf.dma_seg_trans_en = 0; +#endif + spi->dev->user.usr_mosi = 1; + spi->dev->user.usr_miso = 1; + spi->dev->user.doutdin = 1; + int i; + for(i=0; i<16; i++) { + spi->dev->data_buf[i] = 0x00000000; + } + SPI_MUTEX_UNLOCK(); + + spiSetDataMode(spi, dataMode); + spiSetBitOrder(spi, bitOrder); + spiSetClockDiv(spi, clockDiv); + + addApbChangeCallback(spi, _on_apb_change); + + return spi; +} + +void spiWaitReady(spi_t * spi) +{ + if(!spi) { + return; + } + while(spi->dev->cmd.usr); +} + +#if CONFIG_IDF_TARGET_ESP32S2 +#define usr_mosi_dbitlen usr_mosi_bit_len +#define usr_miso_dbitlen usr_miso_bit_len +#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 +#define usr_mosi_dbitlen ms_data_bitlen +#define usr_miso_dbitlen ms_data_bitlen +#define mosi_dlen ms_dlen +#define miso_dlen ms_dlen +#endif + +void spiWrite(spi_t * spi, const uint32_t *data, uint8_t len) +{ + if(!spi) { + return; + } + int i; + if(len > 16) { + len = 16; + } + SPI_MUTEX_LOCK(); + spi->dev->mosi_dlen.usr_mosi_dbitlen = (len * 32) - 1; +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 + spi->dev->miso_dlen.usr_miso_dbitlen = 0; +#endif + for(i=0; idev->data_buf[i] = data[i]; + } +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + spi->dev->cmd.update = 1; + while (spi->dev->cmd.update); +#endif + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + SPI_MUTEX_UNLOCK(); +} + +void spiTransfer(spi_t * spi, uint32_t *data, uint8_t len) +{ + if(!spi) { + return; + } + int i; + if(len > 16) { + len = 16; + } + SPI_MUTEX_LOCK(); + spi->dev->mosi_dlen.usr_mosi_dbitlen = (len * 32) - 1; + spi->dev->miso_dlen.usr_miso_dbitlen = (len * 32) - 1; + for(i=0; idev->data_buf[i] = data[i]; + } +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + spi->dev->cmd.update = 1; + while (spi->dev->cmd.update); +#endif + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + for(i=0; idev->data_buf[i]; + } + SPI_MUTEX_UNLOCK(); +} + +void spiWriteByte(spi_t * spi, uint8_t data) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + spi->dev->mosi_dlen.usr_mosi_dbitlen = 7; +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 + spi->dev->miso_dlen.usr_miso_dbitlen = 0; +#endif + spi->dev->data_buf[0] = data; +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + spi->dev->cmd.update = 1; + while (spi->dev->cmd.update); +#endif + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + SPI_MUTEX_UNLOCK(); +} + +uint8_t spiTransferByte(spi_t * spi, uint8_t data) +{ + if(!spi) { + return 0; + } + SPI_MUTEX_LOCK(); + spi->dev->mosi_dlen.usr_mosi_dbitlen = 7; + spi->dev->miso_dlen.usr_miso_dbitlen = 7; + spi->dev->data_buf[0] = data; +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + spi->dev->cmd.update = 1; + while (spi->dev->cmd.update); +#endif + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + data = spi->dev->data_buf[0] & 0xFF; + SPI_MUTEX_UNLOCK(); + return data; +} + +static uint32_t __spiTranslate32(uint32_t data) +{ + union { + uint32_t l; + uint8_t b[4]; + } out; + out.l = data; + return out.b[3] | (out.b[2] << 8) | (out.b[1] << 16) | (out.b[0] << 24); +} + +void spiWriteWord(spi_t * spi, uint16_t data) +{ + if(!spi) { + return; + } + if(!spi->dev->ctrl.wr_bit_order){ + data = (data >> 8) | (data << 8); + } + SPI_MUTEX_LOCK(); + spi->dev->mosi_dlen.usr_mosi_dbitlen = 15; +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 + spi->dev->miso_dlen.usr_miso_dbitlen = 0; +#endif + spi->dev->data_buf[0] = data; +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + spi->dev->cmd.update = 1; + while (spi->dev->cmd.update); +#endif + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + SPI_MUTEX_UNLOCK(); +} + +uint16_t spiTransferWord(spi_t * spi, uint16_t data) +{ + if(!spi) { + return 0; + } + if(!spi->dev->ctrl.wr_bit_order){ + data = (data >> 8) | (data << 8); + } + SPI_MUTEX_LOCK(); + spi->dev->mosi_dlen.usr_mosi_dbitlen = 15; + spi->dev->miso_dlen.usr_miso_dbitlen = 15; + spi->dev->data_buf[0] = data; +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + spi->dev->cmd.update = 1; + while (spi->dev->cmd.update); +#endif + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + data = spi->dev->data_buf[0]; + SPI_MUTEX_UNLOCK(); + if(!spi->dev->ctrl.rd_bit_order){ + data = (data >> 8) | (data << 8); + } + return data; +} + +void spiWriteLong(spi_t * spi, uint32_t data) +{ + if(!spi) { + return; + } + if(!spi->dev->ctrl.wr_bit_order){ + data = __spiTranslate32(data); + } + SPI_MUTEX_LOCK(); + spi->dev->mosi_dlen.usr_mosi_dbitlen = 31; +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 + spi->dev->miso_dlen.usr_miso_dbitlen = 0; +#endif + spi->dev->data_buf[0] = data; +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + spi->dev->cmd.update = 1; + while (spi->dev->cmd.update); +#endif + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + SPI_MUTEX_UNLOCK(); +} + +uint32_t spiTransferLong(spi_t * spi, uint32_t data) +{ + if(!spi) { + return 0; + } + if(!spi->dev->ctrl.wr_bit_order){ + data = __spiTranslate32(data); + } + SPI_MUTEX_LOCK(); + spi->dev->mosi_dlen.usr_mosi_dbitlen = 31; + spi->dev->miso_dlen.usr_miso_dbitlen = 31; + spi->dev->data_buf[0] = data; +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + spi->dev->cmd.update = 1; + while (spi->dev->cmd.update); +#endif + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + data = spi->dev->data_buf[0]; + SPI_MUTEX_UNLOCK(); + if(!spi->dev->ctrl.rd_bit_order){ + data = __spiTranslate32(data); + } + return data; +} + +static void __spiTransferBytes(spi_t * spi, const uint8_t * data, uint8_t * out, uint32_t bytes) +{ + if(!spi) { + return; + } + uint32_t i; + + if(bytes > 64) { + bytes = 64; + } + + uint32_t words = (bytes + 3) / 4;//16 max + + uint32_t wordsBuf[16] = {0,}; + uint8_t * bytesBuf = (uint8_t *) wordsBuf; + + if(data) { + memcpy(bytesBuf, data, bytes);//copy data to buffer + } else { + memset(bytesBuf, 0xFF, bytes); + } + + spi->dev->mosi_dlen.usr_mosi_dbitlen = ((bytes * 8) - 1); + spi->dev->miso_dlen.usr_miso_dbitlen = ((bytes * 8) - 1); + + for(i=0; idev->data_buf[i] = wordsBuf[i]; //copy buffer to spi fifo + } + +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + spi->dev->cmd.update = 1; + while (spi->dev->cmd.update); +#endif + spi->dev->cmd.usr = 1; + + while(spi->dev->cmd.usr); + + if(out) { + for(i=0; idev->data_buf[i];//copy spi fifo to buffer + } + memcpy(out, bytesBuf, bytes);//copy buffer to output + } +} + +void spiTransferBytes(spi_t * spi, const uint8_t * data, uint8_t * out, uint32_t size) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + while(size) { + if(size > 64) { + __spiTransferBytes(spi, data, out, 64); + size -= 64; + if(data) { + data += 64; + } + if(out) { + out += 64; + } + } else { + __spiTransferBytes(spi, data, out, size); + size = 0; + } + } + SPI_MUTEX_UNLOCK(); +} + +void spiTransferBits(spi_t * spi, uint32_t data, uint32_t * out, uint8_t bits) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + spiTransferBitsNL(spi, data, out, bits); + SPI_MUTEX_UNLOCK(); +} + +/* + * Manual Lock Management + * */ + +#define MSB_32_SET(var, val) { uint8_t * d = (uint8_t *)&(val); (var) = d[3] | (d[2] << 8) | (d[1] << 16) | (d[0] << 24); } +#define MSB_24_SET(var, val) { uint8_t * d = (uint8_t *)&(val); (var) = d[2] | (d[1] << 8) | (d[0] << 16); } +#define MSB_16_SET(var, val) { (var) = (((val) & 0xFF00) >> 8) | (((val) & 0xFF) << 8); } +#define MSB_PIX_SET(var, val) { uint8_t * d = (uint8_t *)&(val); (var) = d[1] | (d[0] << 8) | (d[3] << 16) | (d[2] << 24); } + +void spiTransaction(spi_t * spi, uint32_t clockDiv, uint8_t dataMode, uint8_t bitOrder) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); + spi->dev->clock.val = clockDiv; + switch (dataMode) { + case SPI_MODE1: +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 + spi->dev->misc.ck_idle_edge = 0; +#else + spi->dev->pin.ck_idle_edge = 0; +#endif + spi->dev->user.ck_out_edge = 1; + break; + case SPI_MODE2: +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 + spi->dev->misc.ck_idle_edge = 1; +#else + spi->dev->pin.ck_idle_edge = 1; +#endif + spi->dev->user.ck_out_edge = 1; + break; + case SPI_MODE3: +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 + spi->dev->misc.ck_idle_edge = 1; +#else + spi->dev->pin.ck_idle_edge = 1; +#endif + spi->dev->user.ck_out_edge = 0; + break; + case SPI_MODE0: + default: +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 + spi->dev->misc.ck_idle_edge = 0; +#else + spi->dev->pin.ck_idle_edge = 0; +#endif + spi->dev->user.ck_out_edge = 0; + break; + } + if (SPI_MSBFIRST == bitOrder) { + spi->dev->ctrl.wr_bit_order = 0; + spi->dev->ctrl.rd_bit_order = 0; + } else if (SPI_LSBFIRST == bitOrder) { + spi->dev->ctrl.wr_bit_order = 1; + spi->dev->ctrl.rd_bit_order = 1; + } +} + +void spiSimpleTransaction(spi_t * spi) +{ + if(!spi) { + return; + } + SPI_MUTEX_LOCK(); +} + +void spiEndTransaction(spi_t * spi) +{ + if(!spi) { + return; + } + SPI_MUTEX_UNLOCK(); +} + +void ARDUINO_ISR_ATTR spiWriteByteNL(spi_t * spi, uint8_t data) +{ + if(!spi) { + return; + } + spi->dev->mosi_dlen.usr_mosi_dbitlen = 7; +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 + spi->dev->miso_dlen.usr_miso_dbitlen = 0; +#endif + spi->dev->data_buf[0] = data; +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + spi->dev->cmd.update = 1; + while (spi->dev->cmd.update); +#endif + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); +} + +uint8_t spiTransferByteNL(spi_t * spi, uint8_t data) +{ + if(!spi) { + return 0; + } + spi->dev->mosi_dlen.usr_mosi_dbitlen = 7; + spi->dev->miso_dlen.usr_miso_dbitlen = 7; + spi->dev->data_buf[0] = data; +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + spi->dev->cmd.update = 1; + while (spi->dev->cmd.update); +#endif + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + data = spi->dev->data_buf[0] & 0xFF; + return data; +} + +void ARDUINO_ISR_ATTR spiWriteShortNL(spi_t * spi, uint16_t data) +{ + if(!spi) { + return; + } + if(!spi->dev->ctrl.wr_bit_order){ + MSB_16_SET(data, data); + } + spi->dev->mosi_dlen.usr_mosi_dbitlen = 15; +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 + spi->dev->miso_dlen.usr_miso_dbitlen = 0; +#endif + spi->dev->data_buf[0] = data; +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + spi->dev->cmd.update = 1; + while (spi->dev->cmd.update); +#endif + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); +} + +uint16_t spiTransferShortNL(spi_t * spi, uint16_t data) +{ + if(!spi) { + return 0; + } + if(!spi->dev->ctrl.wr_bit_order){ + MSB_16_SET(data, data); + } + spi->dev->mosi_dlen.usr_mosi_dbitlen = 15; + spi->dev->miso_dlen.usr_miso_dbitlen = 15; + spi->dev->data_buf[0] = data; +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + spi->dev->cmd.update = 1; + while (spi->dev->cmd.update); +#endif + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + data = spi->dev->data_buf[0] & 0xFFFF; + if(!spi->dev->ctrl.rd_bit_order){ + MSB_16_SET(data, data); + } + return data; +} + +void ARDUINO_ISR_ATTR spiWriteLongNL(spi_t * spi, uint32_t data) +{ + if(!spi) { + return; + } + if(!spi->dev->ctrl.wr_bit_order){ + MSB_32_SET(data, data); + } + spi->dev->mosi_dlen.usr_mosi_dbitlen = 31; +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 + spi->dev->miso_dlen.usr_miso_dbitlen = 0; +#endif + spi->dev->data_buf[0] = data; +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + spi->dev->cmd.update = 1; + while (spi->dev->cmd.update); +#endif + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); +} + +uint32_t spiTransferLongNL(spi_t * spi, uint32_t data) +{ + if(!spi) { + return 0; + } + if(!spi->dev->ctrl.wr_bit_order){ + MSB_32_SET(data, data); + } + spi->dev->mosi_dlen.usr_mosi_dbitlen = 31; + spi->dev->miso_dlen.usr_miso_dbitlen = 31; + spi->dev->data_buf[0] = data; +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + spi->dev->cmd.update = 1; + while (spi->dev->cmd.update); +#endif + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + data = spi->dev->data_buf[0]; + if(!spi->dev->ctrl.rd_bit_order){ + MSB_32_SET(data, data); + } + return data; +} + +void spiWriteNL(spi_t * spi, const void * data_in, uint32_t len){ + if(!spi) { + return; + } + size_t longs = len >> 2; + if(len & 3){ + longs++; + } + uint32_t * data = (uint32_t*)data_in; + size_t c_len = 0, c_longs = 0; + + while(len){ + c_len = (len>64)?64:len; + c_longs = (longs > 16)?16:longs; + + spi->dev->mosi_dlen.usr_mosi_dbitlen = (c_len*8)-1; +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 + spi->dev->miso_dlen.usr_miso_dbitlen = 0; +#endif + for (size_t i=0; idev->data_buf[i] = data[i]; + } +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + spi->dev->cmd.update = 1; + while (spi->dev->cmd.update); +#endif + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + + data += c_longs; + longs -= c_longs; + len -= c_len; + } +} + +void spiTransferBytesNL(spi_t * spi, const void * data_in, uint8_t * data_out, uint32_t len){ + if(!spi) { + return; + } + size_t longs = len >> 2; + if(len & 3){ + longs++; + } + uint32_t * data = (uint32_t*)data_in; + uint32_t * result = (uint32_t*)data_out; + size_t c_len = 0, c_longs = 0; + + while(len){ + c_len = (len>64)?64:len; + c_longs = (longs > 16)?16:longs; + + spi->dev->mosi_dlen.usr_mosi_dbitlen = (c_len*8)-1; + spi->dev->miso_dlen.usr_miso_dbitlen = (c_len*8)-1; + if(data){ + for (size_t i=0; idev->data_buf[i] = data[i]; + } + } else { + for (size_t i=0; idev->data_buf[i] = 0xFFFFFFFF; + } + } +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + spi->dev->cmd.update = 1; + while (spi->dev->cmd.update); +#endif + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + if(result){ + if(c_len & 3){ + for (size_t i=0; i<(c_longs-1); i++) { + result[i] = spi->dev->data_buf[i]; + } + uint32_t last_data = spi->dev->data_buf[c_longs-1]; + uint8_t * last_out8 = (uint8_t *)&result[c_longs-1]; + uint8_t * last_data8 = (uint8_t *)&last_data; + for (size_t i=0; i<(c_len & 3); i++) { + last_out8[i] = last_data8[i]; + } + } else { + for (size_t i=0; idev->data_buf[i]; + } + } + } + if(data){ + data += c_longs; + } + if(result){ + result += c_longs; + } + longs -= c_longs; + len -= c_len; + } +} + +void spiTransferBitsNL(spi_t * spi, uint32_t data, uint32_t * out, uint8_t bits) +{ + if(!spi) { + return; + } + + if(bits > 32) { + bits = 32; + } + uint32_t bytes = (bits + 7) / 8;//64 max + uint32_t mask = (((uint64_t)1 << bits) - 1) & 0xFFFFFFFF; + data = data & mask; + if(!spi->dev->ctrl.wr_bit_order){ + if(bytes == 2) { + MSB_16_SET(data, data); + } else if(bytes == 3) { + MSB_24_SET(data, data); + } else { + MSB_32_SET(data, data); + } + } + + spi->dev->mosi_dlen.usr_mosi_dbitlen = (bits - 1); + spi->dev->miso_dlen.usr_miso_dbitlen = (bits - 1); + spi->dev->data_buf[0] = data; +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + spi->dev->cmd.update = 1; + while (spi->dev->cmd.update); +#endif + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + data = spi->dev->data_buf[0]; + if(out) { + *out = data; + if(!spi->dev->ctrl.rd_bit_order){ + if(bytes == 2) { + MSB_16_SET(*out, data); + } else if(bytes == 3) { + MSB_24_SET(*out, data); + } else { + MSB_32_SET(*out, data); + } + } + } +} + +void ARDUINO_ISR_ATTR spiWritePixelsNL(spi_t * spi, const void * data_in, uint32_t len){ + size_t longs = len >> 2; + if(len & 3){ + longs++; + } + bool msb = !spi->dev->ctrl.wr_bit_order; + uint32_t * data = (uint32_t*)data_in; + size_t c_len = 0, c_longs = 0, l_bytes = 0; + + while(len){ + c_len = (len>64)?64:len; + c_longs = (longs > 16)?16:longs; + l_bytes = (c_len & 3); + + spi->dev->mosi_dlen.usr_mosi_dbitlen = (c_len*8)-1; +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 + spi->dev->miso_dlen.usr_miso_dbitlen = 0; +#endif + for (size_t i=0; idev->data_buf[i], data[i]); + } else { + spi->dev->data_buf[i] = data[i] & 0xFF; + } + } else { + MSB_PIX_SET(spi->dev->data_buf[i], data[i]); + } + } else { + spi->dev->data_buf[i] = data[i]; + } + } +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + spi->dev->cmd.update = 1; + while (spi->dev->cmd.update); +#endif + spi->dev->cmd.usr = 1; + while(spi->dev->cmd.usr); + + data += c_longs; + longs -= c_longs; + len -= c_len; + } +} + + + +/* + * Clock Calculators + * + * */ + +typedef union { + uint32_t value; + struct { + uint32_t clkcnt_l: 6; /*it must be equal to spi_clkcnt_N.*/ + uint32_t clkcnt_h: 6; /*it must be floor((spi_clkcnt_N+1)/2-1).*/ + uint32_t clkcnt_n: 6; /*it is the divider of spi_clk. So spi_clk frequency is system/(spi_clkdiv_pre+1)/(spi_clkcnt_N+1)*/ +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + uint32_t clkdiv_pre: 4; /*it is pre-divider of spi_clk.*/ + uint32_t reserved: 9; /*reserved*/ +#else + uint32_t clkdiv_pre: 13; /*it is pre-divider of spi_clk.*/ +#endif + uint32_t clk_equ_sysclk: 1; /*1: spi_clk is eqaul to system 0: spi_clk is divided from system clock.*/ + }; +} spiClk_t; + +#define ClkRegToFreq(reg) (apb_freq / (((reg)->clkdiv_pre + 1) * ((reg)->clkcnt_n + 1))) + +uint32_t spiClockDivToFrequency(uint32_t clockDiv) +{ + uint32_t apb_freq = getApbFrequency(); + spiClk_t reg = { clockDiv }; + return ClkRegToFreq(®); +} + +uint32_t spiFrequencyToClockDiv(uint32_t freq) +{ + uint32_t apb_freq = getApbFrequency(); + + if(freq >= apb_freq) { + return SPI_CLK_EQU_SYSCLK; + } + + const spiClk_t minFreqReg = { 0x7FFFF000 }; + uint32_t minFreq = ClkRegToFreq((spiClk_t*) &minFreqReg); + if(freq < minFreq) { + return minFreqReg.value; + } + + uint8_t calN = 1; + spiClk_t bestReg = { 0 }; + int32_t bestFreq = 0; + + while(calN <= 0x3F) { + spiClk_t reg = { 0 }; + int32_t calFreq; + int32_t calPre; + int8_t calPreVari = -2; + + reg.clkcnt_n = calN; + + while(calPreVari++ <= 1) { + calPre = (((apb_freq / (reg.clkcnt_n + 1)) / freq) - 1) + calPreVari; +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 + if(calPre > 0xF) { + reg.clkdiv_pre = 0xF; +#else + if(calPre > 0x1FFF) { + reg.clkdiv_pre = 0x1FFF; +#endif + } else if(calPre <= 0) { + reg.clkdiv_pre = 0; + } else { + reg.clkdiv_pre = calPre; + } + reg.clkcnt_l = ((reg.clkcnt_n + 1) / 2); + calFreq = ClkRegToFreq(®); + if(calFreq == (int32_t) freq) { + memcpy(&bestReg, ®, sizeof(bestReg)); + break; + } else if(calFreq < (int32_t) freq) { + if(abs(freq - calFreq) < abs(freq - bestFreq)) { + bestFreq = calFreq; + memcpy(&bestReg, ®, sizeof(bestReg)); + } + } + } + if(calFreq == (int32_t) freq) { + break; + } + calN++; + } + return bestReg.value; +} diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-spi.h b/src/esp32/hardware/cores/esp32/esp32-hal-spi.h new file mode 100644 index 0000000..89ceefc --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-spi.h @@ -0,0 +1,149 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef MAIN_ESP32_HAL_SPI_H_ +#define MAIN_ESP32_HAL_SPI_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sdkconfig.h" +#include +#include + +#define SPI_HAS_TRANSACTION + +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 +#define FSPI 0 +#define HSPI 1 +#else +#define FSPI 1 //SPI bus attached to the flash (can use the same data lines but different SS) +#define HSPI 2 //SPI bus normally mapped to pins 12 - 15, but can be matrixed to any pins +#if CONFIG_IDF_TARGET_ESP32 +#define VSPI 3 //SPI bus normally attached to pins 5, 18, 19 and 23, but can be matrixed to any pins +#endif +#endif + +// This defines are not representing the real Divider of the ESP32 +// the Defines match to an AVR Arduino on 16MHz for better compatibility +#define SPI_CLOCK_DIV2 0x00101001 //8 MHz +#define SPI_CLOCK_DIV4 0x00241001 //4 MHz +#define SPI_CLOCK_DIV8 0x004c1001 //2 MHz +#define SPI_CLOCK_DIV16 0x009c1001 //1 MHz +#define SPI_CLOCK_DIV32 0x013c1001 //500 KHz +#define SPI_CLOCK_DIV64 0x027c1001 //250 KHz +#define SPI_CLOCK_DIV128 0x04fc1001 //125 KHz + +#define SPI_MODE0 0 +#define SPI_MODE1 1 +#define SPI_MODE2 2 +#define SPI_MODE3 3 + +#define SPI_CS0 0 +#define SPI_CS1 1 +#define SPI_CS2 2 +#define SPI_CS_MASK_ALL 0x7 + +#define SPI_LSBFIRST 0 +#define SPI_MSBFIRST 1 + +struct spi_struct_t; +typedef struct spi_struct_t spi_t; + +spi_t * spiStartBus(uint8_t spi_num, uint32_t clockDiv, uint8_t dataMode, uint8_t bitOrder); +void spiStopBus(spi_t * spi); + +//Attach/Detach Signal Pins +void spiAttachSCK(spi_t * spi, int8_t sck); +void spiAttachMISO(spi_t * spi, int8_t miso); +void spiAttachMOSI(spi_t * spi, int8_t mosi); +void spiDetachSCK(spi_t * spi, int8_t sck); +void spiDetachMISO(spi_t * spi, int8_t miso); +void spiDetachMOSI(spi_t * spi, int8_t mosi); + +//Attach/Detach SS pin to SPI_CSx signal +void spiAttachSS(spi_t * spi, uint8_t cs_num, int8_t ss); +void spiDetachSS(spi_t * spi, int8_t ss); + +//Enable/Disable SPI_CSx pins +void spiEnableSSPins(spi_t * spi, uint8_t cs_mask); +void spiDisableSSPins(spi_t * spi, uint8_t cs_mask); + +//Enable/Disable hardware control of SPI_CSx pins +void spiSSEnable(spi_t * spi); +void spiSSDisable(spi_t * spi); + +//Activate enabled SPI_CSx pins +void spiSSSet(spi_t * spi); +//Deactivate enabled SPI_CSx pins +void spiSSClear(spi_t * spi); + +void spiWaitReady(spi_t * spi); + +uint32_t spiGetClockDiv(spi_t * spi); +uint8_t spiGetDataMode(spi_t * spi); +uint8_t spiGetBitOrder(spi_t * spi); + + +/* + * Non transaction based lock methods (each locks and unlocks when called) + * */ +void spiSetClockDiv(spi_t * spi, uint32_t clockDiv); +void spiSetDataMode(spi_t * spi, uint8_t dataMode); +void spiSetBitOrder(spi_t * spi, uint8_t bitOrder); + +void spiWrite(spi_t * spi, const uint32_t *data, uint8_t len); +void spiWriteByte(spi_t * spi, uint8_t data); +void spiWriteWord(spi_t * spi, uint16_t data); +void spiWriteLong(spi_t * spi, uint32_t data); + +void spiTransfer(spi_t * spi, uint32_t *out, uint8_t len); +uint8_t spiTransferByte(spi_t * spi, uint8_t data); +uint16_t spiTransferWord(spi_t * spi, uint16_t data); +uint32_t spiTransferLong(spi_t * spi, uint32_t data); +void spiTransferBytes(spi_t * spi, const uint8_t * data, uint8_t * out, uint32_t size); +void spiTransferBits(spi_t * spi, uint32_t data, uint32_t * out, uint8_t bits); + +/* + * New (EXPERIMENTAL) Transaction lock based API (lock once until endTransaction) + * */ +void spiTransaction(spi_t * spi, uint32_t clockDiv, uint8_t dataMode, uint8_t bitOrder); +void spiSimpleTransaction(spi_t * spi); +void spiEndTransaction(spi_t * spi); + +void spiWriteNL(spi_t * spi, const void * data_in, uint32_t len); +void spiWriteByteNL(spi_t * spi, uint8_t data); +void spiWriteShortNL(spi_t * spi, uint16_t data); +void spiWriteLongNL(spi_t * spi, uint32_t data); +void spiWritePixelsNL(spi_t * spi, const void * data_in, uint32_t len); + +#define spiTransferNL(spi, data, len) spiTransferBytesNL(spi, data, data, len) +uint8_t spiTransferByteNL(spi_t * spi, uint8_t data); +uint16_t spiTransferShortNL(spi_t * spi, uint16_t data); +uint32_t spiTransferLongNL(spi_t * spi, uint32_t data); +void spiTransferBytesNL(spi_t * spi, const void * data_in, uint8_t * data_out, uint32_t len); +void spiTransferBitsNL(spi_t * spi, uint32_t data_in, uint32_t * data_out, uint8_t bits); + +/* + * Helper functions to translate frequency to clock divider and back + * */ +uint32_t spiFrequencyToClockDiv(uint32_t freq); +uint32_t spiClockDivToFrequency(uint32_t freq); + +#ifdef __cplusplus +} +#endif + +#endif /* MAIN_ESP32_HAL_SPI_H_ */ diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-time.c b/src/esp32/hardware/cores/esp32/esp32-hal-time.c new file mode 100644 index 0000000..e704830 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-time.c @@ -0,0 +1,97 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp32-hal.h" +#include "lwip/apps/sntp.h" +//#include "tcpip_adapter.h" +#include "esp_netif.h" + +static void setTimeZone(long offset, int daylight) +{ + char cst[17] = {0}; + char cdt[17] = "DST"; + char tz[33] = {0}; + + if(offset % 3600){ + sprintf(cst, "UTC%ld:%02u:%02u", offset / 3600, abs((offset % 3600) / 60), abs(offset % 60)); + } else { + sprintf(cst, "UTC%ld", offset / 3600); + } + if(daylight != 3600){ + long tz_dst = offset - daylight; + if(tz_dst % 3600){ + sprintf(cdt, "DST%ld:%02u:%02u", tz_dst / 3600, abs((tz_dst % 3600) / 60), abs(tz_dst % 60)); + } else { + sprintf(cdt, "DST%ld", tz_dst / 3600); + } + } + sprintf(tz, "%s%s", cst, cdt); + setenv("TZ", tz, 1); + tzset(); +} + +/* + * configTime + * Source: https://github.com/esp8266/Arduino/blob/master/cores/esp8266/time.c + * */ +void configTime(long gmtOffset_sec, int daylightOffset_sec, const char* server1, const char* server2, const char* server3) +{ + //tcpip_adapter_init(); // Should not hurt anything if already inited + esp_netif_init(); + if(sntp_enabled()){ + sntp_stop(); + } + sntp_setoperatingmode(SNTP_OPMODE_POLL); + sntp_setservername(0, (char*)server1); + sntp_setservername(1, (char*)server2); + sntp_setservername(2, (char*)server3); + sntp_init(); + setTimeZone(-gmtOffset_sec, daylightOffset_sec); +} + +/* + * configTzTime + * sntp setup using TZ environment variable + * */ +void configTzTime(const char* tz, const char* server1, const char* server2, const char* server3) +{ + //tcpip_adapter_init(); // Should not hurt anything if already inited + esp_netif_init(); + if(sntp_enabled()){ + sntp_stop(); + } + sntp_setoperatingmode(SNTP_OPMODE_POLL); + sntp_setservername(0, (char*)server1); + sntp_setservername(1, (char*)server2); + sntp_setservername(2, (char*)server3); + sntp_init(); + setenv("TZ", tz, 1); + tzset(); +} + +bool getLocalTime(struct tm * info, uint32_t ms) +{ + uint32_t start = millis(); + time_t now; + while((millis()-start) <= ms) { + time(&now); + localtime_r(&now, info); + if(info->tm_year > (2016 - 1900)){ + return true; + } + delay(10); + } + return false; +} + diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-timer.c b/src/esp32/hardware/cores/esp32/esp32-hal-timer.c new file mode 100644 index 0000000..87f48e5 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-timer.c @@ -0,0 +1,273 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp32-hal-timer.h" +#include "driver/timer.h" +#include "soc/soc_caps.h" + +typedef union { + struct { + uint32_t reserved0: 10; + uint32_t alarm_en: 1; /*When set alarm is enabled*/ + uint32_t level_int_en: 1; /*When set level type interrupt will be generated during alarm*/ + uint32_t edge_int_en: 1; /*When set edge type interrupt will be generated during alarm*/ + uint32_t divider: 16; /*Timer clock (T0/1_clk) pre-scale value.*/ + uint32_t autoreload: 1; /*When set timer 0/1 auto-reload at alarming is enabled*/ + uint32_t increase: 1; /*When set timer 0/1 time-base counter increment. When cleared timer 0 time-base counter decrement.*/ + uint32_t enable: 1; /*When set timer 0/1 time-base counter is enabled*/ + }; + uint32_t val; +} timer_cfg_t; + +#define NUM_OF_TIMERS SOC_TIMER_GROUP_TOTAL_TIMERS + +typedef struct hw_timer_s +{ + uint8_t group; + uint8_t num; +} hw_timer_t; + +// Works for all chips +static hw_timer_t timer_dev[4] = { + {0,0}, {1,0}, {0,1}, {1,1} +}; + +// NOTE: (in IDF 5.0 there wont be need to know groups/numbers +// timer_init() will list thru all timers and return free timer handle) + + +inline uint64_t timerRead(hw_timer_t *timer){ + + uint64_t value; + timer_get_counter_value(timer->group, timer->num,&value); + return value; +} + +uint64_t timerAlarmRead(hw_timer_t *timer){ + uint64_t value; + timer_get_alarm_value(timer->group, timer->num, &value); + return value; +} + +void timerWrite(hw_timer_t *timer, uint64_t val){ + timer_set_counter_value(timer->group, timer->num, val); +} + +void timerAlarmWrite(hw_timer_t *timer, uint64_t alarm_value, bool autoreload){ + timer_set_alarm_value(timer->group, timer->num, alarm_value); + timerSetAutoReload(timer,autoreload); +} + +void timerSetConfig(hw_timer_t *timer, uint32_t config){ + timer_cfg_t cfg; + cfg.val = config; + timer_set_alarm(timer->group, timer->num, cfg.alarm_en); + timerSetDivider(timer,cfg.divider); + timerSetAutoReload(timer,cfg.autoreload); + timerSetCountUp(timer, cfg.increase); + + if (cfg.enable) { + timerStart(timer); + } + else{ + timerStop(timer); + } + return; +} + +uint32_t timerGetConfig(hw_timer_t *timer){ + timer_config_t timer_cfg; + timer_get_config(timer->group, timer->num,&timer_cfg); + + //Translate to default uint32_t + timer_cfg_t cfg; + cfg.alarm_en = timer_cfg.alarm_en; + cfg.autoreload = timer_cfg.auto_reload; + cfg.divider = timer_cfg.divider; + cfg.edge_int_en = timer_cfg.intr_type; + cfg.level_int_en = !timer_cfg.intr_type; + cfg.enable = timer_cfg.counter_en; + cfg.increase = timer_cfg.counter_dir; + + return cfg.val; +} + +void timerSetCountUp(hw_timer_t *timer, bool countUp){ + timer_set_counter_mode(timer->group, timer->num,countUp); +} + +bool timerGetCountUp(hw_timer_t *timer){ + timer_cfg_t config; + config.val = timerGetConfig(timer); + return config.increase; +} + +void timerSetAutoReload(hw_timer_t *timer, bool autoreload){ + timer_set_auto_reload(timer->group, timer->num,autoreload); +} + +bool timerGetAutoReload(hw_timer_t *timer){ + timer_cfg_t config; + config.val= timerGetConfig(timer); + return config.autoreload; +} + +// Set divider from 2 to 65535 +void timerSetDivider(hw_timer_t *timer, uint16_t divider){ + if(divider < 2) + { + log_e("Timer divider must be set in range of 2 to 65535"); + return; + } + timer_set_divider(timer->group, timer->num,divider); +} + +uint16_t timerGetDivider(hw_timer_t *timer){ + timer_cfg_t config; + config.val = timerGetConfig(timer); + return config.divider; +} + +void timerStart(hw_timer_t *timer){ + timer_start(timer->group, timer->num); +} + +void timerStop(hw_timer_t *timer){ + timer_pause(timer->group, timer->num); +} + +void timerRestart(hw_timer_t *timer){ + timerWrite(timer,0); +} + +bool timerStarted(hw_timer_t *timer){ + timer_cfg_t config; + config.val = timerGetConfig(timer); + return config.enable; +} + +void timerAlarmEnable(hw_timer_t *timer){ + timer_set_alarm(timer->group, timer->num,true); +} + +void timerAlarmDisable(hw_timer_t *timer){ + timer_set_alarm(timer->group, timer->num,false); +} + +bool timerAlarmEnabled(hw_timer_t *timer){ + timer_cfg_t config; + config.val = timerGetConfig(timer); + return config.alarm_en; +} + +static void _on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb){ + hw_timer_t * timer = (hw_timer_t *)arg; + if(ev_type == APB_BEFORE_CHANGE){ + timerStop(timer); + } else { + old_apb /= 1000000; + new_apb /= 1000000; + uint16_t divider = (new_apb * timerGetDivider(timer)) / old_apb; + timerSetDivider(timer,divider); + timerStart(timer); + } +} + +hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp){ + if(num >= NUM_OF_TIMERS) + { + log_e("Timer number %u exceeds available number of Timers.", num); + return NULL; + } + + hw_timer_t * timer = &timer_dev[num]; //Get Timer group/num from 0-3 number + + timer_config_t config = { + .divider = divider, + .counter_dir = countUp, + .counter_en = TIMER_PAUSE, + .alarm_en = TIMER_ALARM_DIS, + .auto_reload = false, + }; + + timer_init(timer->group, timer->num, &config); + timer_set_counter_value(timer->group, timer->num, 0); + timerStart(timer); + addApbChangeCallback(timer, _on_apb_change); + return timer; +} + +void timerEnd(hw_timer_t *timer){ + removeApbChangeCallback(timer, _on_apb_change); + timer_deinit(timer->group, timer->num); +} + +bool IRAM_ATTR timerFnWrapper(void *arg){ + void (*fn)(void) = arg; + fn(); + + // some additional logic or handling may be required here to approriately yield or not + return false; +} + +void timerAttachInterruptFlag(hw_timer_t *timer, void (*fn)(void), bool edge, int intr_alloc_flags){ + if(edge){ + log_w("EDGE timer interrupt is not supported! Setting to LEVEL..."); + } + timer_isr_callback_add(timer->group, timer->num, timerFnWrapper, fn, intr_alloc_flags); +} + +void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge){ + timerAttachInterruptFlag(timer, fn, edge, 0); +} + +void timerDetachInterrupt(hw_timer_t *timer){ + timer_isr_callback_remove(timer->group, timer->num); +} + +uint64_t timerReadMicros(hw_timer_t *timer){ + uint64_t timer_val = timerRead(timer); + uint16_t div = timerGetDivider(timer); + return timer_val * div / (getApbFrequency() / 1000000); +} + +uint64_t timerReadMilis(hw_timer_t *timer){ + uint64_t timer_val = timerRead(timer); + uint16_t div = timerGetDivider(timer); + return timer_val * div / (getApbFrequency() / 1000); +} + +double timerReadSeconds(hw_timer_t *timer){ + uint64_t timer_val = timerRead(timer); + uint16_t div = timerGetDivider(timer); + return (double)timer_val * div / getApbFrequency(); +} + +uint64_t timerAlarmReadMicros(hw_timer_t *timer){ + uint64_t timer_val = timerAlarmRead(timer); + uint16_t div = timerGetDivider(timer); + return timer_val * div / (getApbFrequency() / 1000000); +} + +uint64_t timerAlarmReadMilis(hw_timer_t *timer){ + uint64_t timer_val = timerAlarmRead(timer); + uint16_t div = timerGetDivider(timer); + return timer_val * div / (getApbFrequency() / 1000); +} + +double timerAlarmReadSeconds(hw_timer_t *timer){ + uint64_t timer_val = timerAlarmRead(timer); + uint16_t div = timerGetDivider(timer); + return (double)timer_val * div / getApbFrequency(); +} diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-timer.h b/src/esp32/hardware/cores/esp32/esp32-hal-timer.h new file mode 100644 index 0000000..5f0413b --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-timer.h @@ -0,0 +1,74 @@ +/* + Arduino.h - Main include file for the Arduino SDK + Copyright (c) 2005-2013 Arduino Team. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MAIN_ESP32_HAL_TIMER_H_ +#define MAIN_ESP32_HAL_TIMER_H_ + +#include "esp32-hal.h" +#include "freertos/FreeRTOS.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct hw_timer_s; +typedef struct hw_timer_s hw_timer_t; + +hw_timer_t * timerBegin(uint8_t timer, uint16_t divider, bool countUp); +void timerEnd(hw_timer_t *timer); + +void timerSetConfig(hw_timer_t *timer, uint32_t config); +uint32_t timerGetConfig(hw_timer_t *timer); + +void timerAttachInterruptFlag(hw_timer_t *timer, void (*fn)(void), bool edge, int intr_alloc_flags); +void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge); +void timerDetachInterrupt(hw_timer_t *timer); + +void timerStart(hw_timer_t *timer); +void timerStop(hw_timer_t *timer); +void timerRestart(hw_timer_t *timer); +void timerWrite(hw_timer_t *timer, uint64_t val); +void timerSetDivider(hw_timer_t *timer, uint16_t divider); +void timerSetCountUp(hw_timer_t *timer, bool countUp); +void timerSetAutoReload(hw_timer_t *timer, bool autoreload); + +bool timerStarted(hw_timer_t *timer); +uint64_t timerRead(hw_timer_t *timer); +uint64_t timerReadMicros(hw_timer_t *timer); +uint64_t timerReadMilis(hw_timer_t *timer); +double timerReadSeconds(hw_timer_t *timer); +uint16_t timerGetDivider(hw_timer_t *timer); +bool timerGetCountUp(hw_timer_t *timer); +bool timerGetAutoReload(hw_timer_t *timer); + +void timerAlarmEnable(hw_timer_t *timer); +void timerAlarmDisable(hw_timer_t *timer); +void timerAlarmWrite(hw_timer_t *timer, uint64_t alarm_value, bool autoreload); + +bool timerAlarmEnabled(hw_timer_t *timer); +uint64_t timerAlarmRead(hw_timer_t *timer); +uint64_t timerAlarmReadMicros(hw_timer_t *timer); +double timerAlarmReadSeconds(hw_timer_t *timer); + + +#ifdef __cplusplus +} +#endif + +#endif /* MAIN_ESP32_HAL_TIMER_H_ */ diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-tinyusb.c b/src/esp32/hardware/cores/esp32/esp32-hal-tinyusb.c new file mode 100644 index 0000000..319a89e --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-tinyusb.c @@ -0,0 +1,783 @@ + +#include "sdkconfig.h" +#if CONFIG_TINYUSB_ENABLED +#include +#include + +#include "esp_log.h" + +#include "soc/soc.h" +#include "soc/efuse_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/usb_struct.h" +#include "soc/usb_reg.h" +#include "soc/usb_wrap_reg.h" +#include "soc/usb_wrap_struct.h" +#include "soc/usb_periph.h" +#include "soc/periph_defs.h" +#include "soc/timer_group_struct.h" +#include "soc/system_reg.h" + +#include "hal/usb_hal.h" +#include "hal/gpio_ll.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "driver/gpio.h" +#include "driver/periph_ctrl.h" + +#include "esp_efuse.h" +#include "esp_efuse_table.h" +#include "esp_rom_gpio.h" + +#include "esp32-hal.h" + +#include "esp32-hal-tinyusb.h" +#if CONFIG_IDF_TARGET_ESP32S2 +#include "esp32s2/rom/usb/usb_persist.h" +#include "esp32s2/rom/usb/usb_dc.h" +#include "esp32s2/rom/usb/chip_usb_dw_wrapper.h" +#elif CONFIG_IDF_TARGET_ESP32S3 +#include "hal/usb_serial_jtag_ll.h" +#include "hal/usb_phy_ll.h" +#include "esp32s3/rom/usb/usb_persist.h" +#include "esp32s3/rom/usb/usb_dc.h" +#include "esp32s3/rom/usb/chip_usb_dw_wrapper.h" +#endif + +typedef enum{ + TINYUSB_USBDEV_0, +} tinyusb_usbdev_t; + +typedef char *tusb_desc_strarray_device_t[USB_STRING_DESCRIPTOR_ARRAY_SIZE]; + +typedef struct { + bool external_phy; +} tinyusb_config_t; + +static void configure_pins(usb_hal_context_t *usb) +{ + for (const usb_iopin_dsc_t *iopin = usb_periph_iopins; iopin->pin != -1; ++iopin) { + if ((usb->use_external_phy) || (iopin->ext_phy_only == 0)) { + esp_rom_gpio_pad_select_gpio(iopin->pin); + if (iopin->is_output) { + esp_rom_gpio_connect_out_signal(iopin->pin, iopin->func, false, false); + } else { + esp_rom_gpio_connect_in_signal(iopin->pin, iopin->func, false); + if ((iopin->pin != GPIO_FUNC_IN_LOW) && (iopin->pin != GPIO_FUNC_IN_HIGH)) { + gpio_ll_input_enable(&GPIO, iopin->pin); + } + } + esp_rom_gpio_pad_unhold(iopin->pin); + } + } + if (!usb->use_external_phy) { + gpio_set_drive_capability(USBPHY_DM_NUM, GPIO_DRIVE_CAP_3); + gpio_set_drive_capability(USBPHY_DP_NUM, GPIO_DRIVE_CAP_3); + } +} + +esp_err_t tinyusb_driver_install(const tinyusb_config_t *config) +{ + usb_hal_context_t hal = { + .use_external_phy = config->external_phy + }; + usb_hal_init(&hal); + configure_pins(&hal); + if (!tusb_init()) { + log_e("Can't initialize the TinyUSB stack."); + return ESP_FAIL; + } + return ESP_OK; +} + + + + + + + + + + +typedef char tusb_str_t[127]; + +static bool WEBUSB_ENABLED = false; + +static tusb_str_t WEBUSB_URL = ""; +static tusb_str_t USB_DEVICE_PRODUCT = ""; +static tusb_str_t USB_DEVICE_MANUFACTURER = ""; +static tusb_str_t USB_DEVICE_SERIAL = ""; +static tusb_str_t USB_DEVICE_LANGUAGE = "\x09\x04";//English (0x0409) + +static uint8_t USB_DEVICE_ATTRIBUTES = 0; +static uint16_t USB_DEVICE_POWER = 0; + +/* + * Device Descriptor + * */ +static tusb_desc_device_t tinyusb_device_descriptor = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0, + .bDeviceClass = 0, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = CFG_TUD_ENDOINT0_SIZE, + + .idVendor = 0, + .idProduct = 0, + .bcdDevice = 0, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01 +}; + +/* + * String Descriptors + * */ +#define MAX_STRING_DESCRIPTORS 20 +static uint32_t tinyusb_string_descriptor_len = 4; +static char * tinyusb_string_descriptor[MAX_STRING_DESCRIPTORS] = { + // array of pointer to string descriptors + USB_DEVICE_LANGUAGE, // 0: is supported language + USB_DEVICE_MANUFACTURER,// 1: Manufacturer + USB_DEVICE_PRODUCT, // 2: Product + USB_DEVICE_SERIAL, // 3: Serials, should use chip ID +}; + + +/* Microsoft OS 2.0 registry property descriptor +Per MS requirements https://msdn.microsoft.com/en-us/library/windows/hardware/hh450799(v=vs.85).aspx +device should create DeviceInterfaceGUIDs. It can be done by driver and +in case of real PnP solution device should expose MS "Microsoft OS 2.0 +registry property descriptor". Such descriptor can insert any record +into Windows registry per device/configuration/interface. In our case it +will insert "DeviceInterfaceGUIDs" multistring property. + +GUID is freshly generated and should be OK to use. + +https://developers.google.com/web/fundamentals/native-hardware/build-for-webusb/ +(Section Microsoft OS compatibility descriptors) + */ + +#define MS_OS_20_DESC_LEN 0xB2 + +static uint8_t const tinyusb_ms_os_20_descriptor[] = +{ + // Set header: length, type, windows version, total length + U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN), + + // Configuration subset header: length, type, configuration index, reserved, configuration total length + U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_CONFIGURATION), 0, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A), + + // Function Subset header: length, type, first interface, reserved, subset length + U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), 0, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08), + + // MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID + U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sub-compatible + + // MS OS 2.0 Registry property descriptor: length, type + U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08-0x08-0x14), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY), + U16_TO_U8S_LE(0x0007), U16_TO_U8S_LE(0x002A), // wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16 + 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00, + 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00, + U16_TO_U8S_LE(0x0050), // wPropertyDataLength + //bPropertyData: “{975F44D9-0D08-43FD-8B3E-127CA8AFFF9D}”. + '{', 0x00, '9', 0x00, '7', 0x00, '5', 0x00, 'F', 0x00, '4', 0x00, '4', 0x00, 'D', 0x00, '9', 0x00, '-', 0x00, + '0', 0x00, 'D', 0x00, '0', 0x00, '8', 0x00, '-', 0x00, '4', 0x00, '3', 0x00, 'F', 0x00, 'D', 0x00, '-', 0x00, + '8', 0x00, 'B', 0x00, '3', 0x00, 'E', 0x00, '-', 0x00, '1', 0x00, '2', 0x00, '7', 0x00, 'C', 0x00, 'A', 0x00, + '8', 0x00, 'A', 0x00, 'F', 0x00, 'F', 0x00, 'F', 0x00, '9', 0x00, 'D', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +TU_VERIFY_STATIC(sizeof(tinyusb_ms_os_20_descriptor) == MS_OS_20_DESC_LEN, "Incorrect size"); + + +/* + * BOS Descriptor (required for webUSB) + * */ +#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_WEBUSB_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN) + +enum { + VENDOR_REQUEST_WEBUSB = 1, + VENDOR_REQUEST_MICROSOFT = 2 +}; + +static uint8_t const tinyusb_bos_descriptor[] = { + // total length, number of device caps + TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 2), + + // Vendor Code, iLandingPage + TUD_BOS_WEBUSB_DESCRIPTOR(VENDOR_REQUEST_WEBUSB, 1), + + // Microsoft OS 2.0 descriptor + TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, VENDOR_REQUEST_MICROSOFT) +}; + +/* + * URL Descriptor (required for webUSB) + * */ +typedef struct TU_ATTR_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bScheme; + char url[127]; +} tinyusb_desc_webusb_url_t; + +static tinyusb_desc_webusb_url_t tinyusb_url_descriptor = { + .bLength = 3, + .bDescriptorType = 3, // WEBUSB URL type + .bScheme = 255, // URL Scheme Prefix: 0: "http://", 1: "https://", 255: "" + .url = "" +}; + +/* + * Configuration Descriptor + * */ + +static tinyusb_descriptor_cb_t tinyusb_loaded_interfaces_callbacks[USB_INTERFACE_MAX]; +static uint32_t tinyusb_loaded_interfaces_mask = 0; +static uint8_t tinyusb_loaded_interfaces_num = 0; +static uint16_t tinyusb_config_descriptor_len = 0; +static uint8_t * tinyusb_config_descriptor = NULL; + +/* + * Endpoint Usage Tracking + * */ +typedef union { + struct { + uint32_t in:16; + uint32_t out:16; + }; + uint32_t val; +} tinyusb_endpoints_usage_t; + +static tinyusb_endpoints_usage_t tinyusb_endpoints; + + +/* + * TinyUSB Callbacks + * */ + +/** + * @brief Invoked when received GET CONFIGURATION DESCRIPTOR. + */ +__attribute__ ((weak)) uint8_t const *tud_descriptor_configuration_cb(uint8_t index) +{ + //log_d("%u", index); + return tinyusb_config_descriptor; +} + +/** + * @brief Invoked when received GET DEVICE DESCRIPTOR. + */ +__attribute__ ((weak)) uint8_t const *tud_descriptor_device_cb(void) +{ + //log_d(""); + return (uint8_t const *)&tinyusb_device_descriptor; +} + +/** + * @brief Invoked when received GET STRING DESCRIPTOR request. + */ +__attribute__ ((weak)) uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) +{ + //log_d("%u (0x%x)", index, langid); + static uint16_t _desc_str[127]; + uint8_t chr_count; + + if (index == 0) { + memcpy(&_desc_str[1], tinyusb_string_descriptor[0], 2); + chr_count = 1; + } else { + // Convert ASCII string into UTF-16 + if (index >= tinyusb_string_descriptor_len) { + return NULL; + } + const char *str = tinyusb_string_descriptor[index]; + // Cap at max char + chr_count = strlen(str); + if (chr_count > 126) { + chr_count = 126; + } + for (uint8_t i = 0; i < chr_count; i++) { + _desc_str[1 + i] = str[i]; + } + } + + // first byte is len, second byte is string type + _desc_str[0] = (TUSB_DESC_STRING << 8 ) | (2*chr_count + 2); + + return _desc_str; +} + +/** + * @brief Invoked when received GET BOS DESCRIPTOR request. + */ +uint8_t const * tud_descriptor_bos_cb(void) +{ + //log_v(""); + return tinyusb_bos_descriptor; +} + +__attribute__ ((weak)) bool tinyusb_vendor_control_request_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request){ return false; } + +/** + * @brief Handle WebUSB and Vendor requests. + */ +bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) +{ + if(WEBUSB_ENABLED && (request->bRequest == VENDOR_REQUEST_WEBUSB + || (request->bRequest == VENDOR_REQUEST_MICROSOFT && request->wIndex == 7))){ + // we only care for SETUP stage + if (stage == CONTROL_STAGE_SETUP) { + if(request->bRequest == VENDOR_REQUEST_WEBUSB){ + // match vendor request in BOS descriptor + // Get landing page url + tinyusb_url_descriptor.bLength = 3 + strlen(WEBUSB_URL); + snprintf(tinyusb_url_descriptor.url, 127, "%s", WEBUSB_URL); + return tud_control_xfer(rhport, request, (void*) &tinyusb_url_descriptor, tinyusb_url_descriptor.bLength); + } + // Get Microsoft OS 2.0 compatible descriptor + uint16_t total_len; + memcpy(&total_len, tinyusb_ms_os_20_descriptor + 8, 2); + return tud_control_xfer(rhport, request, (void*) tinyusb_ms_os_20_descriptor, total_len); + } + return true; + } + log_v("rhport: %u, stage: %u, type: 0x%x, request: 0x%x", rhport, stage, request->bmRequestType_bit.type, request->bRequest); + return tinyusb_vendor_control_request_cb(rhport, stage, request); +} + +/* + * Required Callbacks + * */ +#if CFG_TUD_DFU +__attribute__ ((weak)) uint32_t tud_dfu_get_timeout_cb(uint8_t alt, uint8_t state){return 0;} +__attribute__ ((weak)) void tud_dfu_download_cb (uint8_t alt, uint16_t block_num, uint8_t const *data, uint16_t length){} +__attribute__ ((weak)) void tud_dfu_manifest_cb(uint8_t alt){} +#endif +#if CFG_TUD_HID +__attribute__ ((weak)) const uint8_t * tud_hid_descriptor_report_cb(uint8_t itf){return NULL;} +__attribute__ ((weak)) uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen){return 0;} +__attribute__ ((weak)) void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, const uint8_t * buffer, uint16_t bufsize){} +#endif +#if CFG_TUD_MSC +__attribute__ ((weak)) bool tud_msc_test_unit_ready_cb(uint8_t lun){return false;} +__attribute__ ((weak)) void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]){} +__attribute__ ((weak)) void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size){} +__attribute__ ((weak)) int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize){return -1;} +__attribute__ ((weak)) int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize){return -1;} +__attribute__ ((weak)) int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize){return -1;} +#endif + + +/* + * Private API + * */ +static bool usb_persist_enabled = false; +static restart_type_t usb_persist_mode = RESTART_NO_PERSIST; + +#if CONFIG_IDF_TARGET_ESP32S3 + +static void hw_cdc_reset_handler(void *arg) { + portBASE_TYPE xTaskWoken = 0; + uint32_t usbjtag_intr_status = usb_serial_jtag_ll_get_intsts_mask(); + usb_serial_jtag_ll_clr_intsts_mask(usbjtag_intr_status); + + if (usbjtag_intr_status & USB_SERIAL_JTAG_INTR_BUS_RESET) { + xSemaphoreGiveFromISR((xSemaphoreHandle)arg, &xTaskWoken); + } + + if (xTaskWoken == pdTRUE) { + portYIELD_FROM_ISR(); + } +} + +static void usb_switch_to_cdc_jtag(){ + // Disable USB-OTG + periph_module_reset(PERIPH_USB_MODULE); + //periph_module_enable(PERIPH_USB_MODULE); + periph_module_disable(PERIPH_USB_MODULE); + + // Switch to hardware CDC+JTAG + CLEAR_PERI_REG_MASK(RTC_CNTL_USB_CONF_REG, (RTC_CNTL_SW_HW_USB_PHY_SEL|RTC_CNTL_SW_USB_PHY_SEL|RTC_CNTL_USB_PAD_ENABLE)); + + // Do not use external PHY + CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_PHY_SEL); + + // Release GPIO pins from CDC+JTAG + CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_USB_PAD_ENABLE); + + // Force the host to re-enumerate (BUS_RESET) + pinMode(USBPHY_DM_NUM, OUTPUT_OPEN_DRAIN); + pinMode(USBPHY_DP_NUM, OUTPUT_OPEN_DRAIN); + digitalWrite(USBPHY_DM_NUM, LOW); + digitalWrite(USBPHY_DP_NUM, LOW); + + // Initialize CDC+JTAG ISR to listen for BUS_RESET + usb_phy_ll_int_jtag_enable(&USB_SERIAL_JTAG); + usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_LL_INTR_MASK); + usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_LL_INTR_MASK); + usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_BUS_RESET); + intr_handle_t intr_handle = NULL; + xSemaphoreHandle reset_sem = xSemaphoreCreateBinary(); + if(reset_sem){ + if(esp_intr_alloc(ETS_USB_SERIAL_JTAG_INTR_SOURCE, 0, hw_cdc_reset_handler, reset_sem, &intr_handle) != ESP_OK){ + vSemaphoreDelete(reset_sem); + reset_sem = NULL; + log_e("HW USB CDC failed to init interrupts"); + } + } else { + log_e("reset_sem init failed"); + } + + // Connect GPIOs to integrated CDC+JTAG + SET_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_USB_PAD_ENABLE); + + // Wait for BUS_RESET to give us back the semaphore + if(reset_sem){ + if(xSemaphoreTake(reset_sem, 1000 / portTICK_PERIOD_MS) != pdPASS){ + log_e("reset_sem timeout"); + } + usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_LL_INTR_MASK); + esp_intr_free(intr_handle); + vSemaphoreDelete(reset_sem); + } +} +#endif + +static void IRAM_ATTR usb_persist_shutdown_handler(void) +{ + if(usb_persist_mode != RESTART_NO_PERSIST){ + if (usb_persist_enabled) { + usb_dc_prepare_persist(); + } + if (usb_persist_mode == RESTART_BOOTLOADER) { + //USB CDC Download + if (usb_persist_enabled) { + chip_usb_set_persist_flags(USBDC_PERSIST_ENA); +#if CONFIG_IDF_TARGET_ESP32S2 + } else { + periph_module_reset(PERIPH_USB_MODULE); + periph_module_enable(PERIPH_USB_MODULE); +#endif + } + REG_WRITE(RTC_CNTL_OPTION1_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT); + } else if (usb_persist_mode == RESTART_BOOTLOADER_DFU) { + //DFU Download +#if CONFIG_IDF_TARGET_ESP32S2 + // Reset USB Core + USB0.grstctl |= USB_CSFTRST; + while ((USB0.grstctl & USB_CSFTRST) == USB_CSFTRST){} +#endif + chip_usb_set_persist_flags(USBDC_BOOT_DFU); + REG_WRITE(RTC_CNTL_OPTION1_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT); + } else if (usb_persist_enabled) { + //USB Persist reboot + chip_usb_set_persist_flags(USBDC_PERSIST_ENA); + } + } +} + +void usb_persist_restart(restart_type_t mode) +{ + if (mode < RESTART_TYPE_MAX && esp_register_shutdown_handler(usb_persist_shutdown_handler) == ESP_OK) { + usb_persist_mode = mode; +#if CONFIG_IDF_TARGET_ESP32S3 + if (mode == RESTART_BOOTLOADER) { + usb_switch_to_cdc_jtag(); + } +#endif + esp_restart(); + } +} + +static bool tinyusb_reserve_in_endpoint(uint8_t endpoint){ + if(endpoint > 6 || (tinyusb_endpoints.in & BIT(endpoint)) != 0){ + return false; + } + tinyusb_endpoints.in |= BIT(endpoint); + return true; +} + +static bool tinyusb_reserve_out_endpoint(uint8_t endpoint){ + if(endpoint > 6 || (tinyusb_endpoints.out & BIT(endpoint)) != 0){ + return false; + } + tinyusb_endpoints.out |= BIT(endpoint); + return true; +} + +static bool tinyusb_has_available_fifos(void){ + uint8_t max_endpoints = 4, active_endpoints = 0; + if (tinyusb_loaded_interfaces_mask & BIT(USB_INTERFACE_CDC)) { + max_endpoints = 5; //CDC endpoint 0x85 is actually not linked to FIFO and not used + } + for(uint8_t i=1; i<7; i++){ + if((tinyusb_endpoints.in & BIT(i)) != 0){ + active_endpoints++; + } + } + + return active_endpoints < max_endpoints; +} + +static uint16_t tinyusb_load_descriptor(tinyusb_interface_t interface, uint8_t * dst, uint8_t * itf) +{ + if(tinyusb_loaded_interfaces_callbacks[interface]){ + return tinyusb_loaded_interfaces_callbacks[interface](dst, itf); + } + return 0; +} + +static bool tinyusb_load_enabled_interfaces(){ + tinyusb_config_descriptor_len += TUD_CONFIG_DESC_LEN; + tinyusb_config_descriptor = (uint8_t *)malloc(tinyusb_config_descriptor_len); + if (tinyusb_config_descriptor == NULL) { + log_e("Descriptor Malloc Failed"); + return false; + } + uint8_t * dst = tinyusb_config_descriptor + TUD_CONFIG_DESC_LEN; + + for(int i=0; i> 4); + *srl++ = nibble_to_hex_char(b & 0xf); + } + *srl++ = '\0'; +} + +static void tinyusb_apply_device_config(tinyusb_device_config_t *config){ + if(config->product_name){ + snprintf(USB_DEVICE_PRODUCT, 126, "%s", config->product_name); + } + + if(config->manufacturer_name){ + snprintf(USB_DEVICE_MANUFACTURER, 126, "%s", config->manufacturer_name); + } + + if(config->serial_number && config->serial_number[0]){ + snprintf(USB_DEVICE_SERIAL, 126, "%s", config->serial_number); + } else { + set_usb_serial_num(); + } + + if(config->webusb_url){ + snprintf(WEBUSB_URL, 126, "%s", config->webusb_url); + } + + // Windows 10 will not recognize the CDC device if WebUSB is enabled and USB Class is not 2 (CDC) + if( + (tinyusb_loaded_interfaces_mask & BIT(USB_INTERFACE_CDC)) + && config->webusb_enabled + && (config->usb_class != TUSB_CLASS_CDC) + ){ + config->usb_class = TUSB_CLASS_CDC; + config->usb_protocol = 0x00; + } + + WEBUSB_ENABLED = config->webusb_enabled; + USB_DEVICE_ATTRIBUTES = config->usb_attributes; + USB_DEVICE_POWER = config->usb_power_ma; + + tinyusb_device_descriptor.bcdUSB = config->usb_version; + tinyusb_device_descriptor.idVendor = config->vid; + tinyusb_device_descriptor.idProduct = config->pid; + tinyusb_device_descriptor.bcdDevice = config->fw_version; + tinyusb_device_descriptor.bDeviceClass = config->usb_class; + tinyusb_device_descriptor.bDeviceSubClass = config->usb_subclass; + tinyusb_device_descriptor.bDeviceProtocol = config->usb_protocol; +} + +// USB Device Driver task +// This top level thread processes all usb events and invokes callbacks +static void usb_device_task(void *param) { + (void)param; + while(1) tud_task(); // RTOS forever loop +} + +/* + * PUBLIC API + * */ +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_ERROR + const char *tinyusb_interface_names[USB_INTERFACE_MAX] = {"MSC", "DFU", "HID", "VENDOR", "CDC", "MIDI", "CUSTOM"}; +#endif +static bool tinyusb_is_initialized = false; + +esp_err_t tinyusb_enable_interface(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb) +{ + if(tinyusb_is_initialized){ + log_e("TinyUSB has already started! Interface %s not enabled", (interface >= USB_INTERFACE_MAX)?"":tinyusb_interface_names[interface]); + return ESP_FAIL; + } + if((interface >= USB_INTERFACE_MAX) || (tinyusb_loaded_interfaces_mask & (1U << interface))){ + log_e("Interface %s invalid or already enabled", (interface >= USB_INTERFACE_MAX)?"":tinyusb_interface_names[interface]); + return ESP_FAIL; + } + if(interface == USB_INTERFACE_CDC){ + if(!tinyusb_reserve_out_endpoint(3) ||!tinyusb_reserve_in_endpoint(4) || !tinyusb_reserve_in_endpoint(5)){ + log_e("CDC Reserve Endpoints Failed"); + return ESP_FAIL; + } + } + tinyusb_loaded_interfaces_mask |= (1U << interface); + tinyusb_config_descriptor_len += descriptor_len; + tinyusb_loaded_interfaces_callbacks[interface] = cb; + log_d("Interface %s enabled", tinyusb_interface_names[interface]); + return ESP_OK; +} + +esp_err_t tinyusb_init(tinyusb_device_config_t *config) { + if(tinyusb_is_initialized){ + return ESP_OK; + } + tinyusb_is_initialized = true; + + //tinyusb_endpoints.val = 0; + tinyusb_apply_device_config(config); + if (!tinyusb_load_enabled_interfaces()) { + tinyusb_is_initialized = false; + return ESP_FAIL; + } + + bool usb_did_persist = (USB_WRAP.date.val == USBDC_PERSIST_ENA); + + //if(usb_did_persist && usb_persist_enabled){ + // Enable USB/IO_MUX peripheral reset, if coming from persistent reboot + REG_CLR_BIT(RTC_CNTL_USB_CONF_REG, RTC_CNTL_IO_MUX_RESET_DISABLE); + REG_CLR_BIT(RTC_CNTL_USB_CONF_REG, RTC_CNTL_USB_RESET_DISABLE); + //} else + if(!usb_did_persist || !usb_persist_enabled){ + // Reset USB module + periph_module_reset(PERIPH_USB_MODULE); + periph_module_enable(PERIPH_USB_MODULE); + } + + tinyusb_config_t tusb_cfg = { + .external_phy = false // In the most cases you need to use a `false` value + }; + esp_err_t err = tinyusb_driver_install(&tusb_cfg); + if (err != ESP_OK) { + tinyusb_is_initialized = false; + return err; + } + xTaskCreate(usb_device_task, "usbd", 4096, NULL, configMAX_PRIORITIES - 1, NULL); + return err; +} + +uint8_t tinyusb_add_string_descriptor(const char * str){ + if(str == NULL || tinyusb_string_descriptor_len >= MAX_STRING_DESCRIPTORS){ + return 0; + } + uint8_t index = tinyusb_string_descriptor_len; + tinyusb_string_descriptor[tinyusb_string_descriptor_len++] = (char*)str; + return index; +} + +uint8_t tinyusb_get_free_duplex_endpoint(void){ + if(!tinyusb_has_available_fifos()){ + log_e("No available IN endpoints"); + return 0; + } + for(uint8_t i=1; i<7; i++){ + if((tinyusb_endpoints.in & BIT(i)) == 0 && (tinyusb_endpoints.out & BIT(i)) == 0){ + tinyusb_endpoints.in |= BIT(i); + tinyusb_endpoints.out |= BIT(i); + return i; + } + } + log_e("No available duplex endpoints"); + return 0; +} + +uint8_t tinyusb_get_free_in_endpoint(void){ + if(!tinyusb_has_available_fifos()){ + log_e("No available IN endpoints"); + return 0; + } + for(uint8_t i=1; i<7; i++){ + if((tinyusb_endpoints.in & BIT(i)) == 0 && (tinyusb_endpoints.out & BIT(i)) != 0){ + tinyusb_endpoints.in |= BIT(i); + return i; + } + } + for(uint8_t i=1; i<7; i++){ + if((tinyusb_endpoints.in & BIT(i)) == 0){ + tinyusb_endpoints.in |= BIT(i); + return i; + } + } + return 0; +} + +uint8_t tinyusb_get_free_out_endpoint(void){ + for(uint8_t i=1; i<7; i++){ + if((tinyusb_endpoints.out & BIT(i)) == 0 && (tinyusb_endpoints.in & BIT(i)) != 0){ + tinyusb_endpoints.out |= BIT(i); + return i; + } + } + for(uint8_t i=1; i<7; i++){ + if((tinyusb_endpoints.out & BIT(i)) == 0){ + tinyusb_endpoints.out |= BIT(i); + return i; + } + } + return 0; +} + +#endif /* CONFIG_TINYUSB_ENABLED */ diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-tinyusb.h b/src/esp32/hardware/cores/esp32/esp32-hal-tinyusb.h new file mode 100644 index 0000000..84e156e --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-tinyusb.h @@ -0,0 +1,106 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include "esp32-hal.h" + +#if CONFIG_TINYUSB_ENABLED + +#ifdef __cplusplus +extern "C" { +#endif + +#include "tusb.h" +#include "tusb_option.h" +#include "tusb_config.h" + +#define USB_ESPRESSIF_VID 0x303A +#define USB_STRING_DESCRIPTOR_ARRAY_SIZE 10 + +typedef struct { + uint16_t vid; + uint16_t pid; + const char * product_name; + const char * manufacturer_name; + const char * serial_number; + uint16_t fw_version; + + uint16_t usb_version; + uint8_t usb_class; + uint8_t usb_subclass; + uint8_t usb_protocol; + uint8_t usb_attributes; + uint16_t usb_power_ma; + + bool webusb_enabled; + const char * webusb_url; +} tinyusb_device_config_t; + +#define TINYUSB_CONFIG_DEFAULT() { \ + .vid = USB_ESPRESSIF_VID, \ + .pid = 0x0002, \ + .product_name = CONFIG_TINYUSB_DESC_PRODUCT_STRING, \ + .manufacturer_name = CONFIG_TINYUSB_DESC_MANUFACTURER_STRING, \ + .serial_number = CONFIG_TINYUSB_DESC_SERIAL_STRING, \ + .fw_version = CONFIG_TINYUSB_DESC_BCDDEVICE, \ + .usb_version = 0x0200, \ + .usb_class = TUSB_CLASS_MISC, \ + .usb_subclass = MISC_SUBCLASS_COMMON, \ + .usb_protocol = MISC_PROTOCOL_IAD, \ + .usb_attributes = TUSB_DESC_CONFIG_ATT_SELF_POWERED, \ + .usb_power_ma = 500, \ + .webusb_enabled = false, \ + .webusb_url = "espressif.github.io/arduino-esp32/webusb.html" \ +} + +esp_err_t tinyusb_init(tinyusb_device_config_t *config); + +/* + * USB Persistence API + * */ +typedef enum { + RESTART_NO_PERSIST, + RESTART_PERSIST, + RESTART_BOOTLOADER, + RESTART_BOOTLOADER_DFU, + RESTART_TYPE_MAX +} restart_type_t; + +void usb_persist_restart(restart_type_t mode); + +// The following definitions and functions are to be used only by the drivers +typedef enum { + USB_INTERFACE_MSC, + USB_INTERFACE_DFU, + USB_INTERFACE_HID, + USB_INTERFACE_VENDOR, + USB_INTERFACE_CDC, + USB_INTERFACE_MIDI, + USB_INTERFACE_CUSTOM, + USB_INTERFACE_MAX +} tinyusb_interface_t; + +typedef uint16_t (*tinyusb_descriptor_cb_t)(uint8_t * dst, uint8_t * itf); + +esp_err_t tinyusb_enable_interface(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb); +uint8_t tinyusb_add_string_descriptor(const char * str); +uint8_t tinyusb_get_free_duplex_endpoint(void); +uint8_t tinyusb_get_free_in_endpoint(void); +uint8_t tinyusb_get_free_out_endpoint(void); + +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_TINYUSB_ENABLED */ diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-touch.c b/src/esp32/hardware/cores/esp32/esp32-hal-touch.c new file mode 100644 index 0000000..476df60 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-touch.c @@ -0,0 +1,296 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "soc/soc_caps.h" +#if SOC_TOUCH_SENSOR_NUM > 0 + +#include "driver/touch_sensor.h" +#include "esp32-hal-touch.h" + +/* + Internal Private Touch Data Structure and Functions +*/ + +#if SOC_TOUCH_VERSION_1 // ESP32 +static uint16_t __touchSleepCycles = 0x1000; +static uint16_t __touchMeasureCycles = 0x1000; +#elif SOC_TOUCH_VERSION_2 // ESP32S2, ESP32S3 +static uint16_t __touchSleepCycles = TOUCH_PAD_SLEEP_CYCLE_DEFAULT; +static uint16_t __touchMeasureCycles = TOUCH_PAD_MEASURE_CYCLE_DEFAULT; +#endif + +typedef void (*voidFuncPtr)(void); +typedef void (*voidArgFuncPtr)(void *); + +typedef struct { + voidFuncPtr fn; + bool callWithArgs; + void* arg; +#if SOC_TOUCH_VERSION_2 // Only for ESP32S2 and ESP32S3 + bool lastStatusIsPressed; +#endif +} TouchInterruptHandle_t; + +static TouchInterruptHandle_t __touchInterruptHandlers[SOC_TOUCH_SENSOR_NUM] = {0,}; + +static void ARDUINO_ISR_ATTR __touchISR(void * arg) +{ +#if SOC_TOUCH_VERSION_1 // ESP32 + uint32_t pad_intr = touch_pad_get_status(); + //clear interrupt + touch_pad_clear_status(); + // call Pad ISR User callback + for (int i = 0; i < SOC_TOUCH_SENSOR_NUM; i++) { + if ((pad_intr >> i) & 0x01) { + if(__touchInterruptHandlers[i].fn){ + // keeping backward compatibility with "void cb(void)" and with new "void cb(vooid *)" + if (__touchInterruptHandlers[i].callWithArgs) { + ((voidArgFuncPtr)__touchInterruptHandlers[i].fn)(__touchInterruptHandlers[i].arg); + } else { + __touchInterruptHandlers[i].fn(); + } + } + } + } +#elif SOC_TOUCH_VERSION_2 // ESP32S2, ESP32S3 + touch_pad_intr_mask_t evt = touch_pad_read_intr_status_mask(); + uint8_t pad_num = touch_pad_get_current_meas_channel(); + if (evt & TOUCH_PAD_INTR_MASK_ACTIVE) { + // touch has been pressed / touched + __touchInterruptHandlers[pad_num].lastStatusIsPressed = true; + } + if (evt & TOUCH_PAD_INTR_MASK_INACTIVE) { + // touch has been released / untouched + __touchInterruptHandlers[pad_num].lastStatusIsPressed = false; + } + if(__touchInterruptHandlers[pad_num].fn){ + // keeping backward compatibility with "void cb(void)" and with new "void cb(vooid *)" + if (__touchInterruptHandlers[pad_num].callWithArgs) { + ((voidArgFuncPtr)__touchInterruptHandlers[pad_num].fn)(__touchInterruptHandlers[pad_num].arg); + } else { + __touchInterruptHandlers[pad_num].fn(); + } + } +#endif +} + + + +static void __touchSetCycles(uint16_t measure, uint16_t sleep) +{ + __touchSleepCycles = sleep; + __touchMeasureCycles = measure; + touch_pad_set_meas_time(sleep, measure); +} + + + +static void __touchInit() +{ + static bool initialized = false; + if(initialized){ + return; + } + + esp_err_t err = ESP_OK; + +#if SOC_TOUCH_VERSION_1 // ESP32 + err = touch_pad_init(); + if (err != ESP_OK) { + goto err; + } + // the next two lines will drive the touch reading values -- both will return ESP_OK + touch_pad_set_voltage(TOUCH_HVOLT_2V7, TOUCH_LVOLT_0V5, TOUCH_HVOLT_ATTEN_0V); + touch_pad_set_meas_time(__touchMeasureCycles, __touchSleepCycles); + // Touch Sensor Timer initiated + touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER); // returns ESP_OK + err = touch_pad_filter_start(10); + if (err != ESP_OK) { + goto err; + } + // keep ISR activated - it can run all together (ISR + touchRead()) + err = touch_pad_isr_register(__touchISR, NULL); + if (err != ESP_OK) { + goto err; + } + touch_pad_intr_enable(); // returns ESP_OK +#elif SOC_TOUCH_VERSION_2 // ESP32S2, ESP32S3 + err = touch_pad_init(); + if (err != ESP_OK) { + goto err; + } + // the next lines will drive the touch reading values -- all os them return ESP_OK + touch_pad_set_meas_time(__touchSleepCycles, __touchMeasureCycles); + touch_pad_set_voltage(TOUCH_PAD_HIGH_VOLTAGE_THRESHOLD, TOUCH_PAD_LOW_VOLTAGE_THRESHOLD, TOUCH_PAD_ATTEN_VOLTAGE_THRESHOLD); + touch_pad_set_idle_channel_connect(TOUCH_PAD_IDLE_CH_CONNECT_DEFAULT); + touch_pad_denoise_t denoise = { + .grade = TOUCH_PAD_DENOISE_BIT4, + .cap_level = TOUCH_PAD_DENOISE_CAP_L4, + }; + touch_pad_denoise_set_config(&denoise); + touch_pad_denoise_enable(); + // Touch Sensor Timer initiated + touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER); // returns ESP_OK + touch_pad_fsm_start(); // returns ESP_OK + //ISR setup moved to __touchChannelInit +#endif + + initialized = true; + return; +err: + log_e(" Touch sensor initialization error."); + initialized = false; + return; +} + +static void __touchChannelInit(int pad) +{ + static bool channels_initialized[SOC_TOUCH_SENSOR_NUM] = { false }; + if(channels_initialized[pad]){ + return; + } + +#if SOC_TOUCH_VERSION_1 // ESP32 + // Initial no Threshold and setup + __touchInterruptHandlers[pad].fn = NULL; + touch_pad_config(pad, SOC_TOUCH_PAD_THRESHOLD_MAX); // returns ESP_OK +#elif SOC_TOUCH_VERSION_2 // ESP32S2, ESP32S3 + // Initial no Threshold and setup + __touchInterruptHandlers[pad].fn = NULL; + touch_pad_config(pad); // returns ESP_OK + // keep ISR activated - it can run all together (ISR + touchRead()) + esp_err_t err = touch_pad_isr_register(__touchISR, NULL, TOUCH_PAD_INTR_MASK_ACTIVE | TOUCH_PAD_INTR_MASK_INACTIVE); + if (err != ESP_OK) { + log_e(" Touch sensor initialization error."); + return; + } + touch_pad_intr_enable(TOUCH_PAD_INTR_MASK_ACTIVE | TOUCH_PAD_INTR_MASK_INACTIVE); // returns ESP_OK +#endif + + channels_initialized[pad] = true; + delay(20); //delay needed before reading from touch channel after config +} + +static touch_value_t __touchRead(uint8_t pin) +{ + int8_t pad = digitalPinToTouchChannel(pin); + if(pad < 0){ + log_e(" No touch pad on selected pin!"); + return 0; + } + + __touchInit(); + __touchChannelInit(pad); + + touch_value_t touch_value; + touch_pad_read_raw_data(pad, &touch_value); + + return touch_value; +} + +static void __touchConfigInterrupt(uint8_t pin, void (*userFunc)(void), void *Args, touch_value_t threshold, bool callWithArgs) +{ + int8_t pad = digitalPinToTouchChannel(pin); + if(pad < 0){ + log_e(" No touch pad on selected pin!"); + return; + } + + if (userFunc == NULL) { + // dettach ISR User Call + __touchInterruptHandlers[pad].fn = NULL; + threshold = SOC_TOUCH_PAD_THRESHOLD_MAX; // deactivate the ISR with SOC_TOUCH_PAD_THRESHOLD_MAX + } else { + // attach ISR User Call + __touchInit(); + __touchChannelInit(pad); + __touchInterruptHandlers[pad].fn = userFunc; + __touchInterruptHandlers[pad].callWithArgs = callWithArgs; + __touchInterruptHandlers[pad].arg = Args; + } + + touch_pad_set_thresh(pad, threshold); +} + +// it keeps backwards compatibility +static void __touchAttachInterrupt(uint8_t pin, void (*userFunc)(void), touch_value_t threshold) +{ + __touchConfigInterrupt(pin, userFunc, NULL, threshold, false); +} + +// new additional version of the API with User Args +static void __touchAttachArgsInterrupt(uint8_t pin, void (*userFunc)(void), void *args, touch_value_t threshold) +{ + __touchConfigInterrupt(pin, userFunc, args, threshold, true); +} + +// new additional API to dettach touch ISR +static void __touchDettachInterrupt(uint8_t pin) +{ + __touchConfigInterrupt(pin, NULL, NULL, 0, false); // userFunc as NULL acts as dettaching +} + + +/* + External Public Touch API Functions +*/ + +#if SOC_TOUCH_VERSION_1 // Only for ESP32 SoC +void touchInterruptSetThresholdDirection(bool mustbeLower) { + if (mustbeLower) { + touch_pad_set_trigger_mode(TOUCH_TRIGGER_BELOW); + } else { + touch_pad_set_trigger_mode(TOUCH_TRIGGER_ABOVE); + } +} +#elif SOC_TOUCH_VERSION_2 // Only for ESP32S2 and ESP32S3 +// returns true if touch pad has been and continues pressed and false otherwise +bool touchInterruptGetLastStatus(uint8_t pin) { + int8_t pad = digitalPinToTouchChannel(pin); + if(pad < 0){ + return false; + } + + return __touchInterruptHandlers[pad].lastStatusIsPressed; +} +#endif + +void touchSleepWakeUpEnable(uint8_t pin, touch_value_t threshold) +{ + int8_t pad = digitalPinToTouchChannel(pin); + if(pad < 0){ + log_e(" No touch pad on selected pin!"); + return; + } + __touchInit(); + __touchChannelInit(pad); + + #if SOC_TOUCH_VERSION_1 // Only for ESP32 SoC + touch_pad_set_thresh(pad, threshold); + + #elif SOC_TOUCH_VERSION_2 + touch_pad_sleep_channel_enable(pad, true); + touch_pad_sleep_set_threshold(pad, threshold); + + #endif + esp_sleep_enable_touchpad_wakeup(); +} + +extern touch_value_t touchRead(uint8_t) __attribute__ ((weak, alias("__touchRead"))); +extern void touchAttachInterrupt(uint8_t, voidFuncPtr, touch_value_t) __attribute__ ((weak, alias("__touchAttachInterrupt"))); +extern void touchAttachInterruptArg(uint8_t, voidArgFuncPtr, void *, touch_value_t) __attribute__ ((weak, alias("__touchAttachArgsInterrupt"))); +extern void touchDetachInterrupt(uint8_t) __attribute__ ((weak, alias("__touchDettachInterrupt"))); +extern void touchSetCycles(uint16_t, uint16_t) __attribute__ ((weak, alias("__touchSetCycles"))); + +#endif // #if SOC_TOUCH_SENSOR_NUM > 0 diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-touch.h b/src/esp32/hardware/cores/esp32/esp32-hal-touch.h new file mode 100644 index 0000000..235b8f8 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-touch.h @@ -0,0 +1,102 @@ +/* + Arduino.h - Main include file for the Arduino SDK + Copyright (c) 2005-2013 Arduino Team. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MAIN_ESP32_HAL_TOUCH_H_ +#define MAIN_ESP32_HAL_TOUCH_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "soc/soc_caps.h" +#include "esp32-hal.h" + +#if SOC_TOUCH_SENSOR_NUM > 0 + +#if !defined(SOC_TOUCH_VERSION_1) && !defined(SOC_TOUCH_VERSION_2) +#error Touch IDF driver Not supported! +#endif + +#if SOC_TOUCH_VERSION_1 // ESP32 +typedef uint16_t touch_value_t; +#elif SOC_TOUCH_VERSION_2 // ESP32S2 ESP32S3 +typedef uint32_t touch_value_t; +#endif + +/* + * Set cycles that measurement operation takes + * The result from touchRead, threshold and detection + * accuracy depend on these values. Defaults are + * 0x1000 for measure and 0x1000 for sleep. + * With default values touchRead takes 0.5ms + * */ +void touchSetCycles(uint16_t measure, uint16_t sleep); + +/* + * Read touch pad (for ESP32 values close to 0 mean touch detected / + * for ESP32-S2/S3 higher values mean touch detected) + * You can use this method to chose a good threshold value + * to use as value for touchAttachInterrupt + * */ +touch_value_t touchRead(uint8_t pin); + +/* + * Set function to be called if touch pad value falls (ESP32) + * below the given threshold / rises (ESP32-S2/S3) by given increment (threshold). + * Use touchRead to determine a proper threshold between touched and untouched state + * */ +void touchAttachInterrupt(uint8_t pin, void (*userFunc)(void), touch_value_t threshold); +void touchAttachInterruptArg(uint8_t pin, void (*userFunc)(void*), void *arg, touch_value_t threshold); +void touchDetachInterrupt(uint8_t pin); + +/* + * Specific functions to ESP32 + * Tells the driver if it shall activate the ISR if the sensor is Lower or Higher than the Threshold + * Default if Lower. + **/ + +#if SOC_TOUCH_VERSION_1 // Only for ESP32 SoC +void touchInterruptSetThresholdDirection(bool mustbeLower); +#endif + + +/* + * Specific functions to ESP32-S2 and ESP32-S3 + * Returns true when the latest ISR status for the Touchpad is that it is touched (Active) + * and false when the Touchpad is untoouched (Inactive) + * This function can be used in conjunction with ISR User callback in order to take action + * as soon as the touchpad is touched and/or released + **/ + +#if SOC_TOUCH_VERSION_2 // Only for ESP32S2 and ESP32S3 +// returns true if touch pad has been and continues pressed and false otherwise +bool touchInterruptGetLastStatus(uint8_t pin); +#endif + +/* + * Setup touch pad wake up from deep sleep with given threshold. + **/ +void touchSleepWakeUpEnable(uint8_t pin, touch_value_t threshold); + +#endif // SOC_TOUCH_SENSOR_NUM > 0 + +#ifdef __cplusplus +} +#endif +#endif /* MAIN_ESP32_HAL_TOUCH_H_ */ diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-uart.c b/src/esp32/hardware/cores/esp32/esp32-hal-uart.c new file mode 100644 index 0000000..ca21257 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-uart.c @@ -0,0 +1,818 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "esp32-hal-uart.h" +#include "esp32-hal.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" + +#include "driver/uart.h" +#include "hal/uart_ll.h" +#include "soc/soc_caps.h" +#include "soc/uart_struct.h" +#include "soc/uart_periph.h" + +#include "driver/gpio.h" +#include "hal/gpio_hal.h" +#include "esp_rom_gpio.h" + +static int s_uart_debug_nr = 0; + +struct uart_struct_t { + +#if !CONFIG_DISABLE_HAL_LOCKS + xSemaphoreHandle lock; +#endif + + uint8_t num; + bool has_peek; + uint8_t peek_byte; + QueueHandle_t uart_event_queue; // export it by some uartGetEventQueue() function +}; + +#if CONFIG_DISABLE_HAL_LOCKS + +#define UART_MUTEX_LOCK() +#define UART_MUTEX_UNLOCK() + +static uart_t _uart_bus_array[] = { + {0, false, 0, NULL}, +#if SOC_UART_NUM > 1 + {1, false, 0, NULL}, +#endif +#if SOC_UART_NUM > 2 + {2, false, 0, NULL}, +#endif +}; + +#else + +#define UART_MUTEX_LOCK() do {} while (xSemaphoreTake(uart->lock, portMAX_DELAY) != pdPASS) +#define UART_MUTEX_UNLOCK() xSemaphoreGive(uart->lock) + +static uart_t _uart_bus_array[] = { + {NULL, 0, false, 0, NULL}, +#if SOC_UART_NUM > 1 + {NULL, 1, false, 0, NULL}, +#endif +#if SOC_UART_NUM > 2 + {NULL, 2, false, 0, NULL}, +#endif +}; + +#endif + +// IDF UART has no detach function. As consequence, after ending a UART, the previous pins continue +// to work as RX/TX. It can be verified by changing the UART pins and writing to the UART. Output can +// be seen in the previous pins and new pins as well. +// Valid pin UART_PIN_NO_CHANGE is defined to (-1) +// Negative Pin Number will keep it unmodified, thus this function can detach individual pins +void uartDetachPins(uart_t* uart, int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin) +{ + if(uart == NULL) { + return; + } + + UART_MUTEX_LOCK(); + if (txPin >= 0) { + gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[txPin], PIN_FUNC_GPIO); + esp_rom_gpio_connect_out_signal(txPin, SIG_GPIO_OUT_IDX, false, false); + } + + if (rxPin >= 0) { + gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[rxPin], PIN_FUNC_GPIO); + esp_rom_gpio_connect_in_signal(GPIO_FUNC_IN_LOW, UART_PERIPH_SIGNAL(uart->num, SOC_UART_RX_PIN_IDX), false); + } + + if (rtsPin >= 0) { + gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[rtsPin], PIN_FUNC_GPIO); + esp_rom_gpio_connect_out_signal(rtsPin, SIG_GPIO_OUT_IDX, false, false); + } + + if (ctsPin >= 0) { + gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[ctsPin], PIN_FUNC_GPIO); + esp_rom_gpio_connect_in_signal(GPIO_FUNC_IN_LOW, UART_PERIPH_SIGNAL(uart->num, SOC_UART_CTS_PIN_IDX), false); + } + UART_MUTEX_UNLOCK(); +} + +// solves issue https://github.com/espressif/arduino-esp32/issues/6032 +// baudrate must be multiplied when CPU Frequency is lower than APB 80MHz +uint32_t _get_effective_baudrate(uint32_t baudrate) +{ + uint32_t Freq = getApbFrequency()/1000000; + if (Freq < 80) { + return 80 / Freq * baudrate; + } + else { + return baudrate; + } +} + +// Routines that take care of UART events will be in the HardwareSerial Class code +void uartGetEventQueue(uart_t* uart, QueueHandle_t *q) +{ + // passing back NULL for the Queue pointer when UART is not initialized yet + *q = NULL; + if(uart == NULL) { + return; + } + *q = uart->uart_event_queue; + return; +} + +bool uartIsDriverInstalled(uart_t* uart) +{ + if(uart == NULL) { + return false; + } + + if (uart_is_driver_installed(uart->num)) { + return true; + } + return false; +} + +// Valid pin UART_PIN_NO_CHANGE is defined to (-1) +// Negative Pin Number will keep it unmodified, thus this function can set individual pins +bool uartSetPins(uart_t* uart, int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin) +{ + if(uart == NULL) { + return false; + } + UART_MUTEX_LOCK(); + // IDF uart_set_pin() will issue necessary Error Message and take care of all GPIO Number validation. + bool retCode = uart_set_pin(uart->num, txPin, rxPin, rtsPin, ctsPin) == ESP_OK; + UART_MUTEX_UNLOCK(); + return retCode; +} + +// +bool uartSetHwFlowCtrlMode(uart_t *uart, uint8_t mode, uint8_t threshold) { + if(uart == NULL) { + return false; + } + // IDF will issue corresponding error message when mode or threshold are wrong and prevent crashing + // IDF will check (mode > HW_FLOWCTRL_CTS_RTS || threshold >= SOC_UART_FIFO_LEN) + UART_MUTEX_LOCK(); + bool retCode = (ESP_OK == uart_set_hw_flow_ctrl(uart->num, (uart_hw_flowcontrol_t) mode, threshold)); + UART_MUTEX_UNLOCK(); + return retCode; +} + + +uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t rx_buffer_size, uint16_t tx_buffer_size, bool inverted, uint8_t rxfifo_full_thrhd) +{ + if(uart_nr >= SOC_UART_NUM) { + return NULL; + } + + uart_t* uart = &_uart_bus_array[uart_nr]; + + if (uart_is_driver_installed(uart_nr)) { + uartEnd(uart); + } + +#if !CONFIG_DISABLE_HAL_LOCKS + if(uart->lock == NULL) { + uart->lock = xSemaphoreCreateMutex(); + if(uart->lock == NULL) { + return NULL; + } + } +#endif + + UART_MUTEX_LOCK(); + + uart_config_t uart_config; + uart_config.data_bits = (config & 0xc) >> 2; + uart_config.parity = (config & 0x3); + uart_config.stop_bits = (config & 0x30) >> 4; + uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE; + uart_config.rx_flow_ctrl_thresh = rxfifo_full_thrhd; +#if SOC_UART_SUPPORT_XTAL_CLK + // works independently of APB frequency + uart_config.source_clk = UART_SCLK_XTAL; // ESP32C3, ESP32S3 + uart_config.baud_rate = baudrate; +#else + uart_config.source_clk = UART_SCLK_APB; // ESP32, ESP32S2 + uart_config.baud_rate = _get_effective_baudrate(baudrate); +#endif + ESP_ERROR_CHECK(uart_driver_install(uart_nr, rx_buffer_size, tx_buffer_size, 20, &(uart->uart_event_queue), 0)); + ESP_ERROR_CHECK(uart_param_config(uart_nr, &uart_config)); + ESP_ERROR_CHECK(uart_set_pin(uart_nr, txPin, rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); + + // Is it right or the idea is to swap rx and tx pins? + if (inverted) { + // invert signal for both Rx and Tx + ESP_ERROR_CHECK(uart_set_line_inverse(uart_nr, UART_SIGNAL_TXD_INV | UART_SIGNAL_RXD_INV)); + } + + UART_MUTEX_UNLOCK(); + + uartFlush(uart); + return uart; +} + +// This function code is under testing - for now just keep it here +void uartSetFastReading(uart_t* uart) +{ + if(uart == NULL) { + return; + } + + UART_MUTEX_LOCK(); + // override default RX IDF Driver Interrupt - no BREAK, PARITY or OVERFLOW + uart_intr_config_t uart_intr = { + .intr_enable_mask = UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT, // only these IRQs - no BREAK, PARITY or OVERFLOW + .rx_timeout_thresh = 1, + .txfifo_empty_intr_thresh = 10, + .rxfifo_full_thresh = 2, + }; + + ESP_ERROR_CHECK(uart_intr_config(uart->num, &uart_intr)); + UART_MUTEX_UNLOCK(); +} + + +bool uartSetRxTimeout(uart_t* uart, uint8_t numSymbTimeout) +{ + if(uart == NULL) { + return false; + } + + UART_MUTEX_LOCK(); + bool retCode = (ESP_OK == uart_set_rx_timeout(uart->num, numSymbTimeout)); + UART_MUTEX_UNLOCK(); + return retCode; +} + +bool uartSetRxFIFOFull(uart_t* uart, uint8_t numBytesFIFOFull) +{ + if(uart == NULL) { + return false; + } + + UART_MUTEX_LOCK(); + bool retCode = (ESP_OK == uart_set_rx_full_threshold(uart->num, numBytesFIFOFull)); + UART_MUTEX_UNLOCK(); + return retCode; +} + +void uartEnd(uart_t* uart) +{ + if(uart == NULL) { + return; + } + + UART_MUTEX_LOCK(); + uart_driver_delete(uart->num); + UART_MUTEX_UNLOCK(); +} + + +void uartSetRxInvert(uart_t* uart, bool invert) +{ + if (uart == NULL) + return; +#if 0 + // POTENTIAL ISSUE :: original code only set/reset rxd_inv bit + // IDF or LL set/reset the whole inv_mask! + if (invert) + ESP_ERROR_CHECK(uart_set_line_inverse(uart->num, UART_SIGNAL_RXD_INV)); + else + ESP_ERROR_CHECK(uart_set_line_inverse(uart->num, UART_SIGNAL_INV_DISABLE)); + +#else + // this implementation is better over IDF API because it only affects RXD + // this is supported in ESP32, ESP32-S2 and ESP32-C3 + uart_dev_t *hw = UART_LL_GET_HW(uart->num); + if (invert) + hw->conf0.rxd_inv = 1; + else + hw->conf0.rxd_inv = 0; +#endif +} + + +uint32_t uartAvailable(uart_t* uart) +{ + + if(uart == NULL) { + return 0; + } + + UART_MUTEX_LOCK(); + size_t available; + uart_get_buffered_data_len(uart->num, &available); + if (uart->has_peek) available++; + UART_MUTEX_UNLOCK(); + return available; +} + + +uint32_t uartAvailableForWrite(uart_t* uart) +{ + if(uart == NULL) { + return 0; + } + UART_MUTEX_LOCK(); + uint32_t available = uart_ll_get_txfifo_len(UART_LL_GET_HW(uart->num)); + size_t txRingBufferAvailable = 0; + if (ESP_OK == uart_get_tx_buffer_free_size(uart->num, &txRingBufferAvailable)) { + available += txRingBufferAvailable; + } + UART_MUTEX_UNLOCK(); + return available; +} + +size_t uartReadBytes(uart_t* uart, uint8_t *buffer, size_t size, uint32_t timeout_ms) +{ + if(uart == NULL || size == 0 || buffer == NULL) { + return 0; + } + + size_t bytes_read = 0; + + UART_MUTEX_LOCK(); + + if (uart->has_peek) { + uart->has_peek = false; + *buffer++ = uart->peek_byte; + size--; + bytes_read = 1; + } + + if (size > 0) { + int len = uart_read_bytes(uart->num, buffer, size, pdMS_TO_TICKS(timeout_ms)); + if (len < 0) len = 0; // error reading UART + bytes_read += len; + } + + + UART_MUTEX_UNLOCK(); + return bytes_read; +} + +// DEPRICATED but the original code will be kepts here as future reference when a final solution +// to the UART driver is defined in the use case of reading byte by byte from UART. +uint8_t uartRead(uart_t* uart) +{ + if(uart == NULL) { + return 0; + } + uint8_t c = 0; + + UART_MUTEX_LOCK(); + + if (uart->has_peek) { + uart->has_peek = false; + c = uart->peek_byte; + } else { + + int len = uart_read_bytes(uart->num, &c, 1, 20 / portTICK_RATE_MS); + if (len <= 0) { // includes negative return from IDF in case of error + c = 0; + } + } + UART_MUTEX_UNLOCK(); + return c; +} + + +uint8_t uartPeek(uart_t* uart) +{ + if(uart == NULL) { + return 0; + } + uint8_t c = 0; + + UART_MUTEX_LOCK(); + + if (uart->has_peek) { + c = uart->peek_byte; + } else { + int len = uart_read_bytes(uart->num, &c, 1, 20 / portTICK_RATE_MS); + if (len <= 0) { // includes negative return from IDF in case of error + c = 0; + } else { + uart->has_peek = true; + uart->peek_byte = c; + } + } + UART_MUTEX_UNLOCK(); + return c; +} + +void uartWrite(uart_t* uart, uint8_t c) +{ + if(uart == NULL) { + return; + } + UART_MUTEX_LOCK(); + uart_write_bytes(uart->num, &c, 1); + UART_MUTEX_UNLOCK(); +} + +void uartWriteBuf(uart_t* uart, const uint8_t * data, size_t len) +{ + if(uart == NULL || data == NULL || !len) { + return; + } + + UART_MUTEX_LOCK(); + uart_write_bytes(uart->num, data, len); + UART_MUTEX_UNLOCK(); +} + +void uartFlush(uart_t* uart) +{ + uartFlushTxOnly(uart, true); +} + +void uartFlushTxOnly(uart_t* uart, bool txOnly) +{ + if(uart == NULL) { + return; + } + + UART_MUTEX_LOCK(); + while(!uart_ll_is_tx_idle(UART_LL_GET_HW(uart->num))); + + if ( !txOnly ) { + ESP_ERROR_CHECK(uart_flush_input(uart->num)); + } + UART_MUTEX_UNLOCK(); +} + +void uartSetBaudRate(uart_t* uart, uint32_t baud_rate) +{ + if(uart == NULL) { + return; + } + UART_MUTEX_LOCK(); + uart_ll_set_baudrate(UART_LL_GET_HW(uart->num), _get_effective_baudrate(baud_rate)); + UART_MUTEX_UNLOCK(); +} + +uint32_t uartGetBaudRate(uart_t* uart) +{ + if(uart == NULL) { + return 0; + } + + UART_MUTEX_LOCK(); + uint32_t baud_rate = uart_ll_get_baudrate(UART_LL_GET_HW(uart->num)); + UART_MUTEX_UNLOCK(); + return baud_rate; +} + +static void ARDUINO_ISR_ATTR uart0_write_char(char c) +{ + while (uart_ll_get_txfifo_len(&UART0) == 0); + uart_ll_write_txfifo(&UART0, (const uint8_t *) &c, 1); +} + +#if SOC_UART_NUM > 1 +static void ARDUINO_ISR_ATTR uart1_write_char(char c) +{ + while (uart_ll_get_txfifo_len(&UART1) == 0); + uart_ll_write_txfifo(&UART1, (const uint8_t *) &c, 1); +} +#endif + +#if SOC_UART_NUM > 2 +static void ARDUINO_ISR_ATTR uart2_write_char(char c) +{ + while (uart_ll_get_txfifo_len(&UART2) == 0); + uart_ll_write_txfifo(&UART2, (const uint8_t *) &c, 1); +} +#endif + +void uart_install_putc() +{ + switch(s_uart_debug_nr) { + case 0: + ets_install_putc1((void (*)(char)) &uart0_write_char); + break; +#if SOC_UART_NUM > 1 + case 1: + ets_install_putc1((void (*)(char)) &uart1_write_char); + break; +#endif +#if SOC_UART_NUM > 2 + case 2: + ets_install_putc1((void (*)(char)) &uart2_write_char); + break; +#endif + default: + ets_install_putc1(NULL); + break; + } +} + +// Routines that take care of UART mode in the HardwareSerial Class code +// used to set UART_MODE_RS485_HALF_DUPLEX auto RTS for TXD for ESP32 chips +bool uartSetMode(uart_t *uart, uint8_t mode) +{ + if (uart == NULL || uart->num >= SOC_UART_NUM) + { + return false; + } + + UART_MUTEX_LOCK(); + bool retCode = (ESP_OK == uart_set_mode(uart->num, mode)); + UART_MUTEX_UNLOCK(); + return retCode; +} + +void uartSetDebug(uart_t* uart) +{ + if(uart == NULL || uart->num >= SOC_UART_NUM) { + s_uart_debug_nr = -1; + } else { + s_uart_debug_nr = uart->num; + } + uart_install_putc(); +} + +int uartGetDebug() +{ + return s_uart_debug_nr; +} + +int log_printfv(const char *format, va_list arg) +{ + static char loc_buf[64]; + char * temp = loc_buf; + uint32_t len; + va_list copy; + va_copy(copy, arg); + len = vsnprintf(NULL, 0, format, copy); + va_end(copy); + if(len >= sizeof(loc_buf)){ + temp = (char*)malloc(len+1); + if(temp == NULL) { + return 0; + } + } +#if !CONFIG_DISABLE_HAL_LOCKS + if(s_uart_debug_nr != -1 && _uart_bus_array[s_uart_debug_nr].lock){ + xSemaphoreTake(_uart_bus_array[s_uart_debug_nr].lock, portMAX_DELAY); + } +#endif + + vsnprintf(temp, len+1, format, arg); + ets_printf("%s", temp); + +#if !CONFIG_DISABLE_HAL_LOCKS + if(s_uart_debug_nr != -1 && _uart_bus_array[s_uart_debug_nr].lock){ + xSemaphoreGive(_uart_bus_array[s_uart_debug_nr].lock); + } +#endif + if(len >= sizeof(loc_buf)){ + free(temp); + } + return len; +} + +int log_printf(const char *format, ...) +{ + int len; + va_list arg; + va_start(arg, format); + len = log_printfv(format, arg); + va_end(arg); + return len; +} + + +static void log_print_buf_line(const uint8_t *b, size_t len, size_t total_len){ + for(size_t i = 0; i 16){ + for(size_t i = len; i<16; i++){ + log_printf(" "); + } + log_printf(" // "); + } else { + log_printf(" // "); + } + for(size_t i = 0; i= 0x20) && (b[i] < 0x80))?b[i]:'.'); + } + log_printf("\n"); +} + +void log_print_buf(const uint8_t *b, size_t len){ + if(!len || !b){ + return; + } + for(size_t i = 0; i 16){ + log_printf("/* 0x%04X */ ", i); + } + log_print_buf_line(b+i, ((len-i)<16)?(len - i):16, len); + } +} + +/* + * if enough pulses are detected return the minimum high pulse duration + minimum low pulse duration divided by two. + * This equals one bit period. If flag is true the function return inmediately, otherwise it waits for enough pulses. + */ +unsigned long uartBaudrateDetect(uart_t *uart, bool flg) +{ +// Baud rate detection only works for ESP32 and ESP32S2 +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 + if(uart == NULL) { + return 0; + } + + uart_dev_t *hw = UART_LL_GET_HW(uart->num); + + while(hw->rxd_cnt.edge_cnt < 30) { // UART_PULSE_NUM(uart_num) + if(flg) return 0; + ets_delay_us(1000); + } + + UART_MUTEX_LOCK(); + //log_i("lowpulse_min_cnt = %d hightpulse_min_cnt = %d", hw->lowpulse.min_cnt, hw->highpulse.min_cnt); + unsigned long ret = ((hw->lowpulse.min_cnt + hw->highpulse.min_cnt) >> 1); + UART_MUTEX_UNLOCK(); + + return ret; +#else + return 0; +#endif +} + + +/* + * To start detection of baud rate with the uart the auto_baud.en bit needs to be cleared and set. The bit period is + * detected calling uartBadrateDetect(). The raw baudrate is computed using the UART_CLK_FREQ. The raw baudrate is + * rounded to the closed real baudrate. + * + * ESP32-C3 reports wrong baud rate detection as shown below: + * + * This will help in a future recall for the C3. + * Baud Sent: Baud Read: + * 300 --> 19536 + * 2400 --> 19536 + * 4800 --> 19536 + * 9600 --> 28818 + * 19200 --> 57678 + * 38400 --> 115440 + * 57600 --> 173535 + * 115200 --> 347826 + * 230400 --> 701754 + * + * +*/ +void uartStartDetectBaudrate(uart_t *uart) { + if(uart == NULL) { + return; + } + +#ifdef CONFIG_IDF_TARGET_ESP32C3 + + // ESP32-C3 requires further testing + // Baud rate detection returns wrong values + + log_e("ESP32-C3 baud rate detection is not supported."); + return; + + // Code bellow for C3 kept for future recall + //hw->rx_filt.glitch_filt = 0x08; + //hw->rx_filt.glitch_filt_en = 1; + //hw->conf0.autobaud_en = 0; + //hw->conf0.autobaud_en = 1; +#elif CONFIG_IDF_TARGET_ESP32S3 + log_e("ESP32-S3 baud rate detection is not supported."); + return; +#else + uart_dev_t *hw = UART_LL_GET_HW(uart->num); + hw->auto_baud.glitch_filt = 0x08; + hw->auto_baud.en = 0; + hw->auto_baud.en = 1; +#endif +} + +unsigned long +uartDetectBaudrate(uart_t *uart) +{ + if(uart == NULL) { + return 0; + } + +// Baud rate detection only works for ESP32 and ESP32S2 +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 + + static bool uartStateDetectingBaudrate = false; + + if(!uartStateDetectingBaudrate) { + uartStartDetectBaudrate(uart); + uartStateDetectingBaudrate = true; + } + + unsigned long divisor = uartBaudrateDetect(uart, true); + if (!divisor) { + return 0; + } + + uart_dev_t *hw = UART_LL_GET_HW(uart->num); + hw->auto_baud.en = 0; + + uartStateDetectingBaudrate = false; // Initialize for the next round + + unsigned long baudrate = getApbFrequency() / divisor; + + //log_i("APB_FREQ = %d\nraw baudrate detected = %d", getApbFrequency(), baudrate); + + static const unsigned long default_rates[] = {300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 74880, 115200, 230400, 256000, 460800, 921600, 1843200, 3686400}; + + size_t i; + for (i = 1; i < sizeof(default_rates) / sizeof(default_rates[0]) - 1; i++) // find the nearest real baudrate + { + if (baudrate <= default_rates[i]) + { + if (baudrate - default_rates[i - 1] < default_rates[i] - baudrate) { + i--; + } + break; + } + } + + return default_rates[i]; +#else +#ifdef CONFIG_IDF_TARGET_ESP32C3 + log_e("ESP32-C3 baud rate detection is not supported."); +#else + log_e("ESP32-S3 baud rate detection is not supported."); +#endif + return 0; +#endif +} + +/* + These functions are for testing puspose only and can be used in Arduino Sketches + Those are used in the UART examples +*/ + +/* + This is intended to make an internal loopback connection using IOMUX + The function uart_internal_loopback() shall be used right after Arduino Serial.begin(...) + This code "replaces" the physical wiring for connecting TX <--> RX in a loopback +*/ + +// gets the right TX SIGNAL, based on the UART number +#if SOC_UART_NUM > 2 +#define UART_TX_SIGNAL(uartNumber) (uartNumber == UART_NUM_0 ? U0TXD_OUT_IDX : (uartNumber == UART_NUM_1 ? U1TXD_OUT_IDX : U2TXD_OUT_IDX)) +#else +#define UART_TX_SIGNAL(uartNumber) (uartNumber == UART_NUM_0 ? U0TXD_OUT_IDX : U1TXD_OUT_IDX) +#endif +/* + Make sure UART's RX signal is connected to TX pin + This creates a loop that lets us receive anything we send on the UART +*/ +void uart_internal_loopback(uint8_t uartNum, int8_t rxPin) +{ + if (uartNum > SOC_UART_NUM - 1 || !GPIO_IS_VALID_GPIO(rxPin)) return; + esp_rom_gpio_connect_out_signal(rxPin, UART_TX_SIGNAL(uartNum), false, false); +} + +/* + This is intended to generate BREAK in an UART line +*/ + +// Forces a BREAK in the line based on SERIAL_8N1 configuration at any baud rate +void uart_send_break(uint8_t uartNum) +{ + uint32_t currentBaudrate = 0; + uart_get_baudrate(uartNum, ¤tBaudrate); + // calculates 10 bits of breaks in microseconds for baudrates up to 500mbps + // This is very sensetive timing... it works fine for SERIAL_8N1 + uint32_t breakTime = (uint32_t) (10.0 * (1000000.0 / currentBaudrate)); + uart_set_line_inverse(uartNum, UART_SIGNAL_TXD_INV); + ets_delay_us(breakTime); + uart_set_line_inverse(uartNum, UART_SIGNAL_INV_DISABLE); +} + +// Sends a buffer and at the end of the stream, it generates BREAK in the line +int uart_send_msg_with_break(uint8_t uartNum, uint8_t *msg, size_t msgSize) +{ + // 12 bits long BREAK for 8N1 + return uart_write_bytes_with_break(uartNum, (const void *)msg, msgSize, 12); +} diff --git a/src/esp32/hardware/cores/esp32/esp32-hal-uart.h b/src/esp32/hardware/cores/esp32/esp32-hal-uart.h new file mode 100644 index 0000000..c9cdd11 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal-uart.h @@ -0,0 +1,168 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef MAIN_ESP32_HAL_UART_H_ +#define MAIN_ESP32_HAL_UART_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" + +#ifdef __cplusplus +enum SerialConfig { +SERIAL_5N1 = 0x8000010, +SERIAL_6N1 = 0x8000014, +SERIAL_7N1 = 0x8000018, +SERIAL_8N1 = 0x800001c, +SERIAL_5N2 = 0x8000030, +SERIAL_6N2 = 0x8000034, +SERIAL_7N2 = 0x8000038, +SERIAL_8N2 = 0x800003c, +SERIAL_5E1 = 0x8000012, +SERIAL_6E1 = 0x8000016, +SERIAL_7E1 = 0x800001a, +SERIAL_8E1 = 0x800001e, +SERIAL_5E2 = 0x8000032, +SERIAL_6E2 = 0x8000036, +SERIAL_7E2 = 0x800003a, +SERIAL_8E2 = 0x800003e, +SERIAL_5O1 = 0x8000013, +SERIAL_6O1 = 0x8000017, +SERIAL_7O1 = 0x800001b, +SERIAL_8O1 = 0x800001f, +SERIAL_5O2 = 0x8000033, +SERIAL_6O2 = 0x8000037, +SERIAL_7O2 = 0x800003b, +SERIAL_8O2 = 0x800003f +}; +#else +#define SERIAL_5N1 0x8000010 +#define SERIAL_6N1 0x8000014 +#define SERIAL_7N1 0x8000018 +#define SERIAL_8N1 0x800001c +#define SERIAL_5N2 0x8000030 +#define SERIAL_6N2 0x8000034 +#define SERIAL_7N2 0x8000038 +#define SERIAL_8N2 0x800003c +#define SERIAL_5E1 0x8000012 +#define SERIAL_6E1 0x8000016 +#define SERIAL_7E1 0x800001a +#define SERIAL_8E1 0x800001e +#define SERIAL_5E2 0x8000032 +#define SERIAL_6E2 0x8000036 +#define SERIAL_7E2 0x800003a +#define SERIAL_8E2 0x800003e +#define SERIAL_5O1 0x8000013 +#define SERIAL_6O1 0x8000017 +#define SERIAL_7O1 0x800001b +#define SERIAL_8O1 0x800001f +#define SERIAL_5O2 0x8000033 +#define SERIAL_6O2 0x8000037 +#define SERIAL_7O2 0x800003b +#define SERIAL_8O2 0x800003f +#endif // __cplusplus + +// These are Hardware Flow Contol possible usage +// equivalent to UDF enum uart_hw_flowcontrol_t from +// https://github.com/espressif/esp-idf/blob/master/components/hal/include/hal/uart_types.h#L75-L81 +#define HW_FLOWCTRL_DISABLE 0x0 // disable HW Flow Control +#define HW_FLOWCTRL_RTS 0x1 // use only RTS PIN for HW Flow Control +#define HW_FLOWCTRL_CTS 0x2 // use only CTS PIN for HW Flow Control +#define HW_FLOWCTRL_CTS_RTS 0x3 // use both CTS and RTS PIN for HW Flow Control + +// These are Hardware Uart Modes possible usage +// equivalent to UDF enum uart_mode_t from +// https://github.com/espressif/esp-idf/blob/master/components/hal/include/hal/uart_types.h#L34-L40 +#define MODE_UART 0x00 // mode: regular UART mode +#define MODE_RS485_HALF_DUPLEX 0x01 // mode: half duplex RS485 UART mode control by RTS pin +#define MODE_IRDA 0x02 // mode: IRDA UART mode +#define MODE_RS485_COLLISION_DETECT 0x03 // mode: RS485 collision detection UART mode (used for test purposes) +#define MODE_RS485_APP_CTRL 0x04 + +struct uart_struct_t; +typedef struct uart_struct_t uart_t; + +uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t rx_buffer_size, uint16_t tx_buffer_size, bool inverted, uint8_t rxfifo_full_thrhd); +void uartEnd(uart_t* uart); + +// This is used to retrieve the Event Queue pointer from a UART IDF Driver in order to allow user to deal with its events +void uartGetEventQueue(uart_t* uart, QueueHandle_t *q); + +uint32_t uartAvailable(uart_t* uart); +uint32_t uartAvailableForWrite(uart_t* uart); +size_t uartReadBytes(uart_t* uart, uint8_t *buffer, size_t size, uint32_t timeout_ms); +uint8_t uartRead(uart_t* uart); +uint8_t uartPeek(uart_t* uart); + +void uartWrite(uart_t* uart, uint8_t c); +void uartWriteBuf(uart_t* uart, const uint8_t * data, size_t len); + +void uartFlush(uart_t* uart); +void uartFlushTxOnly(uart_t* uart, bool txOnly ); + +void uartSetBaudRate(uart_t* uart, uint32_t baud_rate); +uint32_t uartGetBaudRate(uart_t* uart); + +void uartSetRxInvert(uart_t* uart, bool invert); +bool uartSetRxTimeout(uart_t* uart, uint8_t numSymbTimeout); +bool uartSetRxFIFOFull(uart_t* uart, uint8_t numBytesFIFOFull); +void uartSetFastReading(uart_t* uart); + +void uartSetDebug(uart_t* uart); +int uartGetDebug(); + +bool uartIsDriverInstalled(uart_t* uart); + +// Negative Pin Number will keep it unmodified, thus this function can set/reset individual pins +bool uartSetPins(uart_t* uart, int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin); +void uartDetachPins(uart_t* uart, int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin); + +// Enables or disables HW Flow Control function -- needs also to set CTS and/or RTS pins +bool uartSetHwFlowCtrlMode(uart_t *uart, uint8_t mode, uint8_t threshold); + +// Used to set RS485 function -- needs to disable HW Flow Control and set RTS pin to use +// RTS pin becomes RS485 half duplex RE/DE +bool uartSetMode(uart_t *uart, uint8_t mode); + +void uartStartDetectBaudrate(uart_t *uart); +unsigned long uartDetectBaudrate(uart_t *uart); + +/* + These functions are for testing puspose only and can be used in Arduino Sketches + Those are used in the UART examples +*/ + +// Make sure UART's RX signal is connected to TX pin +// This creates a loop that lets us receive anything we send on the UART +void uart_internal_loopback(uint8_t uartNum, int8_t rxPin); + +// Routines that generate BREAK in the UART for testing purpose + +// Forces a BREAK in the line based on SERIAL_8N1 configuration at any baud rate +void uart_send_break(uint8_t uartNum); +// Sends a buffer and at the end of the stream, it generates BREAK in the line +int uart_send_msg_with_break(uint8_t uartNum, uint8_t *msg, size_t msgSize); + + +#ifdef __cplusplus +} +#endif + +#endif /* MAIN_ESP32_HAL_UART_H_ */ diff --git a/src/esp32/hardware/cores/esp32/esp32-hal.h b/src/esp32/hardware/cores/esp32/esp32-hal.h new file mode 100644 index 0000000..2a51c3e --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp32-hal.h @@ -0,0 +1,153 @@ +/* + Arduino.h - Main include file for the Arduino SDK + Copyright (c) 2005-2013 Arduino Team. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef HAL_ESP32_HAL_H_ +#define HAL_ESP32_HAL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sdkconfig.h" +#include "esp_system.h" +#include "esp_sleep.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "freertos/event_groups.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef F_CPU +#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 +#define F_CPU (CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ * 1000000U) +#elif CONFIG_IDF_TARGET_ESP32C3 +#define F_CPU (CONFIG_ESP32C3_DEFAULT_CPU_FREQ_MHZ * 1000000U) +#elif CONFIG_IDF_TARGET_ESP32S2 +#define F_CPU (CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ * 1000000U) +#elif CONFIG_IDF_TARGET_ESP32S3 +#define F_CPU (CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ * 1000000U) +#endif +#endif + +#if CONFIG_ARDUINO_ISR_IRAM +#define ARDUINO_ISR_ATTR IRAM_ATTR +#define ARDUINO_ISR_FLAG ESP_INTR_FLAG_IRAM +#else +#define ARDUINO_ISR_ATTR +#define ARDUINO_ISR_FLAG (0) +#endif + +#ifndef ARDUINO_RUNNING_CORE +#define ARDUINO_RUNNING_CORE CONFIG_ARDUINO_RUNNING_CORE +#endif + +#ifndef ARDUINO_EVENT_RUNNING_CORE +#define ARDUINO_EVENT_RUNNING_CORE CONFIG_ARDUINO_EVENT_RUNNING_CORE +#endif + +//forward declaration from freertos/portmacro.h +void vPortYield(void); +void yield(void); +#define optimistic_yield(u) + +#define ESP_REG(addr) *((volatile uint32_t *)(addr)) +#define NOP() asm volatile ("nop") + +#include "esp32-hal-log.h" +#include "esp32-hal-matrix.h" +#include "esp32-hal-uart.h" +#include "esp32-hal-gpio.h" +#include "esp32-hal-touch.h" +#include "esp32-hal-dac.h" +#include "esp32-hal-adc.h" +#include "esp32-hal-spi.h" +#include "esp32-hal-i2c.h" +#include "esp32-hal-ledc.h" +#include "esp32-hal-rmt.h" +#include "esp32-hal-sigmadelta.h" +#include "esp32-hal-timer.h" +#include "esp32-hal-bt.h" +#include "esp32-hal-psram.h" +#include "esp32-hal-rgb-led.h" +#include "esp32-hal-cpu.h" + +void analogWrite(uint8_t pin, int value); +int8_t analogGetChannel(uint8_t pin); +void analogWriteFrequency(uint32_t freq); +void analogWriteResolution(uint8_t bits); + +//returns chip temperature in Celsius +float temperatureRead(); + +//allows user to bypass SPI RAM test routine +bool testSPIRAM(void); + +#if CONFIG_AUTOSTART_ARDUINO +//enable/disable WDT for Arduino's setup and loop functions +void enableLoopWDT(); +void disableLoopWDT(); +//feed WDT for the loop task +void feedLoopWDT(); +#endif + +//enable/disable WDT for the IDLE task on Core 0 (SYSTEM) +void enableCore0WDT(); +void disableCore0WDT(); +#ifndef CONFIG_FREERTOS_UNICORE +//enable/disable WDT for the IDLE task on Core 1 (Arduino) +void enableCore1WDT(); +void disableCore1WDT(); +#endif + +//if xCoreID < 0 or CPU is unicore, it will use xTaskCreate, else xTaskCreatePinnedToCore +//allows to easily handle all possible situations without repetitive code +BaseType_t xTaskCreateUniversal( TaskFunction_t pxTaskCode, + const char * const pcName, + const uint32_t usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask, + const BaseType_t xCoreID ); + +unsigned long micros(); +unsigned long millis(); +void delay(uint32_t); +void delayMicroseconds(uint32_t us); + +#if !CONFIG_ESP32_PHY_AUTO_INIT +void arduino_phy_init(); +#endif + +#if !CONFIG_AUTOSTART_ARDUINO +void initArduino(); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* HAL_ESP32_HAL_H_ */ diff --git a/src/esp32/hardware/cores/esp32/esp8266-compat.h b/src/esp32/hardware/cores/esp32/esp8266-compat.h new file mode 100644 index 0000000..9f9dd63 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp8266-compat.h @@ -0,0 +1,24 @@ +// esp8266-compat.h - Compatibility functions to help ESP8266 libraries and user code run on ESP32 + +// Copyright (c) 2017 Evandro Luis Copercini. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _ESP8266_COMPAT_H_ +#define _ESP8266_COMPAT_H_ + +#define ICACHE_FLASH_ATTR +#define ICACHE_RAM_ATTR ARDUINO_ISR_ATTR + + +#endif /* _ESP8266_COMPAT_H_ */ \ No newline at end of file diff --git a/src/esp32/hardware/cores/esp32/esp_arduino_version.h b/src/esp32/hardware/cores/esp32/esp_arduino_version.h new file mode 100644 index 0000000..bdc822b --- /dev/null +++ b/src/esp32/hardware/cores/esp32/esp_arduino_version.h @@ -0,0 +1,46 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** Major version number (X.x.x) */ +#define ESP_ARDUINO_VERSION_MAJOR 2 +/** Minor version number (x.X.x) */ +#define ESP_ARDUINO_VERSION_MINOR 0 +/** Patch version number (x.x.X) */ +#define ESP_ARDUINO_VERSION_PATCH 11 + +/** + * Macro to convert ARDUINO version number into an integer + * + * To be used in comparisons, such as ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2, 0, 0) + */ +#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) + +/** + * Current ARDUINO version, as an integer + * + * To be used in comparisons, such as ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(2, 0, 0) + */ +#define ESP_ARDUINO_VERSION ESP_ARDUINO_VERSION_VAL(ESP_ARDUINO_VERSION_MAJOR, \ + ESP_ARDUINO_VERSION_MINOR, \ + ESP_ARDUINO_VERSION_PATCH) + +#ifdef __cplusplus +} +#endif diff --git a/src/esp32/hardware/cores/esp32/firmware_msc_fat.c b/src/esp32/hardware/cores/esp32/firmware_msc_fat.c new file mode 100644 index 0000000..30a1a64 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/firmware_msc_fat.c @@ -0,0 +1,204 @@ +// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "firmware_msc_fat.h" +//copy up to max_len chars from src to dst and do not terminate +static size_t cplstr(void *dst, const void * src, size_t max_len){ + if(!src || !dst || !max_len){ + return 0; + } + size_t l = strlen((const char *)src); + if(l > max_len){ + l = max_len; + } + memcpy(dst, src, l); + return l; +} + +//copy up to max_len chars from src to dst, adding spaces up to max_len. do not terminate +static void cplstrsp(void *dst, const void * src, size_t max_len){ + size_t l = cplstr(dst, src, max_len); + for(; l < max_len; l++){ + ((uint8_t*)dst)[l] = 0x20; + } +} + +// FAT12 +static const char * FAT12_FILE_SYSTEM_TYPE = "FAT12"; + +static uint16_t fat12_sectors_per_alloc_table(uint32_t sector_num){ + uint32_t required_bytes = (((sector_num * 3)+1)/2); + return (required_bytes / DISK_SECTOR_SIZE) + ((required_bytes & (DISK_SECTOR_SIZE - 1))?1:0); +} + +static uint8_t * fat12_add_table(uint8_t * dst, fat_boot_sector_t * boot){ + memset(dst+DISK_SECTOR_SIZE, 0, boot->sectors_per_alloc_table * DISK_SECTOR_SIZE); + uint8_t * d = dst + DISK_SECTOR_SIZE; + d[0] = 0xF8; + d[1] = 0xFF; + d[2] = 0xFF; + return d; +} + +static void fat12_set_table_index(uint8_t * table, uint16_t index, uint16_t value){ + uint16_t offset = (index >> 1) * 3; + uint8_t * data = table + offset; + if(index & 1){ + data[2] = (value >> 4) & 0xFF; + data[1] = (data[1] & 0xF) | ((value & 0xF) << 4); + } else { + data[0] = value & 0xFF; + data[1] = (data[1] & 0xF0) | ((value >> 8) & 0xF); + } +} + +//FAT16 +static const char * FAT16_FILE_SYSTEM_TYPE = "FAT16"; + +static uint16_t fat16_sectors_per_alloc_table(uint32_t sector_num){ + uint32_t required_bytes = sector_num * 2; + return (required_bytes / DISK_SECTOR_SIZE) + ((required_bytes & (DISK_SECTOR_SIZE - 1))?1:0); +} + +static uint8_t * fat16_add_table(uint8_t * dst, fat_boot_sector_t * boot){ + memset(dst+DISK_SECTOR_SIZE, 0, boot->sectors_per_alloc_table * DISK_SECTOR_SIZE); + uint16_t * d = (uint16_t *)(dst + DISK_SECTOR_SIZE); + d[0] = 0xFFF8; + d[1] = 0xFFFF; + return (uint8_t *)d; +} + +static void fat16_set_table_index(uint8_t * table, uint16_t index, uint16_t value){ + uint16_t offset = index * 2; + *(uint16_t *)(table + offset) = value; +} + +//Interface +const char * fat_file_system_type(bool fat16) { + return ((fat16)?FAT16_FILE_SYSTEM_TYPE:FAT12_FILE_SYSTEM_TYPE); +} + +uint16_t fat_sectors_per_alloc_table(uint32_t sector_num, bool fat16){ + if(fat16){ + return fat16_sectors_per_alloc_table(sector_num); + } + return fat12_sectors_per_alloc_table(sector_num); +} + +uint8_t * fat_add_table(uint8_t * dst, fat_boot_sector_t * boot, bool fat16){ + if(fat16){ + return fat16_add_table(dst, boot); + } + return fat12_add_table(dst, boot); +} + +void fat_set_table_index(uint8_t * table, uint16_t index, uint16_t value, bool fat16){ + if(fat16){ + fat16_set_table_index(table, index, value); + } else { + fat12_set_table_index(table, index, value); + } +} + +fat_boot_sector_t * fat_add_boot_sector(uint8_t * dst, uint16_t sector_num, uint16_t table_sectors, const char * file_system_type, const char * volume_label, uint32_t serial_number){ + fat_boot_sector_t *boot = (fat_boot_sector_t*)dst; + boot->jump_instruction[0] = 0xEB; + boot->jump_instruction[1] = 0x3C; + boot->jump_instruction[2] = 0x90; + cplstr(boot->oem_name, "MSDOS5.0", 8); + boot->bytes_per_sector = DISK_SECTOR_SIZE; + boot->sectors_per_cluster = 1; + boot->reserved_sectors_count = 1; + boot->file_alloc_tables_num = 1; + boot->max_root_dir_entries = 16; + boot->fat12_sector_num = sector_num; + boot->media_descriptor = 0xF8; + boot->sectors_per_alloc_table = table_sectors; + boot->sectors_per_track = 1; + boot->num_heads = 1; + boot->hidden_sectors_count = 0; + boot->total_sectors_32 = 0; + boot->physical_drive_number = 0x80; + boot->reserved0 = 0x00; + boot->extended_boot_signature = 0x29; + boot->serial_number = serial_number; + cplstrsp(boot->volume_label, volume_label, 11); + memset(boot->reserved, 0, 448); + cplstrsp(boot->file_system_type, file_system_type, 8); + boot->signature = 0xAA55; + return boot; +} + +fat_dir_entry_t * fat_add_label(uint8_t * dst, const char * volume_label){ + fat_boot_sector_t * boot = (fat_boot_sector_t *)dst; + fat_dir_entry_t * entry = (fat_dir_entry_t *)(dst + ((boot->sectors_per_alloc_table+1) * DISK_SECTOR_SIZE)); + memset(entry, 0, sizeof(fat_dir_entry_t)); + cplstrsp(entry->volume_label, volume_label, 11); + entry->file_attr = FAT_FILE_ATTR_VOLUME_LABEL; + return entry; +} + +fat_dir_entry_t * fat_add_root_file(uint8_t * dst, uint8_t index, const char * file_name, const char * file_extension, size_t file_size, uint16_t data_start_sector, bool is_fat16){ + fat_boot_sector_t * boot = (fat_boot_sector_t *)dst; + uint8_t * table = dst + DISK_SECTOR_SIZE; + fat_dir_entry_t * entry = (fat_dir_entry_t *)(dst + ((boot->sectors_per_alloc_table+1) * DISK_SECTOR_SIZE) + (index * sizeof(fat_dir_entry_t))); + memset(entry, 0, sizeof(fat_dir_entry_t)); + cplstrsp(entry->file_name, file_name, 8); + cplstrsp(entry->file_extension, file_extension, 3); + entry->file_attr = FAT_FILE_ATTR_ARCHIVE; + entry->file_size = file_size; + entry->data_start_sector = data_start_sector; + entry->extended_attr = 0; + + uint16_t file_sectors = file_size / DISK_SECTOR_SIZE; + if(file_size % DISK_SECTOR_SIZE){ + file_sectors++; + } + + uint16_t data_end_sector = data_start_sector + file_sectors; + for(uint16_t i=data_start_sector; i<(data_end_sector-1); i++){ + fat_set_table_index(table, i, i+1, is_fat16); + } + fat_set_table_index(table, data_end_sector-1, 0xFFFF, is_fat16); + + //Set Firmware Date based on the build time + static const char * month_names_short[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + char mstr[8] = {'\0',}; + const char *str = __DATE__ " " __TIME__; + int ms=0, seconds=0, minutes=0, hours=0, year=0, date=0, month=0; + int r = sscanf(str,"%s %d %d %d:%d:%d", mstr, &date, &year, &hours, &minutes, &seconds); + if(r >= 0){ + for(int i=0; i<12; i++){ + if(!strcmp(mstr, month_names_short[i])){ + month = i; + break; + } + } + entry->creation_time_ms = FAT_MS2V(seconds, ms); + entry->creation_time_hms = FAT_HMS2V(hours, minutes, seconds); + entry->creation_time_ymd = FAT_YMD2V(year, month, date); + entry->last_access_ymd = entry->creation_time_ymd; + entry->last_modified_hms = entry->creation_time_hms; + entry->last_modified_ymd = entry->creation_time_ymd; + } + return entry; +} + +uint8_t fat_lfn_checksum(const uint8_t *short_filename){ + uint8_t sum = 0; + for (uint8_t i = 11; i; i--) { + sum = ((sum & 1) << 7) + (sum >> 1) + *short_filename++; + } + return sum; +} diff --git a/src/esp32/hardware/cores/esp32/firmware_msc_fat.h b/src/esp32/hardware/cores/esp32/firmware_msc_fat.h new file mode 100644 index 0000000..dd88cdc --- /dev/null +++ b/src/esp32/hardware/cores/esp32/firmware_msc_fat.h @@ -0,0 +1,141 @@ +// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif + +#define FAT_U8(v) ((v) & 0xFF) +#define FAT_U16(v) FAT_U8(v), FAT_U8((v) >> 8) +#define FAT_U32(v) FAT_U8(v), FAT_U8((v) >> 8), FAT_U8((v) >> 16), FAT_U8((v) >> 24) + +#define FAT12_TBL2B(l,h) FAT_U8(l), FAT_U8(((l >> 8) & 0xF) | ((h << 4) & 0xF0)), FAT_U8(h >> 4) + +#define FAT_MS2B(s,ms) FAT_U8(((((s) & 0x1) * 1000) + (ms)) / 10) +#define FAT_HMS2B(h,m,s) FAT_U8(((s) >> 1)|(((m) & 0x7) << 5)), FAT_U8((((m) >> 3) & 0x7)|((h) << 3)) +#define FAT_YMD2B(y,m,d) FAT_U8(((d) & 0x1F)|(((m) & 0x7) << 5)), FAT_U8((((m) >> 3) & 0x1)|((((y) - 1980) & 0x7F) << 1)) + +#define FAT_MS2V(s,ms) FAT_U8(((((s) & 0x1) * 1000) + (ms)) / 10) +#define FAT_HMS2V(h,m,s) (FAT_U8(((s) >> 1)|(((m) & 0x7) << 5)) | (FAT_U8((((m) >> 3) & 0x7)|((h) << 3)) << 8)) +#define FAT_YMD2V(y,m,d) (FAT_U8(((d) & 0x1F)|(((m) & 0x7) << 5)) | (FAT_U8((((m) >> 3) & 0x1)|((((y) - 1980) & 0x7F) << 1)) << 8)) + +#define FAT_B2HMS(hms) ((hms >> 11) & 0x1F), ((hms >> 5) & 0x3F), ((hms & 0x1F) << 1) +#define FAT_B2YMD(ymd) (((ymd >> 9) & 0x7F) + 1980), ((ymd >> 5) & 0x0F), (ymd & 0x1F) + +#define FAT_FILE_ATTR_READ_ONLY 0x01 +#define FAT_FILE_ATTR_HIDDEN 0x02 +#define FAT_FILE_ATTR_SYSTEM 0x04 +#define FAT_FILE_ATTR_VOLUME_LABEL 0x08 +#define FAT_FILE_ATTR_SUBDIRECTORY 0x10 +#define FAT_FILE_ATTR_ARCHIVE 0x20 +#define FAT_FILE_ATTR_DEVICE 0x40 + +static const uint16_t DISK_SECTOR_SIZE = 512; + +#define FAT_SIZE_TO_SECTORS(bytes) ((bytes) / DISK_SECTOR_SIZE) + (((bytes) % DISK_SECTOR_SIZE)?1:0) + +typedef struct __attribute__ ((packed)) { + uint8_t jump_instruction[3]; + char oem_name[8];//padded with spaces (0x20) + uint16_t bytes_per_sector;//DISK_SECTOR_SIZE usually 512 + uint8_t sectors_per_cluster;//Allowed values are 1, 2, 4, 8, 16, 32, 64, and 128 + uint16_t reserved_sectors_count;//At least 1 for this sector, usually 32 for FAT32 + uint8_t file_alloc_tables_num;//Almost always 2; RAM disks might use 1 + uint16_t max_root_dir_entries;//FAT12 and FAT16 + uint16_t fat12_sector_num;//DISK_SECTOR_NUM FAT12 and FAT16 + uint8_t media_descriptor; + uint16_t sectors_per_alloc_table;//FAT12 and FAT16 + uint16_t sectors_per_track;//A value of 0 may indicate LBA-only access + uint16_t num_heads; + uint32_t hidden_sectors_count; + uint32_t total_sectors_32; + uint8_t physical_drive_number;//0x00 for (first) removable media, 0x80 for (first) fixed disk + uint8_t reserved0; + uint8_t extended_boot_signature;//should be 0x29 + uint32_t serial_number;//0x1234 => 1234 + char volume_label[11];//padded with spaces (0x20) + char file_system_type[8];//padded with spaces (0x20) + uint8_t reserved[448]; + uint16_t signature;//should be 0xAA55 +} fat_boot_sector_t; + +typedef struct __attribute__ ((packed)) { + union { + struct { + char file_name[8];//padded with spaces (0x20) + char file_extension[3];//padded with spaces (0x20) + }; + struct { + uint8_t file_magic;// 0xE5:deleted, 0x05:will_be_deleted, 0x00:end_marker, 0x2E:dot_marker(. or ..) + char file_magic_data[10]; + }; + char volume_label[11];//padded with spaces (0x20) + }; + uint8_t file_attr;//mask of FAT_FILE_ATTR_* + uint8_t reserved;//always 0 + uint8_t creation_time_ms;//ms * 10; max 1990 (1s 990ms) + uint16_t creation_time_hms; // [5:6:5] => h:m:(s/2) + uint16_t creation_time_ymd; // [7:4:5] => (y+1980):m:d + uint16_t last_access_ymd; + uint16_t extended_attr; + uint16_t last_modified_hms; + uint16_t last_modified_ymd; + uint16_t data_start_sector; + uint32_t file_size; +} fat_dir_entry_t; + +typedef struct __attribute__ ((packed)) { + union { + struct { + uint8_t number:5; + uint8_t reserved0:1; + uint8_t llfp:1; + uint8_t reserved1:1; + } seq; + uint8_t seq_num; //0xE5: Deleted Entry + }; + uint16_t name0[5]; + uint8_t attr; //ALWAYS 0x0F + uint8_t type; //ALWAYS 0x00 + uint8_t dos_checksum; + uint16_t name1[6]; + uint16_t first_cluster; //ALWAYS 0x0000 + uint16_t name2[2]; +} fat_lfn_entry_t; + +typedef union { + fat_dir_entry_t dir; + fat_lfn_entry_t lfn; +} fat_entry_t; + +const char * fat_file_system_type(bool fat16); +uint16_t fat_sectors_per_alloc_table(uint32_t sector_num, bool fat16); +uint8_t * fat_add_table(uint8_t * dst, fat_boot_sector_t * boot, bool fat16); +void fat_set_table_index(uint8_t * table, uint16_t index, uint16_t value, bool fat16); +fat_boot_sector_t * fat_add_boot_sector(uint8_t * dst, uint16_t sector_num, uint16_t table_sectors, const char * file_system_type, const char * volume_label, uint32_t serial_number); +fat_dir_entry_t * fat_add_label(uint8_t * dst, const char * volume_label); +fat_dir_entry_t * fat_add_root_file(uint8_t * dst, uint8_t index, const char * file_name, const char * file_extension, size_t file_size, uint16_t data_start_sector, bool is_fat16); +uint8_t fat_lfn_checksum(const uint8_t *short_filename); + +#ifdef __cplusplus + } +#endif diff --git a/src/esp32/hardware/cores/esp32/io_pin_remap.h b/src/esp32/hardware/cores/esp32/io_pin_remap.h new file mode 100644 index 0000000..8c23fb4 --- /dev/null +++ b/src/esp32/hardware/cores/esp32/io_pin_remap.h @@ -0,0 +1,110 @@ +#ifndef __IO_PIN_REMAP_H__ +#define __IO_PIN_REMAP_H__ + +#include "Arduino.h" + +#if defined(BOARD_HAS_PIN_REMAP) && !defined(BOARD_USES_HW_GPIO_NUMBERS) + +// Pin remapping functions +int8_t digitalPinToGPIONumber(int8_t digitalPin); +int8_t digitalPinFromGPIONumber(int8_t gpioNumber); + +// Apply pin remapping to API only when building libraries and user sketch +#ifndef ARDUINO_CORE_BUILD + +// Override APIs requiring pin remapping + +// cores/esp32/Arduino.h +#define pulseInLong(pin, args...) pulseInLong(digitalPinToGPIONumber(pin), args) +#define pulseIn(pin, args...) pulseIn(digitalPinToGPIONumber(pin), args) +#define noTone(_pin) noTone(digitalPinToGPIONumber(_pin)) +#define tone(_pin, args...) tone(digitalPinToGPIONumber(_pin), args) + +// cores/esp32/esp32-hal.h +#define analogGetChannel(pin) analogGetChannel(digitalPinToGPIONumber(pin)) +#define analogWrite(pin, value) analogWrite(digitalPinToGPIONumber(pin), value) + +// cores/esp32/esp32-hal-adc.h +#define adcAttachPin(pin) adcAttachPin(digitalPinToGPIONumber(pin)) +#define analogRead(pin) analogRead(digitalPinToGPIONumber(pin)) +#define analogReadMilliVolts(pin) analogReadMilliVolts(digitalPinToGPIONumber(pin)) +#define analogSetPinAttenuation(pin, attenuation) analogSetPinAttenuation(digitalPinToGPIONumber(pin), attenuation) +#define analogSetVRefPin(pin) analogSetVRefPin(digitalPinToGPIONumber(pin)) + +// cores/esp32/esp32-hal-dac.h +#define dacDisable(pin) dacDisable(digitalPinToGPIONumber(pin)) +#define dacWrite(pin, value) dacWrite(digitalPinToGPIONumber(pin), value) + +// cores/esp32/esp32-hal-gpio.h +#define analogChannelToDigitalPin(channel) gpioNumberToDigitalPin(analogChannelToDigitalPin(channel)) +#define digitalPinToAnalogChannel(pin) digitalPinToAnalogChannel(digitalPinToGPIONumber(pin)) +#define digitalPinToTouchChannel(pin) digitalPinToTouchChannel(digitalPinToGPIONumber(pin)) +#define digitalRead(pin) digitalRead(digitalPinToGPIONumber(pin)) +#define attachInterruptArg(pin, fcn, arg, mode) attachInterruptArg(digitalPinToGPIONumber(pin), fcn, arg, mode) +#define attachInterrupt(pin, fcn, mode) attachInterrupt(digitalPinToGPIONumber(pin), fcn, mode) +#define detachInterrupt(pin) detachInterrupt(digitalPinToGPIONumber(pin)) +#define digitalWrite(pin, val) digitalWrite(digitalPinToGPIONumber(pin), val) +#define pinMode(pin, mode) pinMode(digitalPinToGPIONumber(pin), mode) + +// cores/esp32/esp32-hal-i2c.h +#define i2cInit(i2c_num, sda, scl, clk_speed) i2cInit(i2c_num, digitalPinToGPIONumber(sda), digitalPinToGPIONumber(scl), clk_speed) + +// cores/esp32/esp32-hal-i2c-slave.h +#define i2cSlaveInit(num, sda, scl, slaveID, frequency, rx_len, tx_len) i2cSlaveInit(num, digitalPinToGPIONumber(sda), digitalPinToGPIONumber(scl), slaveID, frequency, rx_len, tx_len) + +// cores/esp32/esp32-hal-ledc.h +#define ledcAttachPin(pin, channel) ledcAttachPin(digitalPinToGPIONumber(pin), channel) +#define ledcDetachPin(pin) ledcDetachPin(digitalPinToGPIONumber(pin)) + +// cores/esp32/esp32-hal-matrix.h +#define pinMatrixInAttach(pin, signal, inverted) pinMatrixInAttach(digitalPinToGPIONumber(pin), signal, inverted) +#define pinMatrixOutAttach(pin, function, invertOut, invertEnable) pinMatrixOutAttach(digitalPinToGPIONumber(pin), function, invertOut, invertEnable) +#define pinMatrixOutDetach(pin, invertOut, invertEnable) pinMatrixOutDetach(digitalPinToGPIONumber(pin), invertOut, invertEnable) + +// cores/esp32/esp32-hal-rgb-led.h +#define neopixelWrite(pin, red_val, green_val, blue_val) neopixelWrite(digitalPinToGPIONumber(pin), red_val, green_val, blue_val) + +// cores/esp32/esp32-hal-rmt.h +#define rmtInit(pin, tx_not_rx, memsize) rmtInit(digitalPinToGPIONumber(pin), tx_not_rx, memsize) + +// cores/esp32/esp32-hal-sigmadelta.h +#define sigmaDeltaSetup(pin, channel, freq) sigmaDeltaSetup(digitalPinToGPIONumber(pin), channel, freq) +#define sigmaDeltaDetachPin(pin) sigmaDeltaDetachPin(digitalPinToGPIONumber(pin)) + +// cores/esp32/esp32-hal-spi.h +#define spiAttachSCK(spi, sck) spiAttachSCK(spi, digitalPinToGPIONumber(sck)) +#define spiAttachMISO(spi, miso) spiAttachMISO(spi, digitalPinToGPIONumber(miso)) +#define spiAttachMOSI(spi, mosi) spiAttachMOSI(spi, digitalPinToGPIONumber(mosi)) +#define spiDetachSCK(spi, sck) spiDetachSCK(spi, digitalPinToGPIONumber(sck)) +#define spiDetachMISO(spi, miso) spiDetachMISO(spi, digitalPinToGPIONumber(miso)) +#define spiDetachMOSI(spi, mosi) spiDetachMOSI(spi, digitalPinToGPIONumber(mosi)) +#define spiAttachSS(spi, cs_num, ss) spiAttachSS(spi, cs_num, digitalPinToGPIONumber(ss)) +#define spiDetachSS(spi, ss) spiDetachSS(spi, digitalPinToGPIONumber(ss)) + +// cores/esp32/esp32-hal-touch.h +#define touchInterruptGetLastStatus(pin) touchInterruptGetLastStatus(digitalPinToGPIONumber(pin)) +#define touchRead(pin) touchRead(digitalPinToGPIONumber(pin)) +#define touchAttachInterruptArg(pin, userFunc, arg, threshold) touchAttachInterruptArg(digitalPinToGPIONumber(pin), userFunc, arg, threshold) +#define touchAttachInterrupt(pin, userFunc, threshold) touchAttachInterrupt(digitalPinToGPIONumber(pin), userFunc, threshold) +#define touchDetachInterrupt(pin) touchDetachInterrupt(digitalPinToGPIONumber(pin)) +#define touchSleepWakeUpEnable(pin, threshold) touchSleepWakeUpEnable(digitalPinToGPIONumber(pin), threshold) + +// cores/esp32/esp32-hal-uart.h +#define uartBegin(uart_nr, baudrate, config, rxPin, txPin, rx_buffer_size, tx_buffer_size, inverted, rxfifo_full_thrhd) \ + uartBegin(uart_nr, baudrate, config, digitalPinToGPIONumber(rxPin), digitalPinToGPIONumber(txPin), rx_buffer_size, tx_buffer_size, inverted, rxfifo_full_thrhd) +#define uartSetPins(uart, rxPin, txPin, ctsPin, rtsPin) \ + uartSetPins(uart, digitalPinToGPIONumber(rxPin), digitalPinToGPIONumber(txPin), digitalPinToGPIONumber(ctsPin), digitalPinToGPIONumber(rtsPin)) +#define uartDetachPins(uart, rxPin, txPin, ctsPin, rtsPin) \ + uartDetachPins(uart, digitalPinToGPIONumber(rxPin), digitalPinToGPIONumber(txPin), digitalPinToGPIONumber(ctsPin), digitalPinToGPIONumber(rtsPin)) + +#endif // ARDUINO_CORE_BUILD + +#else + +// pin remapping disabled: use stubs +#define digitalPinToGPIONumber(digitalPin) (digitalPin) +#define gpioNumberToDigitalPin(gpioNumber) (gpioNumber) + +#endif + +#endif /* __GPIO_PIN_REMAP_H__ */ diff --git a/src/ruangong.apk b/src/ruangong.apk new file mode 100644 index 0000000..f8e56c3 Binary files /dev/null and b/src/ruangong.apk differ diff --git a/src/sketch_oct16a.c b/src/sketch_oct16a.c new file mode 100644 index 0000000..7afc989 --- /dev/null +++ b/src/sketch_oct16a.c @@ -0,0 +1,65 @@ +#include +#include +SoftwareSerial gpsSerial(4,5); // 使用8号和9号引脚创建软件串口对象 +SoftwareSerial bleSerial(6,7); // 使用10号和11号引脚创建软件串口对象 + +double distance; +const int TrigPin = 0; +const int EchoPin = 1; +void setup() { + Serial.begin(9600); // 初始化硬件串口 + gpsSerial.begin(9600); // 初始化软件串口 + bleSerial.begin(9600); // 初始化软件串口 + pinMode(TrigPin, OUTPUT); + pinMode(EchoPin, INPUT); +} + +void loop() { + if (gpsSerial.available()) { + char c = gpsSerial.read(); + // 判断是否收到完整的一行GPS数据 + if (c == '\n') { + // 解析GPS数据 + String gpsData = gpsSerial.readStringUntil('\n'); + // 假设GPS数据格式为 $GPRMC,HHMMSS.sss,A,纬度,N,经度,E... + // 你可能需要根据你的GPS模块的数据格式进行适当的修改 + if (gpsData.startsWith("$GNRMC")) { + // 使用逗号分割数据字段 + String fields[13]; + int fieldCount = 0; + int startIndex = 0; + int endIndex = gpsData.indexOf(','); + while (endIndex != -1) { + fields[fieldCount++] = gpsData.substring(startIndex, endIndex); + startIndex = endIndex + 1; + endIndex = gpsData.indexOf(',', startIndex); + } + + // 获取纬度和经度数据 + String latitude = fields[3]; + String longitude = fields[5]; + + // 发送经纬度数据到串口 + Serial.print("Latitude: "); + Serial.println(latitude); + Serial.print("Longitude: "); + Serial.println(longitude); + + // 产生一个10us的高脉冲去触发TrigPin + digitalWrite(TrigPin, LOW); + delayMicroseconds(2); + digitalWrite(TrigPin, HIGH); + delayMicroseconds(10); + digitalWrite(TrigPin, LOW); + // 检测脉冲宽度,并计算出距离 + distance = pulseIn(EchoPin, HIGH)/58 ; + Serial.print(distance); + Serial.println("cm"); + String blemsg =""; + if(latitude==0) blemsg = "gps 0000.00000 0000.00000 dis " + String(distance); + else blemsg = "gps " + latitude + " " + longitude +" dis " + String(distance); + bleSerial.println(blemsg); + } + } + } +} \ No newline at end of file