--- /dev/null
+#
+# NOTE! Don't add files that are generated in specific
+# subdirectories here. Add them in the ".gitignore" file
+# in that subdirectory instead.
+#
+# NOTE! Please use 'git ls-files -i --exclude-standard'
+# command after changing this file, to see if there are
+# any tracked files which get ignored after the change.
+#
+# Normal rules
+#
+*.o
+*.d
+*.bin
+*.elf
+*.map
+*/objs/*
+*.zip
+*.svg
+*.dump
+*.img
+*.bak
+tags
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
--- /dev/null
+# Makefile for LPC82x
+
+TARGET_DIR = apps/$(MODULE)/$(NAME)
+
+LPC = lpc82x
+CPU = cortex-m0
+ARCH = armv6-m
+
+CROSS_COMPILE ?= arm-none-eabi-
+CC = $(CROSS_COMPILE)gcc
+FOPTS = -fno-builtin -ffunction-sections -fdata-sections -ffreestanding
+CFLAGS = -Wall -g -O2 -mthumb -mcpu=$(CPU) $(FOPTS)
+LINKOPTS = -static -g -nostartfiles -nostdlib \
+ -Wl,--gc-sections -Wl,--build-id=none \
+ -Wl,-Map=$(TARGET_DIR)/lpc_map_$(LPC).map -Tlpc_link_$(LPC).ld
+
+
+APPS = $(subst apps/,,$(wildcard apps/*/*))
+
+.PHONY: all $(APPS)
+all: $(APPS)
+
+INCLUDES = include/
+TARGET_INCLUDES = $(TARGET_DIR)/
+OBJDIR = objs
+
+SRC = $(wildcard */*.c)
+SRC += $(wildcard lib/*/*.c)
+SRC += $(wildcard lib/protocols/*/*.c)
+OBJS = ${SRC:%.c=${OBJDIR}/%.o}
+DEPS = ${OBJS:%.o=$(OBJDIR)/%.d}
+
+NAME_SRC = $(wildcard $(TARGET_DIR)/*.c)
+NAME_OBJS = ${NAME_SRC:%.c=${OBJDIR}/%.o}
+NAME_DEPS = ${NAME_OBJS:%.o=$(OBJDIR)/%.d}
+
+-include $(DEPS) $(NAME_DEPS)
+
+.SECONDARY: $(OBJS) $(NAME_OBJS)
+.PRECIOUS: %.elf
+%.elf: $(OBJS) $(NAME_OBJS)
+ @echo "Linking $(MODULE)/$(NAME) ..."
+ @$(CC) $(LINKOPTS) $(OBJS) $(NAME_OBJS) -o $@
+
+%.bin: %.elf
+ @echo "Creating image : \e[32m$@\e[39m"
+ @$(CROSS_COMPILE)objcopy -R .stack -R .bss -O binary $^ $@
+ @ls -l $@
+ @$(CROSS_COMPILE)size $^
+ @echo Done.
+
+${OBJDIR}/%.o: %.c
+ @mkdir -p $(dir $@)
+ @echo "-- compiling" $<
+ @$(CC) -MMD -MP -MF ${OBJDIR}/$*.d $(CFLAGS) $< -c -o $@ -I$(INCLUDES) -I$(TARGET_INCLUDES)
+
+
+$(APPS):
+ @make --no-print-directory MODULE=$(shell dirname $@) NAME=$(shell basename $@) apps/$(shell dirname $@)/$(shell basename $@)/$(shell basename $@).bin
+
+all_apps: $(APPS)
+
+clean:
+ rm -rf $(OBJDIR)
+
+mrproper: clean
+ rm -f apps/*/*/*.bin apps/*/*/*.elf apps/*/*/*.map
+
+
+# Some notes :
+# The command "make -f /path/to/here/Makefile module/app_name" does not work, as well
+# as the command "make -f /path/to/here/apps/module/app_name/Makefile".
+# This could be solved in the app Makefiles by replacing
+# "NAME = $(shell basename $(CURDIR))"
+# with
+# "NAME = $(shell basename $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))))"
+# and possibly some similar trick for the base Makefile but this is just
+# unreadable and moslty unusefull, so it won't be supported.
+# Use "make -C /path/to/here/ module/app_name" or "make -C /path/to/here/apps/module/app_name"
+# instead.
+
--- /dev/null
+This repository provides support and example code for LPC82x based boards.
+
+It has support for the LPC82x micro-controller and the peripherals used on
+the boards we made.
+Support for various other cool stuff is added when we play with it.
+
+Example applications are created in the apps/[module name] directories for
+each module, with one sub-directory for each application. These are (and must
+stay) independent.
+Each module has it's own directory under apps/.
+
+More usefull stuff is up to you. Creating an app is *very* simple. Copy
+an example from apps/[module name]/ to the name you want and start coding.
+** Please, no spaces (or special characters) in the directory names ! **
+
+
+********************
+BUID and FLASH
+
+Build has been tested using gcc, and only gcc, in the version provided by
+Debian GNU/Linux distribution, and a few other binary versions available for
+download on the Internet, but any ARM gcc toolchain should do.
+
+In order to get the debian ARM gcc cross-toolchain you should install the
+following packaages : gcc-arm-none-eabi, binutils-arm-none-eabi
+
+There's no need for the related libc package here, the libc does not fit
+in our micro-controller memory. Instead have a look at the content of the
+lib/ directory, and add stuff there.
+
+Once done you should build using the provided makefile by running the
+simple "make" command in the base directory, which will build all apps, or
+run "make <module_name>/<app_name>" to build a specific application. You can
+also run the simple "make" command in the specific app subdirectory to compile
+this application alone.
+
+To flash the binary (the one with .bin) to the LPC Flash you will need the
+lpctool package, now packaged for Debian, starting with Jessie, or available
+in our git repository : http://git.techno-innov.fr/lpctools (Clone using :
+git clone http://gitclone.techno-innov.fr/lpctools and then build (make) and
+use :)
+Usual command lines :
+ lpcprog -d /dev/ttyUSB0 -c id
+ lpcprog -d /dev/ttyUSB0 -c flash app_name.bin
+See lpctools readme and lpcprog or isp help (-h) or manpages for more
+information.
+
+
+********************
+SUPPORTED FEATURES and INTERFACES
+
+- LPC82x micro-controller definitions
+ - Cortex-M0 specific definitions
+ - Cortex-M0 and LPC82x Registers
+ - Interrupts
+ - ROM based division routines
+ - Utility functions to replace ctz and clz instructions (not present in Cortex M0)
+ - IAP ROM based functions (Not tested on LPC82x)
+
+- Bootstrap
+ - vector table
+ - reset handler
+
+- System
+ - watchdog (Not tested yet)
+ - clock / PLL config (Tested using internal RC only)
+ - systick
+ - precise msleep and usleep functions (using systick)
+ - pio configuration
+
+- Simple C Library
+ - memcpy and memset
+ - strcpy, strncpy, strcmp, strncmp, strchr, strrchr, strlen, strnlen
+ - snprintf, vsnprintf
+ - uprintf for easy debug over UART
+
+- Integrated Interface drivers
+ - UART (as UART or RS485)
+ - I²C
+ - ADC
+ - GPIO
+ - GPIO interrupts
+
+- External Device drivers
+ - TMP101 I²C temperature sensor
+ - WS2812 chainable leds
+
+- Other
+
+
+********************
+TODO :
+
+- Sleep mode support
+- Test all the GPIO in different modes
+- Add SPI Support
+- Comparator support
+- Test Watchdog support
+- CRC engine support
+- DMA support
+- More external drivers !
+
--- /dev/null
+/****************************************************************************
+ * core/bootstrap.c
+ *
+ * This file holds the bootstrap code, the vector table, and nothing more.
+ * The bootstrap code is the code of the reset handler, which is called by
+ * the bootloader (executed from internal ROM after power on or reset).
+ * The reset handler code perform copy of data section, clears bss, and
+ * calls the main function.
+ *
+ *
+ * Copyright 2016 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * Example code from frozeneskimo.com :
+ * http://dev.frozeneskimo.com/notes/getting_started_with_cortex_m3_cmsis_and_gnu_tools
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *****************************************************************************/
+
+
+extern unsigned int _end_stack;
+extern unsigned int _end_text;
+extern unsigned int _start_data;
+extern unsigned int _end_data;
+extern unsigned int _start_bss;
+extern unsigned int _end_bss;
+
+extern int main(void);
+
+/* Cortex M0 core interrupt handlers */
+void Reset_Handler(void);
+void NMI_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void HardFault_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void SVC_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void PendSV_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void SysTick_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+/* LPC82x specific interrupt handlers */
+void WAKEUP_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void I2C_0_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void I2C_1_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void I2C_2_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void I2C_3_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void SSP_0_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void SSP_1_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void UART_0_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void UART_1_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void UART_2_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void Comparator_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void ADC_SEQA_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void ADC_SEQB_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void ADC_THCMP_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void ADC_OVR_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void WDT_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void BOD_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void FLASH_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void PININT_0_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void PININT_1_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void PININT_2_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void PININT_3_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void PININT_4_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void PININT_5_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void PININT_6_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void PININT_7_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void DMA_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void RTC_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void SCT_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void MRT_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+void WKT_Handler(void) __attribute__ ((weak, alias ("Dummy_Handler")));
+
+
+void Dummy_Handler(void);
+
+
+/***************************************************************************** */
+/* Vector table */
+/***************************************************************************** */
+void *vector_table[] __attribute__ ((section(".vectors"))) = {
+ &_end_stack, /* Initial SP value */ /* 0 */
+ Reset_Handler,
+ NMI_Handler,
+ HardFault_Handler,
+ 0,
+ 0, /* 5 */
+ 0,
+ /* Entry 7 (8th entry) must contain the 2’s complement of the check-sum
+ of table entries 0 through 6. This causes the checksum of the first 8
+ table entries to be 0 */
+ (void *)0xDEADBEEF, /* Actually, this is done using an external tool. */
+ 0,
+ 0,
+ 0, /* 10 */
+ SVC_Handler,
+ 0,
+ 0,
+ PendSV_Handler,
+ SysTick_Handler, /* 15 */
+ /* LPC82x specific interrupt vectors, see chapter 4 of LPC82x User manual (UM10800) */
+ SSP_0_Handler, /* 16 */ /* IRQ0 */
+ SSP_1_Handler,
+ 0,
+ UART_0_Handler,
+ UART_1_Handler, /* 20 */
+ UART_2_Handler, /* 21 */ /* IRQ5 */
+ 0,
+ I2C_1_Handler,
+ I2C_0_Handler,
+ SCT_Handler, /* 25 */
+ MRT_Handler, /* 26 */ /* IRQ10 */
+ Comparator_Handler,
+ WDT_Handler,
+ BOD_Handler,
+ FLASH_Handler, /* 30 */
+ WKT_Handler, /* IRQ15 */
+ ADC_SEQA_Handler,
+ ADC_SEQB_Handler,
+ ADC_THCMP_Handler,
+ ADC_OVR_Handler, /* 35 */
+ DMA_Handler, /* IRQ20 */
+ I2C_2_Handler,
+ I2C_3_Handler,
+ 0,
+ PININT_0_Handler, /* 40 */
+ PININT_1_Handler, /* IRQ25 */
+ PININT_2_Handler,
+ PININT_3_Handler,
+ PININT_4_Handler,
+ PININT_5_Handler, /* 45 */
+ PININT_6_Handler, /* IRQ30 */
+ PININT_7_Handler,
+};
+
+
+extern void rom_helpers_init(void);
+/*
+ * This is the entry point of the programm
+ * It does the set up of the memory and then starts the main.
+ */
+void Reset_Handler(void)
+{
+ unsigned int *src, *dst;
+
+ /* Copy data section from flash to RAM */
+ src = &_end_text;
+ dst = &_start_data;
+ while (dst < &_end_data)
+ *dst++ = *src++;
+
+ /* Clear the bss section */
+ dst = &_start_bss;
+ while (dst < &_end_bss)
+ *dst++ = 0;
+
+ /* Initialize rom based division helpers */
+ rom_helpers_init();
+ /* Start main programm */
+ main();
+}
+
+void Dummy_Handler(void) {
+ while (1);
+}
+
--- /dev/null
+/****************************************************************************
+ * core/fault_handlers.c
+ *
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * Example code from frozeneskimo.com :
+ * http://dev.frozeneskimo.com/notes/getting_started_with_cortex_m3_cmsis_and_gnu_tools
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *****************************************************************************/
+
+/* Default "fault" handlers, which catch the fault exceptions.
+ * These are defined as weak aliases of a dummy fault handler which enters an empty infinite
+ * loop and chould be overidden by user defined handlers.
+ */
+
+#include "lib/stdint.h"
+
+void fault_info(const char* name, uint32_t len) __attribute__ ((weak, alias ("Dummy_Fault_Handler")));
+
+void Dummy_Fault_Handler(const char* name, uint32_t len) {
+ while (1);
+}
+
+/* Cortex M0 core interrupt handlers */
+void NMI_Handler(void)
+{
+ fault_info(__FUNCTION__, sizeof(__FUNCTION__));
+}
+void HardFault_Handler(void)
+{
+ fault_info(__FUNCTION__, sizeof(__FUNCTION__));
+}
+void SVC_Handler(void)
+{
+ fault_info(__FUNCTION__, sizeof(__FUNCTION__));
+}
+void PendSV_Handler(void)
+{
+ fault_info(__FUNCTION__, sizeof(__FUNCTION__));
+}
+
+
+
--- /dev/null
+/****************************************************************************
+ * core/pio.c
+ *
+ * Copyright 2016 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+/***************************************************************************** */
+/* GPIOs */
+/***************************************************************************** */
+
+/* Public access to Pins setup
+ * Refer to LPC82x documentation (UM10800.pdf) for more information.
+ */
+
+
+#include "lib/stdint.h"
+#include "core/system.h"
+#include "core/pio.h"
+#include "lib/string.h"
+
+
+
+/***************************************************************************** */
+static volatile uint32_t* pio_regs_handles_port0[PORT0_NB_PINS] = {
+ &(LPC_IO_CONTROL->pio0_0),
+ &(LPC_IO_CONTROL->pio0_1),
+ &(LPC_IO_CONTROL->pio0_2),
+ &(LPC_IO_CONTROL->pio0_3),
+ &(LPC_IO_CONTROL->pio0_4),
+ &(LPC_IO_CONTROL->pio0_5),
+ &(LPC_IO_CONTROL->pio0_6),
+ &(LPC_IO_CONTROL->pio0_7),
+ &(LPC_IO_CONTROL->pio0_8),
+ &(LPC_IO_CONTROL->pio0_9),
+ &(LPC_IO_CONTROL->pio0_10),
+ &(LPC_IO_CONTROL->pio0_11),
+ &(LPC_IO_CONTROL->pio0_12),
+ &(LPC_IO_CONTROL->pio0_13),
+ &(LPC_IO_CONTROL->pio0_14),
+ &(LPC_IO_CONTROL->pio0_15),
+ &(LPC_IO_CONTROL->pio0_16),
+ &(LPC_IO_CONTROL->pio0_17),
+ &(LPC_IO_CONTROL->pio0_18),
+ &(LPC_IO_CONTROL->pio0_19),
+ &(LPC_IO_CONTROL->pio0_20),
+ &(LPC_IO_CONTROL->pio0_21),
+ &(LPC_IO_CONTROL->pio0_22),
+ &(LPC_IO_CONTROL->pio0_23),
+ &(LPC_IO_CONTROL->pio0_24),
+ &(LPC_IO_CONTROL->pio0_25),
+ &(LPC_IO_CONTROL->pio0_26),
+ &(LPC_IO_CONTROL->pio0_27),
+ &(LPC_IO_CONTROL->pio0_28),
+};
+
+/* IO config clock */
+/* To change GPIO config the io config block must be powered on */
+static void io_config_clk_on(void)
+{
+ subsystem_power(LPC_SYS_ABH_CLK_CTRL_IO_CONFIG, 1);
+ subsystem_power(LPC_SYS_ABH_CLK_CTRL_SWM, 1);
+}
+static void io_config_clk_off(void)
+{
+ subsystem_power(LPC_SYS_ABH_CLK_CTRL_IO_CONFIG, 0);
+ subsystem_power(LPC_SYS_ABH_CLK_CTRL_SWM, 0);
+}
+
+
+/* Simple copy function. */
+void pio_copy(struct pio* dst, const struct pio* src)
+{
+ if ((dst == NULL) || (src == NULL)) {
+ return;
+ }
+ memcpy(dst, src, sizeof(struct pio));
+}
+
+/* Configure the pin in the requested function and mode. */
+static void config_pio_safe(const struct pio* pp, const struct pin_matrix_entry* me, uint16_t mode)
+{
+ struct lpc_switch_matrix* matrix = LPC_SWITCH_MATRIX;
+ volatile uint32_t* handle = NULL;
+
+ if (pp->pin >= PORT0_NB_PINS) {
+ return;
+ }
+ /* Movable (offset0 & 0x80) or Fixed pin function */
+ if (pp->offset0 & 0x80) {
+ /* Disable fixed pin functions for this pin */
+ if ((pp->offset0 & 0x7F) <= 31) {
+ matrix->pin_enable[0] |= (1 << (pp->offset0 & 0x7F));
+ }
+ if (pp->offset1 <= 31) {
+ matrix->pin_enable[0] |= (1 << pp->offset1);
+ }
+ /* Assign the function to the pin if not a GPIO or fixed pin function */
+ if ((me != NULL) && (me->reg_offset < LPC_MATRIX_NB_PIN_ASSIGN)) {
+ matrix->pin_assign[me->reg_offset] &= ~(0xFF << me->bit_shift);
+ matrix->pin_assign[me->reg_offset] |= (pp->pin << me->bit_shift);
+ }
+ /* Configure the GPIO mode */
+ handle = pio_regs_handles_port0[pp->pin];
+ *handle = mode;
+ } else {
+ /* Maybe disable the other fixed pin entry ? */
+ if (pp->offset1 <= 31) {
+ matrix->pin_enable[0] |= (1 << pp->offset1);
+ }
+ /* Enable our own fixed pin entry */
+ if (pp->offset0 <= 31) {
+ matrix->pin_enable[0] &= ~(1 << pp->offset0);
+ }
+ }
+
+}
+void config_pio(const struct pio* pp, const struct pin_matrix_entry* matrix, uint16_t mode)
+{
+ if (pp == NULL) {
+ return;
+ }
+
+ /* Make sure IO_Config is clocked */
+ io_config_clk_on();
+
+ config_pio_safe(pp, matrix, mode);
+
+ /* Config done, power off IO_CONFIG block */
+ io_config_clk_off();
+}
+
+
+void set_pins(const struct pio_config* pins)
+{
+ int i = 0;
+ /* Make sure IO_Config is clocked */
+ io_config_clk_on();
+ for (i = 0; pins[i].pio.pin != 0xFF; i++) {
+ config_pio_safe(&(pins[i].pio), &(pins[i].matrix), pins[i].mode);
+ }
+ /* Config done, power off IO_CONFIG block */
+ io_config_clk_off();
+}
+
--- /dev/null
+/****************************************************************************
+ * core/rom_helpers.c
+ *
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+/* ROM helpers are functions avalable for the user which do not use space in the
+ * internal reprogrammable flash.
+ * They are stored in the internal ROM memory and may be called using specific
+ * calls with defined parameters depending on the ROM call used.
+ * Refer to LPC82x documentation (UM10800.pdf) for more information.
+ */
+
+#include "lib/stdint.h"
+#include "core/system.h"
+#include "core/lpc_core.h"
+
+/*******************************************************************************/
+/* Integer division using ROM based division routines */
+/*******************************************************************************/
+
+struct idiv_return {
+ int quotient;
+ int remainder;
+};
+struct uidiv_return {
+ unsigned quotient;
+ unsigned remainder;
+};
+
+struct lpc_rom_div_helpers {
+ /* Signed integer division */
+ int (*rom_sidiv)(int numerator, int denominator);
+ /* Unsigned integer division */
+ unsigned (*rom_uidiv)(unsigned numerator, unsigned denominator);
+ /* Signed integer division with remainder */
+ struct idiv_return (*rom_sidivmod)(int numerator, int denominator);
+ /* Unsigned integer division with remainder */
+ struct uidiv_return (*rom_uidivmod)(unsigned numerator, unsigned denominator);
+};
+
+static struct lpc_rom_div_helpers* rom_div_helpers;
+
+/* Division (/) */
+int __aeabi_idiv(int numerator, int denominator)
+{
+ return rom_div_helpers->rom_sidiv(numerator, denominator);
+}
+unsigned __aeabi_uidiv(unsigned numerator, unsigned denominator)
+{
+ return rom_div_helpers->rom_uidiv(numerator, denominator);
+}
+
+/* Modulo (%) */
+void __aeabi_idivmod(int numerator, int denominator)
+{
+ struct idiv_return result = rom_div_helpers->rom_sidivmod(numerator, denominator);
+ register uint32_t r0 asm("r0") = result.quotient;
+ register uint32_t r1 asm("r1") = result.remainder;
+ asm volatile("" : "+r"(r0), "+r"(r1) : "r"(r0), "r"(r1));
+}
+void __aeabi_uidivmod(unsigned numerator, unsigned denominator)
+{
+ struct uidiv_return result = rom_div_helpers->rom_uidivmod(numerator, denominator);
+ register uint32_t r0 asm("r0") = result.quotient;
+ register uint32_t r1 asm("r1") = result.remainder;
+ asm volatile("" : "+r"(r0), "+r"(r1) : "r"(r0), "r"(r1));
+}
+
+
+/*******************************************************************************/
+/* In Application Programming ROM based routines */
+/*******************************************************************************/
+
+enum iap_status {
+ IAP_STATUS_CMD_SUCCESS = 0,
+ IAP_STATUS_INVALID_COMMAND,
+ IAP_STATUS_SRC_ADDR_ERROR,
+ IAP_STATUS_DST_ADDR_ERROR,
+ IAP_STATUS_SRC_ADDR_NOT_MAPPED,
+ IAP_STATUS_DST_ADDR_NOT_MAPPED,
+ IAP_STATUS_COUNT_ERROR,
+ IAP_STATUS_INVALID_SECTOR,
+ IAP_STATUS_SECTOR_NOT_BLANK,
+ IAP_STATUS_SECTOR_NOT_PREPARED_FOR_WRITE_OPERATION,
+ IAP_STATUS_COMPARE_ERROR,
+ IAP_STATUS_BUSY,
+};
+
+enum iap_commands {
+ IAP_CMD_PREPARE_SECTORS_FOR_WRITE = 50,
+ IAP_CMD_COPY_RAM_TO_FLASH = 51,
+ IAP_CMD_ERASE_SECTORS = 52,
+ IAP_CMD_BLANK_CHECK_SECTORS = 53,
+ IAP_CMD_READ_PART_ID = 54,
+ IAP_CMD_READ_BOOT_CODE_VERSION = 55,
+ IAP_CMD_COMPARE = 56,
+ IAP_CMD_REINVOQUE_ISP = 57,
+ IAP_CMD_READ_UID = 58,
+ IAP_CMD_ERASE_PAGE = 59,
+};
+
+typedef void (*iap_entry_func)(uint32_t*, uint32_t*);
+iap_entry_func iap_entry;
+
+static uint32_t params[5];
+static uint32_t results[4];
+
+
+int iap_prepare_flash(uint32_t start_sector, uint32_t end_sector)
+{
+ params[0] = IAP_CMD_PREPARE_SECTORS_FOR_WRITE;
+ params[1] = start_sector;
+ params[2] = end_sector;
+ iap_entry(params, results);
+ return results[0];
+}
+
+int iap_erase_flash_sectors(uint32_t start_sector, uint32_t end_sector)
+{
+ params[0] = IAP_CMD_ERASE_SECTORS;
+ params[1] = start_sector;
+ params[2] = end_sector;
+ params[3] = (get_main_clock() / 1000);
+ lpc_disable_irq();
+ iap_entry(params, results);
+ lpc_enable_irq();
+ return results[0];
+}
+
+int iap_erase_flash_pages(uint32_t start_page, uint32_t end_page)
+{
+ params[0] = IAP_CMD_ERASE_PAGE;
+ params[1] = start_page;
+ params[2] = end_page;
+ params[3] = (get_main_clock() / 1000);
+ lpc_disable_irq();
+ iap_entry(params, results);
+ lpc_enable_irq();
+ return results[0];
+}
+int iap_copy_ram_to_flash(uint32_t dest, uint32_t src, uint32_t size)
+{
+ params[0] = IAP_CMD_COPY_RAM_TO_FLASH;
+ params[1] = dest & ~(0x03);
+ params[2] = src & ~(0x03);
+ params[3] = size & ~(0x03);
+ params[4] = (get_main_clock() / 1000);
+ lpc_disable_irq();
+ iap_entry(params, results);
+ lpc_enable_irq();
+ return results[0];
+}
+
+uint32_t iap_read_part_id(void)
+{
+ params[0] = IAP_CMD_READ_PART_ID;
+ iap_entry(params, results);
+ return results[1];
+}
+
+
+/*******************************************************************************/
+/* Rom based routines initialisation */
+/*******************************************************************************/
+#define LPC_82x_ROM_HELPERS (0x1FFF1FF8)
+#define LPC_82x_IAP_ROM_LOC (0x1FFF1FF1)
+
+struct rom_helpers {
+ const unsigned p_dev0;
+ const unsigned p_dev1;
+ const unsigned p_dev2;
+ const unsigned rom_power_profiles;
+ const unsigned rom_div;
+ const unsigned rom_i2c_driver;
+ const unsigned p_dev6;
+ const unsigned rom_spi_driver;
+ const unsigned rom_adc_driver;
+ const unsigned rom_usart_driver;
+ const unsigned p_dev10;
+};
+#define ROM_DRIVERS ((struct rom_helpers *)(*((uint32_t *)LPC_82x_ROM_HELPERS)))
+
+void rom_helpers_init(void)
+{
+ rom_div_helpers = (struct lpc_rom_div_helpers*)(ROM_DRIVERS->rom_div);
+ iap_entry = (iap_entry_func)LPC_82x_IAP_ROM_LOC;
+}
+
--- /dev/null
+/****************************************************************************
+ * core/system.c
+ *
+ * All low-level functions for clocks configuration and switch, system
+ * power-up, reset, and power-down.
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+/*
+ * This file holds some system wide initialisation functions and clock or sleep
+ * related functions.
+ */
+
+#include "lib/stdint.h"
+
+#include "core/lpc_regs.h"
+#include "core/lpc_core.h"
+#include "core/system.h"
+
+
+/* Private defines */
+#define LPC_IRC_OSC_CLK (12000000UL) /* Internal RC oscillator frequency : 12MHz */
+
+/***************************************************************************** */
+/* Global data */
+/***************************************************************************** */
+struct lpc_desc_private {
+ uint32_t main_clock;
+ uint8_t brown_out_detection_enabled;
+ uint8_t need_IRC;
+};
+static struct lpc_desc_private lpc_private = {
+ .main_clock = LPC_IRC_OSC_CLK,
+ .brown_out_detection_enabled = 0,
+ .need_IRC = 1,
+};
+
+/***************************************************************************** */
+/* Required system inits */
+/***************************************************************************** */
+
+/* Configure the brown-out detection */
+void system_brown_out_detection_config(uint32_t level)
+{
+ struct lpc_sys_config* sys_config = LPC_SYS_CONFIG;
+
+ if (level == 0) {
+ /* Disable Brown-Out Detection, power it down */
+ sys_config->powerdown_run_cfg |= LPC_POWER_DOWN_BOD;
+ lpc_private.brown_out_detection_enabled = 0;
+ } else {
+ /* Power on Brown-Out Detection. */
+ sys_config->powerdown_run_cfg &= ~(LPC_POWER_DOWN_BOD);
+ lpc_private.brown_out_detection_enabled = 1;
+ /* Configure Brown-Out Detection */
+ /* FIXME */
+ }
+}
+
+/***************************************************************************** */
+/* Power */
+/***************************************************************************** */
+/* Set up power-down control
+ * Change reset power state to our default, removing power from unused interfaces
+ */
+void system_set_default_power_state(void)
+{
+ struct lpc_sys_config* sys_config = LPC_SYS_CONFIG;
+ /* Start with all memory powered on and nothing else */
+ sys_config->sys_AHB_clk_ctrl = LPC_SYS_ABH_CLK_CTRL_MEM_ALL;
+}
+
+/* Enter deep sleep.
+ * NOTE : entering deep sleep implies a lot of side effects. I'll try to list them all here
+ * so this can be done right.
+ *
+ * Note : see remark about RTC and deep sleep in section 5.3.3 of UM10441
+ */
+void enter_deep_sleep(void)
+{
+ struct lpc_sys_config* sys_config = LPC_SYS_CONFIG;
+
+ /* Ask for the same clock status when waking up */
+ sys_config->powerdown_wake_cfg = sys_config->powerdown_run_cfg;
+ /* Set deep_sleep config */
+ if (lpc_private.brown_out_detection_enabled) {
+ sys_config->powerdown_sleep_cfg = LPC_DEEP_SLEEP_CFG_NOWDTLOCK_BOD_ON;
+ } else {
+ sys_config->powerdown_sleep_cfg = LPC_DEEP_SLEEP_CFG_NOWDTLOCK_BOD_OFF;
+ }
+ /* Enter deep sleep */
+ /* FIXME */
+}
+
+/* Power on or off a subsystem */
+void subsystem_power(uint32_t power_bit, uint32_t on_off)
+{
+ struct lpc_sys_config* sys_config = LPC_SYS_CONFIG;
+ if (on_off == 1) {
+ sys_config->sys_AHB_clk_ctrl |= power_bit;
+ } else {
+ sys_config->sys_AHB_clk_ctrl &= ~(power_bit);
+ }
+}
+/* Check whether a subsystem is powered or not */
+uint8_t subsystem_powered(uint32_t power_bit)
+{
+ struct lpc_sys_config* sys_config = LPC_SYS_CONFIG;
+ return (sys_config->sys_AHB_clk_ctrl & power_bit);
+}
+
+/* Reset a subsystem */
+void subsystem_reset_hold(uint32_t reset_bit)
+{
+ struct lpc_sys_config* sys_config = LPC_SYS_CONFIG;
+ sys_config->peripheral_reset_ctrl &= ~(reset_bit);
+}
+void subsystem_reset_release(uint32_t reset_bit)
+{
+ struct lpc_sys_config* sys_config = LPC_SYS_CONFIG;
+ sys_config->peripheral_reset_ctrl |= reset_bit;
+}
+void subsystem_reset(uint32_t reset_bit)
+{
+ subsystem_reset_hold(reset_bit);
+ /* Arbitrary and short delay */
+ nop();
+ nop();
+ subsystem_reset_release(reset_bit);
+}
+
+/***************************************************************************** */
+/* System Clock */
+/***************************************************************************** */
+static void propagate_main_clock(void);
+
+/* Main clock config : set up the system clock
+ * We use internal RC oscilator as sys_pllclkin if pll is ussed
+ * Note that during PLL lock wait we are running on internal RC
+ * Note for M and P calculation :
+ * FCCO must be between 156MHz and 320MHz
+ * M ranges from 1 to 32, steps of 1, M = Freq_out / Freq_in
+ * P ranges from 1 to 8, P must be a power of two, FCCO = 2 * P * Freq_out
+ * Freq_out < 100 MHz
+ * Freq_in is between 10 and 25 MHz
+ * M = Freq_out / Freq_in
+ *
+ * freq_sel : set to one of the predefined values. See core/system.h
+ */
+void clock_config(uint32_t freq_sel)
+{
+ struct lpc_sys_config* sys_config = LPC_SYS_CONFIG;
+
+ lpc_disable_irq();
+ /* Turn on IRC */
+ sys_config->powerdown_run_cfg &= ~(LPC_POWER_DOWN_IRC);
+ sys_config->powerdown_run_cfg &= ~(LPC_POWER_DOWN_IRC_OUT);
+ /* Use IRC clock for main clock */
+ sys_config->main_clk_sel = LPC_MAIN_CLK_SRC_IRC_OSC;
+ lpc_private.need_IRC = 1;
+ /* Switch the main clock source */
+ sys_config->main_clk_upd_en = 0;
+ sys_config->main_clk_upd_en = 1;
+
+ /* Turn off / power off external crystal */
+ sys_config->powerdown_run_cfg |= LPC_POWER_DOWN_SYS_OSC;
+ /* Set AHB clock divider : divide by one ... */
+ sys_config->sys_AHB_clk_div = 1;
+ /* power off PLL */
+ sys_config->powerdown_run_cfg |= LPC_POWER_DOWN_SYSPLL;
+
+ /* If using only internal RC, we are done */
+ if (freq_sel == FREQ_SEL_IRC) {
+ lpc_private.main_clock = LPC_IRC_OSC_CLK;
+ } else {
+ uint32_t M = ((freq_sel >> 3) & 0xFF);
+ uint32_t N = 0; /* P = 2 ^ N */
+
+ /* Select value for N */
+ switch (freq_sel) {
+ case 3: /* FREQ_SEL_36MHz */
+ case 2: /* FREQ_SEL_24MHz */
+ N = 2; /* P = 4 */
+ break;
+ default: /* 48MHz to 60 MHz */
+ N = 1; /* P = 2 */
+ break;
+ }
+ lpc_private.main_clock = (((freq_sel >> 3) & 0xFF) * 12 * 1000 * 1000);
+ /* Setup PLL dividers */
+ sys_config->sys_pll_ctrl = (((M - 1) & 0x1F) | (N << 5)); /* 5 is PSEL shift */
+ /* Set sys_pll_clk to internal RC */
+ sys_config->sys_pll_clk_sel = LPC_PLL_CLK_SRC_IRC_OSC;
+ sys_config->sys_pll_clk_upd_en = 0; /* SYSPLLCLKUEN must go from LOW to HIGH */
+ sys_config->sys_pll_clk_upd_en = 1;
+ /* Power-up PLL */
+ sys_config->powerdown_run_cfg &= ~(LPC_POWER_DOWN_SYSPLL);
+ /* Wait Until PLL Locked */
+ while (!(sys_config->sys_pll_status & 0x01));
+ /* Use PLL as main clock */
+ sys_config->main_clk_sel = LPC_MAIN_CLK_SRC_PLL_OUT;
+ /* Switch the main clock source */
+ sys_config->main_clk_upd_en = 0;
+ sys_config->main_clk_upd_en = 1;
+ }
+
+ /* And call all clock updaters */
+ propagate_main_clock();
+ /* Turn interrupts on once again*/
+ lpc_enable_irq();
+}
+
+uint32_t get_main_clock(void)
+{
+ return lpc_private.main_clock;
+}
+
+/***************************************************************************** */
+/* Peripheral Clocks */
+/***************************************************************************** */
+void Dummy_Clk_Updater(void) {
+ do { } while (0);
+}
+
+void uart_clk_update(void) __attribute__ ((weak, alias ("Dummy_Clk_Updater")));
+void i2c_clock_update(void) __attribute__ ((weak, alias ("Dummy_Clk_Updater")));
+void ssp_clk_update(void) __attribute__ ((weak, alias ("Dummy_Clk_Updater")));
+void adc_clk_update(void) __attribute__ ((weak, alias ("Dummy_Clk_Updater")));
+
+static void propagate_main_clock(void)
+{
+ uart_clk_update();
+ i2c_clock_update();
+ ssp_clk_update();
+ adc_clk_update();
+}
+
+/***************************************************************************** */
+/* CLK Out */
+/***************************************************************************** */
+/* This is mainly a debug feature, but can be used to provide a clock to an
+ * external peripheral */
+/* Note that PIO0_12 is the only pin possible for CLK_Out, and is multiplexed
+ * with ISP mode selection on reset.
+ * The pin must be enabled using a pio table passed to the set_pins() function.
+ */
+
+void clkout_on(uint32_t src, uint32_t div)
+{
+ struct lpc_sys_config* sys_config = LPC_SYS_CONFIG;
+
+ /* Select clk_out clock source */
+ sys_config->clk_out_src_sel = (src & 0x03);
+ /* Activate clk_out */
+ sys_config->clk_out_div = (div & 0xFF);
+ sys_config->clk_out_upd_en = 0;
+ sys_config->clk_out_upd_en = 1;
+}
+void clkout_off(void)
+{
+ struct lpc_sys_config* sys_config = LPC_SYS_CONFIG;
+ sys_config->clk_out_div = 0; /* Disable CLKOUT */
+ sys_config->clk_out_upd_en = 0;
+ sys_config->clk_out_upd_en = 1;
+}
+
+
+/***************************************************************************** */
+/* Default sleep function */
+/***************************************************************************** */
+/* Note that if the systick core functions are used these will be overridden */
+
+/* This "msleep" is a simple wait routine which is close to a millisecond sleep
+ * whith a CPU Clock of 24MHz, but has no exact relation to time.
+ * It is highly dependent to CPU clock speed anyway.
+ * Note : This is an active sleep !
+ */
+static void def_msleep(uint32_t ms)
+{
+ volatile uint32_t dec = ms * 2667;
+ while (dec--);
+}
+
+/* Something that's not too far from a microsecond sleep at 24MHz CPU Clock
+ * Note : This is an active sleep !
+ */
+static inline void def_usleep(uint32_t us)
+{
+ volatile uint32_t dec = us * 2;
+ while (dec--);
+}
+
+void msleep(uint32_t ms) __attribute__ ((weak, alias ("def_msleep")));
+void usleep(uint32_t us) __attribute__ ((weak, alias ("def_usleep")));
+
--- /dev/null
+/****************************************************************************
+ * core/systick.c
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+
+/***************************************************************************** */
+/* System Tick Timer */
+/***************************************************************************** */
+
+/* Driver for the internal systick timer of the LPC82x.
+ * Refer to the LPC82x documentation (UM10800.pdf) for more information
+ */
+
+#include "core/system.h"
+#include "core/systick.h"
+#include "lib/errno.h"
+
+
+/* Static variables */
+static volatile uint32_t sleep_count = 0;
+static volatile uint32_t tick_ms = 0;
+static volatile uint32_t systick_running = 0;
+static volatile uint32_t tick_reload = 0;
+static uint32_t usleep_us_count = 0;
+
+/* Wraps every 50 days or so with a 1ms tick */
+static volatile uint32_t global_wrapping_system_ticks = 0;
+/* The systick cycles run at get_main_clock(), and would wrap more often! */
+static volatile uint32_t global_wrapping_system_clock_cycles = 0;
+
+
+struct systick_callback {
+ void (*callback) (uint32_t);
+ uint16_t period;
+ uint16_t countdown;
+};
+static volatile struct systick_callback cbs[MAX_SYSTICK_CALLBACKS] = {};
+
+/* System Tick Timer Interrupt Handler */
+void SysTick_Handler(void)
+{
+ int i = 0;
+ global_wrapping_system_ticks++;
+ global_wrapping_system_clock_cycles += tick_reload;
+ if (sleep_count != 0) {
+ sleep_count--;
+ }
+ for (i = 0; i < MAX_SYSTICK_CALLBACKS; i++) {
+ if (cbs[i].callback != NULL) {
+ cbs[i].countdown--;
+ if (cbs[i].countdown == 0) {
+ cbs[i].countdown = cbs[i].period;
+ cbs[i].callback(global_wrapping_system_ticks);
+ }
+ }
+ }
+}
+
+
+/* Register a callback to be called every 'period' system ticks.
+ * returns the callback number if registration was OK.
+ * returns negative value on error.
+ * The callback will get the "global_wrapping_system_ticks" as argument, which wraps every 50 days
+ * or so with a 1ms tick
+ */
+int add_systick_callback(void (*callback) (uint32_t), uint16_t period)
+{
+ int i = 0;
+ if (period == 0) {
+ return -EINVAL;
+ }
+ for (i = 0; i < MAX_SYSTICK_CALLBACKS; i++) {
+ if (cbs[i].callback == NULL) {
+ cbs[i].callback = callback;
+ cbs[i].period = period;
+ cbs[i].countdown = period;
+ return i;
+ }
+ }
+ return -EBUSY;
+}
+
+int remove_systick_callback(void (*callback) (uint32_t))
+{
+ int i = 0;
+ for (i = 0; i < MAX_SYSTICK_CALLBACKS; i++) {
+ if (cbs[i].callback == callback) {
+ cbs[i].callback = NULL;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+/***************************************************************************** */
+/* systick timer control function */
+
+/* Start the system tick timer
+ * Starting the systick timer also resets the internal tick counters.
+ * If you need a value that goes beyond one start/stop cycle and accross resets,
+ * then it's up to you to keep track of this using systick_get_tick_count() and/or
+ * systick_get_clock_cycles().
+ */
+void systick_start(void)
+{
+ struct lpc_system_tick* systick = LPC_SYSTICK;
+ systick->value = 0;
+ systick_running = 1;
+ global_wrapping_system_ticks = 0;
+ global_wrapping_system_clock_cycles = tick_reload;
+ systick->control |= LPC_SYSTICK_CTRL_ENABLE;
+}
+/* Stop the system tick timer */
+void systick_stop(void)
+{
+ struct lpc_system_tick* systick = LPC_SYSTICK;
+ systick->control &= ~(LPC_SYSTICK_CTRL_ENABLE);
+ systick_running = 0;
+ systick->value = 0;
+}
+/* Reset the system tick timer, making it count down from the reload value again
+ * Reseting the systick timer also resets the internal tick counters.
+ * If you need a value that goes beyond one start/stop cycle and accross resets,
+ * then it's up to you to keep track of this using systick_get_tick_count() and/or
+ * systick_get_clock_cycles().
+ */
+void systick_reset(void)
+{
+ struct lpc_system_tick* systick = LPC_SYSTICK;
+ systick->value = 0;
+ global_wrapping_system_ticks = 0;
+ global_wrapping_system_clock_cycles = tick_reload;
+}
+
+/* Get system tick timer current value (counts at get_main_clock() !)
+ * systick_get_timer_val returns a value between 0 and systick_get_timer_reload_val()
+ */
+uint32_t systick_get_timer_val(void)
+{
+ struct lpc_system_tick* systick = LPC_SYSTICK;
+ return systick->value;
+}
+/* Get system tick timer reload value */
+uint32_t systick_get_timer_reload_val(void)
+{
+ return tick_reload;
+}
+
+/* Check if systick is running (return 1) or not (return 0) */
+uint32_t is_systick_running(void)
+{
+ return systick_running;
+}
+
+/* Get the system tick period in ms
+ * A vaue of 0 means the system tick timer has not been configured.
+ * Note : calls to msleep() or usleep() will configure the system tick timer
+ * with a value of 1ms if it was not configured yet.
+ */
+uint32_t systick_get_tick_ms_period(void)
+{
+ return tick_ms;
+}
+
+/* Get the "timer wrapped" indicator.
+ * Used in usleep() function.
+ * Note : the first to call this function will get the right information.
+ * All subsequent calls will get wrong indication.
+ * Thus this function is not exported to user space, user should compare global
+ * ticks values or tick counter values. */
+static uint32_t systick_counted_to_zero(void)
+{
+ struct lpc_system_tick* systick = LPC_SYSTICK;
+ return (systick->control & LPC_SYSTICK_CTRL_COUNTFLAG);
+}
+
+
+/* Get the number of system ticks ... since last wrapping of the counter, which
+ * is about 50 days with a 1ms system tick. */
+uint32_t systick_get_tick_count(void)
+{
+ return global_wrapping_system_ticks;
+}
+
+/* Get the number of clock cycles ... since last wrapping of the counter. */
+uint32_t systick_get_clock_cycles(void)
+{
+ struct lpc_system_tick* systick = LPC_SYSTICK;
+ /* global_wrapping_system_clock_cycles has been initialised to reload value, thus there is
+ * no need to add it here, making the call quicker */
+ return global_wrapping_system_clock_cycles - systick->value;
+}
+
+/***************************************************************************** */
+/* Power up the system tick timer.
+ * ms is the interval between system tick timer interrupts. If set to 0, the default
+ * value is used, which should provide a 1ms period.
+ */
+void systick_timer_on(uint32_t ms)
+{
+ struct lpc_system_tick* systick = LPC_SYSTICK;
+ uint32_t reload; /* The reload value for the counter */
+
+ /* Set the reload value */
+ if (ms != 0) {
+ reload = ((get_main_clock() / 1000) * ms) - 1;
+ } else {
+ reload = (get_main_clock() / 1000) - 1;
+ ms = 1;
+ }
+ /* For the LPC82x the system tick clock is fixed to half the frequency of the system clock */
+ reload = reload >> 1; /* Divide by 2 */
+ systick->reload_val = (reload & 0xffffff);
+ tick_ms = ms;
+ tick_reload = systick->reload_val;
+
+ /* Start counting from the reload value, writting anything would do ... */
+ systick->value = reload;
+ /* Consider we already counted one cycle, making further reading of this count easier */
+ global_wrapping_system_clock_cycles = tick_reload;
+
+ /* And enable counter interrupt */
+ systick->control = LPC_SYSTICK_CTRL_TICKINT;
+ systick_running = 0;
+
+ /* Perform this division now for the usleep function. */
+ usleep_us_count = get_main_clock() / (1000 * 1000);
+ /* For the LPC82x the system tick clock is fixed to half the frequency of the system clock */
+ usleep_us_count = (usleep_us_count >> 1); /* Divide by two */
+
+ /* FIXME : document this */
+ NVIC_SetPriority(SYSTICK_IRQ, ((1 << LPC_NVIC_PRIO_BITS) - 1));
+}
+
+/* Removes the main clock from the selected timer block */
+void systick_timer_off(void)
+{
+ struct lpc_system_tick* systick = LPC_SYSTICK;
+ systick->control = 0;
+ systick->reload_val = 0;
+ tick_ms = 0;
+ tick_reload = 0;
+ systick_running = 0;
+}
+
+
+/***************************************************************************** */
+/* Sleeping functions */
+
+/* Set the sleep countdown value
+ * A sleep will end when this value reaches 0
+ * Note that calls to this function while a sleep() has been initiated will change the
+ * sleep duration ....
+ */
+static inline void set_sleep(uint32_t ticks)
+{
+ sleep_count = ticks;
+}
+
+/* Actual sleep function, checks that system tick counter is configured to generate
+ * an interrupt and to move sleep_count down to 0
+ */
+#define SYSTICK_CAN_SLEEP (LPC_SYSTICK_CTRL_TICKINT | LPC_SYSTICK_CTRL_ENABLE)
+static uint32_t sleep(void)
+{
+ struct lpc_system_tick* systick = LPC_SYSTICK;
+ if ((systick->control & SYSTICK_CAN_SLEEP) != SYSTICK_CAN_SLEEP) {
+ return -1;
+ }
+ do { } while (sleep_count != 0);
+ return 0;
+}
+
+/* This msleep sleeps less than the required amount of time as it forgets about
+ * the already elapsed time of the systick timer since last tick. */
+void msleep(uint32_t ms)
+{
+ uint32_t ticks = 0;
+
+ if (tick_ms == 0) {
+ systick_timer_on(1);
+ ticks = ms;
+ } else {
+ ticks = ms / tick_ms;
+ }
+ set_sleep(ticks);
+ if (systick_running == 0) {
+ systick_start();
+ }
+ sleep();
+}
+
+/* This usleep function tries to sleep at most the required amount of time.
+ * The setup is so long that it cannot sleep for less than 10us when running at 48MHz
+ */
+void usleep(uint32_t us)
+{
+ struct lpc_system_tick* systick = LPC_SYSTICK;
+ uint32_t start = systick->value; /* Grab the starting (call time) value now */
+ uint32_t count = 0;
+ uint32_t end = 0;
+
+ end = systick_counted_to_zero(); /* erase loop indicator */
+ if (us > 1000) {
+ msleep(us / 1000);
+ us = us % 1000;
+ } else {
+ if (systick_running == 0) {
+ if (tick_ms == 0) {
+ systick_timer_on(1);
+ }
+ systick_start();
+ }
+ }
+ count = usleep_us_count * us;
+ /* Remember that systick is a decrementing counter */
+ if (count > start) {
+ end = (systick->reload_val - (count - start));
+ do { } while (systick_counted_to_zero() == 0); /* Wait for timer loop */
+ do { } while (systick->value > end); /* Wait for remaining part of sleep duration */
+ } else {
+ end = start - count;
+ /* Wait for sleep duration.
+ * If the counter looped, it means we already waited too much */
+ do { } while ((systick->value > end) && (systick_counted_to_zero() == 0));
+ }
+}
+
+void usleep_short(uint32_t us)
+{
+ struct lpc_system_tick* systick = LPC_SYSTICK;
+ uint32_t start = systick->value; /* Grab the starting (call time) value now */
+ uint32_t count = usleep_us_count * us;
+ uint32_t end = systick_counted_to_zero(); /* Erase loop indicator */
+
+ /* Remember that systick is a decrementing counter */
+ if (count > start) {
+ end = (systick->reload_val - (count - start));
+ do { } while (systick_counted_to_zero() == 0); /* Wait for timer loop */
+ do { } while (systick->value > end); /* Wait for remaining part of sleep duration */
+ } else {
+ end = start - count;
+ /* Wait for sleep duration.
+ * If the counter looped, it means we already waited too much */
+ do { } while ((systick->value > end) && (systick_counted_to_zero() == 0));
+ }
+}
+
--- /dev/null
+/****************************************************************************
+ * core/watchdog.c
+ *
+ * Watchdog support
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+/*
+ * This file implements support of the Windowed Watchdog (WWDT)
+ */
+
+#include "core/system.h"
+#include "core/watchdog.h"
+
+
+
+/***************************************************************************** */
+void watchdog_feed(void)
+{
+ struct lpc_watchdog* wdt = LPC_WDT;
+ lpc_disable_irq();
+ wdt->feed_seqence = 0xAA;
+ wdt->feed_seqence = 0x55;
+ lpc_enable_irq();
+}
+
+static void (*wdt_callback)(void) = NULL;
+
+
+/* With no callback, the watchdog interupt may be used to wake up by generating an interrupt */
+void WDT_Handler(void)
+{
+ struct lpc_watchdog* wdt = LPC_WDT;
+ wdt->mode |= LPC_WDT_INTR_FLAG;
+ /* Call user callback if the user registered one */
+ if (wdt_callback != NULL) {
+ wdt_callback();
+ }
+}
+
+
+/* Lock the watchdog clock source power.
+ * Once locked, writes to the current watchdog clock power bits in powerdown_*_cfg will
+ * have no effect.
+ * It is still possible to switch the watchdog clock source and turn of the clock if the
+ * watchdog clock source has not been locked yet.
+ */
+void watchdog_lock_clk_src_power(void)
+{
+ struct lpc_sys_config* sys_config = LPC_SYS_CONFIG;
+ struct lpc_watchdog* wdt = LPC_WDT;
+
+ sys_config->powerdown_sleep_cfg &= ~(LPC_POWER_DOWN_WDT_OSC);
+ sys_config->powerdown_wake_cfg &= ~(LPC_POWER_DOWN_WDT_OSC);
+
+ wdt->mode |= LPC_WDT_CLK_POWER_LOCK;
+}
+
+/* Lock the watchdog timer value */
+void watchdog_lock_timer_val(void)
+{
+ struct lpc_watchdog* wdt = LPC_WDT;
+ wdt->mode |= LPC_WDT_TIMER_VAL_PROTECT;
+}
+
+/* Change the watchdog timer value, if not protected */
+void watchdog_set_timer_val(uint32_t nb_clk)
+{
+ struct lpc_watchdog* wdt = LPC_WDT;
+
+ if (!(wdt->mode & LPC_WDT_TIMER_VAL_PROTECT)) {
+ wdt->timer_const = ((nb_clk >> 2) & LPC_WDT_TIMER_MAX);
+ }
+}
+
+/* Lock the watchdog and all related features.
+ * Calls all the other watchdog_lock_* functions (clk_src, clk_src_power, timer_val, enable).
+ */
+void watchdog_lock_full(void)
+{
+ watchdog_lock_timer_val();
+ watchdog_lock_clk_src_power();
+}
+
+/*
+ * Configure the watchdog.
+ * clk_sel is either 0 (IRC) or 1 (WDTCLK). The corresponding clock source will be powered on.
+ * Note : only WDTCLK is running in deep power down mode
+ * Note : protecting the clock source power will prevent turning off the IRC for power saving
+ * if it is selected as main clock source.
+ */
+void watchdog_config(const struct wdt_config* wd_conf)
+{
+ struct lpc_sys_config* sys_config = LPC_SYS_CONFIG;
+ struct lpc_watchdog* wdt = LPC_WDT;
+
+ NVIC_DisableIRQ(WDT_IRQ);
+ /* Power wadchdog block before changing it's configuration */
+ subsystem_power(LPC_SYS_ABH_CLK_CTRL_Watchdog, 1);
+ /* Configure watchdog timeout for normal operation */
+ wdt->timer_const = ((wd_conf->nb_clk >> 2) & LPC_WDT_TIMER_MAX);
+ /* If intr_mode_only is set, a watchdog timeout will trigger an interrupt instead of a reset */
+ if (wd_conf->intr_mode_only == 1) {
+ wdt->mode = LPC_WDT_EN;
+ } else {
+ wdt->mode = LPC_WDT_EN | LPC_WDT_RESET_ON_TIMEOUT;
+ }
+ /* Register the callback for the interrupt */
+ wdt_callback = wd_conf->callback;
+ /* Power ON Watchdog clock */
+ sys_config->powerdown_run_cfg &= ~(LPC_POWER_DOWN_WDT_OSC);
+ /* Use the windows functionnality ? */
+ if (wd_conf->wdt_window > 0x100) {
+ wdt->window_compare = (wd_conf->wdt_window & LPC_WDT_TIMER_MAX);
+ } else {
+ wdt->window_compare = LPC_WDT_TIMER_MAX;
+ }
+ /* Warning interrupt ? */
+ if (wd_conf->wdt_warn != 0) {
+ if (wd_conf->wdt_warn > LPC_WDT_WARNINT_MAXVAL) {
+ wdt->warning_int_compare = LPC_WDT_WARNINT_MAXVAL;
+ } else {
+ wdt->warning_int_compare = wd_conf->wdt_warn;
+ }
+ }
+ /* Protect any of the watchdog functions now ? */
+ if (wd_conf->locks != 0) {
+ uint32_t mode = wdt->mode;
+ if (wd_conf->locks & WDT_CLK_POWER_LOCK) {
+ mode |= LPC_WDT_CLK_POWER_LOCK;
+ }
+ if (wd_conf->locks & WDT_TIMER_VAL_LOCK) {
+ mode |= LPC_WDT_TIMER_VAL_PROTECT;
+ }
+ wdt->mode = mode;
+ }
+ /* Feed sequence to validate the configuration */
+ watchdog_feed();
+ NVIC_EnableIRQ(WDT_IRQ);
+}
+
+
+/*
+ * Stop the watchdog
+ * This function can be used during system operation to stop the watchdog if it has not
+ * been locked or protected against clock source modification, for example when entering
+ * sleep or deep sleep.
+ * Return 0 if a solution has been found to stop the watchdog, or -1 if watchdog is still
+ * running after this call.
+ * TODO : Check this function
+ */
+int stop_watchdog(void)
+{
+ struct lpc_sys_config* sys_config = LPC_SYS_CONFIG;
+ struct lpc_watchdog* wdt = LPC_WDT;
+
+ if (wdt->mode & LPC_WDT_CLK_POWER_LOCK) {
+ return -1;
+ }
+ NVIC_DisableIRQ(WDT_IRQ);
+ subsystem_power(LPC_SYS_ABH_CLK_CTRL_Watchdog, 1);
+ sys_config->powerdown_run_cfg |= LPC_POWER_DOWN_WDT_OSC;
+ subsystem_power(LPC_SYS_ABH_CLK_CTRL_Watchdog, 0);
+ return 0;
+}
+
+
+
+/*
+ * Disable the watchdog
+ * This function can be used upon system startup to disable watchdog operation
+ */
+void startup_watchdog_disable(void)
+{
+ struct lpc_sys_config* sys_config = LPC_SYS_CONFIG;
+ struct lpc_watchdog* wdt = LPC_WDT;
+
+ /* Power wadchdog block before changing it's configuration */
+ sys_config->sys_AHB_clk_ctrl |= LPC_SYS_ABH_CLK_CTRL_Watchdog;
+ /* Stop watchdog */
+ wdt->mode = 0;
+ watchdog_feed();
+ /* And power it down */
+ subsystem_power(LPC_SYS_ABH_CLK_CTRL_Watchdog, 0);
+ sys_config->powerdown_run_cfg |= LPC_POWER_DOWN_WDT_OSC;
+ NVIC_DisableIRQ(WDT_IRQ);
+}
+
+
--- /dev/null
+/****************************************************************************
+ * drivers/adc.c
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+
+
+/***************************************************************************** */
+/* Analog to Digital Converter (ADC) */
+/***************************************************************************** */
+
+/* ADC driver for the integrated ADC module of the LPC82x.
+ * Refer to LPC82x documentation (UM10800.pdf) for more information.
+ */
+
+#include "core/system.h"
+#include "drivers/adc.h"
+
+/* ADC Clock should be as near to 30MHz as possible */
+#define adc_clk_Val (30 * 1000 * 1000)
+
+
+
+/***************************************************************************** */
+/* Generic ADC handler */
+void (*adc_int_callback)(uint32_t) = NULL;
+void ADC_Handler(void)
+{
+ volatile struct lpc_adc* adc = LPC_ADC;
+ uint32_t flags = adc->flags;
+
+ if (adc_int_callback != NULL) {
+ adc_int_callback(flags);
+ }
+}
+void ADC_SEQA_Handler(void) __attribute__ ((alias ("ADC_Handler")));
+void ADC_SEQB_Handler(void) __attribute__ ((alias ("ADC_Handler")));
+void ADC_THCMP_Handler(void) __attribute__ ((alias ("ADC_Handler")));
+void ADC_OVR_Handler(void) __attribute__ ((alias ("ADC_Handler")));
+
+/* Read the conversion from the given channel
+ * This function reads the conversion value directly in the data register and
+ * always returns a value.
+ * Return 0 if the value is a new one and no overrun occured.
+ * Return -1 if channel does not exist
+ * Retuen 1 if the value is an old one
+ * Return 2 if an overrun occured
+ */
+int adc_get_value(uint16_t * val, unsigned int channel)
+{
+ struct lpc_adc* adc = LPC_ADC;
+ uint32_t save_reg = 0;
+
+ if (channel > NB_ADC_CHANNELS)
+ return -1;
+
+ /* Save the whole register as some bits are cleared when register is read */
+ save_reg = adc->data[channel];
+ *val = ((save_reg >> LPC_ADC_RESULT_SHIFT) & LPC_ADC_RESULT_MASK);
+ /* Has this conversion value been already read ? */
+ if (! (save_reg & LPC_ADC_CONV_DONE)) {
+ return 1;
+ }
+ if (save_reg & LPC_ADC_OVERRUN) {
+ return 2;
+ }
+ return 0;
+}
+
+/* Start a conversion on the given channel (0 to 7) */
+void adc_start_convertion_once(unsigned int channel, uint8_t seq_num, int use_int)
+{
+ struct lpc_adc* adc = LPC_ADC;
+ uint32_t reg_val = 0;
+
+ if (channel >= NB_ADC_CHANNELS) {
+ return;
+ }
+ if (seq_num >= NB_ADC_SEQUENCES) {
+ return;
+ }
+
+ /* Set conversion channel bit */
+ reg_val = LPC_ADC_CHANNEL(channel);
+
+ /* Use of interrupts for the specified channel ? */
+ if (use_int) {
+ /* Set interrupt Bit */
+ adc->int_en = LPC_ADC_CHANNEL(channel);
+ } else {
+ adc->int_en = 0;
+ }
+
+ /* Start conversion */
+ reg_val |= (LPC_ADC_START_CONV_NOW | LPC_ADC_SEQ_EN);
+ adc->seqctrl[seq_num] = reg_val;
+}
+
+
+/* Start burst conversions.
+ * channels is a bit mask of requested channels.
+ * Use LPC_ADC_CHANNEL(x) (x = 0 .. 7) for channels selection.
+ */
+void adc_start_burst_conversion(uint16_t channels, uint8_t seq_num)
+{
+ struct lpc_adc* adc = LPC_ADC;
+ uint32_t reg_val = 0;
+
+ if (seq_num >= NB_ADC_SEQUENCES) {
+ return;
+ }
+
+ /* Set conversion channel bits and burst mode */
+ reg_val = (channels & LPC_ADC_CHANNEL_MASK);
+ reg_val |= LPC_ADC_BURST;
+
+ /* Do not use of interrupts in burst mode. */
+ /* If you need to, then you should also use DMA for data transfer, and make sure your code is
+ * able to handle the load */
+ if (seq_num == LPC_ADC_SEQA) {
+ adc->int_en &= ~(LPC_ADC_SEQA_INTEN);
+ } else {
+ adc->int_en &= ~(LPC_ADC_SEQB_INTEN);
+ }
+
+ /* Start conversion */
+ adc->seqctrl[seq_num] = (reg_val | LPC_ADC_SEQ_EN);
+}
+void adc_stop_burst_conversion(uint8_t seq_num)
+{
+ struct lpc_adc* adc = LPC_ADC;
+ if (seq_num >= NB_ADC_SEQUENCES) {
+ return;
+ }
+ adc->seqctrl[seq_num] &= ~LPC_ADC_BURST;
+}
+
+
+/* This should be used to configure conversion start on falling or rising edges of
+ * some signals, or on timer for burst conversions.
+ */
+void adc_prepare_conversion_on_event(uint16_t channels, uint8_t event, uint8_t seq_num,
+ int use_int, uint32_t mode)
+{
+ struct lpc_adc* adc = LPC_ADC;
+ uint32_t reg_val = 0;
+
+ if (seq_num >= NB_ADC_SEQUENCES) {
+ return;
+ }
+
+ /* Set conversion channel bits and burst mode */
+ reg_val |= (channels & LPC_ADC_CHANNEL_MASK);
+ /* Set conversion condition bits */
+ switch (event) {
+ case LPC_ADC_START_CONV_ADC_PINTRIG_0:
+ case LPC_ADC_START_CONV_ADC_PINTRIG_1:
+ case LPC_ADC_START_CONV_SCT0_OUT3:
+ case LPC_ADC_START_CONV_ACMP_OUT:
+ case LPC_ADC_START_CONV_ARM_TXEV:
+ reg_val |= LPC_ADC_START_CONV_EVENT(event);
+ break;
+ default:
+ case LPC_ADC_START_CONV_SOFT:
+ /* Trigger = 0 : already OK */
+ break;
+ }
+ if (mode != 0) {
+ reg_val |= (mode & LPC_ADC_ADDITIONAL_MODE_MASK);
+ }
+
+ /* Use of interrupts for the specified channel ? */
+ if (use_int) {
+ if (seq_num == LPC_ADC_SEQA) {
+ adc->int_en = LPC_ADC_SEQA_INTEN;
+ } else {
+ adc->int_en = LPC_ADC_SEQB_INTEN;
+ }
+ } else {
+ adc->int_en = 0;
+ }
+
+ /* Enable conversion on selected event */
+ adc->seqctrl[seq_num] = (reg_val | LPC_ADC_SEQ_EN);
+}
+
+/* Software trigger of the given configured sequence */
+void adc_trigger_sequence_conversion(uint8_t seq_num)
+{
+ struct lpc_adc* adc = LPC_ADC;
+
+ if (seq_num >= NB_ADC_SEQUENCES) {
+ return;
+ }
+
+ adc->seqctrl[seq_num] |= (LPC_ADC_START_CONV_NOW);
+}
+
+
+/***************************************************************************** */
+/* ADC Setup : private part : Clocks, Power and Mode */
+
+/* When lowpower_en is not 0 the ADC low power mode is selected, which adds a 15ADC clock delay
+ * before each set of consecutive conversions (but not between consecutive conversions)
+ */
+void adc_set_low_power(int lowpower_en)
+{
+ struct lpc_adc* adc = LPC_ADC;
+ if (lowpower_en) {
+ adc->ctrl |= LPC_ADC_LOW_POWER_EN;
+ } else {
+ adc->ctrl &= ~(LPC_ADC_LOW_POWER_EN);
+ }
+}
+
+void adc_clk_update(void)
+{
+ struct lpc_adc* adc = LPC_ADC;
+ uint32_t main_clock = get_main_clock();
+ uint32_t clkdiv = 0;
+
+ /* Configure ADC clock to get the 9MHz sample clock */
+ clkdiv = (main_clock / adc_clk_Val);
+ adc->ctrl &= ~LPC_ADC_CLK_MASK;
+ adc->ctrl |= LPC_ADC_CLK_DIV(clkdiv);
+}
+
+
+void adc_on(void (*adc_callback)(uint32_t))
+{
+ struct lpc_sys_config* sys_config = LPC_SYS_CONFIG;
+ struct lpc_adc* adc = LPC_ADC;
+
+ /* Disable ADC Interrupts */
+ NVIC_DisableIRQ(ADC_SEQA_IRQ);
+ NVIC_DisableIRQ(ADC_SEQB_IRQ);
+ NVIC_DisableIRQ(ADC_THCMP_IRQ);
+ NVIC_DisableIRQ(ADC_OVR_IRQ);
+
+ /* Power-up ADC */
+ sys_config->powerdown_run_cfg &= ~LPC_POWER_DOWN_ADC;
+ /* Provide clock to ADC */
+ subsystem_power(LPC_SYS_ABH_CLK_CTRL_ADC, 1);
+ adc->ctrl = 0;
+ adc_clk_update();
+
+ /* Prevent unconfigured conversion start */
+ adc->seqctrl[0] = 0;
+ adc->seqctrl[1] = 0;
+
+ /* Remove the default global interrupt enabled setting */
+ adc->int_en = 0;
+ /* Register a possible calback */
+ adc_int_callback = adc_callback;
+
+ /* Enable ADC Interrupts */
+ NVIC_EnableIRQ(ADC_SEQA_IRQ);
+ NVIC_EnableIRQ(ADC_SEQB_IRQ);
+ NVIC_EnableIRQ(ADC_THCMP_IRQ);
+ NVIC_EnableIRQ(ADC_OVR_IRQ);
+}
+
+void adc_off(void)
+{
+ struct lpc_sys_config* sys_config = LPC_SYS_CONFIG;
+
+ /* Disable ADC Interrupts */
+ NVIC_DisableIRQ(ADC_SEQA_IRQ);
+ NVIC_DisableIRQ(ADC_SEQB_IRQ);
+ NVIC_DisableIRQ(ADC_THCMP_IRQ);
+ NVIC_DisableIRQ(ADC_OVR_IRQ);
+ /* Power Down ADC */
+ sys_config->powerdown_run_cfg |= LPC_POWER_DOWN_ADC;
+ /* Remove clock from ADC block */
+ subsystem_power(LPC_SYS_ABH_CLK_CTRL_ADC, 0);
+}
+
--- /dev/null
+/****************************************************************************
+ * drivers/gpio.c
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+/***************************************************************************** */
+/* GPIOs and GPIO Interrupts */
+/***************************************************************************** */
+
+/* Driver for GPIO configuration and access (including GPIO interrupts) on the LPC82x.
+ * Refer to LPC82x documentation (UM10800.pdf) for more information.
+ */
+
+
+#include "core/system.h"
+#include "core/pio.h"
+#include "drivers/gpio.h"
+#include "lib/errno.h"
+
+
+
+/***************************************************************************** */
+/* GPIO setup */
+
+void gpio_on(void)
+{
+ /* Provide power to GPIO control blocks */
+ subsystem_power(LPC_SYS_ABH_CLK_CTRL_GPIO, 1);
+}
+void gpio_off(void)
+{
+ /* Remove power from GPIO control blocks */
+ subsystem_power(LPC_SYS_ABH_CLK_CTRL_GPIO, 0);
+}
+
+/*
+ * This function calls the config_pio() function for the gpio with the given
+ * mode, configures the direction of the pin and sets the initial state.
+ */
+void config_gpio(const struct pio* gpio, uint32_t mode, uint8_t dir, uint8_t ini_val)
+{
+ struct lpc_gpio* gpio_port = LPC_GPIO_REGS(0);
+
+ /* Configure as GPIO */
+ config_pio(gpio, NULL, mode);
+
+ if (dir == GPIO_DIR_IN) {
+ gpio_port->data_dir &= ~(1 << gpio->pin);
+ } else {
+ gpio_port->data_dir |= (1 << gpio->pin);
+ if (ini_val == 0) {
+ gpio_port->clear = (1 << gpio->pin);
+ } else {
+ gpio_port->set = (1 << gpio->pin);
+ }
+ }
+}
+
+/***************************************************************************** */
+/* GPIO Interrupts Callbacks */
+#define NB_PININT_INTERRUPTS 8
+static void (*gpio_callbacks[NB_PININT_INTERRUPTS]) (uint32_t) = {0};
+
+/* Return the pin interrupt number if OK, or a negative value in case of error */
+int set_gpio_callback(void (*callback) (uint32_t), const struct pio* gpio, uint8_t sense)
+{
+ struct lpc_gpio* gpio_port = LPC_GPIO_REGS(0);
+ struct lpc_pin_int* pinint = LPC_GPIO_INTERRUPTS;
+ struct lpc_sys_config* sys_conf = LPC_SYS_CONFIG;
+ uint32_t irq = 0;
+
+ /* Register the callback */
+ if (gpio->pin >= PORT0_NB_PINS)
+ return -EINVAL;
+
+ /* Get the next available interrupt */
+ for (irq = 0; irq < NB_PININT_INTERRUPTS; irq++) {
+ if (gpio_callbacks[irq] == NULL) {
+ gpio_callbacks[irq] = callback;
+ break;
+ }
+ }
+ /* If not found, return. */
+ if (irq == NB_PININT_INTERRUPTS) {
+ return -1;
+ }
+
+ /* Power on the Pin interrupt subsystem */
+ subsystem_power(LPC_SYS_ABH_CLK_CTRL_GPIO, 1);
+
+ /* Configure the pin as interrupt source */
+ gpio_port->data_dir &= ~(1 << gpio->pin); /* Input */
+ config_pio(gpio, NULL, 0);
+ sys_conf->pin_int_sel[irq] = gpio->pin;
+
+ /* Setup the înint as requested */
+ switch (sense) {
+ case EDGES_BOTH:
+ pinint->mode &= ~(0x01 << irq);
+ pinint->lvl_rising_set_en = (0x01 << irq);
+ pinint->lvl_falling_set_en = (0x01 << irq);
+ break;
+ case EDGE_RISING:
+ pinint->mode &= ~(0x01 << irq);
+ pinint->lvl_rising_set_en = (0x01 << irq);
+ pinint->lvl_falling_clr_en = (0x01 << irq);
+ break;
+ case EDGE_FALLING:
+ pinint->mode &= ~(0x01 << irq);
+ pinint->lvl_rising_clr_en = (0x01 << irq);
+ pinint->lvl_falling_set_en = (0x01 << irq);
+ break;
+ case LEVEL_LOW:
+ pinint->mode |= (0x01 << irq);
+ pinint->lvl_rising_set_en = (0x01 << irq);
+ pinint->lvl_falling_clr_en = (0x01 << irq); /* Active low */
+ break;
+ case LEVEL_HIGH:
+ pinint->mode |= (0x01 << irq);
+ pinint->lvl_rising_set_en = (0x01 << irq);
+ pinint->lvl_falling_set_en = (0x01 << irq); /* Active high */
+ break;
+ default: /* Not handled, do not activate the interrupt */
+ return -EINVAL;
+ }
+ NVIC_EnableIRQ(irq + PININT0_IRQ);
+ return irq;
+}
+
+void remove_gpio_callback(unsigned int irq)
+{
+ struct lpc_pin_int* pinint = LPC_GPIO_INTERRUPTS;
+ struct lpc_sys_config* sys_conf = LPC_SYS_CONFIG;
+
+ /* Remove the handler */
+ if (irq > NB_PININT_INTERRUPTS)
+ return;
+ gpio_callbacks[irq] = NULL;
+
+ /* And disable the interrupt */
+ sys_conf->pin_int_sel[irq] = 0xFF;
+ pinint->mode &= ~(0x01 << irq);
+ pinint->lvl_rising_clr_en = (0x01 << irq);
+ pinint->lvl_falling_clr_en = (0x01 << irq);
+
+ NVIC_DisableIRQ(irq + PININT0_IRQ);
+}
+
+
+/* Interrupt Handlers */
+#define DECLARE_PIO_INT_HANDLER(x) \
+ void PININT_ ## x ## _Handler(void) \
+ { \
+ struct lpc_pin_int* pinint = LPC_GPIO_INTERRUPTS; \
+ if (gpio_callbacks[x] != NULL) { \
+ gpio_callbacks[x](x); \
+ } \
+ pinint->status = (0x01 << x); \
+ }
+
+DECLARE_PIO_INT_HANDLER(0);
+DECLARE_PIO_INT_HANDLER(1);
+DECLARE_PIO_INT_HANDLER(2);
+DECLARE_PIO_INT_HANDLER(3);
+DECLARE_PIO_INT_HANDLER(4);
+DECLARE_PIO_INT_HANDLER(5);
+DECLARE_PIO_INT_HANDLER(6);
+DECLARE_PIO_INT_HANDLER(7);
+
--- /dev/null
+/****************************************************************************
+ * drivers/i2c.c
+ *
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+
+/**************************************************************************** */
+/* I2C */
+/**************************************************************************** */
+
+/* I2C driver for the I2C bus integrated module of the LPC82x.
+ * Refer to LPC82x documentation (UM10800.pdf) for more information.
+ */
+
+#include "lib/stdint.h"
+
+#include "core/lpc_regs.h"
+#include "core/lpc_core.h"
+#include "core/system.h"
+#include "lib/string.h"
+#include "lib/errno.h"
+#include "drivers/i2c.h"
+
+
+/*
+ * I2C Bus structure
+ *
+ * clock : current i2c clock.
+ * state : global state of the i2c engine.
+ * master_status : status returned by i2c block as found in "status" register.
+ *
+ * ctrl_buf : This buffer, if used, must have the SAME size as out_buff.
+ * Then, instead of simply moving to the next byte, the corresponding condition
+ * will be executed : I2C_CONT, I2C_DO_REPEATED_START, I2C_DO_STOP_START or I2C_STOP.
+ */
+struct i2c_bus {
+ volatile struct lpc_i2c* regs;
+ uint8_t irq;
+ uint8_t mode;
+ uint32_t reset_bit;
+ uint32_t power_bit;
+ volatile uint32_t clock;
+ volatile uint32_t state;
+ volatile uint32_t master_status;
+ volatile uint32_t slave_status;
+
+ volatile const char* out_buff;
+ volatile const char* ctrl_buf;
+ volatile uint32_t write_length;
+ volatile uint32_t write_index;
+
+ volatile char* in_buff;
+ volatile uint32_t read_length;
+ volatile uint32_t read_index;
+};
+
+
+static struct i2c_bus i2c_buses[NB_I2C_BUSSES] = {
+ {
+ .regs = (struct lpc_i2c*)LPC_I2C0,
+ .irq = I2C0_IRQ,
+ .reset_bit = LPC_I2C0_RESET_N,
+ .power_bit = LPC_SYS_ABH_CLK_CTRL_I2C0,
+ .state = I2C_OK,
+ },
+ {
+ .regs = (struct lpc_i2c*)LPC_I2C1,
+ .irq = I2C1_IRQ,
+ .reset_bit = LPC_I2C1_RESET_N,
+ .power_bit = LPC_SYS_ABH_CLK_CTRL_I2C1,
+ .state = I2C_OK,
+ },
+ {
+ .regs = (struct lpc_i2c*)LPC_I2C2,
+ .irq = I2C2_IRQ,
+ .reset_bit = LPC_I2C2_RESET_N,
+ .power_bit = LPC_SYS_ABH_CLK_CTRL_I2C2,
+ .state = I2C_OK,
+ },
+ {
+ .regs = (struct lpc_i2c*)LPC_I2C3,
+ .irq = I2C3_IRQ,
+ .reset_bit = LPC_I2C3_RESET_N,
+ .power_bit = LPC_SYS_ABH_CLK_CTRL_I2C3,
+ .state = I2C_OK,
+ },
+};
+
+
+/* FIXME : Should add a field "what to do on addr NACK" to i2c_bus structure, and use
+ it with the timeout to create a retry mechanism */
+/* FIXME : For case 58 ... What would be the use of a restart ?? perform periodic reads ? */
+/* FIXME : Implement Slave when arbitration lost ? */
+
+
+
+/* I2C Interrupt handler */
+/* Actual version will stop on NACKs */
+/* See LPC1764 user's manual UM10360 on page 457 (19.9.5) for details on I2C State machine */
+void I2C_Handler(uint32_t bus_num)
+{
+ uint8_t status = 0;
+ uint8_t condition = I2C_CONT;
+ struct i2c_bus* i2c = &(i2c_buses[bus_num]);
+
+ /* This handler deals with master read and master write only */
+ i2c->master_status = i2c->regs->status; /* Store current status */
+ i2c->slave_status = i2c->regs->status;
+ status = (i2c->regs->status & 0xFF);
+
+ /* I2C State Machine ! */
+ switch (status) {
+
+ /* Errors */
+ case 0x00: /* Should never happen */
+ i2c->regs->int_en_clr = I2C_ST_MASTER_PENDING;
+ i2c->state = I2C_ERROR_UNKNOWN;
+ break;
+
+ case 0x10:
+ case 0x11: /* Arbitration lost. We don't deal with multiple master situation */
+ /* FIXME : We have two option :
+ * - do nothing, which will release the bus.
+ * This options allows handling of multiple master topologies.
+ * - Set start flag, and a start condition will be transmitted when bus becomes free.
+ */
+ /* We choose to do nothing. User code should retry later. */
+ i2c->state = I2C_ARBITRATION_LOST;
+ break;
+
+ case 0x40:
+ case 0x41: /* Start/Stop error. */
+ /* A start or stop condition has been detected while it was not allowed by the I2C
+ * specification. The master interface has stoped driving the bus and is in idle state.
+ * We have three option :
+ * - Do nothing.
+ * - Set start flag, and a start condition will be transmitted when bus becomes free.
+ * - Try to detect whether the I2C bus has stalled (?? how ?)
+ */
+ /* We choose to do nothing. User code should retry later. */
+ i2c->state = I2C_BUS_ERROR;
+ break;
+
+ /* NACKs */
+ case 0x07: /* NACK on Address */
+ case 0x09: /* NACK on Data byte */
+ /* Send a STOP condition will end transactions and let the driver retry later. */
+ i2c->regs->master_ctrl = I2C_MASTER_STOP;
+ i2c->state = I2C_NACK;
+ break;
+
+ /* Rx */
+ case 0x02:
+ case 0x03: /* Receive ready. Data byte has been received */
+ if (i2c->in_buff != NULL) {
+ i2c->in_buff[i2c->read_index] = i2c->regs->master_data;
+ }
+ i2c->read_index++;
+ if (i2c->read_index < i2c->read_length) {
+ /* Send ACK (tells the slave to continue) */
+ i2c->regs->master_ctrl = I2C_MASTER_CONTINUE;
+ } else {
+ /* Send a NACK (tells the slave this was the last byte)
+ * FIXME : We have two other options : Repeated START or STOP + START,
+ * but what for ? periodic reads ?
+ */
+ i2c->regs->master_ctrl = I2C_MASTER_STOP;
+ i2c->state = I2C_OK;
+ }
+ break;
+
+ /* Tx */
+ case 0x04:
+ case 0x05: /* Transmit ready. */
+ if (i2c->write_length <= 1) { /* Nothing more to send, we must stop. */
+ /* This one is used to probe devices, or wait for completion */
+ i2c->regs->master_ctrl = I2C_MASTER_STOP;
+ i2c->state = I2C_NO_DATA;
+ break;
+ }
+
+ if (i2c->ctrl_buf) {
+ condition = i2c->ctrl_buf[i2c->write_index];
+ }
+ i2c->write_index++;
+
+ switch (condition) {
+ case I2C_DO_STOP_START: /* Send STOP / START condition */
+ i2c->regs->master_ctrl = I2C_MASTER_STOP;
+ case I2C_DO_REPEATED_START: /* Send repeated start condition */
+ case I2C_CONT: /* Send next data byte */
+ if (i2c->write_index < i2c->write_length) {
+ i2c->regs->master_data = i2c->out_buff[i2c->write_index];
+ } else {
+ /* We should have data to send ! Stop the controller. */
+ i2c->regs->master_ctrl = I2C_MASTER_STOP;
+ i2c->state = I2C_NO_DATA;
+ break;
+ }
+ if (condition == I2C_CONT) {
+ i2c->regs->master_ctrl = I2C_MASTER_CONTINUE;
+ } else {
+ i2c->regs->master_ctrl = I2C_MASTER_START;
+ }
+ break;
+ case I2C_STOP: /* Send STOP condition */
+ i2c->regs->master_ctrl = I2C_MASTER_STOP;
+ i2c->state = I2C_OK;
+ break;
+ }
+ break;
+
+ default:
+ i2c->state = I2C_ERROR_UNKNOWN;
+ break;
+ }
+
+ /* When communication is over, disable the idle/pending interrupt */
+ if (i2c->state != I2C_BUSY) {
+ i2c->regs->int_en_clr = I2C_ST_MASTER_PENDING;
+ }
+ /* Clear interrupt flag. This has to be done last. */
+ i2c->regs->status = I2C_ST_CLEAR_ALL;
+ return;
+}
+
+
+void I2C_0_Handler(void)
+{
+ I2C_Handler(0);
+}
+void I2C_1_Handler(void)
+{
+ I2C_Handler(1);
+}
+void I2C_2_Handler(void)
+{
+ I2C_Handler(2);
+}
+void I2C_3_Handler(void)
+{
+ I2C_Handler(3);
+}
+
+
+
+/***************************************************************************** */
+/* I2C access */
+/***************************************************************************** */
+static int i2c_perform_data_transfer(struct i2c_bus* i2c)
+{
+ int ret = 0;
+
+ if (i2c->mode != I2C_MASTER) {
+ return -EINVAL;
+ }
+
+ /* FIXME : This test is only valid for master */
+ if ((i2c->regs->status & 0x0F) != 0x01) {
+ return -EBUSY;
+ }
+ /* Start the process */
+ i2c->state = I2C_BUSY;
+ i2c->write_index = 0;
+ i2c->read_index = 0;
+ i2c->regs->master_data = i2c->out_buff[0];
+ i2c->regs->master_ctrl = I2C_MASTER_START;
+ i2c->regs->int_en_set = (I2C_ST_MASTER_PENDING | I2C_ST_MASTER_ARB_LOSS | I2C_ST_MASTER_STERR);
+
+ /* Wait for process completion */
+ do {} while (i2c->state == I2C_BUSY); /* FIXME : OS should schedule here */
+
+ /* Handle returned state (errors or OK) */
+ switch (i2c->state) {
+ case I2C_OK:
+ case I2C_NO_DATA:
+ /* Return 0 : success */
+ break;
+ case I2C_NACK:
+ ret = -EREMOTEIO;
+ break;
+ case I2C_ARBITRATION_LOST:
+ ret = -EBUSY;
+ break;
+ case I2C_BUS_ERROR: /* This one is bad ... */
+ case I2C_ERROR_UNKNOWN:
+ default:
+ ret = -EIO;
+ break;
+ }
+
+ /* Force device to release the bus :
+ * send a START followed by a STOP (initiate transmission with nul write_length) */
+ i2c->state = I2C_BUSY;
+ i2c->write_length = 0;
+ i2c->regs->master_data = 0;
+ i2c->regs->master_ctrl = I2C_MASTER_START;
+ i2c->regs->int_en_set = I2C_ST_MASTER_PENDING;
+ do {} while (i2c->state == I2C_BUSY); /* FIXME : OS should schedule here */
+ i2c->state = I2C_OK;
+
+ /* Done with the Bus, Disable the interrupts */
+ i2c->regs->int_en_clr = (I2C_ST_MASTER_PENDING | I2C_ST_MASTER_ARB_LOSS | I2C_ST_MASTER_STERR);
+
+ return ret;
+}
+
+/* Read
+ * Performs a non-blocking read on the module's i2c bus.
+ * bus_num : I2C bus number to use.
+ * cmd_buf : buffer containing all control byte to be sent on the i2c bus
+ * cmd_size : size of the cmd_buf command buffer
+ * ctrl_buf : buffer containing action to be done after sending, like repeated START conditions
+ * if not NULL, ctrl_buf has the same size as cmd_buf
+ * inbuff : the buffer where read data will be put. May be NULL if count is 0.
+ * count : the number of bytes to be read.
+ * RETURN VALUE
+ * Upon successfull completion, returns the number of bytes read. On error, returns a negative
+ * integer equivalent to errors from glibc.
+ * -EBADFD : Device not initialized (-77)
+ * -EBUSY : Device or ressource Busy or Arbitration lost (-16)
+ * -EAGAIN : Device already in use (-11)
+ * -EINVAL : Invalid argument (no cmd_buf, or no inbuff when count > 0) (-22)
+ * -EREMOTEIO : Device did not acknowledge (-121)
+ * -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine (-5)
+ */
+int i2c_read(uint8_t bus_num, const void *cmd_buf, size_t cmd_size, const void* ctrl_buf, void* inbuff, size_t count)
+{
+ int ret = 0;
+ struct i2c_bus* i2c = NULL;
+
+ if (bus_num >= NB_I2C_BUSSES) {
+ return -EINVAL;
+ }
+ i2c = &(i2c_buses[bus_num]);
+
+ /* Checks */
+ if (i2c->clock == 0)
+ return -EBADFD;
+ if (cmd_buf == NULL)
+ return -EINVAL;
+ if ((inbuff == NULL) && (count > 0))
+ return -EINVAL;
+
+ if (i2c->state == I2C_BUSY) {
+ return -EBUSY;
+ }
+ if (i2c->state != I2C_OK) {
+ /* What should we do ??? someone failed to reset status ? */
+ }
+
+ /* Set up i2c for read operation */
+ /* command (write) buffer */
+ i2c->out_buff = cmd_buf;
+ i2c->write_length = cmd_size;
+ /* Control buffer, if any.
+ * Used to control operations on i2C bus to simplify the interface */
+ i2c->ctrl_buf = ctrl_buf;
+ /* Read buffer */
+ i2c->in_buff = inbuff;
+ i2c->read_length = count;
+ i2c->read_index = 0;
+
+ ret = i2c_perform_data_transfer(i2c);
+ if (ret == 0) {
+ return i2c->read_index;
+ }
+ return ret;
+}
+
+/* Write
+ * Performs a non-blocking write on the module's i2c bus.
+ * bus_num : I2C bus number to use.
+ * buf : buffer containing all byte to be sent on the i2c bus,
+ * including conrtol bytes (address, offsets, ...)
+ * count : the number of bytes to be sent, including address bytes and so on.
+ * ctrl_buf : buffer containing action to be done after sending, like repeated START conditions
+ * FIXME : note that STOP + START conditions are not allowed, the STOP would lead to sending
+ * the first bytes of buf, creating an infinite loop.
+ * RETURN VALUE
+ * Upon successfull completion, returns the number of bytes written. On error, returns a negative
+ * integer equivalent to errors from glibc.
+ * -EBADFD : Device not initialized
+ * -EBUSY : Device or ressource Busy or Arbitration lost
+ * -EAGAIN : Device already in use
+ * -EINVAL : Invalid argument (buf)
+ * -EREMOTEIO : Device did not acknowledge
+ * -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine
+ */
+int i2c_write(uint8_t bus_num, const void *buf, size_t count, const void* ctrl_buf)
+{
+ int ret = 0;
+ struct i2c_bus* i2c = NULL;
+
+ if (bus_num >= NB_I2C_BUSSES) {
+ return -EINVAL;
+ }
+ i2c = &(i2c_buses[bus_num]);
+
+ /* Checks */
+ if (i2c->clock == 0)
+ return -EBADFD;
+ if (buf == NULL)
+ return -EINVAL;
+
+ if (i2c->state == I2C_BUSY) {
+ return -EBUSY;
+ }
+ if (i2c->state != I2C_OK) {
+ /* What should we do ??? someone failed to reset status ? */
+ }
+
+ /* Clear read information to prevent entering master receiver states */
+ i2c->read_length = 0;
+ i2c->in_buff = NULL;
+ /* Set up i2c for write operation */
+ i2c->out_buff = buf;
+ i2c->write_length = count;
+ /* Control buffer, if any.
+ * Used to control operations on i2C bus to simplify the interface */
+ i2c->ctrl_buf = ctrl_buf;
+
+ ret = i2c_perform_data_transfer(i2c);
+ if (ret == 0) {
+ return count;
+ }
+ return ret;
+}
+
+
+int i2c_set_timeout(uint8_t bus_num, uint16_t timeout)
+{
+ struct i2c_bus* i2c = NULL;
+
+ if (bus_num >= NB_I2C_BUSSES) {
+ return -EINVAL;
+ }
+ i2c = &(i2c_buses[bus_num]);
+ i2c->regs->timeout = timeout;
+
+ return 0;
+}
+
+/***************************************************************************** */
+/* I2C Init */
+/***************************************************************************** */
+
+static void i2c_clock_on(struct i2c_bus* i2c)
+{
+ uint32_t main_clock = get_main_clock();
+ uint32_t scl_div = 0;
+
+ /* Setup I2C clock */
+ scl_div = (main_clock / (i2c->clock * 4)); /* Two clocks low and two clocks high -> divide by 4 */
+ i2c->regs->clkdiv = (scl_div - 1);
+ i2c->regs->master_timing = 0; /* Two clocks low and two clocks high */
+}
+
+/* I2C on / off
+ * bus_num : I2C bus number to use.
+ * i2c_clk_freq : I2C clock freqeuncy in Hz
+ * mode is one of I2C_MASTER, I2C_SLAVE or I2C_MONITOR.
+ */
+int i2c_on(uint8_t bus_num, uint32_t i2c_clk_freq, uint8_t mode)
+{
+ struct i2c_bus* i2c = NULL;
+
+ if (bus_num >= NB_I2C_BUSSES) {
+ return -EINVAL;
+ }
+ i2c = &(i2c_buses[bus_num]);
+
+ NVIC_DisableIRQ(i2c->irq);
+ /* Power on I2C block */
+ subsystem_power(i2c->power_bit, 1);
+ subsystem_reset(i2c->reset_bit);
+ /* Set clock */
+ i2c->clock = i2c_clk_freq;
+ i2c_clock_on(i2c);
+ /* Disable all interrupt condition */
+ i2c->regs->int_en_clr = ~(0UL);
+ i2c->mode = mode;
+ /* Enable I2C */
+ switch (mode) {
+ case I2C_MASTER:
+ i2c->regs->config = I2C_MASTER_EN;
+ break;
+ case I2C_SLAVE:
+ i2c->regs->config = I2C_SLAVE_EN;
+ break;
+ case I2C_MONITOR:
+ default:
+ i2c->regs->config = I2C_MONITOR_EN;
+ break;
+ }
+ /* And enable interrupts */
+ NVIC_EnableIRQ(i2c->irq);
+
+ return 0;
+}
+
+int i2c_off(uint8_t bus_num)
+{
+ struct i2c_bus* i2c = NULL;
+
+ if (bus_num >= NB_I2C_BUSSES) {
+ return -EINVAL;
+ }
+ i2c = &(i2c_buses[bus_num]);
+
+ NVIC_DisableIRQ(i2c->irq);
+ subsystem_power(i2c->power_bit, 0);
+ i2c->clock = 0;
+
+ return 0;
+}
+
+/* Allow system to propagate main clock */
+void i2c_clock_update(void)
+{
+ int i = 0;
+
+ for (i = 0; i < NB_I2C_BUSSES; i++) {
+ if (! subsystem_powered(i2c_buses[i].power_bit)) {
+ continue;
+ }
+ if (i2c_buses[i].clock != 0) {
+ /* FIXME : we should stop I2C transfers, disable I2C interrupts and stop I2C clock. */
+ i2c_clock_on(&(i2c_buses[i]));
+ }
+ }
+}
+
--- /dev/null
+/****************************************************************************
+ * drivers/serial.c
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+
+
+/***************************************************************************** */
+/* UARTs */
+/***************************************************************************** */
+/* UART driver for the integrated UARTs of the LPC82x.
+ * Refer to LPC82x documentation (UM10800.pdf) for more information.
+ */
+
+/* All three UARTs are available, UART numbers are in the range 0 - 2 */
+
+#include "core/system.h"
+#include "core/pio.h"
+#include "lib/string.h"
+#include "lib/utils.h"
+#include "lib/errno.h"
+#include "drivers/serial.h"
+
+
+struct uart_device
+{
+ struct lpc_usart* regs;
+ uint32_t baudrate;
+ uint32_t config;
+ uint8_t num;
+ uint8_t capabilities;
+ uint8_t current_mode;
+
+ /* Output buffer */
+ volatile char out_buff[SERIAL_OUT_BUFF_SIZE];
+ volatile uint32_t sending; /* Actual sending position in out buffer */
+ /* This lock only prevents multiple calls to serial_write() to execute simultaneously */
+ volatile uint32_t out_lock;
+ volatile uint32_t out_length; /* actual position to add in out buffer */
+
+ /* Input */
+ void (*rx_callback)(uint8_t); /* Possible RX callback */
+};
+
+#define NUM_UARTS 3
+static struct uart_device uarts[NUM_UARTS] = {
+ {
+ .num = 0,
+ .regs = (struct lpc_usart*)LPC_UART_0,
+ .baudrate = 0,
+ .config = (LPC_UART_8BIT | LPC_UART_NO_PAR | LPC_UART_1STOP),
+ .out_buff = {0},
+ .sending = 0,
+ .out_lock = 0,
+ .rx_callback = NULL,
+ .capabilities = (SERIAL_CAP_UART | SERIAL_CAP_RS485),
+ .current_mode = SERIAL_MODE_UART,
+ },
+ {
+ .num = 1,
+ .regs = (struct lpc_usart*)LPC_UART_1,
+ .baudrate = 0,
+ .config = (LPC_UART_8BIT | LPC_UART_NO_PAR | LPC_UART_1STOP),
+ .out_buff = {0},
+ .sending = 0,
+ .out_lock = 0,
+ .rx_callback = NULL,
+ .capabilities = (SERIAL_CAP_UART | SERIAL_CAP_RS485),
+ .current_mode = SERIAL_MODE_UART,
+ },
+ {
+ .num = 2,
+ .regs = (struct lpc_usart*)LPC_UART_2,
+ .baudrate = 0,
+ .config = (LPC_UART_8BIT | LPC_UART_NO_PAR | LPC_UART_1STOP),
+ .out_buff = {0},
+ .sending = 0,
+ .out_lock = 0,
+ .rx_callback = NULL,
+ .capabilities = (SERIAL_CAP_UART | SERIAL_CAP_RS485),
+ .current_mode = SERIAL_MODE_UART,
+ },
+};
+
+static void uart_check_rx(struct uart_device* uart, uint32_t intr)
+{
+ if (intr & LPC_UART_ST_RX_READY) {
+ uint8_t data = uart->regs->rx_data;
+ if (uart->rx_callback != NULL) {
+ /* Call the Rx callback */
+ uart->rx_callback(data);
+ } else {
+ /* Echo */
+ uart->regs->tx_data = data;
+ }
+ }
+ /* FIXME : handle erors */
+}
+
+static void uart_check_tx(struct uart_device* uart, uint32_t intr)
+{
+ /* We are currently sending, send next char */
+ if (intr & LPC_UART_ST_TX_READY) {
+ if (uart->out_buff && uart->sending && (uart->out_length > uart->sending)) {
+ uart->regs->tx_data = uart->out_buff[uart->sending];
+ uart->sending++;
+ } else {
+ uart->sending = 0;
+ uart->regs->inten_clear = LPC_UART_ST_TX_READY;
+ }
+ }
+}
+
+/* Generic UART handler */
+static void UART_Handler(struct uart_device* uart)
+{
+ uint32_t intr = uart->regs->status;
+
+ uart_check_rx(uart, intr);
+ uart_check_tx(uart, intr);
+ /* Clear all flags */
+ uart->regs->status = LPC_UART_ST_CLEAR;
+}
+
+
+/* Handlers */
+void UART_0_Handler(void)
+{
+ UART_Handler(&uarts[0]);
+}
+void UART_1_Handler(void)
+{
+ UART_Handler(&uarts[1]);
+}
+void UART_2_Handler(void)
+{
+ UART_Handler(&uarts[2]);
+}
+
+
+/* Start sending buffer content */
+static void uart_start_sending(uint32_t uart_num)
+{
+ struct uart_device* uart = &uarts[uart_num];
+
+ if (!uart->sending && (uart->out_length != 0)) {
+ uart->sending++;
+ uart->regs->tx_data = uart->out_buff[0];
+ /* Activate only LPC_UART_ST_TX_READY in order to prevent multiple calls to
+ * the interrupt handler, which will cause data loss */
+ uart->regs->inten_set = (LPC_UART_ST_TX_READY);
+ }
+}
+
+
+/****************************************************************************** */
+/* Serial send byte - quick function with almost no tests.
+ * If the uart is not sending, the byte is placed directly in the data buffer and
+ * the call returns 0.
+ * Else, the call returns -EBUSY and nothing is sent.
+ */
+int serial_send_quickbyte(uint32_t uart_num, uint8_t data)
+{
+ struct uart_device* uart = &uarts[uart_num];
+ if (!uart->sending) {
+ uart->regs->tx_data = data;
+ return 0;
+ } else {
+ return -EBUSY;
+ }
+}
+
+/***************************************************************************** */
+/* Serial Write
+ *
+ * Try to send at most "length" characters from "buf" on the requested uart.
+ * Returns a negative value on error, or number of characters copied into output buffer,
+ * witch may be less than requested "length"
+ * Possible errors: requested uart does not exists (-EINVAL) or unable to acquire uart
+ * lock (-EBUSY).
+ *
+ * Warning for Real Time : This implementation will block if there's already a
+ * transmission ongoing.
+ */
+int serial_write(uint32_t uart_num, const char *buf, uint32_t length)
+{
+ struct uart_device* uart = NULL;
+
+ if (uart_num >= NUM_UARTS)
+ return -EINVAL;
+
+ uart = &uarts[uart_num];
+ /* Lock acquire */
+ if (sync_lock_test_and_set(&uart->out_lock, 1) == 1) {
+ return -EBUSY;
+ }
+
+ /* If UART is sending wait for buffer empty */
+ /* FIXME : be smart for OS, call scheduler or return */
+ while (uart->sending != 0) {
+ /* If interrupt are masked, check for tx ourselves */
+ if (get_priority_mask() != 0) {
+ uart_check_tx(uart, uart->regs->status);
+ }
+ }
+
+ if (length > SERIAL_OUT_BUFF_SIZE) {
+ length = SERIAL_OUT_BUFF_SIZE;
+ }
+ memcpy((char*)uart->out_buff, buf, length);
+ uart->out_length = length;
+
+ /* Turn output on */
+ uart_start_sending(uart_num);
+
+ /* Release the lock */
+ sync_lock_release(&uart->out_lock);
+
+ return length;
+}
+
+
+/***************************************************************************** */
+/* Serial Flush
+ *
+ * Wait until all characters have been sent
+ * Returns -EINVAL on error, 0 on success.
+ * Possible errors: requested uart does not exists or unable to acquire uart lock.
+ *
+ * Warning for Real Time : This implementation will block if there's already a
+ * transmission ongoing.
+ */
+int serial_flush(uint32_t uart_num)
+{
+ struct uart_device* uart = NULL;
+
+ if (uart_num >= NUM_UARTS)
+ return -EINVAL;
+
+ uart = &uarts[uart_num];
+
+ /* Active wait for message to be sent. If interrupts are
+ * disabled, call the UART handler while waiting. */
+ while (uart->sending) {
+ if (get_priority_mask() == 0) {
+ uart_check_tx(uart, uart->regs->status);
+ }
+ }
+
+ return 0;
+}
+
+
+/***************************************************************************** */
+/* UART Setup : private part : Clocks, Pins, Power and Mode */
+
+/* UART Clock Setup */
+/* Note : for all uarts we use full peripheral clock.
+ * The common uart clock is the main_clock divided by UARTCLKDIV (which we set to 1)
+ * The uart pclk (U_PCLK) is common uart clock divided by (1 + mult/div)
+ * - Documentation states that div must be 256 (uart_common_fclk_div = 0xFF)
+ * - In order to get the closest value for 115200 bauds, the mult value is 22, which
+ * provides a 11050360 Hz clock with a 12Mhz system clock (ideal value would be
+ * 11059200 Hz). Then the uart specific baud rate generator divisor can be set to 6
+ * (BRGVAL = 5) in order to get the 1843200 Hz clock which is 16x the 115200 baud
+ * rate (oversampling of 16).
+ * Depending on the main clock frequency, other values can be achieved by selecting
+ * the right baud rate generator divisor (BRGVAL).
+ * - In order to get the closest value for 1500000 bauds (1.5Mbauds) the mult value
+* should be 128 and the main clock set to 36MHz, which gives a 24MHz uart pclk.
+ * A baud rate generator divisor of 1 (BRGVAL = 0) would then provide a 1.5MHz baudrate
+ * with 16x oversampling.
+ */
+enum uart_speed_types {
+ LPC_UART_CLK_NONE,
+ LPC_UART_CLK_COMMON,
+ LPC_UART_CLK_HIGH_SPEED,
+};
+static uint8_t uart_speed_type = LPC_UART_CLK_NONE;
+static int uart_clk_on(uint32_t uart_num, uint32_t baudrate)
+{
+ struct lpc_sys_config* sys_config = LPC_SYS_CONFIG;
+ struct lpc_usart* uart_regs = uarts[uart_num].regs; /* Get the right registers */
+ uint32_t div = 0;
+ /* clk_mult is the 12MHz multiplier which gives the main clock freq */
+ uint8_t clk_mult = (get_main_clock() / (12 * 1000 * 1000));
+
+ /* Save baudrate value */
+ uarts[uart_num].baudrate = baudrate;
+
+ /* Configure common UART clock */
+ sys_config->uart_clk_div = 0x01; /* Do not divide peripheral clock for uarts */
+ sys_config->uart_common_fclk_div = 0xFF; /* divider value must be 256 */
+ if (baudrate <= 230400) {
+ if (uart_speed_type == LPC_UART_CLK_HIGH_SPEED) {
+ return -1;
+ }
+ sys_config->uart_common_fclk_mult = 22; /* Closest value to get 115200 baudrate */
+ /* Get the divider value */
+ div = (691200 * clk_mult) / baudrate; /* 691200 is (11059200 / 16) */
+ uart_regs->baudrate_gen = (div - 1);
+ uart_speed_type = LPC_UART_CLK_COMMON;
+ } else {
+ if (uart_speed_type == LPC_UART_CLK_COMMON) {
+ return -EINVAL;
+ }
+ sys_config->uart_common_fclk_mult = 128; /* Value to get 1500000 baudrate */
+ uart_speed_type = LPC_UART_CLK_HIGH_SPEED;
+ uart_regs->baudrate_gen = 0;
+ }
+ return 0;
+}
+static void uart_clk_off(uint32_t uart_num)
+{
+ /* Erase saved baudrate */
+ uarts[uart_num].baudrate = 0;
+}
+
+static void uart_setup(uint32_t uart_num)
+{
+ struct lpc_usart* uart_regs = uarts[uart_num].regs; /* Get the right registers */
+ /* Set up UART mode */
+ uart_regs->config = (uarts[uart_num].config | LPC_UART_ENABLE);
+ uart_regs->control = 0;
+ /* Clear the Status Register */
+ uart_regs->status = LPC_UART_ST_CLEAR;
+ /* Enable UART Interrupt */
+ uart_regs->inten_set = LPC_UART_ST_RX_READY;
+}
+
+/* Change UART configuration (number of data, parity and stop bits).
+ * config is usually a mask of LPC_UART_xBIT (x = 5..8), LPC_UART_xSTOP (x = 1..2)
+ * and one of LPC_UART_NO_PAR, LPC_UART_ODD_PAR or LPC_UART_EVEN_PAR.
+ */
+int uart_set_config(uint8_t uart_num, uint32_t config)
+{
+ struct uart_device* uart = NULL;
+
+ if (uart_num >= NUM_UARTS)
+ return -EINVAL;
+ uart = &uarts[uart_num];
+ uart->config = config;
+ return 0;
+}
+
+struct uart_def
+{
+ uint32_t irq;
+ uint32_t power_offset;
+};
+static struct uart_def uart_defs[NUM_UARTS] = {
+ { UART0_IRQ, LPC_SYS_ABH_CLK_CTRL_UART0 },
+ { UART1_IRQ, LPC_SYS_ABH_CLK_CTRL_UART1 },
+ { UART2_IRQ, LPC_SYS_ABH_CLK_CTRL_UART2 },
+};
+
+/***************************************************************************** */
+/* Public access to UART setup */
+
+/* Allow any change to the main clock to be forwarded to us */
+void uart_clk_update(void)
+{
+ int i = 0;
+ for (i = 0; i < NUM_UARTS; i++) {
+ if (uarts[i].baudrate != 0) {
+ uart_clk_on(i, uarts[i].baudrate);
+ }
+ }
+}
+
+/* Change uart mode to RS485
+ * return -ENODEV when the device does not support RS485 mode.
+ */
+int uart_set_mode_rs485(uint32_t uart_num, uint32_t control, uint8_t addr, uint8_t delay)
+{
+ struct uart_device* uart = NULL;
+ struct lpc_usart* uart_regs = NULL;
+ if (uart_num >= NUM_UARTS)
+ return -EINVAL;
+ uart = &uarts[uart_num];
+ uart_regs = uart->regs; /* Get the right registers */
+
+ if (!(uart->capabilities & SERIAL_CAP_RS485)) {
+ return -ENODEV;
+ }
+
+ /* Set current mode */
+ uart->current_mode = SERIAL_MODE_RS485;
+ uart_regs->config = 0; /* FIXME */
+ uart_regs->control = 0; /* FIXME */
+ uart_regs->address = LPC_RS485_ADDR(addr);
+ return 0;
+}
+
+
+/* Do we need to allow setting of other parameters ? (Other than 8n1) */
+int uart_on(uint32_t uart_num, uint32_t baudrate, void (*rx_callback)(uint8_t))
+{
+ struct uart_def* uartdef = NULL;
+ uint32_t status = 0;
+
+ if (uart_num >= NUM_UARTS)
+ return -EINVAL;
+ uartdef = &uart_defs[uart_num];
+ uarts[uart_num].rx_callback = rx_callback;
+
+ NVIC_DisableIRQ( uartdef->irq );
+ /* Turn On power */
+ subsystem_power(uartdef->power_offset, 1);
+ /* Setup clock acording to baudrate */
+ status = uart_clk_on(uart_num, baudrate);
+ /* Setup */
+ uart_setup(uart_num);
+ /* Enable interrupts back on */
+ NVIC_EnableIRQ( uartdef->irq );
+
+ return status;
+}
+
+void uart_off(uint32_t uart_num)
+{
+ struct uart_def* uartdef = NULL;
+ if (uart_num >= NUM_UARTS)
+ return;
+ uartdef = &uart_defs[uart_num];
+
+ NVIC_DisableIRQ( uartdef->irq );
+ uart_clk_off(uart_num);
+ /* Turn Off power */
+ subsystem_power(uartdef->power_offset, 0);
+ uarts[uart_num].rx_callback = NULL;
+ uarts[uart_num].regs->config = 0;
+}
+
+
+
--- /dev/null
+/****************************************************************************
+ * extdrv/tmp101_temp_sensor.c
+ *
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#include "lib/stdint.h"
+
+#include "core/lpc_core.h"
+#include "core/system.h"
+#include "lib/errno.h"
+#include "drivers/i2c.h"
+#include "extdrv/tmp101_temp_sensor.h"
+
+
+/***************************************************************************** */
+/* Support for TMP101 temperature sensors from Texas Instrument */
+/***************************************************************************** */
+/* This driver is made for the TMP101 version of the chip, though there's few
+ * diferences between the TMP100 and TMP101 version.
+ * This driver does not handle the SMBus Alert command.
+ */
+
+
+enum tmp10x_internal_reg_numbers {
+ TMP_REG_TEMPERATURE = 0,
+ TMP_REG_CONFIG,
+ TMP_REG_ALERT_LOW,
+ TMP_REG_ALERT_HIGH,
+};
+
+
+/* Aditional defines, not exported to userspace */
+/* Mode config */
+#define TMP_SHUTDOWN_MODE_ON (1 << 0)
+#define TMP_THERMOSTAT_COMPARATOR_MODE (0 << 1)
+#define TMP_THERMOSTAT_INTERRUPT_MODE (1 << 1)
+/* Alert signal polarity */
+#define TMP_ALERT_POLARITY_LOW (0 << 2)
+#define TMP_ALERT_POLARITY_HIGH (1 << 2)
+/* One-shot measurement trigger */
+#define TMP_ONE_SHOT_TRIGGER (1 << 7)
+
+
+
+/* Check the sensor presence, return 1 if sensor was found.
+ * This is a basic check, it could be anything with the same address ...
+ * addr : the sensor address on most significant bits.
+ */
+int tmp101_probe_sensor(struct tmp101_sensor_config* conf)
+{
+ char cmd_buf = (conf->addr | I2C_READ_BIT);
+
+ /* Did we already probe the sensor ? */
+ if (conf->probe_ok != 1) {
+ conf->probe_ok = i2c_read(conf->bus_num, &cmd_buf, 1, NULL, NULL, 0);
+ }
+ return conf->probe_ok;
+}
+
+/* Convert raw temperature data (expressed as signed value of 16 times the
+ * actual temperature in the twelve higher bits of a sixteen bits wide value)
+ * into a decimal integer value of ten times the actual temperature.
+ * The value returned is thus in tenth of degrees centigrade.
+ */
+int tmp101_convert_to_deci_degrees(uint16_t raw)
+{
+ return (((int16_t)raw * 10) >> 8);
+}
+
+/* Temp Read
+ * Performs a non-blocking read of the temperature from the sensor.
+ * 'raw' and 'deci_degrees' : integer addresses for conversion result, may be NULL.
+ * Return value(s):
+ * Upon successfull completion, returns 0 and the temperature read is placed in the
+ * provided integers. On error, returns a negative integer equivalent to errors from glibc.
+ * -EBADFD : I2C not initialized
+ * -EBUSY : Device or ressource Busy or Arbitration lost
+ * -EINVAL : Invalid argument (buf)
+ * -ENODEV : No such device
+ * -EREMOTEIO : Device did not acknowledge : Any device present ?
+ * -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine
+ */
+#define CMD_BUF_SIZE 3
+int tmp101_sensor_read(struct tmp101_sensor_config* conf, uint16_t* raw, int* deci_degrees)
+{
+ int ret = 0;
+ uint16_t temp = 0;
+ char cmd_buf[CMD_BUF_SIZE] = { conf->addr, TMP_REG_TEMPERATURE, (conf->addr | I2C_READ_BIT), };
+ char ctrl_buf[CMD_BUF_SIZE] = { I2C_CONT, I2C_DO_REPEATED_START, I2C_CONT, };
+
+ if (tmp101_probe_sensor(conf) != 1) {
+ return -ENODEV;
+ }
+
+ /* Read the requested data */
+ if (conf->last_accessed_register == TMP_REG_TEMPERATURE) {
+ /* No need to switch back to temperature register */
+ ret = i2c_read(conf->bus_num, (cmd_buf + 2), 1, (ctrl_buf + 2), (char*)&temp, 2);
+ } else {
+ /* Send (write) temperature register address to TMP101 internal pointer */
+ ret = i2c_read(conf->bus_num, cmd_buf, CMD_BUF_SIZE, ctrl_buf, (char*)&temp, 2);
+ conf->last_accessed_register = TMP_REG_TEMPERATURE;
+ }
+
+ if (ret == 2) {
+ if (raw != NULL) {
+ *raw = byte_swap_16(temp);
+ }
+ if (deci_degrees != NULL) {
+ *deci_degrees = tmp101_convert_to_deci_degrees(byte_swap_16(temp));
+ }
+ return 0;
+ }
+ return ret;
+}
+
+
+/* Sensor config
+ * Performs default configuration of the temperature sensor.
+ * The sensor is thus placed in shutdown mode, the thermostat is in interrupt mode,
+ * and the polarity is set to active high.
+ * Return value :
+ * Upon successfull completion, returns 0. On error, returns a negative integer
+ * equivalent to errors from glibc.
+ * -EBADFD : I2C not initialized
+ * -EBUSY : Device or ressource Busy or Arbitration lost
+ * -EINVAL : Invalid argument (buf)
+ * -ENODEV : No such device
+ * -EREMOTEIO : Device did not acknowledge : Any device present ?
+ * -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine
+ */
+#define CONF_BUF_SIZE 4
+int tmp101_sensor_config(struct tmp101_sensor_config* conf)
+{
+ int ret = 0;
+ char cmd[CONF_BUF_SIZE] = { conf->addr, TMP_REG_CONFIG, };
+
+ if (tmp101_probe_sensor(conf) != 1) {
+ return -ENODEV;
+ }
+
+ /* Store the new configuration */
+ conf->actual_config = (TMP_SHUTDOWN_MODE_ON | TMP_THERMOSTAT_INTERRUPT_MODE | TMP_ALERT_POLARITY_HIGH);
+ conf->actual_config |= (conf->resolution & (0x03 << 5));
+ cmd[2] = conf->actual_config;
+ ret = i2c_write(conf->bus_num, cmd, 3, NULL);
+ conf->last_accessed_register = TMP_REG_CONFIG;
+ if (ret == 3) {
+ return 0; /* Config success */
+ }
+ return ret;
+}
+
+/* Start a conversion when the sensor is in shutdown mode. */
+int tmp101_sensor_start_conversion(struct tmp101_sensor_config* conf)
+{
+ int ret = 0;
+ char cmd[CONF_BUF_SIZE] = { conf->addr, TMP_REG_CONFIG, };
+
+ if (tmp101_probe_sensor(conf) != 1) {
+ return -ENODEV;
+ }
+
+ cmd[2] = conf->actual_config;
+ cmd[2] |= TMP_ONE_SHOT_TRIGGER;
+ ret = i2c_write(conf->bus_num, cmd, 3, NULL);
+ conf->last_accessed_register = TMP_REG_CONFIG;
+ if (ret == 3) {
+ return 0; /* Conversion start success */
+ }
+ return ret;
+}
+
+
--- /dev/null
+/****************************************************************************
+ * extdrv/ws2812.c
+ *
+ *
+ * Copyright 2013 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+/*
+ * Support for the WS2812 Chainable RGB Leds
+ *
+ * WS2812 protocol can be found here : https://www.adafruit.com/datasheets/WS2812.pdf
+ *
+ */
+
+#include "lib/stdint.h"
+#include "core/system.h"
+#include "core/pio.h"
+#include "drivers/gpio.h"
+#include "lib/string.h"
+#include "extdrv/ws2812.h"
+
+
+static struct pio ws2812_gpio = LPC_GPIO_0_0;
+
+/* Configure Selected GPIO as output. */
+void ws2812_config(const struct pio* gpio)
+{
+ struct pin_matrix_entry mat = LPC_GPIO;
+ config_pio(gpio, &mat, LPC_IO_MODE_PULL_UP);
+ pio_copy(&ws2812_gpio, gpio);
+ gpio_dir_out(ws2812_gpio);
+}
+
+static uint8_t led_data[NB_LEDS * 3];
+static uint16_t max_led = 0;
+static uint32_t nb_bytes = 0;
+
+
+static void ws2812_bit_sender(void)
+{
+ struct lpc_gpio* gpio_port = LPC_GPIO_REGS(ws2812_gpio.port);
+ uint32_t gpio_bit = (1 << ws2812_gpio.pin);
+ uint32_t byte_idx = 0;
+ uint8_t bit_idx = 7;
+ uint8_t bit = 0;
+
+ lpc_disable_irq();
+
+ /* Send data */
+ while (byte_idx < nb_bytes) {
+ bit = (led_data[byte_idx] & (0x01 << bit_idx));
+
+ if (bit == 0) {
+ /* "high" time : 350ns */
+ gpio_port->set = gpio_bit;
+ nop();
+ nop();
+ nop();
+ nop();
+ nop();
+ nop();
+ nop();
+ nop();
+ /* "low" time : 800ns */
+ gpio_port->clear = gpio_bit;
+ nop();
+ nop();
+ nop();
+ nop();
+ nop();
+ nop();
+ nop();
+ nop();
+ } else {
+ /* "high" time : 700ns */
+ gpio_port->set = gpio_bit;
+ nop();
+ nop();
+ nop();
+ nop();
+ nop();
+ nop();
+ nop();
+ nop();
+ nop();
+ nop();
+ nop();
+ nop();
+ nop();
+ nop();
+ nop();
+ nop();
+ /* "high" time : 600ns */
+ gpio_port->clear = gpio_bit;
+ nop();
+ nop();
+ }
+
+ /* Move to the next bit */
+ if (bit_idx == 0) {
+ bit_idx = 7;
+ byte_idx++;
+ } else {
+ bit_idx--;
+ }
+ }
+
+ lpc_enable_irq();
+}
+
+/* Send led data from internal buffer (set leds to the selected color).
+ * If nb_leds is 0 then all led data set using ws2812_set_pixel() since the last call
+ * to ws2812_clear_buffer(), ws2812_clear() or ws2812_stop() will be sent.
+ * Call to this function will disable interrupts due to timming restrictions during
+ * the call to ws2812_bit_sender().
+ */
+int ws2812_send_frame(uint16_t nb_leds)
+{
+ if (nb_leds > NB_LEDS) {
+ return -1;
+ }
+ if (nb_leds == 0) {
+ nb_leds = max_led;
+ }
+ nb_bytes = (nb_leds + 1) * 3;
+ ws2812_bit_sender();
+ /* Reset delay */
+ usleep(60);
+ return 0;
+}
+
+/* Set a pixel (led) color in the data buffer */
+int ws2812_set_pixel(uint16_t pixel_num, uint8_t red, uint8_t green, uint8_t blue)
+{
+ if (pixel_num >= NB_LEDS) {
+ return -1;
+ }
+ led_data[ ((pixel_num * 3) + 0) ] = green;
+ led_data[ ((pixel_num * 3) + 1) ] = red;
+ led_data[ ((pixel_num * 3) + 2) ] = blue;
+ if (max_led < pixel_num) {
+ max_led = pixel_num;
+ }
+ return 0;
+}
+
+/* Clear the internal data buffer. */
+void ws2812_clear_buffer(void)
+{
+ memset(led_data, 0, (NB_LEDS * 3));
+ max_led = 0;
+}
+
+/* Clear the internal data buffer and send it to the Leds, turning them all off */
+void ws2812_clear(void)
+{
+ /* Start at first led and send all leds off */
+ ws2812_clear_buffer();
+ ws2812_send_frame(NB_LEDS);
+ max_led = 0;
+}
+
+void ws2812_stop(void) __attribute__ ((alias ("ws2812_clear")));
+
--- /dev/null
+/****************************************************************************
+ * core/iap.h
+ *
+ * Copyright 2015 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef CORE_IAP_H
+#define CORE_IAP_H
+
+#include "lib/stdint.h"
+
+
+/*******************************************************************************/
+/* In Application Programming ROM based routines */
+/*******************************************************************************/
+
+/* Provide access to IAP ROM based routines.
+ * This is the only access to User information block, and allows in-application re-programming
+ * of the micro-controller (for bootloaders, drivers, loadable RTOS tasks, ....)
+ */
+
+
+
+/* Prepare sectors from the programm flash memory for erasing or writting
+ * Sectors numbers start at 0. A flash sector size is 4kB (0x1000 bytes)
+ * Provide the same sector number as start and end to prepare a single sector.
+ * Of course, end sector number MUST be higher than (or equal to) start sector number.
+ */
+int iap_prepare_flash(uint32_t start_sector, uint32_t end_sector);
+
+/* Erase full flash sectors from the programm memory
+ * Sectors numbers start at 0. A flash sector size is 4kB (0x1000 bytes)
+ * Provide the same sector number as start and end to erase a single sector.
+ * Of course, end sector number MUST be higher than (or equal to) start sector number.
+ * Use iap_erase_flash_pages() to erase a single page within a sector.
+ * A sector must be prepared for writing before the erase operation.
+ * Interrputs are disabled during this operation.
+ */
+int iap_erase_flash_sectors(uint32_t start_sector, uint32_t end_sector);
+
+/* Erase pages from the programm memory
+ * Page numbers start at 0. A flash page size is 512 bytes.
+ * Provide the same page number as start and end to erase a single page.
+ * Of course, end page number MUST be higher than (or equal to) start page number.
+ * There is no way to erase only parts of a page.
+ * The sector in which the page reside must be prepared for writing before the page erase
+ * operation.
+ * Interrputs are disabled during this operation.
+ */
+int iap_erase_flash_pages(uint32_t start_page, uint32_t end_page);
+
+
+/* Copy some data from RAM to Flash.
+ * When writting to the programm flash memory, the sectors must be prepared for writing
+ * before the copy operation.
+ * Both dest and src parameters must be aligned to 4 bytes boundary and size must be a
+ * multiple of 4.
+ * dest is the destination address and must be the address of some flash memory.
+ * ser is the source address and must be the address of some RAM memory.
+ * The sector or page in dest must have been erased before writing and no writing operation
+ * performed between (dest) and (dest+size) addresses. (Writting in other parts of the
+ * page or sector is OK)
+ * Interrputs are disabled during this operation.
+ */
+int iap_copy_ram_to_flash(uint32_t dest, uint32_t src, uint32_t size);
+
+
+/* Return the part ID */
+uint32_t iap_read_part_id(void);
+
+
+#endif /* CORE_IAP_H */
+
--- /dev/null
+/****************************************************************************
+ * core/lpc_core.h
+ *
+ * Helper functions to access some registers and Cortex M0 core functionalities.
+ *
+ *
+ * Some of the code from here comes from CMSIS. Should this be rewritten ?
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef LPC_CORE_H
+#define LPC_CORE_H
+
+#include "lib/stdint.h"
+#include "core/lpc_regs.h"
+
+
+
+/***************************************************************************** */
+/* Cortex-M0 System Control Block */
+/***************************************************************************** */
+/* Cortex-M0 System Control Block Registers */
+struct syst_ctrl_block_regs {
+ volatile const uint32_t cpuid; /* 0x000 : CPU ID Base Register (R/ ) */
+ volatile uint32_t icsr; /* 0x004 : Interrupt Control State Register (R/W) */
+ uint32_t reserved_0;
+ volatile uint32_t aircr; /* 0x00C : Application Interrupt / Reset Control Register (R/W) */
+ volatile uint32_t scr; /* 0x010 : System Control Register (R/W) */
+ volatile uint32_t ccr; /* 0x014 : Configuration Control Register (R/W) */
+ uint32_t reserved_1;
+ volatile uint32_t shp[2]; /* 0x01C : System Handlers Priority Registers. [0] is reserved_ (R/W) */
+};
+#define LPC_SCB ((struct syst_ctrl_block_regs *) LPC_SCB_BASE) /* SCB configuration struct */
+
+/* SCB CPUID Register Definitions */
+#define SCB_CPUID_IMPLEMENTER (0xFFUL << 24) /* SCB CPUID: IMPLEMENTER Mask */
+#define SCB_CPUID_VARIANT (0xFUL << 20) /* SCB CPUID: VARIANT Mask */
+#define SCB_CPUID_ARCHITECTURE (0xFUL << 16) /* SCB CPUID: ARCHITECTURE Mask */
+#define SCB_CPUID_PARTNO (0xFFFUL << 4) /* SCB CPUID: PARTNO Mask */
+#define SCB_CPUID_REVISION (0xFUL << 0) /* SCB CPUID: REVISION Mask */
+
+/* SCB Interrupt Control State Register Definitions */
+#define SCB_ICSR_NMIPENDSET (1UL << 31) /* SCB ICSR: NMIPENDSET Mask */
+#define SCB_ICSR_PENDSVSET (1UL << 28) /* SCB ICSR: PENDSVSET Mask */
+#define SCB_ICSR_PENDSVCLR (1UL << 27) /* SCB ICSR: PENDSVCLR Mask */
+#define SCB_ICSR_PENDSTSET (1UL << 26) /* SCB ICSR: PENDSTSET Mask */
+#define SCB_ICSR_PENDSTCLR (1UL << 25) /* SCB ICSR: PENDSTCLR Mask */
+#define SCB_ICSR_ISRPREEMPT (1UL << 23) /* SCB ICSR: ISRPREEMPT Mask */
+#define SCB_ICSR_ISRPENDING (1UL << 22) /* SCB ICSR: ISRPENDING Mask */
+#define SCB_ICSR_VECTPENDING (0x1FFUL << 12) /* SCB ICSR: VECTPENDING Mask */
+#define SCB_ICSR_VECTACTIVE (0x1FFUL << 0) /* SCB ICSR: VECTACTIVE Mask */
+
+/* SCB Application Interrupt and Reset Control Register Definitions */
+#define SCB_AIRCR_VECTKEY_OFFSET 16
+#define SCB_AIRCR_VECTKEY (0xFFFFUL << 16) /* SCB AIRCR: VECTKEY Mask */
+#define SCB_AIRCR_VECTKEYSTAT (0xFFFFUL << 16) /* SCB AIRCR: VECTKEYSTAT Mask */
+#define SCB_AIRCR_ENDIANESS (1UL << 15) /* SCB AIRCR: ENDIANESS Mask */
+#define SCB_AIRCR_SYSRESETREQ (1UL << 2) /* SCB AIRCR: SYSRESETREQ Mask */
+#define SCB_AIRCR_VECTCLRACTIVE (1UL << 1) /* SCB AIRCR: VECTCLRACTIVE Mask */
+
+/* SCB System Control Register Definitions */
+#define SCB_SCR_SEVONPEND (1UL << 4) /* SCB SCR: SEVONPEND Mask */
+#define SCB_SCR_SLEEPDEEP (1UL << 2) /* SCB SCR: SLEEPDEEP Mask */
+#define SCB_SCR_SLEEPONEXIT (1UL << 1) /* SCB SCR: SLEEPONEXIT Mask */
+
+/* SCB Configuration Control Register Definitions */
+#define SCB_CCR_STKALIGN (1UL << 9) /* SCB CCR: STKALIGN Mask */
+#define SCB_CCR_UNALIGN_TRP (1UL << 3) /* SCB CCR: UNALIGN_TRP Mask */
+
+
+
+/*******************************************************************************/
+/* Core Instructions */
+/*******************************************************************************/
+/* NOP */
+#define nop() __asm volatile ("nop" : : : "memory")
+/* SEV : Send Event */
+#define sev() __asm volatile ("sev" : : : "memory")
+/* WFE : Wait For Event */
+#define wfe() __asm volatile ("wfe" : : : "memory")
+/* WFI : Wait For Interrupt */
+#define wfi() __asm volatile ("wfi" : : : "memory")
+
+
+/* Synchronization */
+/* Instruction Synchronization Barrier : Flushes pipeline */
+#define isb() __asm volatile ("isb" : : : "memory")
+/* Data Synchronization Barrier : All explicit memory access completed */
+#define dsb() __asm volatile ("dsb" : : : "memory")
+/* Data Memory Barrier : Ensures apparent order but not completion */
+#define dmb() __asm volatile ("dmb" : : : "memory")
+
+
+
+/* Access to the Application Program Status Register (APSR). */
+static inline uint32_t get_APSR(void)
+{
+ uint32_t r; __asm volatile ("mrs %0, apsr" : "=r" (r)); return (r);
+}
+#define APSR_SATURATION (get_APSR() & (0x1 << 27)) /* bit 27 Saturation condition flag */
+#define APSR_OVERFLOW (get_APSR() & (0x1 << 28)) /* bit 28 Overflow condition code flag */
+#define APSR_CARRY (get_APSR() & (0x1 << 29)) /* bit 29 Carry condition code flag */
+#define APSR_ZERO (get_APSR() & (0x1 << 30)) /* bit 30 Zero condition code flag */
+#define APSR_NEGATIVE (get_APSR() & (0x1 << 31)) /* bit 31 Negative condition code flag */
+
+
+/* Access the Interrupt Program Status Register (IPSR). */
+static inline uint32_t get_IPSR(void)
+{
+ uint32_t r; __asm volatile ("mrs %0, ipsr" : "=r" (r)); return (r);
+}
+#define IPSR (get_IPSR() & 0x1FF) /* bit: 0..8 Exception number */
+#define IPSR_IRQ0 16
+#define IRQ_NUM (IPSR - IPSR_IRQ0)
+
+
+/* Control Register */
+static inline uint32_t get_CONTROL(void)
+{
+ uint32_t r; __asm volatile ("mrs %0, control" : "=r" (r)); return (r);
+}
+static inline void set_CONTROL(uint32_t control)
+{
+ __asm volatile ("msr control, %0" : : "r" (control));
+}
+
+
+/* Process Stack Pointer */
+static inline uint32_t get_process_stack_pointer(void)
+{
+ register uint32_t r; __asm volatile ("mrs %0, psp" : "=r" (r)); return (r);
+}
+static inline void set_process_stack_pointer(uint32_t top_of_stack)
+{
+ __asm volatile ("msr psp, %0" : : "r" (top_of_stack));
+}
+
+
+/* Main Stack Pointer */
+static inline uint32_t get_main_stack_pointer(void)
+{
+ register uint32_t r; __asm volatile ("mrs %0, msp" : "=r" (r)); return (r);
+}
+static inline void set_main_stack_pointer(uint32_t top_of_stack)
+{
+ __asm volatile ("msr msp, %0" : : "r" (top_of_stack));
+}
+
+
+/* Priority Mask */
+static inline uint32_t get_priority_mask(void)
+{
+ uint32_t r; __asm volatile ("mrs %0, primask" : "=r" (r)); return (r);
+}
+static inline void set_priority_mask(uint32_t mask)
+{
+ __asm volatile ("msr primask, %0" : : "r" (mask));
+}
+
+
+/* Base Priority */
+static inline uint32_t get_base_priority(void)
+{
+ uint32_t r; __asm volatile ("mrs %0, basepri_max" : "=r" (r)); return (r);
+}
+static inline void set_base_priority(uint32_t prio)
+{
+ __asm volatile ("msr basepri, %0" : : "r" (prio));
+}
+
+
+/* Fault Mask */
+static inline uint32_t get_fault_mask(void)
+{
+ uint32_t r; __asm volatile ("mrs %0, faultmask" : "=r" (r)); return (r);
+}
+static inline void set_fault_mask(uint32_t mask)
+{
+ __asm volatile ("msr faultmask, %0" : : "r" (mask));
+}
+
+
+
+/*******************************************************************************/
+/* Byte swap instructions */
+/*******************************************************************************/
+/* Swap bytes of each 16-bit halfword in a 32-bit word, keeping halfword order */
+static inline uint32_t double_byte_swap_16(volatile uint32_t value)
+{
+ uint32_t result = 0;
+ __asm volatile ("rev16 %0, %1" : "=l" (result) : "l" (value));
+ return result;
+}
+/* Change endianness of a 16-bit halfword */
+static inline uint32_t byte_swap_16(volatile uint16_t value)
+{
+ uint32_t result = 0;
+ __asm volatile ("rev16 %0, %1" : "=l" (result) : "l" (value));
+ return (result & 0xFFFF);
+}
+/* Change endianness of a 32-bit word */
+static inline uint32_t byte_swap_32(volatile uint32_t value)
+{
+ uint32_t result = 0;
+ __asm volatile ("rev %0, %1" : "=l" (result) : "l" (value));
+ return result;
+}
+
+
+
+
+
+/*******************************************************************************/
+/* Interrupts */
+/*******************************************************************************/
+
+/* Cortex-M0 Processor Exceptions Numbers */
+/* Note : entry 0 is "end stack pointer" */
+#define RESET_IRQ ( -15 ) /* 1 - Reset ... not an interrupt ... */
+#define NMI_IRQ ( -14 ) /* 2 - Non Maskable Interrupt */
+#define HARD_FAULT_IRQ ( -13 ) /* 3 - Cortex-M0 Hard Fault Interrupt */
+/* Note : entry 7 is the 2’s complement of the check-sum of the previous 7 entries */
+#define SV_CALL_IRQ ( -5 ) /* 11 - Cortex-M0 Supervisor Call Interrupt */
+#define PEND_SV_IRQ ( -2 ) /* 14 - Cortex-M0 Pend SV Interrupt */
+#define SYSTICK_IRQ ( -1 ) /* 15 - Cortex-M0 System Tick Interrupt */
+/* LPC82x Specific Interrupt Numbers */
+#define SSP0_IRQ 0
+#define SSP1_IRQ 1
+/* No IRQ 2 */
+#define UART0_IRQ 3
+#define UART1_IRQ 4
+#define UART2_IRQ 5
+/* No IRQ 6 */
+#define I2C1_IRQ 7
+#define I2C0_IRQ 8
+#define SCT_IRQ 9
+#define MRT_IRQ 10
+#define CMP_IRQ 11
+#define WDT_IRQ 12
+#define BOD_IRQ 13
+#define FLASH_IRQ 14
+#define WKT_IRQ 15
+#define ADC_SEQA_IRQ 16
+#define ADC_SEQB_IRQ 17
+#define ADC_THCMP_IRQ 18
+#define ADC_OVR_IRQ 19
+#define SDMA_IRQ 20
+#define I2C2_IRQ 21
+#define I2C3_IRQ 22
+/* No IRQ 23 */
+#define PININT0_IRQ 24
+#define PININT1_IRQ 25
+#define PININT2_IRQ 26
+#define PININT3_IRQ 26
+#define PININT4_IRQ 28
+#define PININT5_IRQ 28
+#define PININT6_IRQ 30
+#define PININT7_IRQ 31
+
+
+/* Enable IRQ Interrupts
+ Enables IRQ interrupts by clearing the I-bit in the CPSR.
+ Can only be executed in Privileged modes.
+ */
+static inline void lpc_enable_irq(void)
+{
+ __asm volatile ("cpsie i");
+}
+
+/* Disable IRQ Interrupts
+ Disables IRQ interrupts by setting the I-bit in the CPSR.
+ Can only be executed in Privileged modes.
+ */
+static inline void lpc_disable_irq(void)
+{
+ __asm volatile ("cpsid i");
+}
+
+
+/*******************************************************************************/
+/* Hardware Abstraction Layer : NVIC Functions */
+/*******************************************************************************/
+
+/* Cortex-M0 NVIC Registers */
+struct nvic_regs {
+ volatile uint32_t int_set_enable; /* 0x000 : Interrupt Set Enable Register (R/W) */
+ uint32_t reserved_0[31];
+ volatile uint32_t int_clear_enable; /* 0x080 : Interrupt Clear Enable Register (R/W) */
+ uint32_t reserved_1[31];
+ volatile uint32_t int_set_pending; /* 0x100 : Interrupt Set Pending Register (R/W) */
+ uint32_t reserved_2[31];
+ volatile uint32_t int_clear_pending; /* 0x180 : Interrupt Clear Pending Register (R/W) */
+ uint32_t reserved_3[31];
+ volatile uint32_t int_active_bit; /* 0x200 : Interrupt Active Bit Register (R/-) */
+ uint32_t reserved_4[63];
+ volatile uint32_t int_priority[8]; /* 0x300 to 0x31C : Interrupt Priority Register (R/W) */
+};
+#define LPC_NVIC ((struct nvic_regs *) LPC_NVIC_BASE) /* NVIC configuration struct */
+
+
+
+/* Enable External Interrupt
+ This function enables a device specific interrupt in the NVIC interrupt controller.
+ The interrupt number cannot be a negative value.
+ IRQ : Number of the external interrupt to enable
+ */
+static inline void NVIC_EnableIRQ(uint32_t IRQ)
+{
+ struct nvic_regs* nvic = LPC_NVIC;
+ nvic->int_set_enable = (1 << (IRQ & 0x1F));
+}
+
+/* Disable External Interrupt
+ This function disables a device specific interupt in the NVIC interrupt controller.
+ The interrupt number cannot be a negative value.
+ IRQ : Number of the external interrupt to disable
+ */
+static inline void NVIC_DisableIRQ(uint32_t IRQ)
+{
+ struct nvic_regs* nvic = LPC_NVIC;
+ nvic->int_clear_enable = (1 << (IRQ & 0x1F));
+}
+
+/* Get Pending Interrupt
+ This function reads the pending register in the NVIC and returns the pending bit
+ for the specified interrupt.
+ IRQ : Number of the interrupt for get pending
+ return :
+ 0 Interrupt status is not pending
+ 1 Interrupt status is pending
+ */
+static inline uint32_t NVIC_GetPendingIRQ(uint32_t IRQ)
+{
+ struct nvic_regs* nvic = LPC_NVIC;
+ return (uint32_t)((nvic->int_set_pending & (1 << (IRQ & 0x1F)))?1:0);
+}
+
+/* Set Pending Interrupt
+ This function sets the pending bit for the specified interrupt.
+ The interrupt number cannot be a negative value.
+ IRQ : Number of the interrupt for set pending
+ */
+static inline void NVIC_SetPendingIRQ(uint32_t IRQ)
+{
+ struct nvic_regs* nvic = LPC_NVIC;
+ nvic->int_set_pending = (1 << (IRQ & 0x1F));
+}
+
+/* Clear Pending Interrupt
+ This function clears the pending bit for the specified interrupt.
+ The interrupt number cannot be a negative value.
+ IRQ : Number of the interrupt for clear pending
+ */
+static inline void NVIC_ClearPendingIRQ(uint32_t IRQ)
+{
+ struct nvic_regs* nvic = LPC_NVIC;
+ nvic->int_clear_pending = (1 << (IRQ & 0x1F));
+}
+
+
+/* The following MACROS handle generation of the register offset and byte masks */
+#define LPC_NVIC_PRIO_BITS 5 /* Number of Bits used for Priority Levels */
+#define LPC_IRQ_BIT_SHIFT(IRQ) (((uint32_t)(IRQ) & 0x03) * 8)
+#define LPC_IRQ_SHP_IDX(IRQ) ((((int32_t)(IRQ) + 16) >> 2) - 1)
+#define LPC_IRQ_IP_IDX(IRQ) ((uint32_t)(IRQ) >> 2)
+
+/* Set Interrupt Priority
+ This function sets the priority for the specified interrupt. The interrupt
+ number can be positive to specify an external (device specific)
+ interrupt, or negative to specify an internal (core) interrupt.
+ Note: The priority cannot be set for every core interrupt.
+ IRQ : Number of the interrupt for set priority
+ priority : Priority to set
+ */
+static inline void NVIC_SetPriority(uint32_t IRQ, uint32_t priority)
+{
+ if ((int32_t)IRQ < 0) {
+ if ((int32_t)IRQ > (-12)) {
+ struct syst_ctrl_block_regs* scb = LPC_SCB;
+ scb->shp[LPC_IRQ_SHP_IDX(IRQ)] = (scb->shp[LPC_IRQ_SHP_IDX(IRQ)] & ~(0xFF << LPC_IRQ_BIT_SHIFT(IRQ))) |
+ (((priority << (8 - LPC_NVIC_PRIO_BITS)) & 0xFF) << LPC_IRQ_BIT_SHIFT(IRQ));
+ }
+ } else if (LPC_IRQ_IP_IDX(IRQ) < 8) {
+ struct nvic_regs* nvic = LPC_NVIC;
+ nvic->int_priority[LPC_IRQ_IP_IDX(IRQ)] = (nvic->int_priority[LPC_IRQ_IP_IDX(IRQ)] & ~(0xFF << LPC_IRQ_BIT_SHIFT(IRQ))) |
+ (((priority << (8 - LPC_NVIC_PRIO_BITS)) & 0xFF) << LPC_IRQ_BIT_SHIFT(IRQ));
+ }
+}
+
+
+/* Get Interrupt Priority
+ This function reads the priority for the specified interrupt. The interrupt
+ number can be positive to specify an external (device specific)
+ interrupt, or negative to specify an internal (core) interrupt.
+ The returned priority value is automatically aligned to the implemented
+ priority bits of the microcontroller.
+ IRQ : Number of the interrupt for get priority
+ return : Interrupt Priority
+ */
+static inline uint32_t NVIC_GetPriority(uint32_t IRQ)
+{
+ if ((int32_t)IRQ < 0) {
+ if ((int32_t)IRQ > (-12)) {
+ struct syst_ctrl_block_regs* scb = LPC_SCB;
+ /* Get priority for Cortex-M0 system interrupts */
+ return ((uint32_t)((scb->shp[LPC_IRQ_SHP_IDX(IRQ)] >> LPC_IRQ_BIT_SHIFT(IRQ) ) >> (8 - LPC_NVIC_PRIO_BITS)));
+ }
+ } else if (LPC_IRQ_IP_IDX(IRQ) < 8) {
+ struct nvic_regs* nvic = LPC_NVIC;
+ /* Get priority for device specific interrupts */
+ return ((uint32_t)((nvic->int_priority[LPC_IRQ_IP_IDX(IRQ)] >> LPC_IRQ_BIT_SHIFT(IRQ) ) >> (8 - LPC_NVIC_PRIO_BITS)));
+ }
+}
+
+
+/* System Reset
+ This function initiate a system reset request to reset the MCU.
+ */
+static inline void NVIC_SystemReset(void)
+{
+ struct syst_ctrl_block_regs* scb = LPC_SCB;
+ dsb(); /* Ensure all outstanding memory accesses included buffered write are completed before reset */
+ scb->aircr = ((0x5FA << SCB_AIRCR_VECTKEY_OFFSET) | SCB_AIRCR_SYSRESETREQ);
+ dsb(); /* Ensure completion of memory access */
+ while (1); /* wait until reset */
+}
+
+
+
+/*******************************************************************************/
+/* Sync lock */
+/*******************************************************************************/
+/* NOTE : There is no syncro instructions on Cortex-M0 */
+
+/* IRQ released version : IRQ are enabled upon return
+ * Returns the old value after the new value has been set.
+ */
+static inline uint32_t sync_lock_test_and_set(volatile uint32_t *addr, uint32_t value)
+{
+ uint32_t oldval;
+ lpc_disable_irq();
+ dsb();
+ oldval = *addr;
+ *addr = value;
+ dsb();
+ lpc_enable_irq();
+ return oldval;
+}
+/* Remove the lock */
+static inline void sync_lock_release(volatile uint32_t *addr)
+{
+ *addr = 0;
+ dsb();
+}
+
+/* IRQ disabled version : If lock is acquired (old value is 0) IRQ are
+ * disabled when the call returns
+ * Returns the old value after the new value has been set.
+ */
+static inline uint32_t irq_sync_lock_test_and_set(volatile uint32_t *addr, uint32_t value)
+{
+ uint32_t oldval;
+ lpc_disable_irq();
+ dsb();
+ oldval = *addr;
+ *addr = value;
+ dsb();
+ if (oldval) {
+ lpc_enable_irq();
+ }
+ return oldval;
+}
+/* Remove the lock */
+static inline void irq_sync_lock_release(volatile uint32_t *addr)
+{
+ *addr = 0;
+ dsb();
+ lpc_enable_irq();
+}
+
+
+#endif /* LPC_CORE_H */
+
--- /dev/null
+/****************************************************************************
+ * core/lpc_regs.h
+ *
+ * Cortex-M0 Core Registers definitions
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+#ifndef LPC_REGS_H
+#define LPC_REGS_H
+
+#include "lib/stdint.h"
+#include "lib/stddef.h"
+
+
+
+/***************************************************************************** */
+/* Memory Map */
+/***************************************************************************** */
+/* Base addresses */
+#define LPC_FLASH_BASE (0x00000000UL)
+#define LPC_RAM_BASE (0x10000000UL)
+#define LPC_APB_BASE (0x40000000UL)
+#define LPC_CRC_BASE (0x50000000UL)
+#define LPC_SCT_PWM_BASE (0x50004000UL)
+#define LPC_DMA_BASE (0x50008000UL)
+#define LPC_GPIO_BASE (0xA0000000UL)
+#define LPC_GPIO_INTR_BASE (0xA0004000UL)
+
+/* Memory mapping of Cortex-M0 Hardware */
+#define LPC_SCS_BASE (0xE000E000UL) /* System Control Space Base Address */
+#define LPC_COREDEBUG_BASE (0xE000EDF0UL) /* Core Debug Base Address */
+#define LPC_SYSTICK_BASE (LPC_SCS_BASE + 0x0010UL) /* SysTick Base Address */
+#define LPC_NVIC_BASE (LPC_SCS_BASE + 0x0100UL) /* NVIC Base Address */
+#define LPC_SCB_BASE (LPC_SCS_BASE + 0x0D00UL) /* System Control Block Base Address */
+
+/* APB0 peripherals */
+#define LPC_WDT_BASE (LPC_APB_BASE + 0x00000)
+#define LPC_MRT_BASE (LPC_APB_BASE + 0x04000)
+#define LPC_SWT_BASE (LPC_APB_BASE + 0x08000)
+#define LPC_SWITCH_MATRIX_BASE (LPC_APB_BASE + 0x0C000)
+#define LPC_ADC_BASE (LPC_APB_BASE + 0x1C000)
+#define LPC_PMU_BASE (LPC_APB_BASE + 0x20000)
+#define LPC_ACMP_BASE (LPC_APB_BASE + 0x24000)
+#define LPC_DMA_TRIG_MUX_BASE (LPC_APB_BASE + 0x28000)
+#define LPC_IMUX_BASE (LPC_APB_BASE + 0x2C000)
+#define LPC_FLASH_CONFIG_BASE (LPC_APB_BASE + 0x40000)
+#define LPC_IOCON_BASE (LPC_APB_BASE + 0x44000)
+#define LPC_SYSCON_BASE (LPC_APB_BASE + 0x48000)
+#define LPC_I2C0_BASE (LPC_APB_BASE + 0x50000)
+#define LPC_I2C1_BASE (LPC_APB_BASE + 0x54000)
+#define LPC_SSP0_BASE (LPC_APB_BASE + 0x58000)
+#define LPC_SSP1_BASE (LPC_APB_BASE + 0x5C000)
+#define LPC_USART0_BASE (LPC_APB_BASE + 0x64000)
+#define LPC_USART1_BASE (LPC_APB_BASE + 0x68000)
+#define LPC_USART2_BASE (LPC_APB_BASE + 0x6C000)
+#define LPC_I2C2_BASE (LPC_APB_BASE + 0x70000)
+#define LPC_I2C3_BASE (LPC_APB_BASE + 0x74000)
+
+
+
+
+
+/***************************************************************************** */
+/* Power Management Unit */
+/***************************************************************************** */
+/* Power Management Unit (PMU) */
+struct lpc_pm_unit
+{
+ volatile uint32_t power_ctrl; /* 0x000 : Power control Register (R/W) */
+ volatile uint32_t gp_data[4]; /* 0x004 to 0x010 : General purpose Register 0 to 3 (R/W) */
+ volatile uint32_t system_config; /* 0x014 : System configuration register (R/W) */
+ /* (RTC clock control and hysteresis of the WAKEUP pin) */
+};
+#define LPC_PMU ((struct lpc_pm_unit *) LPC_PMU_BASE)
+
+/* System config register */
+#define LPC_WAKEUP_PIN_HYST_MASK (0x01 << 10)
+#define LPC_RTC_CLK_SRC_SHIFT 11
+#define LPC_RTC_CLK_SRC_MASK (0x0F << LPC_RTC_CLK_SRC_SHIFT)
+/* See RTC section above for RTC Clock source selection bits */
+
+
+
+/***************************************************************************** */
+/* Synchronous Serial Communication */
+/***************************************************************************** */
+/* Synchronous Serial Communication (SSP) */
+struct lpc_ssp
+{
+ volatile uint32_t ctrl_0; /* 0x000 : Control Register 0 (R/W) */
+ volatile uint32_t ctrl_1; /* 0x004 : Control Register 1 (R/W) */
+ volatile uint32_t data; /* 0x008 : Data Register (R/W) */
+ volatile const uint32_t status; /* 0x00C : Status Registe (R/-) */
+ volatile uint32_t clk_prescale; /* 0x010 : Clock Prescale Register (R/W) */
+ volatile uint32_t int_mask; /* 0x014 : Interrupt Mask Set and Clear Register (R/W) */
+ volatile uint32_t raw_int_status; /* 0x018 : Raw Interrupt Status Register (R/-) */
+ volatile uint32_t masked_int_status; /* 0x01C : Masked Interrupt Status Register (R/-) */
+ volatile uint32_t int_clear; /* 0x020 : SSPICR Interrupt Clear Register (-/W) */
+ volatile uint32_t dma_ctrl; /* 0x024 : DMA Control Register (R/W) */
+};
+#define LPC_SSP0 ((struct lpc_ssp *) LPC_SSP0_BASE)
+
+/* SSP Control 0 register */
+#define LPC_SSP_DATA_WIDTH(x) (((x) - 1) & 0x0F) /* Use 4 for 4 bits, 5 for 5 bits, .... */
+#define LPC_SSP_FRAME_SPI (0x00 << 4)
+#define LPC_SSP_FRAME_TI (0x01 << 4)
+#define LPC_SSP_FRAME_MICROWIRE (0x02 << 4)
+#define LPC_SSP_SPI_CLK_LOW (0x00 << 6)
+#define LPC_SSP_SPI_CLK_HIGH (0x01 << 6)
+#define LPC_SSP_SPI_CLK_FIRST (0x00 << 7)
+#define LPC_SSP_SPI_CLK_LAST (0x01 << 7)
+#define LPC_SSP_SPI_CLK_RATE_DIV(x) (((x) & 0xFF) << 8)
+/* SSP Control 1 register */
+#define LPC_SSP_LOOPBACK_MODE (0x01 << 0)
+#define LPC_SSP_ENABLE (0x01 << 1)
+#define LPC_SSP_MASTER_MODE (0x00 << 2)
+#define LPC_SSP_SLAVE_MODE (0x01 << 2)
+#define LPC_SSP_SLAVE_OUT_DISABLE (0x01 << 3)
+
+/* SSP Status register */
+#define LPC_SSP_ST_TX_EMPTY (0x01 << 0)
+#define LPC_SSP_ST_TX_NOT_FULL (0x01 << 1)
+#define LPC_SSP_ST_RX_NOT_EMPTY (0x01 << 2)
+#define LPC_SSP_ST_RX_FULL (0x01 << 3)
+#define LPC_SSP_ST_BUSY (0x01 << 4)
+
+/* SSP Interrupt Mask, Raw, Status, Clear */
+#define LPC_SSP_INTR_RX_OVERRUN (0x01 << 0)
+#define LPC_SSP_INTR_RX_TIMEOUT (0x01 << 1)
+#define LPC_SSP_INTR_RX_HALF_FULL (0x01 << 2)
+#define LPC_SSP_INTR_TX_HALF_EMPTY (0x01 << 3)
+
+/* SSP DMA Control */
+#define LPC_SSP_RX_DMA_EN (0x01 << 0)
+#define LPC_SSP_TX_DMA_EN (0x01 << 1)
+
+
+
+/***************************************************************************** */
+/* Timer */
+/***************************************************************************** */
+/* Timer (TMR) */
+struct lpc_timer
+{
+ volatile uint32_t int_reg; /* 0x000 : Interrupt Register (R/W) */
+ volatile uint32_t timer_ctrl; /* 0x004 : Timer Control Register (R/W) */
+ volatile uint32_t timer_counter; /* 0x008 : Timer Counter Register (R/W) */
+ volatile uint32_t prescale; /* 0x00C : Prescale Register (R/W) */
+ volatile uint32_t prescale_counter; /* 0x010 : Prescale Counter Register (R/W) */
+ volatile uint32_t match_ctrl; /* 0x014 : Match Control Register (R/W) */
+ volatile uint32_t match_reg[4]; /* 0x018 : Match Register 0 to 3 (R/W) */
+ volatile uint32_t capture_ctrl; /* 0x028 : Capture Control Register (R/W) */
+ volatile const uint32_t capture_reg[4]; /* 0x02C : Capture Register 0 to 3 (R/ ) */
+ volatile uint32_t external_match; /* 0x03C : External Match Register (R/W) */
+ uint32_t reserved_2[12];
+ volatile uint32_t count_ctrl; /* 0x070 : Count Control Register (R/W) */
+ volatile uint32_t pwm_ctrl; /* 0x074 : PWM Control Register (R/W) */
+};
+#define LPC_TMR16B0 ((struct lpc_timer *) LPC_TIMER0_BASE)
+#define LPC_TMR16B1 ((struct lpc_timer *) LPC_TIMER1_BASE)
+#define LPC_TMR32B0 ((struct lpc_timer *) LPC_TIMER2_BASE)
+#define LPC_TMR32B1 ((struct lpc_timer *) LPC_TIMER3_BASE)
+#define LPC_TIMER_REGS(x) ((struct lpc_timer *) (LPC_TIMER0_BASE + ((x) * 0x4000)))
+
+#define LPC_TIMER_COUNTER_ENABLE (1 << 0) /* CEN */
+#define LPC_TIMER_COUNTER_RESET (1 << 1) /* CRST */
+
+/* Match internal configuration */
+#define LPC_TIMER_INTERRUPT_ON_MATCH 0x01
+#define LPC_TIMER_RESET_ON_MATCH 0x02
+#define LPC_TIMER_STOP_ON_MATCH 0x04
+#define LPC_TIMER_MATCH_ERASE(x) (0x07 << (((x) & 0x03) * 3))
+#define LPC_TIMER_MATCH_SHIFT(x) (((x) & 0x03) * 3)
+/* Capture internal configuration */
+#define LPC_TIMER_CAP_ON_RISING_EDGE 0x01
+#define LPC_TIMER_CAP_ON_FALLING_EDGE 0x02
+#define LPC_TIMER_INTERRUPT_ON_CAPTURE 0x04
+#define LPC_TIMER_CAPTURE_ERASE(x) (0x07 << (((x) & 0x03) * 3))
+#define LPC_TIMER_CAPTURE_SHIFT(x) (((x) & 0x03) * 3)
+/* Match external configuration */
+#define LPC_TIMER_NOTHING_ON_MATCH 0x00
+#define LPC_TIMER_CLEAR_ON_MATCH 0x01
+#define LPC_TIMER_SET_ON_MATCH 0x02
+#define LPC_TIMER_TOGGLE_ON_MATCH 0x03
+#define LPC_TIMER_EXT_MATCH0_SHIFT 4
+#define LPC_TIMER_EXT_MATCH1_SHIFT 6
+#define LPC_TIMER_EXT_MATCH2_SHIFT 8
+#define LPC_TIMER_EXT_MATCH3_SHIFT 10
+/* Counter */
+#define LPC_COUNTER_IS_TIMER 0x00
+#define LPC_COUNTER_INC_ON_RISING 0x01
+#define LPC_COUNTER_INC_ON_FALLING 0x02
+#define LPC_COUNTER_INC_ON_BOTH 0x03
+#define LPC_COUNTER_INC_INPUT_SHIFT 2
+#define LPC_COUNTER_INC_INPUT(x) (((x) & 0x03) << LPC_COUNTER_INC_INPUT_SHIFT)
+#define LPC_COUNTER_CLEAR_ON_EVENT_EN (0x01 << 4)
+#define LPC_COUNTER_CLEAR_ON_EVENT_SHIFT 5
+#define LPC_COUNTER_CLEAR_ON_CHAN0_RISE 0x00
+#define LPC_COUNTER_CLEAR_ON_CHAN0_FALL 0x01
+#define LPC_COUNTER_CLEAR_ON_CHAN1_RISE 0x02
+#define LPC_COUNTER_CLEAR_ON_CHAN1_FALL 0x03
+#define LPC_COUNTER_CLEAR_ON_CHAN2_RISE 0x04
+#define LPC_COUNTER_CLEAR_ON_CHAN2_FALL 0x05
+#define LPC_COUNTER_CLEAR_ON_CHAN3_RISE 0x06
+#define LPC_COUNTER_CLEAR_ON_CHAN3_FALL 0x07
+/* PWM */
+#define LPC_PWM_CHANNEL_ENABLE(x) (0x01 << ((x) & 0x03))
+
+
+
+
+
+
+#endif /* LPC_REGS_H */
--- /dev/null
+/****************************************************************************
+ * core/pio.h
+ *
+ * Copyright 2014 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ ****************************************************************************/
+
+#ifndef CORE_PIO_H
+#define CORE_PIO_H
+
+/* The "PIO" module gives access to the configuration of all the pins of the
+ * micro-controller, whatever the function of the pin.
+ * It has nothing related to the GPIO function of the pins.
+ */
+
+#include <core/lpc_regs.h>
+
+
+
+/* Define a pin for the switch matrix fixed functions or GPIO
+ * When used for a GPIO or movable function the bit 7 of offset0 must be 1. Then both
+ * offset0 and offset1 will be used to disable the corresponding fixed functions if any.
+ * When used for a fixed-pin function the bit 7 of offset0 must be 0. Then the offset0
+ * field holds the fixed function to activate, and the offset1 field holds the fixed
+ * function to disable, if any.
+ */
+struct pio {
+ uint8_t port; /* 0xFF indicates the end of a pin array */
+ uint8_t pin; /* 0xFF indicates the end of a pin array */
+ uint8_t offset0; /* 0xFF indicates no fixed function on this pin */
+ uint8_t offset1; /* "" */
+};
+
+#define ARRAY_LAST_PIN {0xFF, 0xFF, 0xFF, 0xFF}
+#define PIO_LAST ARRAY_LAST_PIN
+
+struct pin_matrix_entry {
+ uint8_t reg_offset;
+ uint8_t bit_shift;
+};
+
+struct pio_config {
+ struct pio pio;
+ struct pin_matrix_entry matrix;
+ uint16_t mode;
+};
+#define ARRAY_LAST_PIO { PIO_LAST, {0xFF, 0xFF}, 0 }
+
+
+#define PORT0_NB_PINS 29
+
+/* Simple copy function. */
+void pio_copy(struct pio* dst, const struct pio* src);
+
+/* Configure the pin in the requested function and mode. */
+void config_pio(const struct pio* pp, const struct pin_matrix_entry* matrix, uint16_t mode);
+
+/* Configure a set (array) of pins in a single loop */
+void set_pins(const struct pio_config* pins);
+
+
+/***************************************************************************** */
+/* Pins configuration */
+/***************************************************************************** */
+
+/****************************************************************************/
+/* Defines for struct pio */
+
+/* GPIO Pins */
+#define LPC_GPIO_0_0 {0, 0, (0x80 | 0), 0xFF}
+#define LPC_GPIO_0_1 {0, 1, (0x80 | 1), 9}
+#define LPC_GPIO_0_2 {0, 2, (0x80 | 5), 0xFF}
+#define LPC_GPIO_0_3 {0, 3, (0x80 | 4), 0xFF}
+#define LPC_GPIO_0_4 {0, 4, (0x80 | 24), 0xFF}
+#define LPC_GPIO_0_5 {0, 5, (0x80 | 8), 0xFF}
+#define LPC_GPIO_0_6 {0, 6, (0x80 | 10), 14}
+#define LPC_GPIO_0_7 {0, 7, (0x80 | 13), 0xFF}
+#define LPC_GPIO_0_8 {0, 8, (0x80 | 6), 0xFF}
+#define LPC_GPIO_0_9 {0, 9, (0x80 | 7), 0xFF}
+#define LPC_GPIO_0_10 {0, 10, (0x80 | 12), 0xFF}
+#define LPC_GPIO_0_11 {0, 11, (0x80 | 11), 0xFF}
+#define LPC_GPIO_0_12 {0, 12, (0x80 | 0xFF), 0xFF}
+#define LPC_GPIO_0_13 {0, 13, (0x80 | 23), 0xFF}
+#define LPC_GPIO_0_14 {0, 14, (0x80 | 2), 15}
+#define LPC_GPIO_0_15 {0, 15, (0x80 | 0xFF), 0xFF}
+#define LPC_GPIO_0_16 {0, 16, (0x80 | 0xFF), 0xFF}
+#define LPC_GPIO_0_17 {0, 17, (0x80 | 22), 0xFF}
+#define LPC_GPIO_0_18 {0, 18, (0x80 | 21), 0xFF}
+#define LPC_GPIO_0_19 {0, 19, (0x80 | 20), 0xFF}
+#define LPC_GPIO_0_20 {0, 20, (0x80 | 19), 0xFF}
+#define LPC_GPIO_0_21 {0, 21, (0x80 | 18), 0xFF}
+#define LPC_GPIO_0_22 {0, 22, (0x80 | 17), 0xFF}
+#define LPC_GPIO_0_23 {0, 23, (0x80 | 3), 16}
+#define LPC_GPIO_0_24 {0, 24, (0x80 | 0xFF), 0xFF}
+#define LPC_GPIO_0_25 {0, 25, (0x80 | 0xFF), 0xFF}
+#define LPC_GPIO_0_26 {0, 26, (0x80 | 0xFF), 0xFF}
+#define LPC_GPIO_0_27 {0, 27, (0x80 | 0xFF), 0xFF}
+#define LPC_GPIO_0_28 {0, 28, (0x80 | 0xFF), 0xFF}
+
+/* FIXED functions */
+/* Comparator Pins */
+#define LPC_ACMP_I1_PIO_0_0 {0, 0, 0, 0xFF}
+#define LPC_ACMP_I2_PIO_0_1 {0, 1, 1, 9}
+#define LPC_ACMP_I3_PIO_0_14 {0, 14, 2, 15}
+#define LPC_ACMP_I4_PIO_0_23 {0, 23, 3, 16}
+#define LPC_VDDCMP_PIO_0_6 {0, 6, 10, 14}
+/* ADC Pins */
+#define LPC_ADC_AD0_PIO_0_7 {0, 7, 13, 0xFF}
+#define LPC_ADC_AD1_PIO_0_6 {0, 6, 14, 10}
+#define LPC_ADC_AD2_PIO_0_14 {0, 14, 15, 2}
+#define LPC_ADC_AD3_PIO_0_23 {0, 23, 16, 3}
+#define LPC_ADC_AD4_PIO_0_22 {0, 22, 17, 0xFF}
+#define LPC_ADC_AD5_PIO_0_21 {0, 21, 18, 0xFF}
+#define LPC_ADC_AD6_PIO_0_20 {0, 20, 19, 0xFF}
+#define LPC_ADC_AD7_PIO_0_19 {0, 19, 20, 0xFF}
+#define LPC_ADC_AD8_PIO_0_18 {0, 18, 21, 0xFF}
+#define LPC_ADC_AD9_PIO_0_17 {0, 17, 22, 0xFF}
+#define LPC_ADC_AD10_PIO_0_13 {0, 13, 23, 0xFF}
+#define LPC_ADC_AD11_PIO_0_4 {0, 4, 24, 0xFF}
+/* CLK and Reset Pin */
+#define LPC_XTALIN_PIO_0_8 {0, 8, 6, 0xFF}
+#define LPC_XTALOUT_PIO_0_9 {0, 9, 7, 0xFF}
+#define LPC_CLKIN_PIO_0_1 {0, 1, 9, 1}
+#define LPC_RESET_PIO_0_5 {0, 5, 8, 0xFF}
+/* SWD (Debug) pins */
+#define LPC_SWD_SWCLK_PIO_0_3 {0, 3, 4, 0xFF}
+#define LPC_SWD_SWDIO_PIO_0_2 {0, 2, 5, 0xFF}
+/* I2C Pins */
+#define LPC_I2C0_SCL_PIO_0_10 {0, 10, 12, 0xFF}
+#define LPC_I2C0_SDA_PIO_0_11 {0, 11, 11, 0xFF}
+
+
+/****************************************************************************/
+/* Defines for struct pin_matrix_entry : MOVABLE functions */
+
+#define LPC_GPIO {0xFF, 0xFF}
+#define LPC_FIXED {0xFF, 0xFF}
+
+/* CLKOUT Pin */
+#define LPC_CLKOUT {11, 16}
+
+/* Patern match */
+#define LPC_PATTERN_MATCH {11, 24}
+
+/* UART Pins */
+/* UART0 Pins */
+#define LPC_UART0_TX {0, 0}
+#define LPC_UART0_RX {0, 8}
+#define LPC_UART0_RTS {0, 16}
+#define LPC_UART0_CTS {0, 24}
+#define LPC_UART0_SCLK {1, 0}
+/* UART1 Pins */
+#define LPC_UART1_TX {1, 8}
+#define LPC_UART1_RX {1, 16}
+#define LPC_UART1_RTS {1, 24}
+#define LPC_UART1_CTS {2, 0}
+#define LPC_UART1_SCLK {2, 8}
+/* UART2 Pins */
+#define LPC_UART2_TX {2, 16}
+#define LPC_UART2_RX {2, 24}
+#define LPC_UART2_RTS {3, 0}
+#define LPC_UART2_CTS {3, 8}
+#define LPC_UART2_SCLK {3, 16}
+
+/* SPI Pins */
+/* SSP 0 */
+#define LPC_SSP0_SCLK {3, 24}
+#define LPC_SSP0_MOSI {4, 0}
+#define LPC_SSP0_MISO {4, 8}
+#define LPC_SSP0_SSEL0 {4, 16}
+#define LPC_SSP0_SSEL1 {4, 24}
+#define LPC_SSP0_SSEL2 {5, 0}
+#define LPC_SSP0_SSEL3 {5, 8}
+/* SSP 1 */
+#define LPC_SSP1_SCLK {5, 16}
+#define LPC_SSP1_MOSI {5, 24}
+#define LPC_SSP1_MISO {6, 0}
+#define LPC_SSP1_SSEL0 {6, 8}
+#define LPC_SSP1_SSEL1 {6, 16}
+
+/* I2C Pins */
+/* I2C1 */
+#define LPC_I2C1_SDA {9, 8}
+#define LPC_I2C1_SCL {9, 16}
+/* I2C2 */
+#define LPC_I2C2_SDA {9, 24}
+#define LPC_I2C2_SCL {10, 0}
+/* I2C3 */
+#define LPC_I2C3_SDA {10, 8}
+#define LPC_I2C3_SCL {10, 16}
+
+/* ADC Pins */
+#define LPC_ADC_PINTRIG0 {10, 24}
+#define LPC_ADC_PINTRIG1 {11, 0}
+#define LPC_ACMP_OUT {11, 8}
+
+/* Timer Pins */
+/* State configurable Timer */
+#define LPC_SCT_PIN0 {6, 24}
+#define LPC_SCT_PIN1 {7, 0}
+#define LPC_SCT_PIN2 {7, 8}
+#define LPC_SCT_PIN3 {7, 16}
+#define LPC_SCT_POUT0 {7, 24}
+#define LPC_SCT_POUT1 {8, 0}
+#define LPC_SCT_POUT2 {8, 8}
+#define LPC_SCT_POUT3 {8, 16}
+#define LPC_SCT_POUT4 {8, 24}
+#define LPC_SCT_POUT5 {9, 0}
+
+
+/***************************************************************************** */
+/* Switch matrix */
+/***************************************************************************** */
+/* pin switch matrix (SWM) : configure fixed and movable functions */
+#define LPC_MATRIX_NB_PIN_ASSIGN 12
+struct lpc_switch_matrix
+{
+ volatile uint32_t pin_assign[LPC_MATRIX_NB_PIN_ASSIGN]; /* 0x000 to 0x02C : PINASSIGN0 to PINASSIGN11 (R/W) */
+ uint32_t reserved_0[100];
+ volatile uint32_t pin_enable[1]; /* 0x1C0 : Enabme fixed-pin functions */
+};
+#define LPC_SWITCH_MATRIX ((struct lpc_switch_matrix *) LPC_SWITCH_MATRIX_BASE)
+
+
+
+/***************************************************************************** */
+/* IO Control */
+/***************************************************************************** */
+/* Pin Connect Block (IOCON) */
+struct lpc_io_control
+{
+ volatile uint32_t pio0_17; /* 0x000 : I/O configuration for pin pio0_17 (R/W) */
+ volatile uint32_t pio0_13; /* 0x004 : I/O configuration for pin pio0_13 (R/W) */
+ volatile uint32_t pio0_12; /* 0x008 : I/O configuration for pin pio0_12 (R/W) */
+ volatile uint32_t pio0_5; /* 0x00C : I/O configuration for pin pio0_5 (R/W) */
+ volatile uint32_t pio0_4; /* 0x010 : I/O configuration for pin pio0_4 (R/W) */
+ volatile uint32_t pio0_3; /* 0x014 : I/O configuration for pin pio0_3 (R/W) */
+ volatile uint32_t pio0_2; /* 0x018 : I/O configuration for pin pio0_2 (R/W) */
+ volatile uint32_t pio0_11; /* 0x01C : I/O configuration for pin pio0_11 (R/W) */
+ volatile uint32_t pio0_10; /* 0x020 : I/O configuration for pin pio0_10 (R/W) */
+ volatile uint32_t pio0_16; /* 0x024 : I/O configuration for pin pio0_16 (R/W) */
+ volatile uint32_t pio0_15; /* 0x028 : I/O configuration for pin pio0_15 (R/W) */
+ volatile uint32_t pio0_1; /* 0x02C : I/O configuration for pin pio0_1 (R/W) */
+ uint32_t reserved0; /* 0x030 */
+ volatile uint32_t pio0_9; /* 0x034 : I/O configuration for pin pio0_9 (R/W) */
+ volatile uint32_t pio0_8; /* 0x038 : I/O configuration for pin pio0_8 (R/W) */
+ volatile uint32_t pio0_7; /* 0x03C : I/O configuration for pin pio0_7 (R/W) */
+ volatile uint32_t pio0_6; /* 0x040 : I/O configuration for pin pio0_6 (R/W) */
+ volatile uint32_t pio0_0; /* 0x044 : I/O configuration for pin pio0_0 (R/W) */
+ volatile uint32_t pio0_14; /* 0x048 : I/O configuration for pin pio0_14 (R/W) */
+ uint32_t reserved1; /* 0x04C */
+ volatile uint32_t pio0_28; /* 0x050 : I/O configuration for pin pio0_28 (R/W) */
+ volatile uint32_t pio0_27; /* 0x054 : I/O configuration for pin pio0_27 (R/W) */
+ volatile uint32_t pio0_26; /* 0x058 : I/O configuration for pin pio0_26 (R/W) */
+ volatile uint32_t pio0_25; /* 0x05C : I/O configuration for pin pio0_25 (R/W) */
+ volatile uint32_t pio0_24; /* 0x060 : I/O configuration for pin pio0_24 (R/W) */
+ volatile uint32_t pio0_23; /* 0x064 : I/O configuration for pin pio0_23 (R/W) */
+ volatile uint32_t pio0_22; /* 0x068 : I/O configuration for pin pio0_22 (R/W) */
+ volatile uint32_t pio0_21; /* 0x06C : I/O configuration for pin pio0_21 (R/W) */
+ volatile uint32_t pio0_20; /* 0x070 : I/O configuration for pin pio0_20 (R/W) */
+ volatile uint32_t pio0_19; /* 0x074 : I/O configuration for pin pio0_19 (R/W) */
+ volatile uint32_t pio0_18; /* 0x078 : I/O configuration for pin pio0_18 (R/W) */
+
+};
+#define LPC_IO_CONTROL ((struct lpc_io_control *) LPC_IOCON_BASE)
+
+
+#define LPC_IO_MODE_INACTIVE (0x00 << 3)
+#define LPC_IO_MODE_PULL_DOWN (0x01 << 3)
+#define LPC_IO_MODE_PULL_UP (0x02 << 3)
+#define LPC_IO_MODE_REPEATER (0x03 << 3)
+
+#define LPC_IO_HYSTERESIS_EN (0x01 << 5)
+
+#define LPC_IO_INVERTED (0x01 << 6)
+
+#define LPC_IO_I2CMODE_STD (0x00 << 8)
+#define LPC_IO_I2CMODE_GPIO (0x01 << 8)
+#define LPC_IO_I2CMODE_FPLUS (0x02 << 8)
+
+#define LPC_IO_OPEN_DRAIN_ENABLE (0x01 << 10)
+
+#define LPC_IO_SAMPLE_MODE_BYP (0x00 << 11)
+#define LPC_FILTER_ONE_CLK 1
+#define LPC_FILTER_TWO_CLK 2
+#define LPC_FILTER_THREE_CLK 3
+#define LPC_IO_SAMPLE_MODE(x) ((x & 0x03) << 11)
+
+#define LPC_IO_SAMPLE_CLK_DIV(x) ((x & 0x07) << 13)
+
+
+#endif /* CORE_PIO_H */
--- /dev/null
+/****************************************************************************
+ * core/system.h
+ *
+ * All low-level functions for clocks configuration and switch, system
+ * power-up, reset, and power-down.
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef CORE_SYSTEM_H
+#define CORE_SYSTEM_H
+
+#include "lib/stdint.h"
+
+#include "core/lpc_regs.h"
+#include "core/lpc_core.h"
+
+
+
+/***************************************************************************** */
+/* Power up defaults */
+/***************************************************************************** */
+/* Change reset power state to our default, removing power from unused
+ * interfaces */
+void system_set_default_power_state(void);
+
+/***************************************************************************** */
+/* Power */
+/***************************************************************************** */
+void enter_deep_sleep(void);
+
+/* Power on or off a subsystem */
+void subsystem_power(uint32_t power_bit, uint32_t on_off);
+/* Check whether a subsystem is powered or not */
+uint8_t subsystem_powered(uint32_t power_bit);
+
+/* Reset a subsystem */
+void subsystem_reset(uint32_t reset_bit);
+void subsystem_reset_hold(uint32_t reset_bit);
+void subsystem_reset_release(uint32_t reset_bit);
+
+/* Configure the brown-out detection. */
+void system_brown_out_detection_config(uint32_t level);
+
+/***************************************************************************** */
+/* System Clock */
+/***************************************************************************** */
+/* A clock frequency is defined as the integer value in MHz divided by 12, shifted
+ * by 3.
+ */
+/* PLL may fail to lock for frenquencies above 60MHz */
+#define FREQ_SEL_60MHz (5 << 3)
+#define FREQ_SEL_48MHz (4 << 3)
+#define FREQ_SEL_36MHz (3 << 3)
+#define FREQ_SEL_24MHz (2 << 3)
+#define FREQ_SEL_12MHz (1 << 3)
+#define FREQ_SEL_IRC FREQ_SEL_12MHz
+
+/* Main clock config
+ * We use internal RC and PLL0
+ * Note that during PLL lock wait we are running on internal RC
+ */
+void clock_config(uint32_t freq_sel);
+
+/* return current main clock in HZ */
+uint32_t get_main_clock(void);
+
+
+/* This is mainly a debug feature, but can be used to provide a clock to an
+ * external peripheral */
+void clkout_on(uint32_t src, uint32_t div);
+void clkout_off(void);
+
+
+/***************************************************************************** */
+/* Sleeping functions : these use systick if the systick code is kept. Otherwise
+ * it will use a decrementing while loop which is (badly) calibrated for a 24MHz
+ * main clock.
+ */
+void msleep(uint32_t ms);
+void usleep(uint32_t us);
+
+
+
+/***************************************************************************** */
+/* System Configuration */
+/***************************************************************************** */
+/* System Configuration (SYSCON) */
+struct lpc_sys_start_logic_ctrl
+{
+ volatile uint32_t edge_ctrl; /* 0x00 : edge control Register 0 (R/W) */
+ volatile uint32_t signal_en; /* 0x04 : signal enable Register 0 (R/W) */
+ volatile uint32_t reset; /* 0x08 : reset Register 0 (-/W) */
+ volatile uint32_t status; /* 0x0C : status Register 0 (R/-) */
+};
+struct lpc_sys_config
+{
+ volatile uint32_t sys_mem_remap; /* 0x000 System memory remap (R/W) */
+ volatile uint32_t peripheral_reset_ctrl; /* 0x004 Peripheral reset control (R/W) */
+ volatile uint32_t sys_pll_ctrl; /* 0x008 System PLL control (R/W) */
+ volatile uint32_t sys_pll_status; /* 0x00C System PLL status (R/ ) */
+ uint32_t reserved_0[4];
+
+ volatile uint32_t sys_osc_ctrl; /* 0x020 : System oscillator control (R/W) */
+ volatile uint32_t WDT_osc_ctrl; /* 0x024 : Watchdog oscillator control (R/W) */
+ volatile uint32_t IRC_ctrl; /* 0x028 : IRC control (R/W) */
+ uint32_t reserved_1[1];
+ volatile uint32_t sys_reset_status; /* 0x030 : System reset status Register (R/ ) */
+ uint32_t reserved_2[3];
+ volatile uint32_t sys_pll_clk_sel; /* 0x040 : System PLL clock source select (R/W) */
+ volatile uint32_t sys_pll_clk_upd_en; /* 0x044 : System PLL clock source update enable (R/W) */
+ uint32_t reserved_3[10];
+
+ volatile uint32_t main_clk_sel; /* 0x070 : Main clock source select (R/W) */
+ volatile uint32_t main_clk_upd_en; /* 0x074 : Main clock source update enable (R/W) */
+ volatile uint32_t sys_AHB_clk_div; /* 0x078 : System AHB clock divider (R/W) */
+ uint32_t reserved_4[1];
+
+ volatile uint32_t sys_AHB_clk_ctrl; /* 0x080 : System AHB clock control (R/W) */
+ uint32_t reserved_5[4];
+
+ volatile uint32_t uart_clk_div; /* 0x094 : USART clock divider (R/W) */
+ uint32_t reserved_6[18];
+
+ volatile uint32_t clk_out_src_sel; /* 0x0E0 : CLKOUT clock source select (R/W) */
+ volatile uint32_t clk_out_upd_en; /* 0x0E4 : CLKOUT clock source update enable (R/W) */
+ volatile uint32_t clk_out_div; /* 0x0E8 : CLKOUT clock divider (R/W) */
+ uint32_t reserved_7[1];
+ volatile uint32_t uart_common_fclk_div; /* 0x0F0 : USART 1-4 common fractional gen divider (R/W) */
+ volatile uint32_t uart_common_fclk_mult; /* 0x0F4 : USART 1-4 common fractional gen multiplier (R/W) */
+ uint32_t reserved_8[1];
+ volatile uint32_t ext_trace_buffer_cmd; /* 0x0FC : External trace buffer command (R/W) */
+
+ volatile uint32_t por_captured_io_status_0; /* 0x100 : POR captured PIO status 0 (R/ ) */
+ uint32_t reserved_9[12];
+
+ volatile uint32_t IO_config_clk_div[7]; /* 0x134 - 0x14C : Peripheral clocks 6 to 0 for glitch filter */
+
+ volatile uint32_t BOD_ctrl; /* 0x150 : BOD control (R/W) */
+ volatile uint32_t sys_tick_cal; /* 0x154 : System tick counter calibration (R/W) */
+ uint32_t reserved_10[6];
+ volatile uint32_t irq_latency; /* 0x170 : IRQ delay, alloxs trade-off bw latency and determinism */
+ volatile uint32_t int_nmi_cfg; /* 0x174 : NMI interrupt source configuration control */
+ volatile uint32_t pin_int_sel[8]; /* 0x178 - 0x194 : Pin interrupt Select registers (R/W) */
+ uint32_t reserved_11[26];
+
+ struct lpc_sys_start_logic_ctrl start_log_ctrl[2]; /* 0x200 to 0x20C and 0x210 to 0x21C :
+ Start logic 0 and Start logic 1/peripheral interrupts */
+ uint32_t reserved_12[4];
+
+ volatile uint32_t powerdown_sleep_cfg; /* 0x230 : Power-down states in Deep-sleep mode (R/W) */
+ volatile uint32_t powerdown_wake_cfg; /* 0x234 : Power-down states after wake-up (R/W) */
+ volatile uint32_t powerdown_run_cfg; /* 0x238 : Power-down configuration Register (R/W) */
+ uint32_t reserved_13[110];
+ volatile const uint32_t device_id; /* 0x3F4 : Device ID (R/ ) */
+};
+
+#define LPC_SYS_CONFIG ((struct lpc_sys_config *) LPC_SYSCON_BASE)
+
+
+/* AHB control bits
+ * 0 (System (cortexM0, syscon, PMU, ...)) is a read only bit (system cannot be disabled)
+ */
+#define LPC_SYS_ABH_CLK_CTRL_SYSTEM (1 << 0) /* Read only */
+#define LPC_SYS_ABH_CLK_CTRL_ROM (1 << 1)
+#define LPC_SYS_ABH_CLK_CTRL_RAM (1 << 2)
+#define LPC_SYS_ABH_CLK_CTRL_FLASH_REG (1 << 3)
+#define LPC_SYS_ABH_CLK_CTRL_FLASH (1 << 4)
+#define LPC_SYS_ABH_CLK_CTRL_I2C0 (1 << 5)
+#define LPC_SYS_ABH_CLK_CTRL_GPIO (1 << 6)
+#define LPC_SYS_ABH_CLK_CTRL_SWM (1 << 7)
+#define LPC_SYS_ABH_CLK_CTRL_SCT (1 << 8)
+#define LPC_SYS_ABH_CLK_CTRL_WKT (1 << 9)
+#define LPC_SYS_ABH_CLK_CTRL_MRT (1 << 10)
+#define LPC_SYS_ABH_CLK_CTRL_SSP0 (1 << 11)
+#define LPC_SYS_ABH_CLK_CTRL_SSP1 (1 << 12)
+#define LPC_SYS_ABH_CLK_CTRL_CRC (1 << 13)
+#define LPC_SYS_ABH_CLK_CTRL_UART0 (1 << 14)
+#define LPC_SYS_ABH_CLK_CTRL_UART1 (1 << 15)
+#define LPC_SYS_ABH_CLK_CTRL_UART2 (1 << 16)
+#define LPC_SYS_ABH_CLK_CTRL_Watchdog (1 << 17)
+#define LPC_SYS_ABH_CLK_CTRL_IO_CONFIG (1 << 18)
+#define LPC_SYS_ABH_CLK_CTRL_ACMP (1 << 19)
+#define LPC_SYS_ABH_CLK_CTRL_I2C1 (1 << 21)
+#define LPC_SYS_ABH_CLK_CTRL_I2C2 (1 << 22)
+#define LPC_SYS_ABH_CLK_CTRL_I2C3 (1 << 23)
+#define LPC_SYS_ABH_CLK_CTRL_ADC (1 << 24)
+#define LPC_SYS_ABH_CLK_CTRL_MTB (1 << 26)
+#define LPC_SYS_ABH_CLK_CTRL_DMA (1 << 29)
+/* Helper */
+#define LPC_SYS_ABH_CLK_CTRL_MEM_ALL 0x0000001F
+
+/* Peripheral reset */
+#define LPC_SSP0_RESET_N (1 << 0)
+#define LPC_SSP1_RESET_N (1 << 1)
+#define LPC_UART_CLK_RESET_N (1 << 2)
+#define LPC_UART0_RESET_N (1 << 3)
+#define LPC_UART1_RESET_N (1 << 4)
+#define LPC_UART2_RESET_N (1 << 5)
+#define LPC_I2C0_RESET_N (1 << 6)
+#define LPC_MRT_RESET_N (1 << 7)
+#define LPC_SCT_RESET_N (1 << 8)
+#define LPC_WKT_RESET_N (1 << 9)
+#define LPC_GPIO_RESET_N (1 << 10)
+#define LPC_FLASH_RESET_N (1 << 11)
+#define LPC_ACMP_RESET_N (1 << 12)
+#define LPC_I2C1_RESET_N (1 << 14)
+#define LPC_I2C2_RESET_N (1 << 15)
+#define LPC_I2C3_RESET_N (1 << 16)
+
+/* Peripheral power down */
+#define LPC_POWER_DOWN_IRC_OUT (1 << 0)
+#define LPC_POWER_DOWN_IRC (1 << 1)
+#define LPC_POWER_DOWN_FLASH (1 << 2)
+#define LPC_POWER_DOWN_BOD (1 << 3)
+#define LPC_POWER_DOWN_ADC (1 << 4)
+#define LPC_POWER_DOWN_SYS_OSC (1 << 5)
+#define LPC_POWER_DOWN_WDT_OSC (1 << 6)
+#define LPC_POWER_DOWN_SYSPLL (1 << 7)
+#define LPC_POWER_DOWN_COPARATOR (1 << 15)
+
+#define LPC_DEEP_SLEEP_CFG_NOWDTLOCK_BOD_ON 0x0000FFF7
+#define LPC_DEEP_SLEEP_CFG_NOWDTLOCK_BOD_OFF 0x0000FFFF
+
+#define LPC_MAIN_CLK_SRC_IRC_OSC 0x00
+#define LPC_MAIN_CLK_SRC_PLL_IN 0x01
+#define LPC_MAIN_CLK_SRC_WATCHDOG_OSC 0x02
+#define LPC_MAIN_CLK_SRC_PLL_OUT 0x03
+
+#define LPC_PLL_CLK_SRC_IRC_OSC 0x00
+#define LPC_PLL_CLK_SRC_EXT_OSC 0x01
+#define LPC_PLL_CLK_SRC_CLKIN 0x03
+
+#define LPC_CLKOUT_SRC_IRC_OSC 0x00
+#define LPC_CLKOUT_SRC_XTAL_OSC 0x01
+#define LPC_CLKOUT_SRC_WATCHDOG_OSC 0x02
+#define LPC_CLKOUT_SRC_MAIN_CLK 0x03
+
+#define LPC_WDT_DIVSEL(x) (((x) / 2) - 1)
+#define LPC_WDT_FREQSEL_600KHz (0x01 << 5)
+
+/* Pin interrupt wakeup enable */
+#define LPC_PINT_WAKEUP_EN(x) (1 << (x))
+
+/***************************************************************************** */
+/* Flash Control */
+/***************************************************************************** */
+/* Flash configuration */
+struct lpc_flash_control
+{
+ uint32_t reserved_0[10];
+ volatile uint32_t flash_cfg; /* 0x028 Flash configuration (R/W) */
+};
+#define LPC_FLASH_CONTROL ((struct lpc_flash_control *) LPC_FLASH_CONFIG_BASE)
+#define LPC_FLASH_CFG_MASK 0x03
+#define LPC_FLASH_CFG_SHIFT 0
+
+
+
+
+#endif /* CORE_SYSTEM_H */
--- /dev/null
+/****************************************************************************
+ * core/systick.h
+ *
+ * System tick timer control
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef CORE_SYSTICK_H
+#define CORE_SYSTICK_H
+
+#include "core/lpc_regs.h"
+
+/***************************************************************************** */
+/* System Tick Timer */
+/***************************************************************************** */
+
+/* Driver for the internal systick timer of the LPC82x.
+ * Refer to the LPC82x documentation (UM10800.pdf) for more information
+ */
+
+
+/* Start the system tick timer
+ * Starting the systick timer also resets the internal tick counters.
+ * If you need a value that goes beyond one start/stop cycle and accross resets,
+ * then it's up to you to keep track of this using systick_get_tick_count() and/or
+ * systick_get_clock_cycles().
+ */
+void systick_start(void);
+
+/* Stop the system tick timer */
+void systick_stop(void);
+
+/* Reset the system tick timer, making it count down from the reload value again
+ * Reseting the systick timer also resets the internal tick counters.
+ * If you need a value that goes beyond one start/stop cycle and accross resets,
+ * then it's up to you to keep track of this using systick_get_tick_count() and/or
+ * systick_get_clock_cycles().
+ */
+void systick_reset(void);
+
+/* Get system tick timer current value (counts at get_main_clock() !)
+ * systick_get_timer_val returns a value between 0 and systick_get_timer_reload_val()
+ */
+uint32_t systick_get_timer_val(void);
+
+/* Get system tick timer reload value */
+uint32_t systick_get_timer_reload_val(void);
+
+/* Check if systick is running (return 1) or not (return 0) */
+uint32_t is_systick_running(void);
+
+/* Get the system tick period in ms
+ * A vaue of 0 means the system tick timer has not been configured.
+ * Note : calls to msleep() or usleep() will configure the system tick timer
+ * with a value of 1ms if it was not configured yet.
+ */
+uint32_t systick_get_tick_ms_period(void);
+
+/* Get the number of system ticks ... since last wrapping of the counter, which
+ * is about 50 days with a 1ms system tick. */
+uint32_t systick_get_tick_count(void);
+
+/* Get the number of clock cycles ... since last wrapping of the counter. */
+uint32_t systick_get_clock_cycles(void);
+
+/* Power up the system tick timer.
+ * ms is the interval between system tick timer interrupts. If set to 0, the default
+ * value is used, which should provide a 1ms period.
+ */
+void systick_timer_on(uint32_t ms);
+
+/* Removes the main clock from the selected timer block */
+void systick_timer_off(void);
+
+/* Register a callback to be called every 'period' system ticks.
+ * returns the callback number if registration was OK.
+ * returns negative value on error.
+ * The callback will get the "global_wrapping_system_ticks" as argument, which wraps every 50 days
+ * or so with a 1ms tick
+ */
+#define MAX_SYSTICK_CALLBACKS 4
+int add_systick_callback(void (*callback) (uint32_t), uint16_t period);
+/* Remove a registered callback, given the callback address used to register it. */
+int remove_systick_callback(void (*callback) (uint32_t));
+
+
+
+/* This function can be used when you are absolutly certain that systick timer is running, and when
+ * you need to sleep less than 1000us (1ms)
+ */
+void usleep_short(uint32_t us);
+
+
+/***************************************************************************** */
+/* Cortex-M0 System Timer */
+/***************************************************************************** */
+/* Cortex-M0 System Timer Registers */
+struct lpc_system_tick {
+ volatile uint32_t control; /* 0x000 : SysTick Control and Status Register (R/W) */
+ volatile uint32_t reload_val; /* 0x004 : SysTick Reload Value Register (R/W) */
+ volatile uint32_t value; /* 0x008 : SysTick Current Value Register (R/W) */
+ volatile const uint32_t calibration; /* 0x00C : SysTick Calibration Register (R/ ) */
+};
+#define LPC_SYSTICK ((struct lpc_system_tick*) LPC_SYSTICK_BASE) /* SysTick configuration struct */
+
+/* SysTick Control / Status Register Definitions */
+#define LPC_SYSTICK_CTRL_COUNTFLAG (1UL << 16) /* SysTick CTRL: COUNTFLAG Mask */
+#define LPC_SYSTICK_CTRL_CLKSOURCE (1UL << 2) /* SysTick CTRL: CLKSOURCE Mask */
+#define LPC_SYSTICK_CTRL_TICKINT (1UL << 1) /* SysTick CTRL: TICKINT Mask */
+#define LPC_SYSTICK_CTRL_ENABLE (1UL << 0) /* SysTick CTRL: ENABLE Mask */
+
+/* SysTick Reload Register Definitions */
+#define LPC_SYSTICK_LOAD_RELOAD (0xFFFFFFUL) /* SysTick LOAD: RELOAD Mask */
+
+/* SysTick Current Register Definitions */
+#define LPC_SYSTICK_VAL_CURRENT (0xFFFFFFUL) /* SysTick VAL: CURRENT Mask */
+
+/* SysTick Calibration Register Definitions */
+#define LPC_SYSTICK_CALIB_NOREF (1UL << 31) /* SysTick CALIB: NOREF Mask */
+#define LPC_SYSTICK_CALIB_SKEW (1UL << 30) /* SysTick CALIB: SKEW Mask */
+#define LPC_SYSTICK_CALIB_TENMS (0xFFFFFFUL) /* SysTick CALIB: TENMS Mask */
+
+
+
+#endif /* CORE_SYSTICK_H */
--- /dev/null
+/****************************************************************************
+ * core/watchdog.h
+ *
+ * Watchdog support
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+/*
+ * This file implements support of the Windowed Watchdog (WWDT)
+ */
+
+
+#ifndef CORE_WATCHDOG_H
+#define CORE_WATCHDOG_H
+
+#include "lib/stdint.h"
+#include "core/lpc_regs.h"
+
+
+#define WDT_CLK_POWER_LOCK (0x01 << 0)
+#define WDT_EN_LOCK (0x01 << 1)
+#define WDT_TIMER_VAL_LOCK (0x01 << 2)
+
+struct wdt_config {
+ int intr_mode_only; /* If set to 1, a watchdog timeout will trigger an interrupt instead of a reset */
+ void (*callback)(void);
+ uint32_t locks; /* Bitfield from WDT_*_LOCK defined in watchdog.h */
+ /* Number of clk_src clocks before the watchdog timer times out. Will be divided by 4 to give
+ * the watchdog reload value */
+ uint32_t nb_clk; /* 0x3FF to 0x03FFFFFF */
+ /* The next two values are relative to the timer value, not the number of clk_src clocks. */
+ uint32_t wdt_window; /* Watchdog window value : 0x100 to 0x00FFFFFF */
+ uint16_t wdt_warn; /* 0x00 to 0x3FF */
+};
+
+/***************************************************************************** */
+void watchdog_feed(void);
+
+
+/* Lock the watchdog clock source power.
+ * Once locked, writes to the current watchdog clock power bits in powerdown_*_cfg will
+ * have no effect.
+ * It is still possible to switch the watchdog clock source and turn of the clock if the
+ * watchdog clock source has not been locked yet.
+ */
+void watchdog_lock_clk_src_power(void);
+
+/* Lock the watchdog timer value */
+void watchdog_lock_timer_val(void);
+
+/* Change the watchdog timer value, if not protected */
+void watchdog_set_timer_val(uint32_t nb_clk);
+
+/* Lock the watchdog and all related features.
+ * Calls all the other watchdog_lock_* functions (clk_src, clk_src_power, timer_val, enable).
+ */
+void watchdog_lock_full(void);
+
+/*
+ * Configure the watchdog.
+ * clk_sel is either 0 (IRC) or 1 (WDTCLK). The corresponding clock source will be powered on.
+ * Note : only WDTCLK is running in deep power down mode
+ * Note : protecting the clock source power will prevent turning off the IRC for power saving
+ * if it is selected as main clock source.
+ */
+void watchdog_config(const struct wdt_config* wd_conf);
+
+
+/*
+ * Stop the watchdog
+ * This function can be used during system operation to stop the watchdog if it has not
+ * been locked or protected against clock source modification, for example when entering
+ * sleep or deep sleep.
+ * It will also try to power-down the oscilators if not used for main clock.
+ * Return 0 if a solution has been found to stop the watchdog, or -1 if watchdog is still
+ * running after this call.
+ * TODO : Check this function, and implement the missing cases
+ */
+int stop_watchdog(void);
+
+
+/*
+ * Disable the watchdog
+ * This function can be used upon system startup to disable watchdog operation
+ */
+void startup_watchdog_disable(void);
+
+
+
+
+/***************************************************************************** */
+/* Watchdog Timer */
+/***************************************************************************** */
+/* Watchdog Timer (WDT) */
+struct lpc_watchdog
+{
+ volatile uint32_t mode; /* 0x000 : Watchdog mode register (R/W) */
+ volatile uint32_t timer_const; /* 0x004 : Watchdog timer constant register (R/W) */
+ volatile uint32_t feed_seqence; /* 0x008 : Watchdog feed sequence register ( /W) */
+ volatile const uint32_t timer_value; /* 0x00C : Watchdog timer value register (R/ ) */
+ uint32_t reserved0; /* 0x010 */
+ volatile uint32_t warning_int_compare; /* 0x014 : Watchdog Warning Interrupt compare value. */
+ volatile uint32_t window_compare; /* 0x018 : Watchdog Window compare value. */
+};
+#define LPC_WDT ((struct lpc_watchdog *) LPC_WDT_BASE)
+
+#define LPC_WDT_TIMER_MAX 0xFFFFFF
+
+/* Mode register */
+#define LPC_WDT_EN (0x01 << 0)
+#define LPC_WDT_RESET_ON_TIMEOUT (0x01 << 1)
+#define LPC_WDT_TIMEOUT_FLAG (0x01 << 2)
+#define LPC_WDT_INTR_FLAG (0x01 << 3)
+#define LPC_WDT_TIMER_VAL_PROTECT (0x01 << 4) /* WDPROTECT */
+#define LPC_WDT_CLK_POWER_LOCK (0x01 << 5) /* WDLOCKCLK */
+
+/* Warning Interupt */
+#define LPC_WDT_WARNINT_CMPVAL(x) ((x) & 0x3FF)
+#define LPC_WDT_WARNINT_MAXVAL 0x3FF
+
+
+
+
+#endif /* CORE_WATCHDOG_H */
--- /dev/null
+/****************************************************************************
+ * drivers/adc.h
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef DRIVERS_ADC_H
+#define DRIVERS_ADC_H
+
+
+#include "lib/stdint.h"
+#include "core/lpc_regs.h"
+
+/***************************************************************************** */
+/* Analog to Digital Converter (ADC) */
+/***************************************************************************** */
+
+/* ADC driver for the integrated ADC module of the LPC82x.
+ * Refer to LPC82x documentation (UM10800.pdf) for more information.
+ */
+
+#define NB_ADC_CHANNELS 12
+#define NB_ADC_SEQUENCES 2
+
+#define LPC_ADC_SEQ(x) (x)
+#define LPC_ADC_SEQA 0
+#define LPC_ADC_SEQB 1
+
+/* Read the conversion from the given channel
+ * This function reads the conversion value directly in the data register and
+ * always returns a value.
+ * Return 0 if the value is a new one and no overrun occured.
+ * Return -1 if channel does not exist
+ * Return 1 if the value is an old one.
+ * Return 2 if an overrun occured
+ */
+int adc_get_value(uint16_t * val, unsigned int channel);
+
+/* Start a conversion on the given channel (0 to 7)
+ * Unsupported yet : Set use_int to 1 to have your interrupt callback called upon conversion done.
+ */
+void adc_start_convertion_once(unsigned int channel, uint8_t seq_num, int use_int);
+
+/* Start burst conversions.
+ * channels is a bit mask of requested channels.
+ * Use LPC_ADC_CHANNEL(x) (x = 0 .. 7) for channels selection.
+ */
+void adc_start_burst_conversion(uint16_t channels, uint8_t seq_num);
+void adc_stop_burst_conversion(uint8_t seq_num);
+
+
+enum lpc_adc_start_conv_events {
+ LPC_ADC_START_CONV_SOFT = 0,
+ LPC_ADC_START_CONV_ADC_PINTRIG_0,
+ LPC_ADC_START_CONV_ADC_PINTRIG_1,
+ LPC_ADC_START_CONV_SCT0_OUT3,
+ LPC_ADC_START_CONV_ACMP_OUT,
+ LPC_ADC_START_CONV_ARM_TXEV,
+};
+/* This should be used to configure conversion start on falling or rising edges of
+ * some signals, or on timer for burst conversions.
+ */
+void adc_prepare_conversion_on_event(uint16_t channels, uint8_t seq_num, uint8_t event,
+ int use_int, uint32_t mode);
+
+/* Software trigger of the given configured sequence */
+void adc_trigger_sequence_conversion(uint8_t seq_num);
+
+/***************************************************************************** */
+/* ADC Setup */
+
+/* When lowpower_en is not 0 the ADC low power mode is selected, which adds a 15ADC clock delay
+ * before each set of consecutive conversions (but not between consecutive conversions)
+ */
+void adc_set_low_power(int lowpower_en);
+
+/* Power on/off the ADC block */
+void adc_on(void (*adc_callback)(uint32_t));
+void adc_off(void);
+
+
+
+
+/***************************************************************************** */
+/* Analog-to-Digital Converter */
+/***************************************************************************** */
+/* Analog-to-Digital Converter (ADC) */
+struct lpc_adc
+{
+ volatile uint32_t ctrl; /* 0x000 : A/D Control Register (R/W) */
+ uint32_t reserved_0;
+ volatile uint32_t seqctrl[2]; /* 0x008 - 0x00C : A/D Conversion sequence A and B control register (R/W) */
+ volatile uint32_t global_data[2]; /* 0x010 - 0x014 : A/D sequence A and B Global Data Register (R/W) */
+ uint32_t reserved_1[2];
+ volatile uint32_t data[NB_ADC_CHANNELS]; /* Offset: 0x020-0x04C A/D Channel 0..11 Data Register (R/-) */
+ volatile uint32_t threshold_low[2]; /* 0x050 - 0x054 : A/D Low Compare Threshold Register 0 and 1 (R/W) */
+ volatile uint32_t threshold_high[2]; /* 0x058 - 0x05C : A/D High Compare Threshold Register 0 and 1 (R/W) */
+ volatile uint32_t chan_threshold_sel; /* 0x060 : A/D Channel Threshold select Register (R/W) */
+ volatile uint32_t int_en; /* 0x064 : A/D Interrupt Enable Register (R/W) */
+ volatile const uint32_t flags; /* 0x068 : A/D Flags Register (R/ ) */
+ volatile uint32_t trim; /* 0x06C : A/D Trim Register (R/W) */
+};
+#define LPC_ADC ((struct lpc_adc *) LPC_ADC_BASE)
+
+/* ADC Control register bits */
+#define LPC_ADC_CLK_MASK 0x0FF
+#define LPC_ADC_CLK_DIV(x) ((x) & LPC_ADC_CLK_MASK)
+#define LPC_ADC_LOW_POWER_EN (0x01 << 10)
+#define LPC_ADC_CALIBRATION (0x01 << 30)
+
+/* LPC_ADC_CHANNEL_* are also used for interrupt register */
+#define LPC_ADC_CHANNEL_MASK (0xFFF << 0)
+#define LPC_ADC_CHANNEL(x) (0x01 << (x))
+#define LPC_ADC_START_CONV_EVENT(x) (((x) & 0x7) << 12)
+#define LPC_ADC_START_EDGE_FALLING (0x0 << 18)
+#define LPC_ADC_START_EDGE_RISING (0x1 << 18)
+#define LPC_ADC_SYNC_TRIG_BYPASS (0x1 << 19)
+#define LPC_ADC_START_CONV_NOW (0x01 << 26)
+#define LPC_ADC_BURST (0x01 << 27)
+#define LPC_ADC_SINGLESTEP (0x01 << 28)
+#define LPC_ADC_LOW_PRIORITY (0x01 << 29)
+#define LPC_ADC_INT_MODE_END_OF_SEQ (0x01 << 30)
+#define LPC_ADC_SEQ_EN (0x01 << 31)
+#define LPC_ADC_ADDITIONAL_MODE_MASK ((0x0 << 18) | (0x1 << 19) | (0x01 << 28) | (0x01 << 29) | (0x01 << 30))
+
+
+/* ADC Data register bits */
+#define LPC_ADC_RESULT_MASK 0xFFF
+#define LPC_ADC_RESULT_SHIFT 4
+#define LPC_ADC_RESULT(x) (((x) & LPC_ADC_RESULT_MASK) >> LPC_ADC_RESULT_SHIFT)
+#define LPC_ADC_THRESHOLD_CMP_RANGE(x) (((x) & 0x03) >> 16)
+#define LPC_ADC_THRESHOLD_CMP_CROSS(x) (((x) & 0x03) >> 18)
+#define LPC_ADC_RES_CHAN(x) (((x) & 0x0F) >> 26)
+#define LPC_ADC_OVERRUN (0x01 << 30)
+#define LPC_ADC_CONV_DONE (0x01 << 31)
+
+/* ADC Threshold register bits */
+#define LPC_ADC_THRESHOLD(x) (((x) & LPC_ADC_RESULT_MASK) << LPC_ADC_RESULT_SHIFT)
+#define LPC_ADC_THRESHOLD_LEVEL_0 0
+#define LPC_ADC_THRESHOLD_LEVEL_1 1
+
+/* For more readability when selecting a channel number */
+#define LPC_ADC_NUM(x) (x)
+
+
+
+/* ADC Interrupt Register */
+#define LPC_ADC_SEQA_INTEN (0x01 << 0)
+#define LPC_ADC_SEQB_INTEN (0x01 << 1)
+/* TODO : Define other bits */
+
+
+
+#endif /* DRIVERS_ADC_H */
+
--- /dev/null
+/****************************************************************************
+ * drivers/gpio.h
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+/***************************************************************************** */
+/* GPIOs and GPIO Interrupts */
+/***************************************************************************** */
+
+/* Driver for GPIO configuration and access (including GPIO interrupts) on the LPC82x.
+ * Refer to LPC82x documentation (UM10800.pdf) for more information.
+ */
+
+#ifndef DRIVERS_GPIO_H
+#define DRIVERS_GPIO_H
+
+
+#include "lib/stdint.h"
+#include "core/lpc_regs.h"
+#include "core/pio.h"
+
+
+/***************************************************************************** */
+/* Public access to GPIO setup */
+
+
+enum gpio_interrupt_senses {
+ EDGES_BOTH = 0,
+ EDGE_FALLING,
+ EDGE_RISING,
+ LEVEL_HIGH,
+ LEVEL_LOW,
+};
+
+#define GPIO_DIR_IN 0
+#define GPIO_DIR_OUT 1
+
+/* GPIO Interrupts */
+/* Add a callback on a GPIO interrupt.
+ * This call will configure the GPIO (call to config_pio()), set it as input and
+ * activate the interrupt on the given 'sense' event. use the gpio_interrupt_senses
+ * enum for 'sense' values.
+ * The callback will receive the irq number as argument.
+ * Returns the interrupt number if OK, or a negative value in case of error.
+ */
+int set_gpio_callback(void (*callback) (uint32_t), const struct pio* gpio, uint8_t sense);
+void remove_gpio_callback(unsigned int irq);
+
+
+
+/* GPIO Activation */
+void gpio_on(void);
+void gpio_off(void);
+
+#define gpio_dir_in(gpio) \
+{ \
+ struct lpc_gpio* gpio_port = LPC_GPIO_REGS(0); \
+ gpio_port->dir_clear = (1 << gpio.pin);\
+}
+
+#define gpio_dir_out(gpio) \
+{ \
+ struct lpc_gpio* gpio_port = LPC_GPIO_REGS(0); \
+ gpio_port->dir_set = (1 << gpio.pin);\
+}
+
+
+#define gpio_set(gpio) \
+{ \
+ struct lpc_gpio* gpio_port = LPC_GPIO_REGS(0); \
+ gpio_port->set = (1 << gpio.pin);\
+}
+
+#define gpio_clear(gpio) \
+{ \
+ struct lpc_gpio* gpio_port = LPC_GPIO_REGS(0); \
+ gpio_port->clear = (1 << gpio.pin);\
+}
+
+#define gpio_toggle(gpio) \
+{ \
+ struct lpc_gpio* gpio_port = LPC_GPIO_REGS(0); \
+ gpio_port->toggle = (1 << gpio.pin);\
+}
+
+
+/* GPIO Configuration
+ * This function calls the config_pio() function for the gpio with the given
+ * mode, configures the direction of the pin and sets the initial state.
+ * Use GPIO_DIR_IN or GPIO_DIR_OUT for the direction "dir", and 0 or 1 for the initial
+ * value "ini_val".
+ */
+void config_gpio(const struct pio* gpio, uint32_t mode, uint8_t dir, uint8_t ini_val);
+
+
+
+/***************************************************************************** */
+/* General Purpose Input/Output */
+/***************************************************************************** */
+/* General Purpose Input/Output (GPIO) */
+struct lpc_gpio
+{
+ volatile uint8_t bval[32]; /* 0x0000 - 0x001C : Pin value of each pin on a byte for quick access */
+ uint32_t reserved_0[1016];
+ volatile uint32_t wval[32]; /* 0x1000 - 0x1074 : Pin value of each pin on a 32bits word for quick access */
+ uint32_t reserved_1[992];
+ volatile uint32_t data_dir; /* 0x2000 : Data direction Register (R/W) */
+ uint32_t reserved_2[31];
+ volatile uint32_t mask; /* 0x2080 : Pin mask, affects data, out, set, clear and invert */
+ uint32_t reserved_3[31];
+ volatile uint32_t val; /* 0x2100 : Port data Register (R/W) */
+ uint32_t reserved_4[31];
+ volatile uint32_t mval; /* 0x2180 : Port masked data Register (R/W) */
+ uint32_t reserved_5[31];
+ volatile uint32_t set; /* 0x2200 : Port output set Register (-/W) */
+ uint32_t reserved_6[31];
+ volatile uint32_t clear; /* 0x2280 : Port output clear Register (-/W) */
+ uint32_t reserved_7[31];
+ volatile uint32_t toggle; /* 0x2300 : Port output invert Register (-/W) */
+ uint32_t reserved_8[31];
+ volatile uint32_t dir_set; /* 0x2380 : Port direction set (set as output) Register (-/W) */
+ uint32_t reserved_9[31];
+ volatile uint32_t dir_clear; /* 0x2400 : Port direction clear (set as input) Register (-/W) */
+ uint32_t reserved_10[31];
+ volatile uint32_t dir_toggle; /* 0x2480 : Port direction toggle Register (-/W) */
+};
+#define LPC_GPIO_0 ((struct lpc_gpio *) LPC_GPIO_BASE)
+#define LPC_GPIO_REGS(x) ((struct lpc_gpio *) (LPC_GPIO_BASE))
+
+
+
+
+
+/***************************************************************************** */
+/* Pin interrupts and patern interrupts */
+/***************************************************************************** */
+/* Pin and pattern interrupts */
+struct lpc_pin_int
+{
+ volatile uint32_t mode; /* 0x000 : Pin interrupt mode (R/W) */
+ volatile uint32_t lvl_rising_enable; /* 0x004 : Pin interrupt level or rising edge int enable (R/W) */
+ volatile uint32_t lvl_rising_set_en; /* 0x008 : Pin interrupt level or rising edge int set (-/W) */
+ volatile uint32_t lvl_rising_clr_en; /* 0x00C : Pin interrupt level or rising edge int clear (-/W) */
+ volatile uint32_t lvl_falling_enable; /* 0x010 : Pin interrupt active level or falling edge int enable (R/W) */
+ volatile uint32_t lvl_falling_set_en; /* 0x014 : Pin interrupt active level or falling edge int set (-/W) */
+ volatile uint32_t lvl_falling_clr_en; /* 0x018 : Pin interrupt active level or falling edge int clear (-/W) */
+ volatile uint32_t rising; /* 0x01C : Pin interrupt rising edge register (R/W) */
+ volatile uint32_t falling; /* 0x020 : Pin interrupt falling edge register (R/W) */
+ volatile uint32_t status; /* 0x024 : Pin interrupt status register (R/W) */
+ volatile uint32_t pmatch_ctrl; /* 0x028 : Pattern match interrupt control (R/W) */
+ volatile uint32_t pmatch_bitslice_src; /* 0x02C : Pattern match interrupt bit-slice source (R/W) */
+ volatile uint32_t pmatch_bitslice_config; /* 0x030 : Pattern match interrupt bit-slice configuration (R/W) */
+};
+#define LPC_GPIO_INTERRUPTS ((struct lpc_pin_int *) LPC_GPIO_INTR_BASE)
+
+
+
+
+
+#endif /* DRIVERS_GPIO_H */
--- /dev/null
+/****************************************************************************
+ * drivers/i2c.h
+ *
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+/**************************************************************************** */
+/* I2C */
+/**************************************************************************** */
+
+/* I2C driver for the I2C bus integrated module of the LPC1224.
+ * Refer to LPC1224 documentation (UM10441.pdf) for more information.
+ */
+
+#ifndef DRIVERS_I2C_H
+#define DRIVERS_I2C_H
+
+#include "lib/stdint.h"
+
+
+#define NB_I2C_BUSSES 4
+
+#define I2C_CLK_100KHz (100*1000)
+#define I2C_CLK_400KHz (400*1000)
+
+#define I2C_READ_BIT 0x01
+#define I2C_WRITE_BIT 0x00
+
+#define I2C0 0
+#define I2C1 1
+#define I2C2 2
+#define I2C3 3
+
+
+enum i2c_type {
+ I2C_MASTER = 0,
+ I2C_SLAVE,
+ I2C_MONITOR,
+};
+
+enum i2c_conditions {
+ I2C_CONT = 0,
+ I2C_DO_REPEATED_START,
+ I2C_DO_STOP_START,
+ I2C_STOP,
+};
+
+enum i2c_states {
+ /* Must be set before starting the state machine to be able to wait for completion. */
+ I2C_BUSY = 0,
+ I2C_OK, /* All right, default state after init has been done. */
+ /* Sent slave address and it got ACK'ed, but no data.
+ * Used to probe or wait for completion. */
+ I2C_NO_DATA,
+ I2C_NACK, /* NACK received */
+ I2C_TIME_OUT,
+ I2C_ARBITRATION_LOST, /* Another master took the I2C Bus from us ... */
+ I2C_BUS_ERROR, /* Illegal start or stop (status of 0x00) */
+ I2C_ERROR_UNKNOWN,
+};
+
+enum i2c_state_machine_states {
+ /* Error states */
+ I2C_ILLEGAL = 0x00, /* Illegal start or stop */
+ I2C_ARBIT_LOST = 0x38,
+ /* Start condition states */
+ I2C_STARTED = 0x08,
+ I2C_RESTARTED, /* Unused, should be set when restarting (STOP+START) after a NACK */
+ I2C_REPEATED_START = 0x10,
+ /* Transmitter states */
+ I2C_ACK_ON_ADDRESS_W = 0x18,
+ I2C_NACK_ON_ADDRESS_W = 0x20,
+ I2C_ACK_ON_DATA_W = 0x28,
+ I2C_NACK_ON_DATA_W = 0x30,
+ /* Receiver states */
+ I2C_ACK_ON_ADDRESS_R = 0x40,
+ I2C_NACK_ON_ADDRESS_R = 0x48,
+ I2C_DATA_ACK = 0x50,
+ I2C_DATA_NACK = 0x58,
+};
+
+
+
+
+/***************************************************************************** */
+/* Modules I2C access */
+/***************************************************************************** */
+/* I2C Read
+ * Performs a non-blocking read on the i2c bus.
+ * cmd_buf : buffer containing all control byte to be sent on the i2c bus
+ * cmd_size : size of the cmd_buf command buffer
+ * ctrl_buf : buffer containing action to be done after sending, like repeated START conditions
+ * ctrl_buf has the same size as cmd_buf
+ * inbuff : the buffer where read data will be put.
+ * count : the number of bytes to be read.
+ * RETURN VALUE
+ * Upon successfull completion, returns the number of bytes read. On error, returns a negative
+ * integer equivalent to errors from glibc.
+ * -EBADFD : Device not initialized
+ * -EBUSY : Device or ressource Busy or Arbitration lost
+ * -EAGAIN : Device already in use
+ * -EINVAL : Invalid argument (buf)
+ * -EREMOTEIO : Device did not acknowledge
+ * -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine
+ */
+int i2c_read(uint8_t bus_num, const void *cmd_buf, size_t cmd_size, const void* ctrl_buf, void* inbuff, size_t count);
+
+/* I2C Write
+ * Performs a non-blocking write on the i2c bus.
+ * buf : buffer containing all byte to be sent on the i2c bus,
+ * including conrtol bytes (address, offsets, ...)
+ * count : the number of bytes to be sent, including address bytes and so on.
+ * ctrl_buf : buffer containing action to be done after sending, like repeated START conditions
+ * ctrl_buf has the same size as cmd_buf
+ * FIXME : note that STOP + START conditions are not allowed, the STOP would lead to sending
+ * the first bytes of buf, creating an infinite loop.
+ * RETURN VALUE
+ * Upon successfull completion, returns the number of bytes written. On error, returns a negative
+ * integer equivalent to errors from glibc.
+ * -EBADFD : Device not initialized
+ * -EBUSY : Device or ressource Busy or Arbitration lost
+ * -EAGAIN : Device already in use
+ * -EINVAL : Invalid argument (buf)
+ * -EREMOTEIO : Device did not acknowledge
+ * -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine
+ */
+int i2c_write(uint8_t bus_num, const void *buf, size_t count, const void* ctrl_buf);
+
+
+
+int i2c_set_timeout(uint8_t bus_num, uint16_t timeout);
+
+
+/***************************************************************************** */
+/* I2C Init */
+/***************************************************************************** */
+int i2c_on(uint8_t bus_num, uint32_t i2c_clk_freq, uint8_t mode);
+int i2c_off(uint8_t bus_num);
+/* Allow system to propagate main clock */
+void i2c_clock_update(void);
+
+
+/***************************************************************************** */
+/* Inter-Integrated Circuit */
+/***************************************************************************** */
+/* Inter-Integrated Circuit (I2C) */
+struct lpc_i2c
+{
+ volatile uint32_t config; /* 0x000 : I2C Configuration of shared functions (R/W) */
+ volatile uint32_t status; /* 0x004 : I2C Status Register (R/W) */
+ volatile uint32_t int_en_set; /* 0x008 : I2C Interrupt Enable Set and Read (R/W) */
+ volatile uint32_t int_en_clr; /* 0x00C : I2C Interrupt Enable Clear (-/W) */
+ volatile uint32_t timeout; /* 0x010 : I2C Timeout Value (R/W) */
+ volatile uint32_t clkdiv; /* 0x014 : I2C Clock pre-divider */
+ volatile uint32_t int_status; /* 0x018 : I2C Interrupt Status (R/-) */
+ uint32_t reserved0;
+ volatile uint32_t master_ctrl; /* 0x020 : I2C Master Control Register (R/W) */
+ volatile uint32_t master_timing; /* 0x024 : I2C Master Timing Configuration (R/W) */
+ volatile uint32_t master_data; /* 0x028 : I2C Master Data Register (R/W) */
+ uint32_t reserved1[5];
+ volatile uint32_t slave_ctrl; /* 0x040 : I2C Slave Control Register (R/W) */
+ volatile uint32_t slave_data; /* 0x044 : I2C Slave Data Register (R/W) */
+ volatile uint32_t slave_addr_0; /* 0x048 : I2C Slave Address Register 0 (R/W) */
+ volatile uint32_t slave_addr_1; /* 0x04C : I2C Slave Address Register 1 (R/W) */
+ volatile uint32_t slave_addr_2; /* 0x050 : I2C Slave Address Register 2 (R/W) */
+ volatile uint32_t slave_addr_3; /* 0x054 : I2C Slave Address Register 3 (R/W) */
+ volatile uint32_t slave_a0_qual; /* 0x058 : I2C Slave Address 0 Qualification (R/W) */
+ uint32_t reserved2[9];
+ volatile const uint32_t monitor_data; /* 0x080 : Monitor receiver Data register (R/-) */
+};
+#define LPC_I2C0 ((struct lpc_i2c *) LPC_I2C0_BASE)
+#define LPC_I2C1 ((struct lpc_i2c *) LPC_I2C1_BASE)
+#define LPC_I2C2 ((struct lpc_i2c *) LPC_I2C2_BASE)
+#define LPC_I2C3 ((struct lpc_i2c *) LPC_I2C3_BASE)
+
+/* Config Register */
+#define I2C_MASTER_EN (0x01 << 0)
+#define I2C_SLAVE_EN (0x01 << 1)
+#define I2C_MONITOR_EN (0x01 << 2)
+#define I2C_TIMEOUT_EN (0x01 << 3)
+#define I2C_MONITOR_CLK_STRETCH (0x01 << 4)
+
+/* Status and Interrupt Registers */
+/* Master status */
+#define I2C_ST_MASTER_PENDING (0x01 << 0)
+#define I2C_ST_MASTER_STATE_MASK (0x07 << 1)
+#define I2C_ST_MASTER_STATE_SHIFT 1
+#define I2C_ST_MASTER_STATE(x) (((x) & I2C_ST_MASTER_STATE_MASK) >> I2C_ST_MASTER_STATE_SHIFT)
+#define I2C_ST_MASTER_ARB_LOSS (0x01 << 4)
+#define I2C_ST_MASTER_STERR (0x01 << 6)
+/* Slave status */
+#define I2C_ST_SLAVE_PENDING (0x01 << 8)
+#define I2C_ST_SLAVE_STATE_MASK (0x03 << 9)
+#define I2C_ST_SLAVE_STATE_SHIFT 9
+#define I2C_ST_SLAVE_STATE (((x) & I2C_ST_SLAVE_STATE_MASK) >> I2C_ST_SLAVE_STATE_SHIFT)
+#define I2C_ST_SLAVE_NO_STRETCH (0x01 << 11)
+#define I2C_ST_SLAVE_IDX_MASK (0x03 << 12)
+#define I2C_ST_SLAVE_IDX_SHIFT 12
+#define I2C_ST_SLAVE_IDX(x) (((x) & I2C_ST_SLAVE_IDX_MASK) >> I2C_ST_SLAVE_IDX_SHIFT)
+#define I2C_ST_SLAVE_SELECTED (0x01 << 14)
+#define I2C_ST_SLAVE_DESELECTED (0x01 << 15)
+/* Monitor */
+#define I2C_ST_MONITOR_READY (0x01 << 16)
+#define I2C_ST_MONITOR_OVERRUN (0x01 << 17)
+#define I2C_ST_MONITOR_ACTIVE (0x01 << 18)
+#define I2C_ST_MONITOR_IDLE (0x01 << 19)
+/* Events */
+#define I2C_ST_EVENT_TIMEOUT (0x01 << 24)
+#define I2C_ST_SCL_TIMEOUT (0x01 << 25)
+
+#define I2C_ST_CLR_MASTER (I2C_ST_MASTER_ARB_LOSS | I2C_ST_MASTER_STERR)
+#define I2C_ST_CLR_SLAVE (I2C_ST_SLAVE_DESELECTED)
+#define I2C_ST_CLR_MON (I2C_ST_MONITOR_OVERRUN | I2C_ST_MONITOR_IDLE)
+#define I2C_ST_CLR_EVT (I2C_ST_EVENT_TIMEOUT | I2C_ST_SCL_TIMEOUT)
+#define I2C_ST_CLEAR_ALL (I2C_ST_CLR_MASTER | I2C_ST_CLR_SLAVE | I2C_ST_CLR_MON | I2C_ST_CLR_EVT)
+
+/* Master Control Register */
+#define I2C_MASTER_CONTINUE (0x01 << 0)
+#define I2C_MASTER_START (0x01 << 1)
+#define I2C_MASTER_STOP (0x01 << 2)
+#define I2C_MASTER_DMA (0x01 << 3)
+
+
+#define I2C_ASSERT_ACK (0x01 << 2)
+#define I2C_INTR_FLAG (0x01 << 3)
+#define I2C_STOP_FLAG (0x01 << 4)
+#define I2C_START_FLAG (0x01 << 5)
+#define I2C_ENABLE_FLAG (0x01 << 6)
+
+
+
+
+#endif /* DRIVERS_I2C_H */
--- /dev/null
+/****************************************************************************
+ * drivers/serial.h
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+/***************************************************************************** */
+/* UARTs */
+/***************************************************************************** */
+/* UART driver for the integrated UARTs.
+ * Refer to micro-controller documentation for more information.
+ */
+
+/* All three UARTs are available, UART numbers are in the range 0 - 2 */
+
+#ifndef DRIVERS_SERIAL_H
+#define DRIVERS_SERIAL_H
+
+
+#include "lib/stdint.h"
+
+
+#define UART0 0
+#define UART1 1
+#define UART2 2
+#define UART3 3
+
+
+#define SERIAL_CAP_UART (1 << 0)
+#define SERIAL_CAP_RS485 (1 << 1)
+
+#define SERIAL_MODE_UART SERIAL_CAP_UART
+#define SERIAL_MODE_RS485 SERIAL_CAP_RS485
+
+
+/* This buffer size is the maximum write size for a serial_printf or a uprintf call.
+ * Previously this value was 64, but it is often too short, so I set it to 96, which
+ * should be OK for most cases. In need of a bigger write buffer, change this value
+ * or perform multiple write calls (better).
+ */
+#define SERIAL_OUT_BUFF_SIZE 96
+
+
+/***************************************************************************** */
+/* Serial Write
+ *
+ * Try to send at most "length" characters from "buf" on the requested uart.
+ * Returns a negative value on error, or number of characters copied into output buffer,
+ * witch may be less than requested "length"
+ * Possible errors: requested uart does not exists (-EINVAL) or unable to acquire uart
+ * lock (-EBUSY).
+ *
+ * Warning for Real Time : This implementation will block if there's already a
+ * transmission ongoing.
+ */
+int serial_write(uint32_t uart_num, const char *buf, uint32_t length);
+
+/***************************************************************************** */
+/* Serial Flush
+ *
+ * Wait until all characters have been sent
+ * Returns -EINVAL on error, 0 on success.
+ * Possible errors: requested uart does not exists or unable to acquire uart lock.
+ *
+ * Warning for Real Time : This implementation will block if there's already a
+ * transmission ongoing.
+ */
+int serial_flush(uint32_t uart_num);
+
+
+/****************************************************************************** */
+/* Serial send byte - quick function with almost no tests.
+ * If the uart is not sending, the byte is placed directly in the data buffer and
+ * the call returns 0.
+ * Else, the call returns -EBUSY and nothing is sent.
+ */
+int serial_send_quickbyte(uint32_t uart_num, uint8_t data);
+
+
+/***************************************************************************** */
+/* Public access to UART setup */
+
+
+/* Change UART configuration (number of data, parity and stop bits).
+ * config is usually a mask of LPC_UART_xBIT (x = 5..8), LPC_UART_xSTOP (x = 1..2)
+ * and one of LPC_UART_NO_PAR, LPC_UART_ODD_PAR or LPC_UART_EVEN_PAR.
+ */
+int uart_set_config(uint8_t uart_num, uint32_t config);
+
+/* Change uart mode to RS485
+ * return -ENODEV when the device does not support RS485 mode.
+ */
+int uart_set_mode_rs485(uint32_t uart_num, uint32_t control, uint8_t addr, uint8_t delay);
+
+/* Change uart mode to IRDA
+ * return -ENODEV when the device does not support IrDA mode.
+ */
+int uart_set_mode_irda(uint32_t uart_num, uint32_t control, uint16_t pulse_width);
+
+/* Allow any change to the main clock to be forwarded to us */
+void uart_clk_update(void);
+
+/* Do we need to allow setting of other parameters ? (Other than 8n1) */
+int uart_on(uint32_t uart_num, uint32_t baudrate, void (*rx_callback)(uint8_t));
+
+void uart_off(uint32_t uart_num);
+
+
+/***************************************************************************** */
+/* Universal Synchronous or Asynchronous Receiver Transmitter */
+/***************************************************************************** */
+/* Universal Synchronous or Asynchronous Receiver Transmitter (USART) */
+struct lpc_usart
+{
+ volatile uint32_t config; /* 0x000 : USART configuration register (R/W) */
+ volatile uint32_t control; /* 0x004 : USART control register (R/W) */
+ volatile uint32_t status; /* 0x008 : USART status register (R/W) */
+ volatile uint32_t inten_set; /* 0x00C : Interupt enable set / read (R/W) */
+ volatile uint32_t inten_clear; /* 0x010 : Interupt enable clear (-/W) */
+ volatile uint32_t rx_data; /* 0x014 : Receiver Data register (R/-) */
+ volatile uint32_t rx_datstat; /* 0x018 : Receiver Data with status register (R/-) */
+ volatile uint32_t tx_data; /* 0x01C : Transmit Data register (R/W) */
+ volatile uint32_t baudrate_gen; /* 0x020 : Baud Rate Generator register (R/W) */
+ volatile uint32_t int_status; /* 0x024 : Interrupt status register (R/-) */
+ volatile uint32_t oversample_sel; /* 0x028 : Oversample selection register (R/W) */
+ volatile uint32_t address; /* 0x02C : Address register (R/W) */
+};
+#define LPC_UART_0 ((struct lpc_usart *) LPC_USART0_BASE)
+#define LPC_UART_1 ((struct lpc_usart *) LPC_USART1_BASE)
+#define LPC_UART_2 ((struct lpc_usart *) LPC_USART2_BASE)
+
+/* Configuration Register */
+#define LPC_UART_ENABLE (0x01 << 0)
+#define LPC_UART_7BIT (0x00 << 2)
+#define LPC_UART_8BIT (0x01 << 2)
+#define LPC_UART_9BIT (0x02 << 2)
+#define LPC_UART_NO_PAR (0x00 << 4)
+#define LPC_UART_EVEN_PAR (0x02 << 4)
+#define LPC_UART_ODD_PAR (0x03 << 4)
+#define LPC_UART_1STOP (0x00 << 6)
+#define LPC_UART_2STOP (0x01 << 6)
+#define LPC_UART_CTS_EN (0x01 << 9)
+#define LPC_UART_LOOPBACK (0x01 << 15)
+#define LPC_UART_RX_POL_INVERT (0x01 << 22)
+#define LPC_UART_TX_POL_INVERT (0x01 << 23)
+/* Configuration for synchronous mode */
+#define LPC_UART_SYNCHRONOUS (0x01 << 11)
+#define LPC_UART_CLK_POL_FALL (0x00 << 12)
+#define LPC_UART_CLK_POL_RISE (0x01 << 12)
+#define LPC_UART_SYNC_SLAVE (0x00 << 14)
+#define LPC_UART_SYNC_MASTER (0x01 << 14)
+/* Configuration for RS485 mode */
+#define LPC_RS485_OE_TURNAROUND_EN (0x01 << 18)
+#define LPC_RS485_AUTO_ADDR_EN (0x01 << 19)
+#define LPC_RS485_ENABLE (0x01 << 20)
+#define LPC_RS485_DIR_CTRL_INV (0x01 << 21)
+
+/* Control Register */
+#define LPC_UART_TX_BREAK_EN (0x01 << 1)
+#define LPC_UART_ADDR_DETECT (0x01 << 2)
+#define LPC_UART_TX_DISABLE (0x01 << 6)
+#define LPC_UART_CONT_CLK (0x01 << 8)
+#define LPC_UART_RX_CONT_CLK_CLR (0x01 << 9)
+#define LPC_UART_AUTOBAUD (0x01 << 16)
+
+/* Status, Interrupt Enable, and Interrupt clear Register definition */
+#define LPC_UART_ST_RX_READY (0x01 << 0)
+#define LPC_UART_ST_RX_IDLE (0x01 << 1)
+#define LPC_UART_ST_TX_READY (0x01 << 2)
+#define LPC_UART_ST_TX_IDLE (0x01 << 3)
+#define LPC_UART_ST_CTS (0x01 << 4)
+#define LPC_UART_ST_CTS_CHANGE (0x01 << 5)
+#define LPC_UART_ST_TX_DISABLE (0x01 << 6)
+#define LPC_UART_ST_OVERRUN (0x01 << 8)
+#define LPC_UART_ST_RX_BREAK (0x01 << 10)
+#define LPC_UART_ST_BRK_CHANGE (0x01 << 11)
+#define LPC_UART_ST_START (0x01 << 12)
+#define LPC_UART_ST_FRAME_ERR (0x01 << 13)
+#define LPC_UART_ST_PAR_ERR (0x01 << 14)
+#define LPC_UART_ST_NOISE_ERR (0x01 << 15)
+#define LPC_UART_ST_AUTOBAUD_ERR (0x01 << 16)
+/* Status helpers */
+#define LPC_UART_ST_CLEAR (0x1F920)
+#define LPC_UART_ERR_FLAGS (LPC_UART_ST_FRAME_ERR | LPC_UART_ST_PAR_ERR | LPC_UART_ST_NOISE_ERR)
+
+/* Baud rate generator divisor */
+#define LPC_UART_BRG_DIV(x) (((x) - 1) & 0xFFFF)
+/* Oversampling register */
+#define LPC_UART_OVERSAMPLING(x) (((x) - 1) & 0x0F)
+
+/* RS485 or UART address */
+#define LPC_RS485_ADDR(x) ((x) & 0xFF)
+#define LPC_UART_ADDR(x) ((x) & 0xFF)
+
+
+#endif /* DRIVERS_SERIAL_H */
--- /dev/null
+/****************************************************************************
+ * extdrv/tmp101_temp_sensor.h
+ *
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef EXTDRV_TEMP_H
+#define EXTDRV_TEMP_H
+
+#include "lib/stdint.h"
+
+
+/***************************************************************************** */
+/* Support for TMP101 temperature sensors from Texas Instrument */
+/***************************************************************************** */
+/* This driver is made for the TMP101 version of the chip, though there's few
+ * diferences between the TMP100 and TMP101 version.
+ * This driver does not handle the SMBus Alert command.
+ */
+
+
+/* Faulty mesures required to trigger alert */
+#define TMP_FAULT_ONE ((0x00 & 0x03) << 3)
+#define TMP_FAULT_TWO ((0x01 & 0x03) << 3)
+#define TMP_FAULT_FOUR ((0x02 & 0x03) << 3)
+#define TMP_FAULT_SIX ((0x03 & 0x03) << 3)
+
+/* Temp mesurement resolution bits */ /* conversion time */
+#define TMP_RES_NINE_BITS ((0x00 & 0x03) << 5) /* 40ms */
+#define TMP_RES_TEN_BITS ((0x01 & 0x03) << 5) /* 80ms */
+#define TMP_RES_ELEVEN_BITS ((0x02 & 0x03) << 5) /* 160ms */
+#define TMP_RES_TWELVE_BITS ((0x03 & 0x03) << 5) /* 320ms */
+
+
+/* TMP101 sensor instance data.
+ * Use one of this for each sensor you want to access.
+ * - addr is the sensor address on most significant bits.
+ * - last_accessed_register is used to keep track of the last register accessed to
+ * prevent sending the pointer register again if we want to read the same register again.
+ * - resolution is one of the above resolution defined values.
+ */
+struct tmp101_sensor_config {
+ uint8_t addr;
+ uint8_t bus_num;
+ uint8_t probe_ok;
+ uint8_t actual_config;
+ int last_accessed_register;
+ uint32_t resolution;
+};
+
+
+/* Check the sensor presence, return 1 if found
+ * This is a basic check, it could be anything with the same address ...
+ * addr: the sensor address on most significant bits.
+ */
+int tmp101_probe_sensor(struct tmp101_sensor_config* conf);
+
+
+/* Convert raw temperature data (expressed as signed value of 16 times the
+ * actual temperature in the twelve higher bits of a sixteen bits wide value)
+ * into a decimal interger value of ten times the actual temperature.
+ * The value returned is thus in tenth of degrees centigrade.
+ */
+int tmp101_convert_to_deci_degrees(uint16_t raw);
+
+
+/* Temp Read
+ * Performs a non-blocking read of the temperature from the sensor.
+ * addr: the sensor address on most significant bits.
+ * 'raw' and 'deci_degrees': integer addresses for conversion result, may be NULL.
+ * Return value(s):
+ * Upon successfull completion, returns 0 and the temperature read is placed in the
+ * provided integer(s). On error, returns a negative integer equivalent to errors from
+ * glibc.
+ * -EBADFD : I2C not initialized
+ * -EBUSY : Device or ressource Busy or Arbitration lost
+ * -EINVAL : Invalid argument (buf)
+ * -ENODEV : No such device
+ * -EREMOTEIO : Device did not acknowledge : Any device present ?
+ * -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine
+ */
+int tmp101_sensor_read(struct tmp101_sensor_config* conf, uint16_t* raw, int* deci_degrees);
+
+
+/* Sensor config
+ * Performs default configuration of the temperature sensor.
+ * The sensor is thus placed in shutdown mode, the thermostat is in interrupt mode,
+ * and the polarity is set to active high.
+ * The conversion resolution is set to the provided "resolution".
+ * addr: the sensor address on most significant bits.
+ * Return value:
+ * Upon successfull completion, returns 0. On error, returns a negative integer
+ * equivalent to errors from glibc.
+ * -EBADFD : I2C not initialized
+ * -EBUSY : Device or ressource Busy or Arbitration lost
+ * -EINVAL : Invalid argument (buf)
+ * -ENODEV : No such device
+ * -EREMOTEIO : Device did not acknowledge : Any device present ?
+ * -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine
+ */
+int tmp101_sensor_config(struct tmp101_sensor_config* conf);
+
+/* Start a conversion when the sensor is in shutdown mode.
+ * addr : the sensor address on most significant bits.
+ */
+int tmp101_sensor_start_conversion(struct tmp101_sensor_config* conf);
+
+
+#endif /* EXTDRV_TEMP_H */
+
--- /dev/null
+/****************************************************************************
+ * extdrv/ws2812.h
+ *
+ *
+ * Copyright 2013 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+/*
+ * Support for the WS2812 Chainable RGB Leds
+ *
+ * WS2812 protocol can be found here : https://www.adafruit.com/datasheets/WS2812.pdf
+ *
+ */
+
+/*
+ * Preliminary notice :
+ * This driver will only function with a clock frequency of 48MHz (or close to) due to
+ * the use of nop() to get the right timmings.
+ *
+ * Internal data buffer :
+ * This driver uses an internal buffer for NB_LEDS leds or "pixels". The buffer
+ * size is (3 * NB_LEDS) bytes.
+ * The buffer is set pixel per pixel using the ws2812_set_pixel() function.
+ * The buffer is then sent to the led strip using ws2812_send_frame().
+ *
+ * Driving several led strips :
+ * It is possible to drive several led strips using this driver by calling the config
+ * function ws2812_config() between each ws2812_send_frame() call, and setting the led
+ * data again using the ws2812_set_pixel() function for each new led.
+ * This solution is not the most adapted for several led strips, and the driver should be
+ * modified to use an external buffer which address is either passed to each function or
+ * set using a modified version of the ws2812_config() function.
+ * In this case, the timmings should be checked and updated as access to the data may
+ * require more instructions.
+ *
+ * Note : ws2812_send_frame() will call lpc_disable_irq() to disable all interrupts
+ * when entering the timming critical section.
+ * Use of timers and interrupts have been tried but timmings are too short even
+ * whith the micro-controller running at 48MHz and direct access to the timer and
+ * GPIO registers.
+ * Instead the function uses a few nop() to get the right timmings.
+ *
+ */
+
+#include "lib/stdint.h"
+#include "core/pio.h"
+
+
+/* Size of the internal buffer.
+ * Change the value to the number of leds of your led strip.
+ */
+#define NB_LEDS 60
+
+
+/* Configure the pin for the led data signal. */
+void ws2812_config(const struct pio* gpio);
+
+/* Send led data from internal buffer using the configured GPIO pin. (Set leds to the
+ * selected color).
+ * If no pin have been configured, GPIO_0_0 will be used.
+ * If nb_leds is 0 then all led data set using ws2812_set_pixel() since the last call
+ * to ws2812_clear_buffer(), ws2812_clear() or ws2812_stop() will be sent.
+ * Call to this function will disable interrupts due to timming restrictions.
+ * Return -1 on error (nb_leds above NB_LEDS), or 0 on success.
+ */
+int ws2812_send_frame(uint16_t nb_leds);
+
+/* Set a pixel (led) color in the data buffer (frame)
+ * The pixel number 'pixel_num' is the led offset in the led strip.
+ * 'red', 'green' and 'blue' are the color values of the pixel. A value of 0 is off,
+ * while a value of 0xFF (255) is full brightness.
+ * Return -1 on error (pixel_num above NB_LEDS), or 0 on success.
+ */
+int ws2812_set_pixel(uint16_t pixel_num, uint8_t red, uint8_t green, uint8_t blue);
+
+/* Clear the internal data buffer. */
+void ws2812_clear_buffer(void);
+
+/* Clear the internal data buffer and send it to the Leds, turning them all off */
+void ws2812_clear(void);
+/* Alias for ws2812_clear */
+void ws2812_stop(void);
+
--- /dev/null
+/****************************************************************************
+ * lib/errno.h
+ *
+ * Copyright 2016 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef LIB_ERRNO_H
+#define LIB_ERRNO_H
+
+/* Error Values, from glibc errno.h and errno-base.h */
+#define EIO 5 /* Bad one: Input or Output error. */
+#define E2BIG 7 /* Argument list too long or Data size beyond buffer size */
+#define EAGAIN 11 /* Device already in use */
+#define EFAULT 14 /* Address error */
+#define EBUSY 16 /* Device or ressource Busy */
+#define ENODEV 19 /* No such device */
+#define EINVAL 22 /* Invalid argument */
+#define EBADFD 77 /* Device not initialized */
+#define EREMOTEIO 121 /* Device did not acknowledge */
+
+/* Note on error values for I2C :
+ * EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine
+ * EFAULT : address above eeprom size
+ * EBUSY : Device or ressource Busy or Arbitration lost
+ * EREMOTEIO : Device did not acknowledge
+ */
+
+
+#endif /* LIB_ERRNO_H */
--- /dev/null
+/************************************************************************
+ * lib/font.h
+ *
+ * Copyright (C) Lisa Milne 2014 <lisa@ltmnet.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ ************************************************************************/
+
+/*
+ * The font has been found on opengameart.org:
+ * http://opengameart.org/content/8x8-ascii-bitmap-font-with-c-source
+ */
+
+#ifndef LIB_FONT_H
+#define LIB_FONT_H
+
+/*
+ * The values in this array are a 8x8 bitmap font for ascii characters
+ * As memory is a very precious ressource on a micro-controller all chars
+ * before "space" (0x20) have been removed.
+ */
+#define NB_FONT_TILES 95
+extern uint8_t first_font_char;
+extern uint64_t font[NB_FONT_TILES];
+
+#endif /* LIB_FONT_H */
--- /dev/null
+/**
+ * lib/list.h
+ *
+ * Circular linked list implementation
+ * Using the linked list code from linux
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+
+#ifndef LIB_LIST_H
+#define LIB_LIST_H
+
+#include "lib/stddef.h"
+
+struct list_head {
+ struct list_head* next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void INIT_LIST_HEAD(struct list_head* list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head* new, struct list_head* prev, struct list_head* next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head* new, struct list_head* head)
+{
+ __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head* new, struct list_head* head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head* prev, struct list_head* next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+static inline void list_del(struct list_head* entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = NULL;
+ entry->prev = NULL;
+}
+
+/**
+ * list_is_last - tests whether @list is the last entry in list @head
+ * @list: the entry to test
+ * @head: the head of the list
+ */
+static inline int list_is_last(const struct list_head* list, const struct list_head* head)
+{
+ return list->next == head;
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head* list,
+ struct list_head* head)
+{
+ list_del(list);
+ list_add_tail(list, head);
+}
+
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head* head)
+{
+ return head->next == head;
+}
+
+/**
+ * list_rotate_left - rotate the list to the left
+ * @head: the head of the list
+ */
+static inline void list_rotate_left(struct list_head* head)
+{
+ struct list_head* first;
+
+ if (!list_empty(head)) {
+ first = head->next;
+ list_move_tail(first, head);
+ }
+}
+
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/**
+ * list_first_entry - get the first element from a list
+ * @ptr: the list head to take the element from.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_first_entry(ptr, type, member) \
+ list_entry((ptr)->next, type, member)
+
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev - iterate over a list backwards
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+ for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_prev_safe(pos, n, head) \
+ for (pos = (head)->prev, n = pos->prev; \
+ pos != (head); \
+ pos = n, n = pos->prev)
+
+/**
+ * list_for_each_entry - iterate over list of given type
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member) \
+ for (pos = list_entry((head)->prev, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_reverse - iterate backwards over list safe against removal
+ * @pos: the type * to use as a loop cursor.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Iterate backwards over list of given type, safe against removal
+ * of list entry.
+ */
+#define list_for_each_entry_safe_reverse(pos, n, head, member) \
+ for (pos = list_entry((head)->prev, typeof(*pos), member), \
+ n = list_entry(pos->member.prev, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.prev, typeof(*n), member))
+
+
+
+
+#endif /* LIB_LIST_H */
+
--- /dev/null
+/*
+ * lib/protocols/dtplug/defs.h
+ *
+ *
+ * Copyright 2013-2015 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef LIB_PROTOCOLS_DTPLUG_DEFS_H
+#define LIB_PROTOCOLS_DTPLUG_DEFS_H
+
+
+#include "lib/stdint.h"
+
+/******************************************************************************/
+/* These structures define the packets used to transfer data over the serial link
+ * between the dtplug (or domotab or any computing platform) and a module
+ */
+
+/* The header is included in each and every packet we need to transfer
+ * It holds information about the message type and content and provides checksum
+ * information of the header and the data part of the packet if any.
+ *
+ * Checksums information :
+ * The header checksum is a value such that the sum of all bytes of the header
+ * modulo 256 is equal to 0. (Use a mask with 0xFF to get the modulo)
+ * The data checksum the sum of all data bytes modulo 256.
+ *
+ * Sequence numbers, data checksums and error codes
+ * The sequence number is on the 6 least significant bits of the sequence number field.
+ * this gives 64 sequence numbers which is more than enough.
+ * When the most significant bit (bit 7) of the sequence number field (seq_num) is set
+ * in a request (master to slave), the slave MUST send an acknowledge packet or a reply
+ * with the same sequence number.
+ * This may help checking which packet triggered the reply and packet losses.
+ * When bit 7 is set in a reply, it indicates that the packet is an error packet and that
+ * the union holds error information (error code and aditional error information.
+ * No data should be present in the packet.
+ * When set, bit 6 of the sequence number indicates that the union holds 'quick_data'.
+ * This bit cannot be used for error packets.
+ * A packet with "quick-data" cannot have additional data.
+ * When bit 6 is not set, and the packet is not an error packet, the union holds a
+ * 'data_information' structure whith the packet data 'size' (if any) and 'data_checksum'
+ * for the packet data part.
+ *
+ * Data size and big (continued) data packets
+ * When a packet does not have enough room for the whole data the most significant bit
+ * (bit 7) of the data size is set to mark a packet whose data will be continued in a
+ * subsequent PKT_TYPE_CONTINUED_DATA packet.
+ * this bit must be set in all but the last one of a continued data packet serie.
+*/
+
+/* Normal packets 'sel' field information : holds information about the data part.
+ * A 'size' of 0 indicates that there is no data part.
+ */
+struct data_information {
+ uint8_t size; /* Size of the data part. The header size is fixed, so not included */
+ uint8_t checksum; /* Checksum for the data part */
+} __attribute__ ((__packed__));
+/* Error packet 'sel' field */
+struct error_information {
+ uint8_t error_code;
+ uint8_t info;
+} __attribute__ ((__packed__));
+
+/* Packet header */
+struct header {
+ char start; /* Start of paquet indicator */
+ uint8_t type; /* Packet type, used to provide information on the data structure */
+ uint8_t checksum; /* Header checksum */
+ uint8_t seq_num; /* Packet sequence number on bits 0:5, error indicator on bit 7 */
+ union {
+ struct data_information data;
+ struct error_information err;
+ uint8_t quick_data[2];
+ };
+} __attribute__ ((__packed__));
+
+#define FIRST_PACKET_CHAR '#'
+/* Sequence byte decoding */
+#define PACKET_NEEDS_REPLY (0x01 << 7) /* Host to slave */
+#define PACKET_IS_ERROR (0x01 << 7) /* Slave to host */
+#define QUICK_DATA_PACKET (0x01 << 6)
+#define SEQUENCE_MASK (0x3F)
+/* Data size decoding */
+#define BIG_DATA_PKT (0x01 << 7) /* The total data size is above PACKET_DATA_SIZE, the packet will be continued. */
+#define PKT_SIZE_MASK (0x7F)
+
+/* The following struct defines a generic packet.
+ * Specific packets should be defined in specific structures.
+ */
+#define PACKET_DATA_SIZE 64
+struct packet {
+ struct header info; /* Packet header */
+ uint8_t data[PACKET_DATA_SIZE]; /* packet data */
+} __attribute__ ((__packed__));
+
+
+
+enum packet_types {
+ /* Common packet types, must be handled by all the slaves */
+ PKT_TYPE_RESET = 0, /* Soft reset of board */
+ PKT_TYPE_PING, /* Reply with no data or urgent data */
+ PKT_TYPE_GET_BOARD_INFO, /* Return board name, version, module version, and programm version. */
+ PKT_TYPE_SET_TIME, /* Set current time for events timestamping */
+ PKT_TYPE_GET_NUM_PACKETS, /* Get the number of packets received since system start */
+ PKT_TYPE_GET_ERRORS, /* Ask the slave to return any active error code in the data */
+ PKT_TYPE_GET_NUM_ERRORS, /* Get the number of errors since system start */
+ PKT_TYPE_SET_USER_INFO, /* Change the current board user information (and reset board) */
+
+ /* Config */
+ PKT_TYPE_ADD_GPIO, /* Configure one of the GPIO or ADC as GPIO */
+ PKT_TYPE_ADD_CS, /* Configure one of the GPIO or ADC as SPI Chip select */
+ /* ADC config specifies :
+ * the channel number,
+ * periodicity of sample (from on request up to continuous),
+ * and number of values for the continuous average computation
+ */
+ PKT_TYPE_ADD_ADC, /* Configure one of the ADC as ADC */
+ /* PWM config specifies :
+ * the channel number,
+ * the PWM period,
+ * the channel duty cycle,
+ */
+ PKT_TYPE_ADD_PWM, /* Configure one of the PWM capable GPIO as PWM */
+
+ /* Continued data. Use for protocols with data size that goes beyond PACKET_DATA_SIZE */
+ PKT_TYPE_CONTINUED_DATA,
+
+ /* Temperature packets */
+ PKT_TYPE_START_TEMP_CONVERSION, /* Requiered to update the temperature value */
+ PKT_TYPE_GET_TEMPERATURE, /* Get temprature values */
+
+ /* ADC, PWM and GPIO packets */
+ PKT_TYPE_START_ADC_CONVERSION,
+ PKT_TYPE_GET_ADC_VALUE,
+ PKT_TYPE_SET_GPIO,
+ PKT_TYPE_GET_GPIO,
+ PKT_TYPE_SET_PWM_CHAN,
+
+ /* Communication */
+ SEND_ON_BUS,
+};
+
+/* Error / status codes */
+enum reply_statuses {
+ NO_ERROR = 0,
+ GOT_MANY_ERRORS, /* This one is returned when there's not only one error */
+ ERROR_PKT_NOT_HANDLED,
+ ERROR_LAST_PKT_IN_PROCESS,
+ ERROR_IN_PKT_STRUCTURE,
+ ERROR_IN_DATA_CHECKSUM,
+ ERROR_IN_DATA_VALUES,
+ ERROR_IN_DATA_SIZE, /* To many data */
+ ERROR_IN_UART_TX, /* This one is critical if received, and much more critical when it occurs and cannot be sent. */
+ /* Temperature */
+ ERROR_NO_TEMP_SENSOR,
+ ERROR_TEMP_CONVERSION,
+ TEMPERATURE_ALERT,
+ /* Configuration problem */
+ ERROR_FLASH_ERASE, /* Error, unable to erase the user information block */
+ ERROR_FLASH_WRITE, /* Error, unable to write the user information block */
+ /* RF */
+ RF_RX_QUEUE_EMPTY, /* Na RX packet in RX queue */
+ ERROR_IN_RF_RX,
+ ERROR_IN_RF_TX,
+ /* Last error number ... must not be above 255 */
+ LAST_ERROR_NUM, /* This one is here to get the value of the last enum. */
+};
+
+
+
+/******************************************************************************/
+/* Other information on the protocol */
+
+/* When a packet is received and requires an ACK but no particular data, the slave replies
+ * using a PING packet, with the sequence field set to the sequence number of the last received packet).
+ * When the slave has urgent data to send (either errors or alerts), it uses the corresponding bits in
+ * the sequence byte and places the 'appropriate data (error_information or quick_dtaa) in the 'sel' field.
+ */
+
+/* Multiple-bytes data are sent in Big Endian (network endian) !!!!
+ * This is the usual way to send data over a network, and even if most of our boards and computers are little endian
+ * there are very little multi-byte information in our protocol so it's OK
+ * The endianness transformation may be done before or after the checksum control, it has no influence on the
+ * checksum algorithm we chose.
+ */
+
+/* What to do on error, and error recovery procedure.
+ * When a slave has an internal error, it has two options.
+ * Either it's the first one, and the master requested a reply. The slave can then send the error
+ * code in the reply.
+ * Or it's not the first one or the master did not ask for a reply or ACK. Then the slave stores the
+ * error code for future transmission.
+ * If the master asked for a reply and the slave has many errors, the slave send "GOT_MANY_ERRORS" as error code.
+ * When the master receives the "GOT_MANY_ERRORS" error code, it must send a "PKT_TYPE_GET_ERRORS" request.
+ * The slave then replies with a list of all errors in the packet data with a header which does not indicate an
+ * error packet (this is the normal reply to a PKT_TYPE_GET_ERRORS request.
+ * A slave cannot store more than 8 error codes, if the count is 8, consider that there is a BIG problem !
+ */
+
+
+
+
+#endif /* LIB_PROTOCOLS_DTPLUG_DEFS_H */
--- /dev/null
+/*
+ * lib/protocols/dtplug/slave.h
+ *
+ *
+ * Copyright 2013-2014 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef LIB_PROTOCOLS_DTPLUG_SLAVE_H
+#define LIB_PROTOCOLS_DTPLUG_SLAVE_H
+
+
+#include "lib/stdint.h"
+#include "lib/protocols/dtplug/defs.h"
+
+
+/******************************************************************************/
+/* DTPlug (or DomoTab, PC, ...) Communication */
+
+#define DTPP_MAX_ERROR_STORED 8
+
+struct dtplug_protocol_handle {
+ /* Store two packets, one being received, one being used */
+ struct packet packets[2];
+ struct packet* rx_packet;
+ volatile struct packet* packet_ok;
+
+ uint32_t packet_count;
+
+ uint32_t errors_count;
+ uint8_t error_storage[(DTPP_MAX_ERROR_STORED * 2)];
+ uint8_t num_errors_stored;
+
+ /* Set to 1 when the packet is handled to tell the decoder we can handle a new one.
+ * MUST be initialised to 1 or the handle will think we have a valid packet to handle upon
+ * system startup */
+ uint8_t done_with_old_packet;
+ uint8_t uart;
+};
+
+
+/* Setup the UART used for communication with the host / master (the module is slave) */
+void dtplug_protocol_set_dtplug_comm_uart(uint8_t uart_num, struct dtplug_protocol_handle* handle);
+
+
+/* Tell the receive routine that the "packet_ok" packet is no more in use and that
+ * we are ready to handle a new one */
+void dtplug_protocol_release_old_packet(struct dtplug_protocol_handle* handle);
+
+
+/* Get a pointer to the new packet received.
+ * Return NULL when no new packet were received since last packet was released.
+ */
+struct packet* dtplug_protocol_get_next_packet_ok(struct dtplug_protocol_handle* handle);
+
+
+/* When a packet has not been handled we must not count it as acknowledged
+ * On the next ping request the master will then see wich packet caused the problem.
+ */
+void dtplug_protocol_add_error_to_list(struct dtplug_protocol_handle* handle, struct header* info, uint8_t error_code);
+
+
+/* This function handle sending replies when requested by the host.
+ * When there is an error but the host did not request a reply, this function stores the error for
+ * future request.
+ * When a reply is effectively sent, the PACKET_NEEDS_REPLY bit is removed from the sequence filed
+ * packet handling code will know if there is still a PING request to be answered.
+ */
+void dtplug_protocol_send_reply(struct dtplug_protocol_handle* handle,
+ struct packet* question, uint8_t error, int size, uint8_t* data);
+
+
+
+#endif /* LIB_PROTOCOLS_DTPLUG_SLAVE_H */
--- /dev/null
+/****************************************************************************
+ * lib/stddef.h
+ *
+ * Copyright 2014 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ r
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef LIB_STDDEF_H
+#define LIB_STDDEF_H
+
+
+#undef __SIZE_TYPE__
+#define __SIZE_TYPE__ long unsigned int
+typedef __SIZE_TYPE__ size_t;
+
+#define NULL ((void *)0)
+
+
+/**
+ * offsetof - return the offset of a member in the containing structure.
+ */
+#ifdef __builtin_offsetof
+ #define offsetof(type, member) __builtin_offsetof (type, member)
+#else
+ #define offsetof(type, member) ((size_t) &((type *)0)->member)
+#endif
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+
+#endif /* LIB_STDDEF_H */
--- /dev/null
+/****************************************************************************
+ * lib/"lib/stdint.h"
+ *
+ * Copyright 2016 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ r
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef LIB_STDINT_H
+#define LIB_STDINT_H
+
+/* Signed */
+typedef signed char int8_t;
+typedef short int int16_t;
+typedef int int32_t;
+__extension__
+typedef long long int int64_t;
+/* Small types - Signed */
+typedef signed char int_least8_t;
+typedef short int int_least16_t;
+typedef int int_least32_t;
+__extension__
+typedef long long int int_least64_t;
+/* Fast types - Signed */
+typedef signed char int_fast8_t;
+typedef int int_fast16_t;
+typedef int int_fast32_t;
+__extension__
+typedef long long int int_fast64_t;
+
+
+/* Unsigned */
+typedef unsigned char uint8_t;
+typedef unsigned short int uint16_t;
+typedef unsigned int uint32_t;
+__extension__
+typedef unsigned long long int uint64_t;
+/* Small types - Unsigned */
+typedef unsigned char uint_least8_t;
+typedef unsigned short int uint_least16_t;
+typedef unsigned int uint_least32_t;
+__extension__
+typedef unsigned long long int uint_least64_t;
+/* Fast types - Unsigned */
+typedef unsigned char uint_fast8_t;
+typedef unsigned int uint_fast16_t;
+typedef unsigned int uint_fast32_t;
+__extension__
+typedef unsigned long long int uint_fast64_t;
+
+typedef long unsigned int size_t;
+
+
+/* Types for `void *' pointers. */
+typedef int intptr_t;
+typedef unsigned int uintptr_t;
+
+
+/* Largest integral types. */
+__extension__
+typedef long long int intmax_t;
+__extension__
+typedef unsigned long long int uintmax_t;
+
+# define __INT64_C(c) c ## LL
+# define __UINT64_C(c) c ## ULL
+
+
+/* Limits of integral types. */
+
+/* Minimum of signed integral types. */
+#define INT8_MIN (-128)
+#define INT16_MIN (-32767-1)
+#define INT32_MIN (-2147483647-1)
+#define INT64_MIN (-__INT64_C(9223372036854775807)-1)
+/* Maximum of signed integral types. */
+#define INT8_MAX (127)
+#define INT16_MAX (32767)
+#define INT32_MAX (2147483647)
+#define INT64_MAX (__INT64_C(9223372036854775807))
+
+/* Maximum of unsigned integral types. */
+#define UINT8_MAX (255)
+#define UINT16_MAX (65535)
+#define UINT32_MAX (4294967295U)
+#define UINT64_MAX (__UINT64_C(18446744073709551615))
+
+
+#define SIZE_MAX (4294967295UL)
+
+#define __WORDSIZE 32
+
+
+#endif /* LIB_STDINT_H */
--- /dev/null
+/****************************************************************************
+ * lib/stdio.h
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef LIB_STDIO_H
+#define LIB_STDIO_H
+
+#include <stdarg.h>
+#include "lib/stdint.h"
+#include "lib/string.h"
+
+
+/* */
+int vsnprintf(char *buf, size_t size, const char *fmt, va_list args);
+
+/* */
+int snprintf(char* buf, size_t size, const char *format, ...);
+
+/* */
+int uprintf(int uart_num, const char *format, ...);
+
+
+#endif /* LIB_STDIO_H */
--- /dev/null
+/****************************************************************************
+ * lib/stdlib.h
+ *
+ * Copyright 2014 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef LIB_STDLIB_H
+#define LIB_STDLIB_H
+
+#include "lib/stdint.h"
+
+/* Simple strtoul implementation.
+ * Returns the value converted from the given string.
+ * Does not check that the base is respected aside for the use of letters in
+ * number representation.
+ */
+uint32_t strtoul(const char* str, char** end, uint8_t base);
+
+#endif /* LIB_STDLIB_H */
+
--- /dev/null
+/*
+ * lib/string.h
+ *
+ * From linux/lib/string.h :
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/*
+ * stupid library routines.. The optimized versions should generally be found
+ * as inline code in <asm-xx/string.h>
+ *
+ * These are buggy as well..
+ *
+ * * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de>
+ * - Added strsep() which will replace strtok() soon (because strsep() is
+ * reentrant and should be faster). Use only strsep() in new code, please.
+ */
+
+#ifndef LIB_STRING_H
+#define LIB_STRING_H
+
+#include <lib/stddef.h>
+#include "lib/stdint.h"
+
+/**
+ * memcpy - Copy one area of memory to another
+ * @dest: Where to copy to
+ * @src: Where to copy from
+ * @count: The size of the area.
+ */
+void * memcpy(void *dest, const void *src, size_t count);
+
+/**
+ * memset - Fill a region of memory with the given value
+ * @s: Pointer to the start of the area.
+ * @c: The byte to fill the area with
+ * @count: The size of the area.
+ */
+void * memset(void * s, int c, size_t count);
+
+/**
+ * strcpy - Copy a %NUL terminated string
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ */
+char * strcpy(char * dest, const char *src);
+
+/**
+ * strncpy - Copy a length-limited, %NUL-terminated string
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ * @count: The maximum number of bytes to copy
+ *
+ * Note that unlike userspace strncpy, this does not %NUL-pad the buffer.
+ * However, the result is not %NUL-terminated if the source exceeds
+ * @count bytes.
+ */
+char * strncpy(char * dest, const char *src, size_t count);
+
+/**
+ * strcmp - Compare two strings
+ * @cs: One string
+ * @ct: Another string
+ */
+int strcmp(const char * cs, const char * ct);
+
+/**
+ * strncmp - Compare two length-limited strings
+ * @cs: One string
+ * @ct: Another string
+ * @count: The maximum number of bytes to compare
+ */
+int strncmp(const char * cs, const char * ct, size_t count);
+
+/**
+ * strchr - Find the first occurrence of a character in a string
+ * @s: The string to be searched
+ * @c: The character to search for
+ */
+char * strchr(const char * s, int c);
+
+/**
+ * strlen - Find the length of a string
+ * @s: The string to be sized
+ */
+size_t strlen(const char * s);
+
+/**
+ * strrchr - Find the last occurrence of a character in a string
+ * @s: The string to be searched
+ * @c: The character to search for
+ */
+char * strrchr(const char * s, int c);
+
+/**
+ * strnlen - Find the length of a length-limited string
+ * @s: The string to be sized
+ * @count: The maximum number of bytes to search
+ */
+size_t strnlen(const char * s, size_t count);
+
+
+#endif /* LIB_STRING_H */
--- /dev/null
+/****************************************************************************
+ * lib/time.h
+ *
+ *
+ *
+ * Copyright 2013-2014 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ ****************************************************************************/
+
+#ifndef LIB_TIME_H
+#define LIB_TIME_H
+
+
+#include "lib/stdint.h"
+
+
+/******************************************************************************/
+/* Common parts for time handling for LPC Boards with or without RTC */
+
+/* Notes on time tracking using this library :
+ *
+ * - Unless there is some known good time source used to set time (using set_time()) the time is
+ * counted from the system power ON or RTC power ON as origin.
+ *
+ * - This code relies on systick timer configured with a 1ms period (systick_timer_on(1)) for whole
+ * time on systems without a RTC oscilator and on systick timer for mili-seconds only on systems
+ * with a 32.768 KHz external oscilator for the RTC.
+ *
+ * - When used with systick only, the time tracking is far from the precision time one should
+ * obtain using the RTC with an external 32.768 KHz cristal. It uses the 12MHz internal RC
+ * Oscilator, which is at 1% accuracy.
+ * Thus the time error may be as much as 1s every 100 seconds !
+ */
+
+struct time_spec {
+ uint32_t seconds;
+ uint16_t msec;
+};
+
+
+/* Call this to set the time from a known good time source. */
+void set_time(struct time_spec* new_time);
+
+
+/* Call this to set the time from a known good time source.
+ * This function returns the time difference in the given time_spec.
+ */
+void set_time_and_get_difference(struct time_spec* new_time, struct time_spec* diff);
+
+
+/* Put a time struct in a buffer, swapping both fields to network endian. */
+void time_to_buff_swapped(uint8_t* buf, struct time_spec* src_time);
+
+
+/* Get a snapshot of the time when this is called.
+ * When in interrupt, use get_time_in_interrupt(). It will get called anyway, but it
+ * will be longer.
+ */
+void get_time(struct time_spec* save_time);
+
+/* Get a snapshot of the time when this is called
+ * It is safe to call this one in interrupt.
+ */
+void get_time_in_interrupt(struct time_spec* save_time);
+
+
+/* Must be called once to register the systick callback.
+ * Can be called anytime, will just return if it has already been called.
+ */
+void time_init(void);
+
+
+/* Compute a time difference
+ * Return 0 if both times are the same, 1 if (t1 > t2), -1 if (t1 < t2)
+ */
+int get_time_diff(const struct time_spec* t1, const struct time_spec* t2, struct time_spec* diff);
+
+
+
+#endif /* LIB_TIME_H */
+
--- /dev/null
+/****************************************************************************
+ * lib/utils.h
+ *
+ * Copyright 2014 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef LIB_UTILS_H
+#define LIB_UTILS_H
+
+/***************************************************************************** */
+/* Library routines */
+/***************************************************************************** */
+
+#include "lib/stdint.h"
+
+
+/***************************************************************************** */
+/* Bit twidling hacks.
+ * http://graphics.stanford.edu/~seander/bithacks.html
+ */
+
+/* Counting consecutive trailing or leading zero bits (or finding bit indices)
+ * The ARM Cortex M0 core does not have the __builtin_clz() and __builtin_ctz()
+ * instructions.
+ */
+
+/* Count leading zeroes
+ * The following function is an effitient way to implement __builtin_clz().
+ */
+uint8_t clz(uint32_t x);
+
+/* Count traling zeroes
+ * The following function is an effitient way to implement __builtin_ctz().
+ */
+uint8_t ctz(uint32_t x);
+
+#endif /* LIB_UTILS_H */
+
--- /dev/null
+/************************************************************************
+ * lib/font.c
+ *
+ * Copyright (C) Lisa Milne 2014 <lisa@ltmnet.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ ************************************************************************/
+
+/*
+ * The font has been found on opengameart.org:
+ * http://opengameart.org/content/8x8-ascii-bitmap-font-with-c-source
+ */
+
+#include "lib/stdint.h"
+#include "lib/font.h"
+
+/*
+ * The values in this array are a 8x8 bitmap font for ascii characters
+ * As memory is a very precious ressource on a micro-controller all chars
+ * before "space" (0x20) have been removed.
+ */
+uint8_t first_font_char = 0x20;
+uint64_t font[NB_FONT_TILES] = {
+ 0x0000000000000000, /* (space) */ /* 0x20 */
+ 0x0808080800080000, /* ! */
+ 0x2828000000000000, /* " */
+ 0x00287C287C280000, /* # */
+ 0x081E281C0A3C0800, /* $ */
+ 0x6094681629060000, /* % */
+ 0x1C20201926190000, /* & */
+ 0x0808000000000000, /* ' */
+ 0x0810202010080000, /* ( */
+ 0x1008040408100000, /* ) */
+ 0x2A1C3E1C2A000000, /* * */
+ 0x0008083E08080000, /* + */
+ 0x0000000000081000, /* , */
+ 0x0000003C00000000, /* - */
+ 0x0000000000080000, /* . */
+ 0x0204081020400000, /* / */
+ 0x1824424224180000, /* 0 */ /*x30 */
+ 0x08180808081C0000, /* 1 */
+ 0x3C420418207E0000, /* 2 */
+ 0x3C420418423C0000, /* 3 */
+ 0x081828487C080000, /* 4 */
+ 0x7E407C02423C0000, /* 5 */
+ 0x3C407C42423C0000, /* 6 */
+ 0x7E04081020400000, /* 7 */
+ 0x3C423C42423C0000, /* 8 */
+ 0x3C42423E023C0000, /* 9 */
+ 0x0000080000080000, /* : */
+ 0x0000080000081000, /* ; */
+ 0x0006186018060000, /* < */
+ 0x00007E007E000000, /* = */
+ 0x0060180618600000, /* > */
+ 0x3844041800100000, /* ? */
+ 0x003C449C945C201C, /* @ */ /* 0x40 */
+ 0x1818243C42420000, /* A */
+ 0x7844784444780000, /* B */
+ 0x3844808044380000, /* C */
+ 0x7844444444780000, /* D */
+ 0x7C407840407C0000, /* E */
+ 0x7C40784040400000, /* F */
+ 0x3844809C44380000, /* G */
+ 0x42427E4242420000, /* H */
+ 0x3E080808083E0000, /* I */
+ 0x1C04040444380000, /* J */
+ 0x4448507048440000, /* K */
+ 0x40404040407E0000, /* L */
+ 0x4163554941410000, /* M */
+ 0x4262524A46420000, /* N */
+ 0x1C222222221C0000, /* O */
+ 0x7844784040400000, /* P */ /* 0x50 */
+ 0x1C222222221C0200, /* Q */
+ 0x7844785048440000, /* R */
+ 0x1C22100C221C0000, /* S */
+ 0x7F08080808080000, /* T */
+ 0x42424242423C0000, /* U */
+ 0x8142422424180000, /* V */
+ 0x4141495563410000, /* W */
+ 0x4224181824420000, /* X */
+ 0x4122140808080000, /* Y */
+ 0x7E040810207E0000, /* Z */
+ 0x3820202020380000, /* [ */
+ 0x4020100804020000, /* \ */
+ 0x3808080808380000, /* ] */
+ 0x1028000000000000, /* ^ */
+ 0x00000000007E0000, /* _ */
+ 0x1008000000000000, /* ` */ /* 0x60 */
+ 0x003C023E463A0000, /* a */
+ 0x40407C42625C0000, /* b */
+ 0x00001C20201C0000, /* c */
+ 0x02023E42463A0000, /* d */
+ 0x003C427E403C0000, /* e */
+ 0x0018103810100000, /* f */
+ 0x0000344C44340438, /* g */
+ 0x2020382424240000, /* h */
+ 0x0800080808080000, /* i */
+ 0x0800180808080870, /* j */
+ 0x20202428302C0000, /* k */
+ 0x1010101010180000, /* l */
+ 0x0000665A42420000, /* m */
+ 0x00002E3222220000, /* n */
+ 0x00003C42423C0000, /* o */
+ 0x00005C62427C4040, /* p */ /* 0x70 */
+ 0x00003A46423E0202, /* q */
+ 0x00002C3220200000, /* r */
+ 0x001C201804380000, /* s */
+ 0x00103C1010180000, /* t */
+ 0x00002222261A0000, /* u */
+ 0x0000424224180000, /* v */
+ 0x000081815A660000, /* w */
+ 0x0000422418660000, /* x */
+ 0x0000422214081060, /* y */
+ 0x00003C08103C0000, /* z */
+ 0x1C103030101C0000, /* { */
+ 0x0808080808080800, /* | */
+ 0x38080C0C08380000, /* } */
+ 0x000000324C000000, /* ~ */
+};
+
--- /dev/null
+/****************************************************************************
+ * lib/protocol/dtplug/slave.c
+ *
+ *
+ * Copyright 2013-2015 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ ****************************************************************************/
+
+#include "lib/stdint.h"
+#include "core/system.h"
+#include "core/iap.h"
+#include "drivers/serial.h"
+#include "lib/stdio.h"
+#include "lib/string.h"
+#include "lib/time.h"
+
+#include "lib/protocols/dtplug/defs.h"
+#include "lib/protocols/dtplug/slave.h"
+
+/******************************************************************************/
+/* DTPlug (or DomoTab, PC, ...) Communication */
+
+#define DTPP_MAX_HANDLERS 2
+static struct dtplug_protocol_handle* dtpp_handles[DTPP_MAX_HANDLERS] = {0};
+
+
+
+static void dtplug_protocol_decode(struct dtplug_protocol_handle* handle, uint8_t c);
+
+static void dtplug_protocol_decoder_0(uint8_t c)
+{
+ dtplug_protocol_decode(dtpp_handles[0], c);
+}
+static void dtplug_protocol_decoder_1(uint8_t c)
+{
+ dtplug_protocol_decode(dtpp_handles[1], c);
+}
+
+
+/* Setup the UART used for communication with the host / master (the module is slave) */
+void dtplug_protocol_set_dtplug_comm_uart(uint8_t uart_num, struct dtplug_protocol_handle* handle)
+{
+ void* decoder = dtplug_protocol_decoder_0;
+
+ /* Basic parameter checks */
+ if (uart_num >= DTPP_MAX_HANDLERS) {
+ return;
+ }
+ if (handle == NULL) {
+ return;
+ }
+
+ /* Configure and register handle and configure uart */
+ handle->rx_packet = handle->packets;
+ handle->packet_ok = NULL;
+ handle->done_with_old_packet = 1;
+ handle->uart = uart_num;
+ dtpp_handles[uart_num] = handle;
+ switch (uart_num) {
+ case 1:
+ decoder = dtplug_protocol_decoder_1;
+ break;
+ case 0:
+ default:
+ break;
+ }
+ uart_on(uart_num, 115200, decoder);
+}
+
+
+/* Tell the receive and decode routine that the "handle->packet_ok" packet is no more in use and that
+ * we are ready to handle a new one.
+ */
+void dtplug_protocol_release_old_packet(struct dtplug_protocol_handle* handle)
+{
+ handle->packet_ok = NULL;
+ handle->done_with_old_packet = 1;
+}
+
+
+
+/* When a packet has not been handled we must not count it as acknowledged
+ * On the next ping request the master will then see wich packet caused the problem.
+ */
+void dtplug_protocol_add_error_to_list(struct dtplug_protocol_handle* handle, struct header* info, uint8_t error_code)
+{
+ if (handle->num_errors_stored < (DTPP_MAX_ERROR_STORED * 2)) {
+ if (error_code == NO_ERROR) {
+ error_code = ERROR_PKT_NOT_HANDLED;
+ }
+ handle->error_storage[handle->num_errors_stored++] = error_code;
+ handle->error_storage[handle->num_errors_stored++] = (info->seq_num & SEQUENCE_MASK);
+ }
+}
+
+
+/* Handle packet reception, including checksums */
+/* 'sum' is used to sum all the received characters, and if the last byte of sum is 0 for each
+ * part (header and data) then the packet is valid.
+ * 'full_size' is the size of the whole packet, including header, updated as soon as the header
+ * is checked and valid
+ */
+static void dtplug_protocol_decode(struct dtplug_protocol_handle* handle, uint8_t c)
+{
+ static uint8_t rx_ptr = 0;
+ static uint8_t sum = 0;
+ static uint8_t full_size = 0;
+ static struct header* info = NULL;
+
+ /* Do not start reception before receiving the packet start character */
+ if ((rx_ptr == 0) && (c != FIRST_PACKET_CHAR)) {
+ return;
+ }
+
+ /* Store the new byte in the packet */
+ if (rx_ptr < sizeof(struct packet)) {
+ ((uint8_t*)handle->rx_packet)[rx_ptr++] = c;
+ sum += c;
+ } else {
+ goto next_packet;
+ }
+
+ /* Is this packet valid ? (at end of header reception) */
+ if (rx_ptr == sizeof(struct header)) {
+ if (sum != 0) {
+ goto next_packet;
+ }
+ /* Start the new checksum for data (if any) */
+ sum = 0;
+
+ info = (struct header*)handle->rx_packet;
+ full_size = sizeof(struct header);
+ if (!(info->seq_num & QUICK_DATA_PACKET)) {
+ /* Do not care about big data packets here */
+ full_size += (info->data.size & PKT_SIZE_MASK);
+ }
+ }
+
+ /* Did we receive the whole packet ? */
+ if (rx_ptr == full_size) {
+ /* From here on, the packet is valid, we can provide some feedback */
+ /* Check data checksum */
+ if (!(info->seq_num & QUICK_DATA_PACKET) && (sum != info->data.checksum)) {
+ dtplug_protocol_add_error_to_list(handle, info, ERROR_IN_DATA_CHECKSUM);
+ handle->errors_count++;
+ goto next_packet;
+ }
+ /* Warning, if we are still using the old packet there's a problem */
+ if (handle->done_with_old_packet == 0) {
+ /* FIXME : what to do then ? inform the master ? ignore the new packet ? */
+ dtplug_protocol_add_error_to_list(handle, info, ERROR_LAST_PKT_IN_PROCESS);
+ handle->errors_count++;
+ goto next_packet;
+ }
+ /* Count received packets */
+ handle->packet_count++;
+ /* Mark packet as OK : switch pointers */
+ handle->packet_ok = handle->rx_packet;
+ handle->done_with_old_packet = 0;
+ /* Switch our receiving buffer (do not overide the last received packet !) */
+ if (handle->rx_packet == handle->packets) {
+ handle->rx_packet = handle->packets + 1;
+ } else {
+ handle->rx_packet = handle->packets;
+ }
+ /* And get ready to receive the next packet */
+ goto next_packet;
+ }
+
+ return;
+
+next_packet:
+#ifdef DEBUG
+ if (handle->done_with_old_packet != 0) {
+ uprintf(handle->uart, "Rx:%d, f:%d, cnt:%d\n", rx_ptr, full_size, handle->packet_count);
+ if (rx_ptr >= sizeof(struct header)) {
+ struct header* h = &(handle->rx_packet->info);
+ uprintf(handle->uart, "P: type:%03d, seq:%d, e:%d, q:%d\n", h->type, h->seq_num,
+ (h->seq_num & PACKET_IS_ERROR), (h->seq_num & QUICK_DATA_PACKET));
+ }
+ }
+#endif
+ /* Wether the packet was OK or not doesn't matter, go on for a new one :) */
+ full_size = 0;
+ rx_ptr = 0;
+ sum = 0;
+}
+
+/* This function handle sending replies when requested by the host.
+ * When there is an error but the host did not request a reply, store the error for future request.
+ * When a reply is effectively sent, the PACKET_NEEDS_REPLY bit is removed from the sequence number so the
+ * packet handling code will know if there is still a PING request to be answered.
+ */
+void dtplug_protocol_send_reply(struct dtplug_protocol_handle* handle,
+ struct packet* question, uint8_t error, int size, uint8_t* data)
+{
+ struct packet reply;
+ struct header* tx_info = &(reply.info);
+ int i = 0, sent = 0, len = 0;
+ uint8_t sum = 0;
+ int data_send_size = size, data_sent = 0;
+ uint8_t* data_src = data;
+ uint8_t type = question->info.type;
+
+ if (error != NO_ERROR) {
+ handle->errors_count++;
+ }
+ /* If no reply requested : we were called thus there have been an error. We should store the error and return. */
+ if (!(question->info.seq_num & PACKET_NEEDS_REPLY)) {
+ /* If we still have some room for the error, then keep track of it (if any),
+ * otherwise ... drop it, we don't have that much memory to keep track of hundreds of errors */
+ if (error != NO_ERROR) {
+ dtplug_protocol_add_error_to_list(handle, &(question->info), error);
+ }
+ return;
+ }
+
+ /* Remove the PACKET_NEEDS_REPLY bit as the reply is being built to prevent multiple replies to the
+ * same packet. (do it now to prevent the mask when building the type field of the reply) */
+ question->info.seq_num &= ~(PACKET_NEEDS_REPLY);
+
+ /* If any error stored, send "got many errors" reply and store the error, but if
+ * the host is reading the error table, no need to say there is an error table.
+ * Rather send any possible new error.
+ */
+ if (handle->num_errors_stored != 0) {
+ /* Store the new error if any */
+ if (error != NO_ERROR) {
+ dtplug_protocol_add_error_to_list(handle, &(question->info), error);
+ }
+ /* The master wants to get all errors, give them even if there is an error in the sequence number */
+ if (question->info.type == PKT_TYPE_GET_ERRORS) {
+ data_send_size = handle->num_errors_stored;
+ size = handle->num_errors_stored;
+ data_src = handle->error_storage;
+ handle->num_errors_stored = 0;
+ } else {
+ error = GOT_MANY_ERRORS;
+ data_send_size = 0;
+ size = 0;
+ }
+ }
+
+ do {
+ /* Does the data fit in the message ? */
+ if (data_send_size > PACKET_DATA_SIZE) {
+ data_send_size = PACKET_DATA_SIZE;
+ }
+
+ /* Build the reply */
+ tx_info->start = FIRST_PACKET_CHAR;
+ tx_info->type = type;
+ tx_info->seq_num = question->info.seq_num;
+
+ len = sizeof(struct header); /* At least, send header on serial link */
+ if (error) {
+ tx_info->seq_num |= PACKET_IS_ERROR;
+ tx_info->err.error_code = error;
+ if (error == GOT_MANY_ERRORS) {
+ tx_info->err.info = handle->num_errors_stored;
+ }
+ } else {
+ /* Append possible data */
+ if ((data_src != NULL) && (data_send_size != 0)) {
+ /* Can we send a quick data packet ? */
+ if (size && (size <= 2)) {
+ tx_info->seq_num |= QUICK_DATA_PACKET;
+ tx_info->quick_data[0] = data_src[0];
+ tx_info->quick_data[1] = data_src[1];
+ } else {
+ /* Copy data, compute checksum (also OK for a data_send_size of 0) */
+ sum = 0;
+ for (i = 0; i < data_send_size; i++) {
+ reply.data[i] = data_src[i];
+ sum += data_src[i]; /* Build checksum */
+ }
+ /* And update header information */
+ tx_info->data.size = data_send_size;
+ /* Will this packet be continued in the following one ? */
+ if (data_send_size < (size - data_sent)) {
+ tx_info->data.size |= BIG_DATA_PKT;
+ }
+ tx_info->data.checksum = sum;
+ /* Update length of data to send on serial link */
+ len += data_send_size;
+ }
+ }
+ }
+
+ /* Compute header checksum */
+ sum = 0;
+ tx_info->checksum = 0;
+ for (i = 0; i < sizeof(struct header); i++) {
+ sum += ((uint8_t*)tx_info)[i];
+ }
+ tx_info->checksum = ((uint8_t)(256 - sum));
+
+ /* And send the reply */
+ sent = 0;
+ while (sent < len) {
+ int ret = serial_write(handle->uart, (((char*)(&reply)) + sent), (len - sent));
+ if (ret >= 0) {
+ sent += ret;
+ } else {
+ /* Store a sending error, though it may never be sent ... */
+ dtplug_protocol_add_error_to_list(handle, &(question->info), ERROR_IN_UART_TX);
+ handle->errors_count++;
+ return;
+ }
+ /* The serial baud rate is 115200, which means 64 bytes are sent in a little less than 6 ms.
+ * When there is not enougth place in the buffer, the return value should be 64, meaning 64 byte
+ * were stored for transmission. Sleep for 6ms and try sending the next part.
+ */
+ if (sent < len) {
+ msleep(5);
+ }
+ }
+ data_sent += data_send_size;
+
+ /* Need to send more ? */
+ if (data_sent < size) {
+ /* Move data pointer */
+ data_src += data_send_size;
+ /* Update size to send. check against PACKET_DATA_SIZE is done at beginning of loop */
+ data_send_size = (size - data_sent);
+ /* Set packet type to continued data packet for following packets */
+ type = PKT_TYPE_CONTINUED_DATA;
+ }
+ } while (data_sent < size);
+}
+
+
+/******************************************************************************/
+/* User information block re-programming
+ * Make sure that data is aligned on 4 bytes boundary, and that size is a multiple of 4.
+ */
+/* FIXME : erase the last flash page instead ! */
+#if 0
+static int dtplug_protocol_user_flash_update(struct dtplug_protocol_handle* handle,
+ struct packet* question, void* data, int size)
+{
+ int ret = 0;
+ /* Erase the user flash information pages */
+ ret = iap_erase_info_page(0, 2);
+ if (ret != 0) {
+ dtplug_protocol_send_reply(handle, question, ERROR_FLASH_ERASE, 0, NULL);
+ return -1;
+ }
+ ret = iap_copy_ram_to_flash((uint32_t)get_user_info(), (uint32_t)data, size);
+ if (ret != 0) {
+ dtplug_protocol_send_reply(handle, question, ERROR_FLASH_WRITE, 0, NULL);
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+
+/******************************************************************************/
+/* Common packets handlers.
+ * Return 1 when packet has been handled, or 0 if the type is not a common one and some
+ * board or application specific code should take care of it.
+ */
+static int dtplug_protocol_common_handles(struct dtplug_protocol_handle* handle, struct packet* question)
+{
+ uint32_t tmp_val_swap = 0;
+ /* These we can always handle */
+ switch (question->info.type) {
+ case PKT_TYPE_PING:
+ question->info.seq_num |= PACKET_NEEDS_REPLY; /* Make sure the reply will be sent */
+ dtplug_protocol_send_reply(handle, question, NO_ERROR, 0, NULL); /* A ping needs no aditional data */
+ dtplug_protocol_release_old_packet(handle);
+ break;
+ case PKT_TYPE_RESET:
+ /* Software reset of the board. No way out. */
+ NVIC_SystemReset();
+ break;
+ case PKT_TYPE_SET_TIME:
+ {
+ struct time_spec new_time, time_diff;
+ uint32_t* seconds = (uint32_t*)question->data;
+ uint16_t* msec = (uint16_t*)&(question->data[4]);
+ uint8_t time_buff[6];
+ if (question->info.seq_num & QUICK_DATA_PACKET) {
+ dtplug_protocol_send_reply(handle, question, ERROR_IN_PKT_STRUCTURE, 0, NULL);
+ break;
+ }
+ if (question->info.data.size != 6) {
+ dtplug_protocol_send_reply(handle, question, ERROR_IN_DATA_VALUES, 0, NULL);
+ break;
+ }
+ new_time.seconds = byte_swap_32(*seconds);
+ new_time.msec = (uint16_t)byte_swap_16(*msec);
+ if (!(question->info.seq_num & PACKET_NEEDS_REPLY)) {
+ set_time(&new_time);
+ } else {
+ set_time_and_get_difference(&new_time, &time_diff);
+ time_to_buff_swapped(time_buff, &time_diff);
+ dtplug_protocol_send_reply(handle, question, NO_ERROR, 6, time_buff);
+ }
+ }
+ dtplug_protocol_release_old_packet(handle);
+ break;
+/* FIXME : erase the last flash page instead ! */
+#if 0
+ case PKT_TYPE_SET_USER_INFO:
+ {
+ uint8_t tmp_data[sizeof(struct user_info)] __attribute__ ((__aligned__(4))) = {};
+ uint8_t offset = question->data[0];
+ uint8_t size = question->data[1];
+ if (question->info.seq_num & QUICK_DATA_PACKET) {
+ dtplug_protocol_send_reply(handle, question, ERROR_IN_PKT_STRUCTURE, 0, NULL);
+ break;
+ }
+ /* Check that amount of data provided is OK and does not go beyond user_info structure end */
+ if ((question->info.data.size != (size + 2)) || ((offset + size) > sizeof(struct user_info))) {
+ dtplug_protocol_send_reply(handle, question, ERROR_IN_DATA_VALUES, 0, NULL);
+ break;
+ }
+ /* Copy all board data before flash erase */
+ memcpy(tmp_data, get_user_info(), sizeof(struct user_info));
+ /* Update information in the copy */
+ memcpy(&(tmp_data[offset]), &(question->data[2]), size);
+ /* Update the user flash information pages */
+ if (dtplug_protocol_user_flash_update(handle, question, tmp_data, sizeof(struct user_info)) != 0) {
+ /* Reply got sent, if return value is not 0 */
+ break;
+ }
+ }
+ /* Software reset of the board. No way out. */
+ NVIC_SystemReset();
+ break;
+#endif
+ case PKT_TYPE_GET_NUM_PACKETS:
+ question->info.seq_num |= PACKET_NEEDS_REPLY; /* Make sure the reply will be sent */
+ tmp_val_swap = byte_swap_32(handle->packet_count);
+ dtplug_protocol_send_reply(handle, question, NO_ERROR, 4, (uint8_t*)(&tmp_val_swap));
+ dtplug_protocol_release_old_packet(handle);
+ break;
+ case PKT_TYPE_GET_ERRORS:
+ question->info.seq_num |= PACKET_NEEDS_REPLY; /* Make sure the reply will be sent */
+ dtplug_protocol_send_reply(handle, question, NO_ERROR, 0, NULL); /* Error handling code will take care of filling the message */
+ dtplug_protocol_release_old_packet(handle);
+ break;
+ case PKT_TYPE_GET_NUM_ERRORS:
+ question->info.seq_num |= PACKET_NEEDS_REPLY; /* Make sure the reply will be sent */
+ tmp_val_swap = byte_swap_32(handle->errors_count);
+ dtplug_protocol_send_reply(handle, question, NO_ERROR, 4, (uint8_t*)(&tmp_val_swap));
+ dtplug_protocol_release_old_packet(handle);
+ break;
+ default:
+ /* We do not handle this type, it must be a board specific one */
+ return 0;
+ }
+ /* Packet handled */
+ return 1;
+}
+
+
+/* Get a pointer to the new packet received.
+ * Return NULL when no new packet were received since last packet was released.
+ * If a new packet is present, call the common handles first.
+ */
+struct packet* dtplug_protocol_get_next_packet_ok(struct dtplug_protocol_handle* handle)
+{
+ if (handle->packet_ok != NULL) {
+ struct packet* pkt_tmp = (struct packet*)handle->packet_ok;
+ if (dtplug_protocol_common_handles(handle, pkt_tmp) == 0) {
+ return pkt_tmp;
+ }
+ }
+ return NULL;
+}
+
--- /dev/null
+/****************************************************************************
+ * lib/stdlib.c
+ *
+ * Copyright 2014 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#include "lib/stdint.h"
+#include "lib/stddef.h"
+
+/* Simple strtoul implementation.
+ * Returns the value convertes from the given string.
+ * Does not check that the base is respected aside for the use of letters in
+ * number representation.
+ */
+uint32_t strtoul(const char* str, char** end, uint8_t base)
+{
+ uint32_t val = 0;
+ while (*str != '\0') {
+ if (*str >= '0' && *str <= '9') {
+ val = (val * base) + ((*str) - '0');
+ str++;
+ continue;
+ }
+ if (*str >= 'A' && *str <= 'F' && base > 10) {
+ val = (val * base) + ((*str) - 'A' + 10);
+ str++;
+ continue;
+ }
+ if (*str >= 'a' && *str <= 'f' && base > 10) {
+ val = (val * base) + ((*str) - 'a' + 10);
+ str++;
+ continue;
+ }
+ break;
+ }
+ if (end != NULL) {
+ *end = str;
+ }
+ return val;
+}
+
--- /dev/null
+/*
+ * linux/lib/string.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/*
+ * stupid library routines.. The optimized versions should generally be found
+ * as inline code in <asm-xx/string.h>
+ *
+ * These are buggy as well..
+ *
+ * * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de>
+ * - Added strsep() which will replace strtok() soon (because strsep() is
+ * reentrant and should be faster). Use only strsep() in new code, please.
+ */
+
+#include <lib/stddef.h>
+#include "lib/stdint.h"
+
+/**
+ * memcpy - Copy one area of memory to another
+ * @dest: Where to copy to
+ * @src: Where to copy from
+ * @count: The size of the area.
+ */
+void* memcpy(void* dest, const void* src, size_t count)
+{
+ unsigned long* dl = (unsigned long*)dest, *sl = (unsigned long*)src;
+ char *d8, *s8;
+
+ if (src == dest)
+ return dest;
+
+ /* while all data is aligned (common case), copy a word at a time */
+ if ( (((uint32_t)dest | (uint32_t)src) & (sizeof(*dl) - 1)) == 0) {
+ while (count >= sizeof(*dl)) {
+ *dl++ = *sl++;
+ count -= sizeof(*dl);
+ }
+ }
+ /* copy the rest one byte at a time */
+ d8 = (char *)dl;
+ s8 = (char *)sl;
+ while (count--) {
+ *d8++ = *s8++;
+ }
+ return dest;
+}
+
+/**
+ * memset - Fill a region of memory with the given value
+ * @s: Pointer to the start of the area.
+ * @c: The byte to fill the area with
+ * @count: The size of the area.
+ */
+void* memset(void* s, int c, size_t count)
+{
+ unsigned long* sl = (unsigned long*) s;
+ unsigned long cl = 0;
+ char* s8;
+ int i;
+
+ /* do it one word at a time (32 bits or 64 bits) while possible */
+ if ( ((uint32_t)s & (sizeof(*sl) - 1)) == 0) {
+ for (i = 0; i < sizeof(*sl); i++) {
+ cl <<= 8;
+ cl |= c & 0xff;
+ }
+ while (count >= sizeof(*sl)) {
+ *sl++ = cl;
+ count -= sizeof(*sl);
+ }
+ }
+ /* fill 8 bits at a time */
+ s8 = (char*)sl;
+ while (count--) {
+ *s8++ = c;
+ }
+ return s;
+}
+
+/**
+ * strcpy - Copy a %NUL terminated string
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ */
+char* strcpy(char* dest, const char* src)
+{
+ char* tmp = dest;
+
+ while ((*dest++ = *src++) != '\0') {
+ /* nothing */;
+ }
+ return tmp;
+}
+
+/**
+ * strncpy - Copy a length-limited, %NUL-terminated string
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ * @count: The maximum number of bytes to copy
+ *
+ * Note that unlike userspace strncpy, this does not %NUL-pad the buffer.
+ * However, the result is not %NUL-terminated if the source exceeds
+ * @count bytes.
+ */
+char* strncpy(char* dest, const char* src, size_t count)
+{
+ char* tmp = dest;
+
+ while (count-- && (*dest++ = *src++) != '\0') {
+ /* nothing */;
+ }
+ return tmp;
+}
+
+/**
+ * strcmp - Compare two strings
+ * @cs: One string
+ * @ct: Another string
+ */
+int strcmp(const char* cs, const char* ct)
+{
+ register signed char __res;
+
+ while (1) {
+ if ((__res = *cs - *ct++) != 0 || !*cs++) {
+ break;
+ }
+ }
+ return __res;
+}
+
+/**
+ * strncmp - Compare two length-limited strings
+ * @cs: One string
+ * @ct: Another string
+ * @count: The maximum number of bytes to compare
+ */
+int strncmp(const char* cs, const char* ct, size_t count)
+{
+ register signed char __res = 0;
+
+ while (count) {
+ if ((__res = *cs - *ct++) != 0 || !*cs++) {
+ break;
+ }
+ count--;
+ }
+
+ return __res;
+}
+
+/**
+ * strchr - Find the first occurrence of a character in a string
+ * @s: The string to be searched
+ * @c: The character to search for
+ */
+char* strchr(const char* s, int c)
+{
+ for(; *s != (char) c; ++s) {
+ if (*s == '\0') {
+ return NULL;
+ }
+ }
+ return (char *) s;
+}
+
+/**
+ * strlen - Find the length of a string
+ * @s: The string to be sized
+ */
+size_t strlen(const char* s)
+{
+ const char* sc;
+
+ for (sc = s; *sc != '\0'; ++sc) {
+ /* nothing */;
+ }
+ return sc - s;
+}
+
+/**
+ * strrchr - Find the last occurrence of a character in a string
+ * @s: The string to be searched
+ * @c: The character to search for
+ */
+char* strrchr(const char* s, int c)
+{
+ const char* p = s + strlen(s);
+ do {
+ if (*p == (char)c) {
+ return (char *)p;
+ }
+ } while (--p >= s);
+ return NULL;
+}
+
+/**
+ * strnlen - Find the length of a length-limited string
+ * @s: The string to be sized
+ * @count: The maximum number of bytes to search
+ */
+size_t strnlen(const char* s, size_t count)
+{
+ const char* sc;
+
+ for (sc = s; count-- && *sc != '\0'; ++sc) {
+ /* nothing */;
+ }
+ return sc - s;
+}
--- /dev/null
+/****************************************************************************
+ * lib/time.c
+ *
+ *
+ * Copyright 2013-2014 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ ****************************************************************************/
+
+#include "lib/stdint.h"
+#include "core/lpc_core.h"
+#include "core/system.h"
+#include "core/systick.h"
+#include "lib/time.h"
+#include "lib/string.h"
+
+
+/******************************************************************************/
+/* Common parts for time handling for LPC Boards with or without RTC */
+
+/* Notes on time tracking using this library :
+ *
+ * - Unless there is some known good time source used to set time (using set_time()) the time is
+ * counted from the system power ON or RTC power ON as origin.
+ *
+ * - This code relies on systick timer configured with a 1ms period (systick_timer_on(1)) for whole
+ * time on systems without a RTC oscilator and on systick timer for mili-seconds only on systems
+ * with a 32.768 KHz external oscilator for the RTC.
+ *
+ * - When used with systick only, the time tracking is far from the precision time one should
+ * obtain using the RTC with an external 32.768 KHz cristal. It uses the 12MHz internal RC
+ * Oscilator, which is at 1% accuracy.
+ * Thus the time error may be as much as 1s every 100 seconds !
+ */
+
+static volatile struct time_spec time = { 0, 0, };
+static volatile uint32_t time_lock = 0;;
+
+/* Interupt routine which keeps track of the time */
+void time_track(uint32_t ms)
+{
+ /* This lock may have us miss one ms when time is changed, but this is perfectly OK, time is
+ * being changed !
+ * Anyway, we are in interrupt context, we MUST NOT loop or sleep !
+ */
+ if (sync_lock_test_and_set(&time_lock, 1) == 1) {
+ return;
+ }
+ time.msec++;
+ if (time.msec >= 1000) {
+ time.msec = 0;
+ time.seconds++;
+ }
+ sync_lock_release(&time_lock);
+}
+
+/* Call this to set the time from a known good time source. */
+void set_time(struct time_spec* new_time)
+{
+ /* We are not in interrupt context, we can wait for the lock to be released */
+ while (sync_lock_test_and_set(&time_lock, 1) == 1) {};
+ time.seconds = new_time->seconds;
+ time.msec = new_time->msec;
+ sync_lock_release(&time_lock);
+}
+
+/* Call this to set the time from a known good time source.
+ * This function returns the time difference in the given time_spec.
+ */
+void set_time_and_get_difference(struct time_spec* new_time, struct time_spec* diff)
+{
+ struct time_spec tmp_old;
+ if (new_time == NULL) {
+ return;
+ }
+ /* We are not in interrupt context, we can wait for the lock to be released */
+ while (sync_lock_test_and_set(&time_lock, 1) == 1) {};
+ /* Save the old time */
+ tmp_old.seconds = time.seconds;
+ tmp_old.msec = time.msec;
+ /* Set the time as soon as possible */
+ time.seconds = new_time->seconds;
+ time.msec = new_time->msec;
+ sync_lock_release(&time_lock);
+
+ /* And now compute the time difference */
+ if (diff == NULL) {
+ return;
+ }
+ if (new_time->seconds == tmp_old.seconds) {
+ diff->msec = new_time->msec - tmp_old.msec;
+ } else {
+ diff->seconds = new_time->seconds - tmp_old.seconds;
+ if (new_time->seconds > tmp_old.seconds) {
+ diff->msec = (1000 - tmp_old.msec) + new_time->msec;
+ } else {
+ diff->msec = (1000 - new_time->msec) + tmp_old.msec;
+ }
+ if (diff->msec > 1000) {
+ diff->msec -= 1000;
+ } else {
+ diff->seconds -= 1;
+ }
+ }
+}
+
+
+/* Put a time struct in a buffer, swapping both fields to network endian. */
+void time_to_buff_swapped(uint8_t* buf, struct time_spec* src_time)
+{
+ struct time_spec time; /* Warning, this one will hold time in network endian ! */
+ time.seconds = byte_swap_32(src_time->seconds);
+ time.msec = (uint16_t)byte_swap_16(src_time->msec);
+ memcpy(buf, &(time.seconds), 4);
+ memcpy((buf + 4), &(time.msec), 2);
+}
+
+
+/* Get a snapshot of the time when this is called.
+ * It is unsafe to call this one when in interrupt, use get_time_in_interrupt()
+ */
+void get_time(struct time_spec* save_time)
+{
+ /* FIXME : Check that we are not in interrupt ... */
+ if (get_priority_mask() == 0) {
+ get_time_in_interrupt(save_time);
+ return;
+ }
+ /* We are not in interrupt context, we can wait for the lock to be released */
+ while (sync_lock_test_and_set(&time_lock, 1) == 1) {};
+ save_time->seconds = time.seconds;
+ save_time->msec = time.msec;
+ sync_lock_release(&time_lock);
+}
+
+/* Get a snapshot of the time when this is called
+ * It is safe to call this one in interrupt.
+ */
+void get_time_in_interrupt(struct time_spec* save_time)
+{
+ /* We are in interrupt context, we can't wait for the lock to be released */
+ save_time->seconds = time.seconds;
+ save_time->msec = time.msec;
+}
+
+/* Must be called once to register the systick callback. */
+static uint8_t time_configured = 0;
+void time_init(void)
+{
+ if (time_configured != 0) {
+ return;
+ }
+ time_configured = 1;
+ add_systick_callback(time_track, 1); /* callback, period (ms) */
+}
+
+
+/* Compute a time difference
+ * Return 0 if both times are the same, 1 if (t1 > t2), -1 if (t1 < t2)
+ */
+int get_time_diff(const struct time_spec* t1, const struct time_spec* t2, struct time_spec* diff)
+{
+ if (t1->seconds < t2->seconds) {
+ diff->seconds = (t2->seconds - t1->seconds - 1);
+ diff->msec = ((t2->msec + 1000) - t1->msec);
+ if (diff->msec >= 1000) {
+ diff->msec -= 1000;
+ diff->seconds++;
+ }
+ return -1;
+ } else if (t1->seconds == t2->seconds) {
+ diff->seconds = 0;
+ if (t1->msec < t2->msec) {
+ diff->msec = t2->msec - t1->msec;
+ return -1;
+ } else if (t1->msec == t2->msec) {
+ diff->msec = 0;
+ return 0;
+ } else {
+ diff->msec = t1->msec - t2->msec;
+ return 1;
+ }
+ } else {
+ diff->seconds = (t1->seconds - t2->seconds - 1);
+ diff->msec = ((t1->msec + 1000) - t2->msec);
+ if (diff->msec >= 1000) {
+ diff->msec -= 1000;
+ diff->seconds++;
+ }
+ return 1;
+ }
+}
+
+
--- /dev/null
+/****************************************************************************
+ * lib/uprintf.c
+ *
+ * UART printf
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#include <stdarg.h>
+#include "lib/stdint.h"
+#include "drivers/serial.h"
+#include "lib/string.h"
+#include "lib/stdio.h"
+
+
+int uprintf(int uart_num, const char* format, ...)
+{
+ char printf_buf[SERIAL_OUT_BUFF_SIZE];
+ va_list args;
+ int r;
+
+ va_start(args, format);
+ r = vsnprintf(printf_buf, SERIAL_OUT_BUFF_SIZE, format, args);
+ va_end(args);
+
+ serial_write(uart_num, printf_buf, r);
+
+ return r;
+}
+
+
--- /dev/null
+/****************************************************************************
+ * lib/utils.c
+ *
+ * Copyright 2014 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#include "lib/stdint.h"
+
+
+/***************************************************************************** */
+/* Bit twidling hacks.
+ * http://graphics.stanford.edu/~seander/bithacks.html
+ */
+
+/* Counting consecutive trailing or leading zero bits (or finding bit indices)
+ * The ARM Cortex M0 core does not have the __builtin_clz() and __builtin_ctz()
+ * instructions.
+ */
+
+
+
+/* Count leading zeroes
+ * The following function is an efficient way to implement __builtin_clz(),
+ * or at least a good compromize between memory usage and speed.
+ */
+uint8_t clz(uint32_t x)
+{
+ static const uint8_t bval_clz[] = {0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4};
+ unsigned int r = 32;
+ if (x >= 0x10000) { /* Quicker than (x & 0xFFFF0000) on a 32bit arm arch */
+ r -= 16;
+ x >>= 16;
+ }
+ if (x & 0xFF00) {
+ r -= 8;
+ x >>= 8;
+ }
+ if (x & 0xF0) {
+ r -= 4;
+ x >>= 4;
+ }
+ return r - bval_clz[x];
+}
+
+/* Count traling zeroes
+ * The following function is an efficient way to implement __builtin_ctz(),
+ * or at least a good compromize between memory usage and speed.
+ */
+uint8_t ctz(uint32_t x)
+{
+ static const uint8_t bval_ctz[] = {4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0};
+ unsigned int r = 0;
+ if (x & 0x1) {
+ /* special case for odd value (assumed to happen half of the time) */
+ return r;
+ }
+ if ((x & 0xFFFF) == 0) {
+ r += 16;
+ x >>= 16;
+ }
+ if ((x & 0xFF) == 0) {
+ r += 8;
+ x >>= 8;
+ }
+ if ((x & 0xF) == 0) {
+ r += 4;
+ x >>= 4;
+ }
+ return r + bval_ctz[(x & 0x0F)];
+}
+
+/* Count bits set
+ *
+ */
+uint8_t bits_set(uint32_t x)
+{
+ static const uint8_t bval_bsets[] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
+ uint8_t r = 0; /* Accumulator for the total bits set in x */
+
+ r = bval_bsets[x & 0xFF];
+ x >>= 8;
+ r += bval_bsets[x & 0xFF];
+ x >>= 8;
+ r += bval_bsets[x & 0xFF];
+ x >>= 8;
+ r += bval_bsets[x & 0xFF];
+
+ return r;
+}
--- /dev/null
+/****************************************************************************
+ * lib/vsprintf.c
+ *
+ * Code based on lib/vsprintf.c from linux kernel.
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#include <stdarg.h>
+#include "lib/stdint.h"
+#include "lib/string.h"
+
+#define ZEROPAD (1 << 0) /* pad with zero */
+#define SIGNED (1 << 1) /* unsigned/signed long */
+#define SIGN (1 << 2) /* show plus */
+#define SPACE (1 << 3) /* space if plus */
+#define LEFT (1 << 4) /* left justified */
+#define LOWERCASE (1 << 5) /* use lowercase in hex (must be 32 == 0x20) */
+#define SPECIAL (1 << 6) /* prefix hex with "0x", octal with "0" */
+#define HEXA (1 << 7) /* output hexa-decimal */
+
+/* In our case, anything that does not fit in 20 chars does not fit in an unsigned int. */
+#define TMP_NUM_BUF_SIZE 20
+static int convert(char* buf, char* end, uint32_t flags, uint32_t width, uint32_t num)
+{
+ static const char digits[16] = "0123456789ABCDEF";
+ char tmp[TMP_NUM_BUF_SIZE];
+ int i = 0, length = 0;
+ char sign = 0;
+
+ if (width > TMP_NUM_BUF_SIZE) {
+ width = TMP_NUM_BUF_SIZE;
+ }
+
+ /* Store sign, and convert to unsigned */
+ if (flags & SIGNED) {
+ if (width) {
+ width--;
+ }
+ if ((signed long)num < 0) {
+ sign = '-';
+ num = -(signed long)num;
+ }
+ } /* Do we need to remove 2 to width in case of "SPECIAL" flag ? */
+
+ /* Generate full string in tmp[], in reverse order */
+ if (num == 0) {
+ tmp[i++] = '0';
+ } else if (flags & HEXA) {
+ /* low_case = 0 or 0x20. ORing digits or letters with 'low_case'
+ * produces same digits or (maybe lowercased) letters */
+ uint32_t low_case = (flags & LOWERCASE) ? 0x20 : 0;
+ do {
+ tmp[i++] = (digits[num & 0x0F] | low_case);
+ num = (num >> 4);
+ } while (num);
+ } else {
+ while (num) {
+ tmp[i++] = (num % 10) + '0';
+ num = num / 10;
+ }
+ }
+
+ /* Add sign, pad if reqiered */
+ if (flags & ZEROPAD) {
+ while (i < width) {
+ tmp[i++] = '0';
+ }
+ }
+ if (sign) {
+ tmp[i++] = sign;
+ } else if (flags & SIGN) {
+ tmp[i++] = '+';
+ } else if (flags & SPACE) {
+ tmp[i++] = ' ';
+ } else if (flags & SPECIAL) {
+ tmp[i++] = 'x';
+ tmp[i++] = '0';
+ }
+ while (i < width) {
+ tmp[i++] = ' ';
+ }
+
+ /* And reverse string */
+ length = i;
+ while (i && (buf < end)) {
+ i--;
+ *(buf++) = tmp[i];
+ }
+ if (i)
+ return -i;
+
+ return length;
+}
+
+int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
+{
+ char* start = buf;
+ char* end = buf + size - 1; /* leave one char for terminating null byte */
+
+ /* Parse format string */
+ while ((buf < end) && *fmt) {
+ uint32_t flags = 0;
+ uint32_t width = 0;
+
+ if (*fmt != '%') {
+ *buf++ = *fmt++;
+ continue;
+ }
+
+ /* Do we have a conversion specifier ? */
+ fmt++;
+ if (*fmt == '%') {
+ *buf++ = *fmt++;
+ continue;
+ }
+ /* We got a conversion specifier, any modifier ? */
+ /* Note that '-' won't be handled */
+ while (1) {
+ int found = 1;
+ switch (*fmt) {
+ case '-': flags |= LEFT; break;
+ case '+': flags |= SIGN; break;
+ case ' ': flags |= SPACE; break;
+ case '#': flags |= SPECIAL; break;
+ case '0': flags |= ZEROPAD; break;
+ default: found = 0;
+ }
+ if (!found)
+ break;
+ fmt++;
+ }
+ /* Field width ? */
+ while ((*fmt >= '0') && (*fmt <= '9')) {
+ width = (width * 10) + (*fmt - '0');
+ fmt++;
+ }
+ /* We do not handle floats, floats have nothing to do with embeded systems */
+ /* Skip any precision */
+ if (*fmt == '.') {
+ do {
+ fmt++;
+ } while ((*fmt >= '0') && (*fmt <= '9'));
+ }
+ /* Qualifier ? Should we really handle length modifiers ?
+ * The while loop is here to skip these. */
+ /* Handle conversion, if supported. Note that we may spend some time in here
+ * while waiting for the buffer to get available again */
+ while (*fmt) {
+ int found = 1;
+ switch (*fmt) {
+ /* signed */
+ case 'd':
+ case 'i':
+ flags |= SIGNED;
+ buf += convert(buf, end, flags, width, (uint32_t)va_arg(args, signed int));
+ break;
+ /* unsigned */
+ case 'x':
+ flags |= LOWERCASE;
+ case 'X':
+ flags |= HEXA;
+ case 'u':
+ buf += convert(buf, end, flags, width, (uint32_t)va_arg(args, unsigned int));
+ break;
+ /* string */
+ case 's': {
+ /* Copy string to buf */
+ char* tmp = va_arg(args, char *);
+ while ((buf < end) && *tmp) {
+ *buf++ = *tmp++;
+ }
+ break;
+ }
+ /* character */
+ case 'c':
+ *buf++ = (char)va_arg(args, unsigned int);
+ break;
+ default:
+ found = 0;
+ }
+ fmt++;
+ if (found)
+ break;
+ }
+ }
+ *buf = '\0';
+ return (buf - start);
+}
+
+
+int snprintf(char* buf, size_t size, const char *format, ...)
+{
+ va_list args;
+ int r;
+
+ va_start(args, format);
+ r = vsnprintf(buf, size, format, args);
+ va_end(args);
+
+ return r;
+}
+
+