From: Nathael Pajani Date: Wed, 15 Jun 2016 16:41:56 +0000 (+0200) Subject: Initial commit Most of the code received basic testing. Most of the code comes from... X-Git-Url: http://git.techno-innov.fr/?a=commitdiff_plain;h=5cf6e6f244251ebfacd70a1b163886ec2e4f2c1d;p=soft%2Flpc82x%2Fcore Initial commit Most of the code received basic testing. Most of the code comes from the LPC1224 support code. --- 5cf6e6f244251ebfacd70a1b163886ec2e4f2c1d diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..92034fa --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# +# 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 diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/LICENCE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (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 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e7a33ed --- /dev/null +++ b/Makefile @@ -0,0 +1,81 @@ +# 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 : $@" + @$(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. + diff --git a/README b/README new file mode 100644 index 0000000..3d1b7d7 --- /dev/null +++ b/README @@ -0,0 +1,102 @@ +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 /" 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 ! + diff --git a/core/bootstrap.c b/core/bootstrap.c new file mode 100644 index 0000000..1ed9e8f --- /dev/null +++ b/core/bootstrap.c @@ -0,0 +1,173 @@ +/**************************************************************************** + * 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 + * + * 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 . + * + *****************************************************************************/ + + +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); +} + diff --git a/core/fault_handlers.c b/core/fault_handlers.c new file mode 100644 index 0000000..f49e63e --- /dev/null +++ b/core/fault_handlers.c @@ -0,0 +1,57 @@ +/**************************************************************************** + * core/fault_handlers.c + * + * + * Copyright 2012 Nathael Pajani + * + * 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 . + * + *****************************************************************************/ + +/* 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__)); +} + + + diff --git a/core/pio.c b/core/pio.c new file mode 100644 index 0000000..a1ffd11 --- /dev/null +++ b/core/pio.c @@ -0,0 +1,158 @@ +/**************************************************************************** + * core/pio.c + * + * Copyright 2016 Nathael Pajani + * + * 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 . + * + *************************************************************************** */ + +/***************************************************************************** */ +/* 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(); +} + diff --git a/core/rom_helpers.c b/core/rom_helpers.c new file mode 100644 index 0000000..dd55c39 --- /dev/null +++ b/core/rom_helpers.c @@ -0,0 +1,203 @@ +/**************************************************************************** + * 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 . + * + *************************************************************************** */ + +/* 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; +} + diff --git a/core/system.c b/core/system.c new file mode 100644 index 0000000..c52e09e --- /dev/null +++ b/core/system.c @@ -0,0 +1,311 @@ +/**************************************************************************** + * core/system.c + * + * All low-level functions for clocks configuration and switch, system + * power-up, reset, and power-down. + * + * Copyright 2012 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +/* + * 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"))); + diff --git a/core/systick.c b/core/systick.c new file mode 100644 index 0000000..3f7310e --- /dev/null +++ b/core/systick.c @@ -0,0 +1,364 @@ +/**************************************************************************** + * core/systick.c + * + * Copyright 2012 Nathael Pajani + * + * 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 . + * + *************************************************************************** */ + + +/***************************************************************************** */ +/* 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)); + } +} + diff --git a/core/watchdog.c b/core/watchdog.c new file mode 100644 index 0000000..5553fa8 --- /dev/null +++ b/core/watchdog.c @@ -0,0 +1,205 @@ +/**************************************************************************** + * core/watchdog.c + * + * Watchdog support + * + * Copyright 2012 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +/* + * 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); +} + + diff --git a/drivers/adc.c b/drivers/adc.c new file mode 100644 index 0000000..bae9d88 --- /dev/null +++ b/drivers/adc.c @@ -0,0 +1,292 @@ +/**************************************************************************** + * drivers/adc.c + * + * Copyright 2012 Nathael Pajani + * + * 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 . + * + *************************************************************************** */ + + + +/***************************************************************************** */ +/* 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); +} + diff --git a/drivers/gpio.c b/drivers/gpio.c new file mode 100644 index 0000000..0b3a8b2 --- /dev/null +++ b/drivers/gpio.c @@ -0,0 +1,184 @@ +/**************************************************************************** + * drivers/gpio.c + * + * Copyright 2012 Nathael Pajani + * + * 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 . + * + *************************************************************************** */ + +/***************************************************************************** */ +/* 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); + diff --git a/drivers/i2c.c b/drivers/i2c.c new file mode 100644 index 0000000..2178b1d --- /dev/null +++ b/drivers/i2c.c @@ -0,0 +1,550 @@ +/**************************************************************************** + * drivers/i2c.c + * + * + * Copyright 2012 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + + +/**************************************************************************** */ +/* 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])); + } + } +} + diff --git a/drivers/serial.c b/drivers/serial.c new file mode 100644 index 0000000..ecc6bbe --- /dev/null +++ b/drivers/serial.c @@ -0,0 +1,451 @@ +/**************************************************************************** + * drivers/serial.c + * + * Copyright 2012 Nathael Pajani + * + * 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 . + * + *************************************************************************** */ + + + +/***************************************************************************** */ +/* 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; +} + + + diff --git a/extdrv/tmp101_temp_sensor.c b/extdrv/tmp101_temp_sensor.c new file mode 100644 index 0000000..81af7f5 --- /dev/null +++ b/extdrv/tmp101_temp_sensor.c @@ -0,0 +1,191 @@ +/**************************************************************************** + * extdrv/tmp101_temp_sensor.c + * + * + * Copyright 2012 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#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; +} + + diff --git a/extdrv/ws2812.c b/extdrv/ws2812.c new file mode 100644 index 0000000..2f1f4f0 --- /dev/null +++ b/extdrv/ws2812.c @@ -0,0 +1,179 @@ +/**************************************************************************** + * extdrv/ws2812.c + * + * + * Copyright 2013 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +/* + * 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"))); + diff --git a/include/core/iap.h b/include/core/iap.h new file mode 100644 index 0000000..69d39c7 --- /dev/null +++ b/include/core/iap.h @@ -0,0 +1,87 @@ +/**************************************************************************** + * core/iap.h + * + * Copyright 2015 Nathael Pajani + * + * 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 . + * + *************************************************************************** */ + +#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 */ + diff --git a/include/core/lpc_core.h b/include/core/lpc_core.h new file mode 100644 index 0000000..6439a1e --- /dev/null +++ b/include/core/lpc_core.h @@ -0,0 +1,497 @@ +/**************************************************************************** + * 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 . + * + *************************************************************************** */ + +#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 */ + diff --git a/include/core/lpc_regs.h b/include/core/lpc_regs.h new file mode 100644 index 0000000..3665689 --- /dev/null +++ b/include/core/lpc_regs.h @@ -0,0 +1,229 @@ +/**************************************************************************** + * core/lpc_regs.h + * + * Cortex-M0 Core Registers definitions + * + * Copyright 2012 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ +#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 */ diff --git a/include/core/pio.h b/include/core/pio.h new file mode 100644 index 0000000..1f6f284 --- /dev/null +++ b/include/core/pio.h @@ -0,0 +1,305 @@ +/**************************************************************************** + * core/pio.h + * + * Copyright 2014 Nathael Pajani + * + * 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 . + * + ****************************************************************************/ + +#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 + + + +/* 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 */ diff --git a/include/core/system.h b/include/core/system.h new file mode 100644 index 0000000..fe75606 --- /dev/null +++ b/include/core/system.h @@ -0,0 +1,276 @@ +/**************************************************************************** + * core/system.h + * + * All low-level functions for clocks configuration and switch, system + * power-up, reset, and power-down. + * + * Copyright 2012 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#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 */ diff --git a/include/core/systick.h b/include/core/systick.h new file mode 100644 index 0000000..78b7873 --- /dev/null +++ b/include/core/systick.h @@ -0,0 +1,141 @@ +/**************************************************************************** + * core/systick.h + * + * System tick timer control + * + * Copyright 2012 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#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 */ diff --git a/include/core/watchdog.h b/include/core/watchdog.h new file mode 100644 index 0000000..6c8ce93 --- /dev/null +++ b/include/core/watchdog.h @@ -0,0 +1,140 @@ +/**************************************************************************** + * core/watchdog.h + * + * Watchdog support + * + * Copyright 2012 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +/* + * 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 */ diff --git a/include/drivers/adc.h b/include/drivers/adc.h new file mode 100644 index 0000000..7590732 --- /dev/null +++ b/include/drivers/adc.h @@ -0,0 +1,169 @@ +/**************************************************************************** + * drivers/adc.h + * + * Copyright 2012 Nathael Pajani + * + * 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 . + * + *************************************************************************** */ + +#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 */ + diff --git a/include/drivers/gpio.h b/include/drivers/gpio.h new file mode 100644 index 0000000..65cbbf3 --- /dev/null +++ b/include/drivers/gpio.h @@ -0,0 +1,175 @@ +/**************************************************************************** + * drivers/gpio.h + * + * Copyright 2012 Nathael Pajani + * + * 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 . + * + *************************************************************************** */ + +/***************************************************************************** */ +/* 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 */ diff --git a/include/drivers/i2c.h b/include/drivers/i2c.h new file mode 100644 index 0000000..b67c96d --- /dev/null +++ b/include/drivers/i2c.h @@ -0,0 +1,249 @@ +/**************************************************************************** + * drivers/i2c.h + * + * + * Copyright 2012 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +/**************************************************************************** */ +/* 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 */ diff --git a/include/drivers/serial.h b/include/drivers/serial.h new file mode 100644 index 0000000..6b0e973 --- /dev/null +++ b/include/drivers/serial.h @@ -0,0 +1,210 @@ +/**************************************************************************** + * drivers/serial.h + * + * Copyright 2012 Nathael Pajani + * + * 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 . + * + *************************************************************************** */ + +/***************************************************************************** */ +/* 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 */ diff --git a/include/extdrv/tmp101_temp_sensor.h b/include/extdrv/tmp101_temp_sensor.h new file mode 100644 index 0000000..72101d6 --- /dev/null +++ b/include/extdrv/tmp101_temp_sensor.h @@ -0,0 +1,126 @@ +/**************************************************************************** + * extdrv/tmp101_temp_sensor.h + * + * + * Copyright 2012 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#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 */ + diff --git a/include/extdrv/ws2812.h b/include/extdrv/ws2812.h new file mode 100644 index 0000000..755886e --- /dev/null +++ b/include/extdrv/ws2812.h @@ -0,0 +1,98 @@ +/**************************************************************************** + * extdrv/ws2812.h + * + * + * Copyright 2013 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +/* + * 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); + diff --git a/include/lib/errno.h b/include/lib/errno.h new file mode 100644 index 0000000..8d563eb --- /dev/null +++ b/include/lib/errno.h @@ -0,0 +1,43 @@ +/**************************************************************************** + * lib/errno.h + * + * Copyright 2016 Nathael Pajani + * + * 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 . + * + *************************************************************************** */ + +#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 */ diff --git a/include/lib/font.h b/include/lib/font.h new file mode 100644 index 0000000..3473f5e --- /dev/null +++ b/include/lib/font.h @@ -0,0 +1,38 @@ +/************************************************************************ + * lib/font.h + * + * Copyright (C) Lisa Milne 2014 + * + * 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 + * + ************************************************************************/ + +/* + * 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 */ diff --git a/include/lib/list.h b/include/lib/list.h new file mode 100644 index 0000000..1baab39 --- /dev/null +++ b/include/lib/list.h @@ -0,0 +1,262 @@ +/** + * 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 . + * + *************************************************************************** */ + + +#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 */ + diff --git a/include/lib/protocols/dtplug/defs.h b/include/lib/protocols/dtplug/defs.h new file mode 100644 index 0000000..188a5b1 --- /dev/null +++ b/include/lib/protocols/dtplug/defs.h @@ -0,0 +1,218 @@ +/* + * lib/protocols/dtplug/defs.h + * + * + * Copyright 2013-2015 Nathael Pajani + * + * 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 . + * + */ + +#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 */ diff --git a/include/lib/protocols/dtplug/slave.h b/include/lib/protocols/dtplug/slave.h new file mode 100644 index 0000000..94ee66e --- /dev/null +++ b/include/lib/protocols/dtplug/slave.h @@ -0,0 +1,87 @@ +/* + * lib/protocols/dtplug/slave.h + * + * + * Copyright 2013-2014 Nathael Pajani + * + * 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 . + * + */ + +#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 */ diff --git a/include/lib/stddef.h b/include/lib/stddef.h new file mode 100644 index 0000000..ec72897 --- /dev/null +++ b/include/lib/stddef.h @@ -0,0 +1,53 @@ +/**************************************************************************** + * lib/stddef.h + * + * Copyright 2014 Nathael Pajani + * + 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 . + * + *************************************************************************** */ + +#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 */ diff --git a/include/lib/stdint.h b/include/lib/stdint.h new file mode 100644 index 0000000..3b5318d --- /dev/null +++ b/include/lib/stdint.h @@ -0,0 +1,107 @@ +/**************************************************************************** + * lib/"lib/stdint.h" + * + * Copyright 2016 Nathael Pajani + * + 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 . + * + *************************************************************************** */ + +#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 */ diff --git a/include/lib/stdio.h b/include/lib/stdio.h new file mode 100644 index 0000000..a3547f7 --- /dev/null +++ b/include/lib/stdio.h @@ -0,0 +1,39 @@ +/**************************************************************************** + * lib/stdio.h + * + * Copyright 2012 Nathael Pajani + * + * 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 . + * + *************************************************************************** */ + +#ifndef LIB_STDIO_H +#define LIB_STDIO_H + +#include +#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 */ diff --git a/include/lib/stdlib.h b/include/lib/stdlib.h new file mode 100644 index 0000000..9ae275d --- /dev/null +++ b/include/lib/stdlib.h @@ -0,0 +1,34 @@ +/**************************************************************************** + * lib/stdlib.h + * + * Copyright 2014 Nathael Pajani + * + * 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 . + * + *************************************************************************** */ + +#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 */ + diff --git a/include/lib/string.h b/include/lib/string.h new file mode 100644 index 0000000..5866b49 --- /dev/null +++ b/include/lib/string.h @@ -0,0 +1,104 @@ +/* + * 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 + * + * These are buggy as well.. + * + * * Fri Jun 25 1999, Ingo Oeser + * - 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 +#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 */ diff --git a/include/lib/time.h b/include/lib/time.h new file mode 100644 index 0000000..be9bbc7 --- /dev/null +++ b/include/lib/time.h @@ -0,0 +1,95 @@ +/**************************************************************************** + * lib/time.h + * + * + * + * Copyright 2013-2014 Nathael Pajani + * + * + * 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 . + * + ****************************************************************************/ + +#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 */ + diff --git a/include/lib/utils.h b/include/lib/utils.h new file mode 100644 index 0000000..838e24d --- /dev/null +++ b/include/lib/utils.h @@ -0,0 +1,52 @@ +/**************************************************************************** + * lib/utils.h + * + * Copyright 2014 Nathael Pajani + * + * 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 . + * + *************************************************************************** */ + +#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 */ + diff --git a/lib/font.c b/lib/font.c new file mode 100644 index 0000000..b98c544 --- /dev/null +++ b/lib/font.c @@ -0,0 +1,132 @@ +/************************************************************************ + * lib/font.c + * + * Copyright (C) Lisa Milne 2014 + * + * 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 + * + ************************************************************************/ + +/* + * 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, /* ~ */ +}; + diff --git a/lib/protocols/dtplug/slave.c b/lib/protocols/dtplug/slave.c new file mode 100644 index 0000000..c6b5714 --- /dev/null +++ b/lib/protocols/dtplug/slave.c @@ -0,0 +1,488 @@ +/**************************************************************************** + * lib/protocol/dtplug/slave.c + * + * + * Copyright 2013-2015 Nathael Pajani + * + * + * 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 . + * + ****************************************************************************/ + +#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; +} + diff --git a/lib/stdlib.c b/lib/stdlib.c new file mode 100644 index 0000000..c37dd56 --- /dev/null +++ b/lib/stdlib.c @@ -0,0 +1,55 @@ +/**************************************************************************** + * lib/stdlib.c + * + * Copyright 2014 Nathael Pajani + * + * 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 . + * + *************************************************************************** */ + +#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; +} + diff --git a/lib/string.c b/lib/string.c new file mode 100644 index 0000000..834e514 --- /dev/null +++ b/lib/string.c @@ -0,0 +1,213 @@ +/* + * linux/lib/string.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* + * stupid library routines.. The optimized versions should generally be found + * as inline code in + * + * These are buggy as well.. + * + * * Fri Jun 25 1999, Ingo Oeser + * - Added strsep() which will replace strtok() soon (because strsep() is + * reentrant and should be faster). Use only strsep() in new code, please. + */ + +#include +#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; +} diff --git a/lib/time.c b/lib/time.c new file mode 100644 index 0000000..deb4bad --- /dev/null +++ b/lib/time.c @@ -0,0 +1,207 @@ +/**************************************************************************** + * lib/time.c + * + * + * Copyright 2013-2014 Nathael Pajani + * + * + * 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 . + * + ****************************************************************************/ + +#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; + } +} + + diff --git a/lib/uprintf.c b/lib/uprintf.c new file mode 100644 index 0000000..064bcfb --- /dev/null +++ b/lib/uprintf.c @@ -0,0 +1,45 @@ +/**************************************************************************** + * lib/uprintf.c + * + * UART printf + * + * Copyright 2012 Nathael Pajani + * + * 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 . + * + *************************************************************************** */ + +#include +#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; +} + + diff --git a/lib/utils.c b/lib/utils.c new file mode 100644 index 0000000..c6899eb --- /dev/null +++ b/lib/utils.c @@ -0,0 +1,103 @@ +/**************************************************************************** + * lib/utils.c + * + * Copyright 2014 Nathael Pajani + * + * 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 . + * + *************************************************************************** */ + +#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; +} diff --git a/lib/vsprintf.c b/lib/vsprintf.c new file mode 100644 index 0000000..138f681 --- /dev/null +++ b/lib/vsprintf.c @@ -0,0 +1,218 @@ +/**************************************************************************** + * lib/vsprintf.c + * + * Code based on lib/vsprintf.c from linux kernel. + * + * Copyright 2012 Nathael Pajani + * + * 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 . + * + *************************************************************************** */ + +#include +#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; +} + +