├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── rebar.config ├── rebar.lock └── src ├── grisp_emulation.app.src ├── grisp_emulation.erl ├── grisp_emulation_device.erl ├── grisp_emulation_grisp2_gpio.erl ├── grisp_emulation_grisp2_i2c.erl ├── grisp_emulation_grisp2_spi.erl ├── grisp_emulation_pmod_acl2.erl ├── grisp_emulation_pmod_acl2.hrl ├── grisp_emulation_pmod_gyro.erl ├── grisp_emulation_pmod_gyro.hrl ├── grisp_emulation_pmod_maxsonar.erl ├── grisp_emulation_pmod_nav.erl ├── grisp_emulation_pmod_nav.hrl └── grisp_emulation_sup.erl /.gitignore: -------------------------------------------------------------------------------- 1 | .rebar3 2 | _* 3 | .eunit 4 | *.o 5 | *.beam 6 | *.plt 7 | *.swp 8 | *.swo 9 | .erlang.cookie 10 | ebin 11 | log 12 | erl_crash.dump 13 | .rebar 14 | logs 15 | _build 16 | .idea 17 | *.iml 18 | rebar3.crashdump 19 | .tool-versions 20 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to 7 | [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 8 | 9 | ## [Unreleased] 10 | 11 | ## [0.2.2] - 2024-06-29 12 | 13 | ### Fixed 14 | 15 | - Fix race condition in EEPROM emulation 16 | 17 | ## [0.2.1] - 2024-06-29 18 | 19 | ### Added 20 | 21 | - Add EEPROM emulation 22 | 23 | ### Fixed 24 | 25 | - Fix GPIO emulation bug 26 | 27 | ## [0.2.0] - 2023-03-20 28 | 29 | ### Added 30 | 31 | - Add GRISP 2 support 32 | 33 | ### Changed 34 | 35 | - New emulation's design and refactoring [grisp_emulation/#2](https://github.com/grisp/grisp_emulation/pull/2) 36 | 37 | ## [0.1.2] - 2020-07-02 38 | 39 | ### Fixed 40 | 41 | - Fixed emulation of PmodGYRO to match the latest implementation in the GRiSP 42 | runtime ([grisp/\#76]) 43 | 44 | ## [0.1.1] - 2020-03-03 45 | 46 | ### Fixed 47 | 48 | - Add missing indices to the emulation GPIO driver ([\#1]) 49 | 50 | ## [0.1.0] - 2019-03-21 51 | 52 | ### Added 53 | 54 | - Initial release 55 | 56 | [Unreleased]: https://github.com/grisp/grisp_emulation/compare/0.2.2...HEAD 57 | [0.2.2]: https://github.com/grisp/grisp_emulation/compare/0.2.1...0.2.2 58 | [0.2.1]: https://github.com/grisp/grisp_emulation/compare/0.2.0...0.2.1 59 | [0.2.0]: https://github.com/grisp/grisp_emulation/compare/0.1.2...0.2.0 60 | [0.1.2]: https://github.com/grisp/grisp_emulation/compare/v0.1.1...0.1.2 61 | [0.1.1]: https://github.com/grisp/grisp_emulation/compare/v0.1.0...v0.1.1 62 | [0.1.0]: https://github.com/grisp/grisp_emulation/releases/v0.1.0 63 | 64 | [grisp/\#76]: https://github.com/grisp/grisp/issues/76 65 | [\#1]: https://github.com/grisp/grisp_emulation/pull/1 66 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | Copyright 2019, Theuns Botha . 179 | 180 | Licensed under the Apache License, Version 2.0 (the "License"); 181 | you may not use this file except in compliance with the License. 182 | You may obtain a copy of the License at 183 | 184 | http://www.apache.org/licenses/LICENSE-2.0 185 | 186 | Unless required by applicable law or agreed to in writing, software 187 | distributed under the License is distributed on an "AS IS" BASIS, 188 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 189 | See the License for the specific language governing permissions and 190 | limitations under the License. 191 | 192 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # grisp_emulation 2 | 3 | This is a hardware emulation layer for the 4 | [GRiSP runtime](https://github.com/grisp/grisp). With this, you can replace the 5 | GRiSP specific low-level drivers with a native Erlang emulation. This allows you 6 | to run the GRiSP runtime on a normal development computer instead of on the 7 | board. 8 | 9 | The emulation layer comes with a few different emulated Pmod drivers: 10 | 11 | * PmodACL2 12 | * PmodGYRO 13 | * PmodNAV 14 | * MaxSonar 15 | 16 | The emulated drivers are in various states of development, ranging from just 17 | barely starting to a semi-full emulated state of hardware components. 18 | Contributions adding new drivers are welcome! 19 | 20 | It also comes with an emulation structure for changing the low-level drivers: 21 | 22 | * SPI 23 | * GPIO 24 | * I2C 25 | 26 | This is what the emulated drivers above hook into to fake actual hardware. 27 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [debug_info]}. 2 | {deps, []}. -------------------------------------------------------------------------------- /rebar.lock: -------------------------------------------------------------------------------- 1 | []. 2 | -------------------------------------------------------------------------------- /src/grisp_emulation.app.src: -------------------------------------------------------------------------------- 1 | {application, grisp_emulation, [ 2 | {description, "Hardware emulation layer for the GRiSP runtime"}, 3 | {vsn, "0.2.2"}, 4 | {registered, []}, 5 | {mod, {grisp_emulation, []}}, 6 | {applications, [ 7 | kernel, 8 | stdlib 9 | ]}, 10 | {env,[]}, 11 | {modules, []}, 12 | 13 | {licenses, ["Apache 2.0"]}, 14 | {links, [ 15 | {"Homepage", "https://www.grisp.org"}, 16 | {"GitHub", "https://github.com/grisp/grisp_emulation"} 17 | ]} 18 | ]}. 19 | -------------------------------------------------------------------------------- /src/grisp_emulation.erl: -------------------------------------------------------------------------------- 1 | -module(grisp_emulation). 2 | 3 | -behaviour(application). 4 | 5 | % Callbacks 6 | -export([start/2]). 7 | -export([stop/1]). 8 | % API 9 | -export([call/4]). 10 | 11 | %--- Callbacks ----------------------------------------------------------------- 12 | 13 | start(_StartType, _StartArgs) -> grisp_emulation_sup:start_link(). 14 | 15 | stop(_State) -> ok. 16 | 17 | %--- API ----------------------------------------------------------------------- 18 | 19 | call(Platform, Module, on_load, []) -> 20 | Impl = implementation(Platform, Module), 21 | try 22 | Impl:on_load() 23 | catch 24 | error:undef:ST -> 25 | case ST of 26 | [{Impl, on_load, [], _}|_] -> ok; 27 | _Else -> erlang:raise(error, undef, ST) 28 | end 29 | end; 30 | call(Platform, grisp_hw, hw_platform_nif, []) -> 31 | Platform; 32 | call(Platform, port, open, [Module, Owner, {spawn_driver, Name}, Settings]) -> 33 | Emu = list_to_atom("grisp_emulation_" ++ atom_to_list(Module)), 34 | grisp_emulation_device:call(Emu, open, [Platform, Owner, Name, Settings]); 35 | call(Platform, port, command, [Module, Owner, Port, Command]) -> 36 | Emu = list_to_atom("grisp_emulation_" ++ atom_to_list(Module)), 37 | grisp_emulation_device:call(Emu, command, [Platform, Owner, Port, Command]); 38 | call(Platform, Module, Function, Args) -> 39 | apply(implementation(Platform, Module), Function, Args). 40 | 41 | %--- Internal ------------------------------------------------------------------ 42 | 43 | implementation(Platform, Module) -> 44 | <<"grisp_", Driver/binary>> = atom_to_binary(Module), 45 | binary_to_atom(<< 46 | "grisp_emulation_", 47 | (atom_to_binary(Platform))/binary, 48 | "_", 49 | Driver/binary 50 | >>). 51 | -------------------------------------------------------------------------------- /src/grisp_emulation_device.erl: -------------------------------------------------------------------------------- 1 | -module(grisp_emulation_device). 2 | 3 | -behavior(gen_server). 4 | 5 | % API 6 | -export([start_link/0]). 7 | -export([call/3]). 8 | -export([message/2]). 9 | -export([broadcast/1]). 10 | 11 | % Callbacks 12 | -export([init/1]). 13 | -export([handle_call/3]). 14 | -export([handle_cast/2]). 15 | -export([handle_info/2]). 16 | -export([code_change/3]). 17 | -export([terminate/2]). 18 | 19 | %--- API ----------------------------------------------------------------------- 20 | 21 | start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, undefined, []). 22 | 23 | call(Emu, FunName, Args) -> 24 | gen_server:call(?MODULE, {call, Emu, FunName, Args}). 25 | 26 | message(Slot, Message) -> 27 | gen_server:call(?MODULE, {message, Slot, Message}). 28 | 29 | broadcast(Message) -> 30 | gen_server:call(?MODULE, {broadcast, Message}). 31 | 32 | %--- Callbacks ----------------------------------------------------------------- 33 | 34 | init(undefined) -> 35 | Devices = application:get_env(grisp, devices, []), 36 | {ok, [init_emulator(Device) || Device <- Devices]}. 37 | 38 | handle_call({call, Emu, FunName, Args}, _From, State) when is_atom(Emu) -> 39 | {value, {Slot, {Emu, SubState}}} 40 | = lists:search(fun({_, {Mod, _}}) -> Mod =:= Emu end, State), 41 | {Result, NewSubState} = apply(Emu, FunName, [SubState | Args]), 42 | {reply, Result, lists:keyreplace(Slot, 1, State, {Slot, {Emu, NewSubState}})}; 43 | handle_call({message, Slot, Message}, _From, State) when is_atom(Slot) -> 44 | {Emu, EmuState} = proplists:get_value(Slot, State), 45 | {Data, NewEmuState} = Emu:message(EmuState, Message), 46 | {reply, Data, lists:keyreplace(Slot, 1, State, {Slot, {Emu, NewEmuState}})}; 47 | handle_call({broadcast, Message}, _From, State) -> 48 | NewState = [ 49 | {Slot, {Emu, Emu:broadcast(EmuState, Message)}} 50 | || {Slot, {Emu, EmuState}} <- State 51 | ], 52 | {reply, ok, NewState}. 53 | 54 | handle_cast(Request, _State) -> error({unknown_cast, Request}). 55 | 56 | handle_info(Info, _State) -> error({unknown_info, Info}). 57 | 58 | code_change(_OldVsn, State, _Extra) -> {ok, State}. 59 | 60 | terminate(_Reason, _State) -> ok. 61 | 62 | %--- Internal ------------------------------------------------------------------ 63 | 64 | init_emulator({Slot, Driver}) -> 65 | init_emulator({Slot, Driver, #{}}); 66 | init_emulator({Slot, Driver, _Opts}) -> 67 | Emu = list_to_atom("grisp_emulation_" ++ atom_to_list(Driver)), 68 | {Slot, {Emu, Emu:init()}}. 69 | -------------------------------------------------------------------------------- /src/grisp_emulation_grisp2_gpio.erl: -------------------------------------------------------------------------------- 1 | -module(grisp_emulation_grisp2_gpio). 2 | 3 | % API 4 | -export([on_load/0]). 5 | -export([gpio_open_nif/2]). 6 | -export([gpio_get_nif/1]). 7 | -export([gpio_set_nif/2]). 8 | 9 | -record(pin, { 10 | key, 11 | mode, 12 | value 13 | }). 14 | 15 | %--- API ----------------------------------------------------------------------- 16 | 17 | on_load() -> 18 | spawn(fun() -> 19 | ets:new(?MODULE, [named_table, public, {keypos, 2}]), 20 | register(?MODULE, self()), 21 | receive stop -> ok end 22 | end), 23 | ok. 24 | 25 | gpio_open_nif(#{path := Path0, index := Index}, Mode) -> 26 | Path = denull(Path0), 27 | Pin = {Path, Index}, 28 | ets:insert_new(?MODULE, #pin{ 29 | key = Pin, 30 | mode = mode(Mode), 31 | value = value(Mode) 32 | }), 33 | Pin. 34 | 35 | gpio_get_nif(Pin) -> ets:lookup_element(?MODULE, Pin, 4). 36 | 37 | gpio_set_nif(Pin, Value) -> ets:update_element(?MODULE, Pin, {4, Value}). 38 | 39 | %--- Internal ------------------------------------------------------------------ 40 | 41 | denull(Bin) -> 42 | Size = byte_size(Bin) - 1, 43 | case Bin of 44 | <> -> Actual; 45 | Else -> Else 46 | end. 47 | 48 | mode({output, _Value}) -> output; 49 | mode(Mode) -> Mode. 50 | 51 | value(input) -> 0; 52 | value({output, Value}) -> Value. 53 | -------------------------------------------------------------------------------- /src/grisp_emulation_grisp2_i2c.erl: -------------------------------------------------------------------------------- 1 | -module(grisp_emulation_grisp2_i2c). 2 | 3 | % API 4 | -export([i2c_open_nif/1]). 5 | -export([i2c_transfer_nif/2]). 6 | 7 | %--- API ----------------------------------------------------------------------- 8 | 9 | i2c_open_nif([Bus, 0]) -> 10 | eeprom_spawn(som), 11 | eeprom_spawn(board), 12 | Bus. 13 | 14 | i2c_transfer_nif(Bus, Messages) -> 15 | try 16 | transfer(Bus, Messages) 17 | catch 18 | not_emulated -> {error, not_emulated}; 19 | device_missing -> {error, {ioctl_failed, "Device not found"}} 20 | end. 21 | 22 | %--- Internal ------------------------------------------------------------------ 23 | 24 | binary_pad(Bin, Size) when is_binary(Bin), is_integer(Size), Size >= 0 -> 25 | BinSize = byte_size(Bin), 26 | if 27 | BinSize >= Size -> Bin; 28 | true -> 29 | PaddingSize = Size - BinSize, 30 | Padding = <<0:(8*PaddingSize)>>, 31 | <> 32 | end. 33 | 34 | eeprom_procname(Tag) -> 35 | list_to_atom("emulated_eeprom_" ++ atom_to_list(Tag)). 36 | 37 | eeprom_spawn(Tag) -> 38 | Self = self(), 39 | Ref = make_ref(), 40 | ProcName = eeprom_procname(Tag), 41 | case erlang:whereis(ProcName) of 42 | Pid when is_pid(Pid) -> Pid; 43 | undefined -> 44 | Pid = erlang:spawn_link(fun() -> 45 | eeprom_proc_init(Self, Ref, ProcName, Tag) 46 | end), 47 | receive {started, Ref} -> Pid end 48 | end. 49 | 50 | eeprom_proc_init(Owner, Ref, ProcName, Tag) -> 51 | Filename = io_lib:format("eeprom_~s.dat", [Tag]), 52 | {ok, File} = file:open(Filename, [read, write, raw, binary]), 53 | process_flag(trap_exit, true), 54 | erlang:register(ProcName, self()), 55 | Owner ! {started, Ref}, 56 | eeprom_proc_loop(File). 57 | 58 | eeprom_proc_loop(File) -> 59 | receive 60 | {read, From, Addr, Size} -> 61 | Data = case file:pread(File, Addr, Size) of 62 | eof -> <<0:(8*Size)>>; 63 | {ok, D} -> binary_pad(D, Size) 64 | end, 65 | From ! {read_result, Data}, 66 | eeprom_proc_loop(File); 67 | {write, From, Addr, Data} -> 68 | ok = file:pwrite(File, Addr, Data), 69 | From ! {write_result, ok}, 70 | eeprom_proc_loop(File); 71 | {'EXIT', _From, _Reason} -> 72 | file:close(File) 73 | end. 74 | 75 | eeprom_read(Tag, Addr, Size) -> 76 | Procname = eeprom_procname(Tag), 77 | Procname ! {read, self(), Addr, Size}, 78 | receive {read_result, Data} -> Data end. 79 | 80 | eeprom_write(Tag, Addr, Data) -> 81 | Procname = eeprom_procname(Tag), 82 | Procname ! {write, self(), Addr, Data}, 83 | receive {write_result, Result} -> Result end. 84 | 85 | eeprom_bin_to_addr(<<>>) -> undefined; 86 | eeprom_bin_to_addr(<>) -> Addr; 87 | eeprom_bin_to_addr(<>) -> Addr. 88 | 89 | transfer(Bus, Messages) -> 90 | State = #{ 91 | eeprom_som_addr => undefined, 92 | eeprom_board_addr => undefined 93 | }, 94 | transfer(State, Bus, Messages, []). 95 | 96 | transfer(_State, _Bus, [], Acc) -> 97 | lists:reverse(Acc); 98 | transfer(State, Bus, [Message | Rest], Acc) -> 99 | {Result, State2} = transfer(State, Bus, Message), 100 | transfer(State2, Bus, Rest, [Result | Acc]). 101 | 102 | %% SOM EEPROM I2C EMULATION 103 | transfer(State, <<"/dev/i2c-0">>, {write, 16#52, 0, AddrBin}) -> 104 | {ok, State#{eeprom_som_addr := eeprom_bin_to_addr(AddrBin)}}; 105 | transfer(State = #{eeprom_som_addr := Addr}, 106 | <<"/dev/i2c-0">>, {write, 16#52, 16#4000, Data}) 107 | when Addr =/= undefined -> 108 | Result = eeprom_write(som, Addr, Data), 109 | {Result, State#{eeprom_som_addr := undefined}}; 110 | transfer(State = #{eeprom_som_addr := Addr}, 111 | <<"/dev/i2c-0">>, {read, 16#52, 1, Size}) 112 | when Addr =/= undefined -> 113 | Result = eeprom_read(som, Addr, Size), 114 | {Result, State#{eeprom_som_addr := undefined}}; 115 | %% BOARD EEPROM I2C EMULATION 116 | transfer(State, <<"/dev/i2c-0">>, {write, 16#57, 0, AddrBin}) -> 117 | {ok, State#{eeprom_board_addr := eeprom_bin_to_addr(AddrBin)}}; 118 | transfer(State = #{eeprom_board_addr := Addr}, 119 | <<"/dev/i2c-0">>, {write, 16#57, 16#4000, Data}) 120 | when Addr =/= undefined -> 121 | Result = eeprom_write(board, Addr, Data), 122 | {Result, State#{eeprom_board_addr := undefined}}; 123 | transfer(State = #{eeprom_board_addr := Addr}, 124 | <<"/dev/i2c-0">>, {read, 16#57, 1, Size}) 125 | when Addr =/= undefined -> 126 | Result = eeprom_read(board, Addr, Size), 127 | {Result, State#{eeprom_board_addr := undefined}}; 128 | %% BACKWARD COMPATIBLE DS2482 EMULATION 129 | transfer(State, <<"/dev/i2c-0">>, {write, 16#18, 0, <<>>}) -> 130 | {ok, State}; 131 | transfer(_State, <<"/dev/i2c-0">>, _) -> 132 | throw(device_missing); 133 | transfer(_State, _, _) -> 134 | throw(not_emulated). 135 | -------------------------------------------------------------------------------- /src/grisp_emulation_grisp2_spi.erl: -------------------------------------------------------------------------------- 1 | -module(grisp_emulation_grisp2_spi). 2 | 3 | % API 4 | -export([open_nif/0]). 5 | -export([ioctl_nif/4]). 6 | 7 | %--- Macros -------------------------------------------------------------------- 8 | 9 | -define(CPOL_HIGH, 1). 10 | -define(CPHA_TRAILING, 2). 11 | -define(CS_DISABLE, 4). 12 | 13 | %--- API ----------------------------------------------------------------------- 14 | 15 | open_nif() -> 16 | maps:from_list([{PinIndex, open(PinIndex)} || PinIndex <- lists:seq(0, 3)]). 17 | 18 | ioctl_nif(State, CS, Mode, Msg) -> 19 | chip_select(State, CS, fun() -> 20 | grisp_emulation_device:message(slot(CS), {spi, CS, mode(Mode), Msg}) 21 | end). 22 | 23 | %--- Internal ------------------------------------------------------------------ 24 | 25 | % FIXME: We probably don't handle custom pins like {gpio, Pin} here! 26 | slot(0) -> spi1; 27 | slot(1) -> spi2; 28 | slot(2) -> spi2; 29 | slot(3) -> spi2. 30 | 31 | mode(0) -> #{clock => {low, leading}}; 32 | mode(?CPHA_TRAILING) -> #{clock => {low, trailing}}; 33 | mode(?CPOL_HIGH) -> #{clock => {high, leading}}; 34 | mode(?CPOL_HIGH bor ?CPHA_TRAILING) -> #{clock => {high, trailing}}. 35 | 36 | chip_select(State, CS, Fun) -> 37 | Pin = maps:get(CS, State), 38 | set(Pin, 0), 39 | try 40 | Fun() 41 | catch 42 | Class:Reason:Stacktrace -> 43 | set(Pin, 1), 44 | erlang:raise(Class, Reason, Stacktrace) 45 | end. 46 | 47 | open(PinIndex) -> 48 | grisp_emulation_grisp2_gpio:gpio_open_nif(pin(PinIndex), {output, 1}). 49 | 50 | set(Pin, Value) -> 51 | grisp_emulation_grisp2_gpio:gpio_set_nif(Pin, Value). 52 | 53 | pin(0) -> 54 | #{path => <<"/pmod-spi\0">>, property => <<"grisp,gpios\0">>, index => 8}; 55 | pin(1) -> 56 | #{path => <<"/pmod-spi\0">>, property => <<"grisp,gpios\0">>, index => 0}; 57 | pin(2) -> 58 | #{path => <<"/pmod-spi\0">>, property => <<"grisp,gpios\0">>, index => 6}; 59 | pin(3) -> 60 | #{path => <<"/pmod-spi\0">>, property => <<"grisp,gpios\0">>, index => 7}. 61 | -------------------------------------------------------------------------------- /src/grisp_emulation_pmod_acl2.erl: -------------------------------------------------------------------------------- 1 | -module(grisp_emulation_pmod_acl2). 2 | 3 | -include("grisp_emulation_pmod_acl2.hrl"). 4 | 5 | % Callbacks 6 | -export([init/0]). 7 | -export([message/2]). 8 | -export([broadcast/2]). 9 | 10 | -define(SPI_MODE, #{cpol := low, cpha := leading}). 11 | 12 | %--- Callbacks ----------------------------------------------------------------- 13 | 14 | init() -> default(). 15 | 16 | message(State, {spi, ?SPI_MODE, <>}) 17 | when Reg >= ?SOFT_RESET andalso Reg =< ?SELF_TEST -> 18 | NewState = grisp_bitmap:set_bits(State, Reg*8, <>), 19 | {<<0, 0, 0>>, NewState}; 20 | message(State, {spi, ?SPI_MODE, <>}) -> 21 | NewState = shake(State), 22 | Result = grisp_bitmap:get_bits(NewState, Reg*8, bit_size(RespBytes)), 23 | {<<0, 0, Result/binary>>, NewState}; 24 | message(State, {spi, ?SPI_MODE, _Command}) -> 25 | {<<0, 0, 0>>, State}. 26 | 27 | broadcast(State, _Message) -> 28 | State. 29 | 30 | %--- Internal ------------------------------------------------------------------ 31 | 32 | shake(State) -> 33 | case grisp_bitmap:get_bits(State, ?POWER_CTL*8, 8) of 34 | <<_:6, ?MEASUREMENT_MODE:2>> -> 35 | lists:foldl(fun({ShortReg, LongReg}, S) -> 36 | shake_axis(S, ShortReg, LongReg) 37 | end, State, [ 38 | {?XDATA, ?XDATA_L}, 39 | {?YDATA, ?YDATA_L}, 40 | {?ZDATA, ?ZDATA_L} 41 | ]); 42 | _ -> 43 | State 44 | end. 45 | 46 | shake_axis(State, ShortReg, LongReg) -> 47 | <> = Long = axis_data_12bit(), 48 | Short = grisp_bitmap:get_bits(<>, 4, 8), 49 | NewState = grisp_bitmap:set_bits(State, ShortReg*8, Short), 50 | grisp_bitmap:set_bits(NewState, LongReg*8, Long). 51 | 52 | axis_data_12bit() -> 53 | <> = crypto:strong_rand_bytes(2), 54 | <>. 55 | 56 | default() -> 57 | <<% Reset Reg Name RW 58 | 16#AD, % 16#00 DEVID_ID R 59 | 16#1D, % 16#01 DEVID_MST R 60 | 16#F2, % 16#02 PARTID R 61 | 16#01, % 16#03 REVID R 62 | 16#00, % 16#04 63 | 16#00, % 16#05 64 | 16#00, % 16#06 65 | 16#00, % 16#07 66 | 16#00, % 16#08 XDATA R 67 | 16#00, % 16#09 YDATA R 68 | 16#00, % 16#0A ZDATA R 69 | 16#40, % 16#0B STATUS R 70 | 16#00, % 16#0C FIFO_ENTRIES_L R 71 | 16#00, % 16#0D FIFO_ENTRIES_H R 72 | 16#00, % 16#0E XDATA_L R 73 | 16#00, % 16#0F XDATA_H R 74 | 16#00, % 16#10 YDATA_L R 75 | 16#00, % 16#11 YDATA_H R 76 | 16#00, % 16#12 ZDATA_L R 77 | 16#00, % 16#13 ZDATA_H R 78 | 16#00, % 16#14 TEMP_L R 79 | 16#00, % 16#15 TEMP_H R 80 | 16#00, % 16#16 81 | 16#00, % 16#17 82 | 16#00, % 16#18 83 | 16#00, % 16#19 84 | 16#00, % 16#1A 85 | 16#00, % 16#1B 86 | 16#00, % 16#1C 87 | 16#00, % 16#1F SOFT_RESET W 88 | 16#00, % 16#1D 89 | 16#00, % 16#1E 90 | 16#00, % 16#20 THRESH_ACT_L RW 91 | 16#00, % 16#21 THRESH_ACT_H RW 92 | 16#00, % 16#22 TIME_ACT RW 93 | 16#00, % 16#23 THRESH_INACT_L RW 94 | 16#00, % 16#24 THRESH_INACT_H RW 95 | 16#00, % 16#25 TIME_INACT_L RW 96 | 16#00, % 16#26 TIME_INACT_H RW 97 | 16#00, % 16#27 ACT_INACT_CTL RW 98 | 16#00, % 16#28 FIFO_CONTROL RW 99 | 16#80, % 16#29 FIFO_SAMPLES RW 100 | 16#00, % 16#2A INTMAP1 RW 101 | 16#00, % 16#2B INTMAP2 RW 102 | 16#13, % 16#2C FILTER_CTL RW 103 | 16#00, % 16#2D POWER_CTL RW 104 | 16#00 % 16#2E SELF_TEST RW 105 | >>. 106 | -------------------------------------------------------------------------------- /src/grisp_emulation_pmod_acl2.hrl: -------------------------------------------------------------------------------- 1 | %--- Commands ------------------------------------------------------------------ 2 | 3 | -define(WRITE_REGISTER, 16#0A). 4 | -define(READ_REGISTER, 16#0B). 5 | -define(READ_FIFO, 16#0D). 6 | 7 | %--- Registers ----------------------------------------------------------------- 8 | 9 | -define(DEVID_AD, 16#00). 10 | -define(DEVID_MST, 16#01). 11 | -define(PARTID, 16#02). 12 | 13 | -define(XDATA, 16#08). 14 | -define(YDATA, 16#09). 15 | -define(ZDATA, 16#0A). 16 | 17 | -define(XDATA_L, 16#0E). 18 | -define(XDATA_H, 16#0F). 19 | -define(YDATA_L, 16#10). 20 | -define(YDATA_H, 16#11). 21 | -define(ZDATA_L, 16#12). 22 | -define(ZDATA_H, 16#13). 23 | 24 | -define(SOFT_RESET, 16#1F). 25 | -define(POWER_CTL, 16#2D). 26 | -define(SELF_TEST, 16#2E). 27 | 28 | %--- Bit Descriptions ---------------------------------------------------------- 29 | 30 | % POWER_CTL 31 | -define(STANDBY, 2#00). 32 | -define(MEASUREMENT_MODE, 2#10). 33 | 34 | % DEVID_AD 35 | -define(AD_DEVID, 16#AD). 36 | 37 | % DEVID_MST 38 | -define(AD_MEMS_DEVID, 16#1D). 39 | 40 | % PARTID 41 | -define(DEVID, 16#F2). 42 | -------------------------------------------------------------------------------- /src/grisp_emulation_pmod_gyro.erl: -------------------------------------------------------------------------------- 1 | -module(grisp_emulation_pmod_gyro). 2 | 3 | -include("grisp_emulation_pmod_gyro.hrl"). 4 | 5 | % Callbacks 6 | -export([init/0]). 7 | -export([message/2]). 8 | 9 | -define(SPI_MODE, #{clock := {low, leading}}). 10 | 11 | %--- Callbacks ----------------------------------------------------------------- 12 | 13 | init() -> default(). 14 | 15 | message(State, {spi, 0, ?SPI_MODE, <>}) -> 16 | NewState = rotate(State), 17 | Result = grisp_bitmap:get_bytes(NewState, Reg, byte_size(RespBytes)), 18 | {<<0, Result/binary>>, NewState}; 19 | message(State, {spi, 0, ?SPI_MODE, <>}) -> 20 | {Result, NewState} = lists:foldl(fun(_, {R, S}) -> 21 | NewS = rotate(S), 22 | IR = grisp_bitmap:get_bytes(NewS, Reg, 1), 23 | {<>, NewS} 24 | end, {<<>>, State}, lists:seq(1, byte_size(RespBytes))), 25 | {<<0, Result/binary>>, NewState}; 26 | message(State, {spi, 0, ?SPI_MODE, <>}) -> 27 | NewState = grisp_bitmap:set_bytes(State, Reg, Value), 28 | {<<0, 0:(bit_size(Value))>>, NewState}. 29 | 30 | %--- Internal ------------------------------------------------------------------ 31 | 32 | rotate(State) -> 33 | case grisp_bitmap:get_bytes(State, ?CTRL_REG1, 1) of 34 | <<_:4, ?PD_NORMAL:1, _:3>> -> 35 | lists:foldl(fun({Reg, Len}, S) -> 36 | grisp_bitmap:set_bytes(S, Reg, crypto:strong_rand_bytes(Len)) 37 | end, State, [ 38 | {?OUT_TEMP, 1}, 39 | {?OUT_X_L, 6} 40 | ]); 41 | _ -> 42 | State 43 | end. 44 | 45 | default() -> 46 | << % Default Addr Name Type 47 | 0, % 00 Reserved - 48 | 0, % 01 Reserved - 49 | 0, % 02 Reserved - 50 | 0, % 03 Reserved - 51 | 0, % 04 Reserved - 52 | 0, % 05 Reserved - 53 | 0, % 06 Reserved - 54 | 0, % 07 Reserved - 55 | 0, % 08 Reserved - 56 | 0, % 09 Reserved - 57 | 0, % 0A Reserved - 58 | 0, % 0B Reserved - 59 | 0, % 0C Reserved - 60 | 0, % 0D Reserved - 61 | 0, % 0E Reserved - 62 | 2#11010011, % 0F WHO_AM_I R 63 | 0, % 10 Reserved - 64 | 0, % 11 Reserved - 65 | 0, % 12 Reserved - 66 | 0, % 13 Reserved - 67 | 0, % 14 Reserved - 68 | 0, % 15 Reserved - 69 | 0, % 16 Reserved - 70 | 0, % 17 Reserved - 71 | 0, % 18 Reserved - 72 | 0, % 19 Reserved - 73 | 0, % 1A Reserved - 74 | 0, % 1B Reserved - 75 | 0, % 1C Reserved - 76 | 0, % 1D Reserved - 77 | 0, % 1E Reserved - 78 | 0, % 1F Reserved - 79 | 2#00000111, % 20 CTRL_REG1 RW 80 | 2#00000000, % 21 CTRL_REG2 RW 81 | 2#00000000, % 22 CTRL_REG3 RW 82 | 2#00000000, % 23 CTRL_REG4 RW 83 | 2#00000000, % 24 CTRL_REG5 RW 84 | 2#00000000, % 25 REFERENCE RW 85 | 0, % 26 OUT_TEMP R 86 | 0, % 27 STATUS_REG R 87 | 0, % 28 OUT_X_L R 88 | 0, % 29 OUT_X_H R 89 | 0, % 2A OUT_Y_L R 90 | 0, % 2B OUT_Y_H R 91 | 0, % 2C OUT_Z_L R 92 | 0, % 2D OUT_Z_H R 93 | 2#00000000, % 2E FIFO_CTRL_REG RW 94 | 0, % 2F FIFO_SRC_REG R 95 | 2#00000000, % 30 INT1_CFG RW 96 | 0, % 31 INT1_SRC R 97 | 2#00000000, % 32 INT1_TSH_XH RW 98 | 2#00000000, % 33 INT1_TSH_XL RW 99 | 2#00000000, % 34 INT1_TSH_YH RW 100 | 2#00000000, % 35 INT1_TSH_YL RW 101 | 2#00000000, % 36 INT1_TSH_ZH RW 102 | 2#00000000, % 37 INT1_TSH_ZL RW 103 | 2#00000000 % 38 INT1_DURATION RW 104 | >>. 105 | -------------------------------------------------------------------------------- /src/grisp_emulation_pmod_gyro.hrl: -------------------------------------------------------------------------------- 1 | %--- Commands ------------------------------------------------------------------ 2 | 3 | -define(RW_WRITE, 2#0). 4 | -define(RW_READ, 2#1). 5 | 6 | -define(MS_SAME, 2#0). 7 | -define(MS_INCR, 2#1). 8 | 9 | %--- Registers ----------------------------------------------------------------- 10 | 11 | -define(WHO_AM_I, 16#0F). 12 | -define(CTRL_REG1, 16#20). 13 | -define(OUT_TEMP, 16#26). 14 | -define(OUT_X_L, 16#28). 15 | 16 | %--- Bit Descriptions ---------------------------------------------------------- 17 | 18 | % WHO_AM_I 19 | -define(DEVID, 2#11010011). 20 | 21 | % CTRL_REG1 22 | -define(PD_POWER_DOWN, 2#0). 23 | -define(PD_NORMAL, 2#1). 24 | -define(Zen_ENABLED, 2#1). 25 | -define(Yen_ENABLED, 2#1). 26 | -define(Xen_ENABLED, 2#1). 27 | -------------------------------------------------------------------------------- /src/grisp_emulation_pmod_maxsonar.erl: -------------------------------------------------------------------------------- 1 | -module(grisp_emulation_pmod_maxsonar). 2 | 3 | % Callbacks 4 | -export([init/0]). 5 | -export([open/5]). 6 | -export([command/5]). 7 | 8 | 9 | %--- Macros -------------------------------------------------------------------- 10 | 11 | -define(UPDATE_INTERVAL, 1000). 12 | 13 | 14 | %--- Records ----------------------------------------------------------------- 15 | 16 | -record(state, { 17 | owner :: pid() | undefined, 18 | proc :: pid() | undefined 19 | }). 20 | 21 | 22 | %--- Callbacks ----------------------------------------------------------------- 23 | 24 | init() -> 25 | #state{}. 26 | 27 | open(State = #state{owner = undefined}, 28 | grisp2, Owner, "grisp_termios_drv", _Settings) -> 29 | Proc = proc_start(Owner), 30 | {Proc, State#state{owner = Owner, proc = Proc}}. 31 | 32 | command(State = #state{owner = Owner, proc = Proc}, 33 | grisp2, Owner, Proc, _Command) -> 34 | {ok, State}. 35 | 36 | 37 | %--- Internal ------------------------------------------------------------------ 38 | 39 | proc_start(Owner) -> 40 | spawn_link(fun() -> proc_init(Owner) end). 41 | 42 | proc_init(Owner) -> 43 | self() ! update, 44 | proc_loop(Owner). 45 | 46 | proc_loop(Owner) -> 47 | receive 48 | update -> 49 | Val = rand:uniform(5) + 39, 50 | D1 = $0, 51 | D2 = $4, 52 | D3 = $0 + Val - 40, 53 | Owner ! {self(), {data, <<$R, D1, D2, D3, $\n>>}}, 54 | erlang:send_after(?UPDATE_INTERVAL, self(), update), 55 | proc_loop(Owner); 56 | stop -> 57 | ok 58 | end. 59 | -------------------------------------------------------------------------------- /src/grisp_emulation_pmod_nav.erl: -------------------------------------------------------------------------------- 1 | -module(grisp_emulation_pmod_nav). 2 | 3 | -compile([export_all]). 4 | 5 | -include("grisp_emulation_pmod_nav.hrl"). 6 | 7 | % Callbacks 8 | -export([init/0]). 9 | -export([message/2]). 10 | % -export([broadcast/2]). 11 | 12 | -define(SPI_MODE, #{clock := {high, trailing}}). 13 | 14 | -define(ACC(State), State#state.pins =:= #{ 15 | ss1 => {periph_c, 2}, 16 | spi1_pin9 => {output_1, 1}, 17 | spi1_pin10 => {output_1, 1} 18 | }). 19 | -define(MAG(State), State#state.pins =:= #{ 20 | ss1 => {output_1, 1}, 21 | spi1_pin9 => {output_1, 0}, 22 | spi1_pin10 => {output_1, 1} 23 | }). 24 | -define(ALT(State), State#state.pins =:= #{ 25 | ss1 => {output_1, 1}, 26 | spi1_pin9 => {output_1, 1}, 27 | spi1_pin10 => {output_1, 0} 28 | }). 29 | -define(PIN(Pin), Pin == ss1 orelse Pin == spi1_pin9 orelse Pin == spi1_pin10). 30 | 31 | %--- Records ------------------------------------------------------------------- 32 | 33 | -record(state, { 34 | pins = #{ 35 | ss1 => {periph_c, 2}, 36 | spi1_pin9 => {output_1, 1}, 37 | spi1_pin10 => {output_1, 1} 38 | }, 39 | bitmaps = #{ 40 | acc_gyro => default_acc_gyro(), 41 | mag => default_mag(), 42 | alt => default_alt() 43 | } 44 | }). 45 | 46 | %--- Callbacks ----------------------------------------------------------------- 47 | 48 | init() -> #state{}. 49 | 50 | message(State, {spi, 1, ?SPI_MODE, <<16#FF>>}) -> 51 | {<<16#FF>>, State}; % Initial bogus SPI read to set clock line high 52 | message(State, {spi, 1, ?SPI_MODE, Req}) -> 53 | component(State, acc_gyro, Req); 54 | message(State, {spi, 2, ?SPI_MODE, Req}) -> 55 | component(State, mag, Req); 56 | message(State, {spi, 3, ?SPI_MODE, Req}) -> 57 | component(State, alt, Req); 58 | message(State, {spi, CS, ?SPI_MODE, <>}) -> 59 | error({unknown_spi_request, State, Req, Value}). 60 | 61 | % broadcast(#state{pins = Pins} = State, {gpio, Pin, {configure, Mode, _}}) when ?PIN(Pin) -> 62 | % State#state{pins = maps:update(Pin, {Mode, value(Mode)}, Pins)}; 63 | % broadcast(#state{pins = Pins} = State, {gpio, Pin, clear}) -> 64 | % NewPins = maps:update_with(Pin, fun({Mode, _}) -> {Mode, 0} end, Pins), 65 | % State#state{pins = NewPins}; 66 | % broadcast(#state{pins = Pins} = State, {gpio, Pin, set}) -> 67 | % NewPins = maps:update_with(Pin, fun({Mode, _}) -> {Mode, 1} end, Pins), 68 | % State#state{pins = NewPins}; 69 | % % FIXME: Remove specific pins and add catchall clause in the end 70 | % broadcast(State, {gpio, jumper_1, get}) -> 71 | % State; 72 | % broadcast(State, {gpio, jumper_2, get}) -> 73 | % State; 74 | % broadcast(State, {gpio, jumper_3, get}) -> 75 | % State; 76 | % broadcast(State, {gpio, jumper_4, get}) -> 77 | % State; 78 | % broadcast(State, {gpio, jumper_5, get}) -> 79 | % State. 80 | 81 | %--- Internal ------------------------------------------------------------------ 82 | 83 | component(#state{bitmaps = Bitmaps} = State, Component, Req) -> 84 | % FIMXE: This code should check for chip select GPIO pin states before 85 | % talking to the actual chips. It should call grisp_gpio directly to do 86 | % this. 87 | {Result, Bitmap} = call(Component, maps:get(Component, Bitmaps), Req), 88 | {Result, State#state{bitmaps = maps:put(Component, Bitmap, Bitmaps)}}. 89 | 90 | call(acc_gyro, Bin, <>) -> 91 | read(shake(acc_gyro, Bin), Reg, byte_size(Val)); 92 | call(acc_gyro, Bin, <>) -> 93 | write(Bin, Reg, Val); 94 | call(mag, Bin, <>) -> 95 | read(Bin, Reg, byte_size(Val)); 96 | call(mag, Bin, <>) -> 97 | Result = grisp_bitmap:get_bytes(Bin, Reg, 1), 98 | {<<0, (binary:copy(Result, byte_size(Val)))/binary>>, Bin}; 99 | call(mag, Bin, <>) -> 100 | write(Bin, Reg, Val); 101 | call(mag, Bin, <>) -> 102 | write(Bin, Reg, binary:last(Val)); 103 | call(alt, Bin, <>) -> 104 | read(Bin, Reg, byte_size(Val)); 105 | call(alt, Bin, <>) -> 106 | Result = grisp_bitmap:get_bytes(Bin, Reg, 1), 107 | {<<0, (binary:copy(Result, byte_size(Val)))/binary>>, Bin}; 108 | call(alt, Bin, <>) -> 109 | write(Bin, Reg, Val); 110 | call(alt, Bin, <>) -> 111 | write(Bin, Reg, binary:last(Val)). 112 | 113 | read(Bin, Reg, Length) -> 114 | Result = grisp_bitmap:get_bytes(Bin, Reg, Length), 115 | {<<0, Result/binary>>, Bin}. 116 | 117 | write(Bin, Reg, Value) -> 118 | NewBin = grisp_bitmap:set_bytes(Bin, Reg, Value), 119 | {<<0, (binary:copy(<<0>>, byte_size(Value)))/binary>>, NewBin}. 120 | 121 | value(output_1) -> 1; 122 | value(periph_c) -> 2; 123 | value(_) -> undefined. 124 | 125 | shake(acc_gyro, Bin) -> 126 | case grisp_bitmap:get_bytes(Bin, 16#20, 1) of 127 | <<2#000:3, _:5>> -> % ODR in power-down mode 128 | Bin; 129 | _ -> 130 | grisp_bitmap:set_bytes(Bin, 16#28, crypto:strong_rand_bytes(6)) 131 | end. 132 | 133 | default_acc_gyro() -> 134 | << % Default Reg Name Type 135 | 0, % 16#00 Reserved -- 136 | 0, % 16#01 Reserved -- 137 | 0, % 16#02 Reserved -- 138 | 0, % 16#03 Reserved -- 139 | 2#00000000, % 16#04 ACT_THS r/w 140 | 2#00000000, % 16#05 ACT_DUR r/w 141 | 2#00000000, % 16#06 INT_GEN_CFG_XL r/w 142 | 2#00000000, % 16#07 INT_GEN_THS_X_XL r/w 143 | 2#00000000, % 16#08 INT_GEN_THS_Y_XL r/w 144 | 2#00000000, % 16#09 INT_GEN_THS_Z_XL r/w 145 | 2#00000000, % 16#0A INT_GEN_DUR_XL r/w 146 | 2#00000000, % 16#0B REFERENCE_G r/w 147 | 2#00000000, % 16#0C INT1_CTRL r/w 148 | 2#00000000, % 16#0D INT2_CTRL r/w 149 | 0, % 16#0E Reserved -- 150 | 2#01101000, % 16#0F WHO_AM_I r 151 | 2#00000000, % 16#10 CTRL_REG1_G r/w 152 | 2#00000000, % 16#11 CTRL_REG2_G r/w 153 | 2#00000000, % 16#12 CTRL_REG3_G r/w 154 | 2#00000000, % 16#13 ORIENT_CFG_G r/w 155 | 0, % 16#14 INT_GEN_SRC_G r 156 | 0, % 16#15 OUT_TEMP_L r 157 | 0, % 16#16 OUT_TEMP_H r 158 | 0, % 16#17 STATUS_REG r 159 | 0, % 16#18 OUT_X_L_G r 160 | 0, % 16#19 OUT_X_H_G r 161 | 0, % 16#1A OUT_Y_L_G r 162 | 0, % 16#1B OUT_Y_H_G r 163 | 0, % 16#1C OUT_Z_L_G r 164 | 0, % 16#1D OUT_Z_H_G r 165 | 2#00111000, % 16#1E CTRL_REG4 r/w 166 | 2#00111000, % 16#1F CTRL_REG5_XL r/w 167 | 2#00000000, % 16#20 CTRL_REG6_XL r/w 168 | 2#00000000, % 16#21 CTRL_REG7_XL r/w 169 | 2#00000100, % 16#22 CTRL_REG8 r/w 170 | 2#00000000, % 16#23 CTRL_REG9 r/w 171 | 2#00000000, % 16#24 CTRL_REG10 r/w 172 | 0, % 16#25 Reserved -- 173 | 0, % 16#26 INT_GEN_SRC_XL r 174 | 0, % 16#27 STATUS_REG r 175 | 0, % 16#28 OUT_X_L_XL r 176 | 0, % 16#29 OUT_X_H_XL r 177 | 0, % 16#2A OUT_Y_L_XL r 178 | 0, % 16#2B OUT_Y_H_XL r 179 | 0, % 16#2C OUT_Z_L_XL r 180 | 0, % 16#2D OUT_Z_H_XL r 181 | 2#00000000, % 16#2E FIFO_CTRL r/w 182 | 0, % 16#2F FIFO_SRC r 183 | 2#00000000, % 16#30 INT_GEN_CFG_G r/w 184 | 2#00000000, % 16#31 INT_GEN_THS_XH_G r/w 185 | 2#00000000, % 16#32 INT_GEN_THS_XL_G r/w 186 | 2#00000000, % 16#33 INT_GEN_THS_YH_G r/w 187 | 2#00000000, % 16#34 INT_GEN_THS_YL_G r/w 188 | 2#00000000, % 16#35 INT_GEN_THS_ZH_G r/w 189 | 2#00000000, % 16#36 INT_GEN_THS_ZL_G r/w 190 | 2#00000000 % 16#37 INT_GEN_DUR_G r/w 191 | >>. 192 | 193 | default_mag() -> 194 | << % Default Reg Name Type 195 | 0, % 16#00 Reserved -- 196 | 0, % 16#01 Reserved -- 197 | 0, % 16#02 Reserved -- 198 | 0, % 16#03 Reserved -- 199 | 0, % 16#04 Reserved -- 200 | 2#00000000, % 16#05 OFFSET_X_REG_L_M r/w 201 | 2#00000000, % 16#06 OFFSET_X_REG_H_M r/w 202 | 2#00000000, % 16#07 OFFSET_Y_REG_L_M r/w 203 | 2#00000000, % 16#08 OFFSET_Y_REG_H_M r/w 204 | 2#00000000, % 16#09 OFFSET_Z_REG_L_M r/w 205 | 2#00000000, % 16#0A OFFSET_Z_REG_H_M r/w 206 | 0, % 16#0B Reserved -- 207 | 0, % 16#0C Reserved -- 208 | 0, % 16#0D Reserved -- 209 | 0, % 16#0E Reserved -- 210 | 2#00111101, % 16#0F WHO_AM_I_M r 211 | 0, % 16#10 Reserved -- 212 | 0, % 16#11 Reserved -- 213 | 0, % 16#12 Reserved -- 214 | 0, % 16#13 Reserved -- 215 | 0, % 16#14 Reserved -- 216 | 0, % 16#15 Reserved -- 217 | 0, % 16#16 Reserved -- 218 | 0, % 16#17 Reserved -- 219 | 0, % 16#18 Reserved -- 220 | 0, % 16#19 Reserved -- 221 | 0, % 16#1A Reserved -- 222 | 0, % 16#1B Reserved -- 223 | 0, % 16#1C Reserved -- 224 | 0, % 16#1D Reserved -- 225 | 0, % 16#1E Reserved -- 226 | 0, % 16#1F Reserved -- 227 | 2#00010000, % 16#20 CTRL_REG1_M r/w 228 | 2#00000000, % 16#21 CTRL_REG2_M r/w 229 | 2#00000011, % 16#22 CTRL_REG3_M r/w 230 | 2#00000000, % 16#23 CTRL_REG4_M r/w 231 | 2#00000000, % 16#24 CTRL_REG5_M r/w 232 | 0, % 16#25 Reserved -- 233 | 0, % 16#26 Reserved -- 234 | 0, % 16#27 STATUS_REG_M r 235 | 0, % 16#28 OUT_X_L_M r 236 | 0, % 16#29 OUT_X_H_M r 237 | 0, % 16#2A OUT_Y_L_M r 238 | 0, % 16#2B OUT_Y_H_M r 239 | 0, % 16#2C OUT_Z_L_M r 240 | 0, % 16#2D OUT_Z_H_M r 241 | 0, % 16#2E Reserved -- 242 | 0, % 16#2F Reserved -- 243 | 2#00001000, % 16#30 INT_CFG_M r/w 244 | 2#00000000, % 16#31 INT_SRC_M r 245 | 2#00000000, % 16#32 INT_THS_L_M r 246 | 2#00000000 % 16#33 INT_THS_H_M r 247 | >>. 248 | 249 | default_alt() -> 250 | << % Default Reg Name Type 251 | 0, % 16#00 Reserved -- 252 | 0, % 16#01 Reserved -- 253 | 0, % 16#02 Reserved -- 254 | 0, % 16#03 Reserved -- 255 | 0, % 16#04 Reserved -- 256 | 0, % 16#05 Reserved -- 257 | 0, % 16#06 Reserved -- 258 | 0, % 16#07 Reserved -- 259 | 0, % 16#08 REF_P_XL R/W 260 | 0, % 16#09 REF_P_L R/W 261 | 0, % 16#0A REF_P_H R/W 262 | 0, % 16#0B ? -- 263 | 0, % 16#0C ? -- 264 | 0, % 16#0D Reserved -- 265 | 0, % 16#0E Reserved -- 266 | 2#10111101, % 16#0F WHO_AM_I R 267 | 2#00001111, % 16#10 RES_CONF R/W 268 | 0, % 16#11 Reserved -- 269 | 0, % 16#12 Reserved -- 270 | 0, % 16#13 Reserved -- 271 | 0, % 16#14 Reserved -- 272 | 0, % 16#15 Reserved -- 273 | 0, % 16#16 Reserved -- 274 | 0, % 16#17 Reserved -- 275 | 0, % 16#18 Reserved -- 276 | 0, % 16#19 Reserved -- 277 | 0, % 16#1A Reserved -- 278 | 0, % 16#1B Reserved -- 279 | 0, % 16#1C Reserved -- 280 | 0, % 16#1D Reserved -- 281 | 0, % 16#1E Reserved -- 282 | 0, % 16#1F Reserved -- 283 | 0, % 16#20 CTRL_REG1 R/W 284 | 0, % 16#21 CTRL_REG2 R/W 285 | 0, % 16#22 CTRL_REG3 R/W 286 | 0, % 16#23 CTRL_REG4 R/W 287 | 0, % 16#24 INTERRUPT_CFG R/W 288 | 0, % 16#25 INT_SOURCE R 289 | 0, % 16#26 Reserved -- 290 | 0, % 16#27 STATUS_REG R 291 | 0, % 16#28 PRESS_OUT_XL R 292 | 0, % 16#29 PRESS_OUT_L R 293 | 0, % 16#2A PRESS_OUT_H R 294 | 0, % 16#2B TEMP_OUT_L R 295 | 0, % 16#2C TEMP_OUT_H R 296 | 0, % 16#2D Reserved -- 297 | 0, % 16#2E FIFO_CTRL R/W 298 | 2#00100000, % 16#2F FIFO_STATUS R 299 | 0, % 16#30 THS_P_L R/W 300 | 0, % 16#31 THS_P_H R/W 301 | 0, % 16#32 Reserved -- 302 | 0, % 16#33 Reserved -- 303 | 0, % 16#34 Reserved -- 304 | 0, % 16#35 Reserved -- 305 | 0, % 16#36 Reserved -- 306 | 0, % 16#37 Reserved -- 307 | 0, % 16#38 Reserved -- 308 | 0, % 16#39 RPDS_L R/W 309 | 0 % 16#3A RPDS_H R/W 310 | >>. 311 | -------------------------------------------------------------------------------- /src/grisp_emulation_pmod_nav.hrl: -------------------------------------------------------------------------------- 1 | %--- Commands ------------------------------------------------------------------ 2 | 3 | -define(RW_WRITE, 2#0). 4 | -define(RW_READ, 2#1). 5 | 6 | -define(MS_SAME, 2#0). 7 | -define(MS_INCR, 2#1). 8 | 9 | %=== Accelerometer / Gyroscope ================================================= 10 | 11 | %--- Bit Descriptions ---------------------------------------------------------- 12 | 13 | -define(ACC_WHO_AM_I_DEFAULT, 2#01101000). 14 | 15 | %=== Magnetometer ============================================================== 16 | 17 | %--- Bit Descriptions ---------------------------------------------------------- 18 | 19 | -define(MAG_WHO_AM_I_DEFAULT, 2#00111101). 20 | 21 | %=== Altimeter ================================================================= 22 | 23 | %--- Bit Descriptions ---------------------------------------------------------- 24 | 25 | -define(ALT_WHO_AM_I_DEFAULT, 2#10111101). 26 | -------------------------------------------------------------------------------- /src/grisp_emulation_sup.erl: -------------------------------------------------------------------------------- 1 | -module(grisp_emulation_sup). 2 | 3 | -behaviour(supervisor). 4 | 5 | % API 6 | -export([start_link/0]). 7 | 8 | % Callbacks 9 | -export([init/1]). 10 | 11 | %--- API ----------------------------------------------------------------------- 12 | 13 | start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). 14 | 15 | %--- Callbacks ----------------------------------------------------------------- 16 | 17 | init([]) -> {ok, {#{}, [worker(grisp_emulation_device)]}}. 18 | 19 | %--- Internal ------------------------------------------------------------------ 20 | 21 | worker(Module) -> #{id => Module, start => {Module, start_link, []}}. 22 | --------------------------------------------------------------------------------