diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9bcedf43deaeb1b98be2f8fe30a27aaa7505ec24..f69f0e130d360e4a90e6aa123c1466c84d0b90d2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -20,9 +20,10 @@ build1: stage: build only: - master + - testing script: - echo "Do your build here" - - python sbs + - python3 sbs test1: stage: test diff --git a/README.md b/README.md index d813dc1cd98365931e614f9e9e95948d128627ab..03541587f6f1cbbace781036b2c250da019573ba 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@  -Skyward Boardcore -[](https://git.skywarder.eu/r2a/skyward-boardcore/commits/master) +Skyward Boardcore +[](https://git.skywarder.eu/scs/skyward-boardcore/commits/master) ------------- Boardcore is a framework for developing and building missile software for custom boards with Miosix . [Miosix](https://miosix.org/) is a lightweigth OS for embedded developing which provides support for basic things such as Threads, GPIO, Time and many other. You can find our fork of the kernel here: [skyward/miosix-kernel](https://git.skywarder.eu/elc/miosix-kernel) -Building is made with [SBS](https://git.skywarder.eu/r2a/skyward-boardcore/wikis/Skyward-Build-Systems-(SBS)), which was created to easily compile and reuse code for different boards. +Building is made with [SBS](https://git.skywarder.eu/scs/skyward-boardcore/wikis/Skyward-Build-Systems-(SBS)), which was created to easily compile and reuse code for different boards. ### Content @@ -28,13 +28,22 @@ In the main folder you will find **sbs.conf** which is used to configure the bui ### Getting Started -Install Python, Git and Miosix toolchain. Also, openocd and clang-format are recommended for a better experience. +#### Dependencies + +* Python3 +* Git +* Miosix toolchain + +Also, openocd, cppcheck and clang-format are recommended for a better experience. + +#### Cloning the repo + Clone this repo with the `--recursive` option and build everything. ``` -git clone --recursive https://git.skywarder.eu/r2a/skyward-boardcore.git +git clone --recursive https://git.skywarder.eu/scs/skyward-boardcore.git cd skyward-boardcore -python sbs -v +python3 sbs -v ``` SBS will start building all the entrypoints. Depending on how many entrypoints there are, this operation can take several minutes. @@ -43,16 +52,8 @@ Once SBS finished, check the resulting message: if every build displays an *OK* ### What's next? -In the [Wiki](https://git.skywarder.eu/r2a/skyward-boardcore/wikis/home) you will find some first-steps **guides** (configuring the IDE, building a firmware etc) as well as the **coding guidelines** and some **best practices** we adopt. - -If you want to contribute to this repository, please read [Git Workflow](https://git.skywarder.eu/r2a/skyward-boardcore/wikis/Git-Workflow). - -If you just want to start messing around, try [this](https://git.skywarder.eu/r2a/skyward-boardcore/wikis/Boardcore-Quick-Start). - +In the [Wiki](https://git.skywarder.eu/scs/skyward-boardcore/wikis/home) you will find some first-steps **guides** (configuring the IDE, building a firmware etc) as well as the **coding guidelines** and some **best practices** we adopt. -Useful links ------------ +If you want to contribute to this repository, please read [Git Workflow](https://git.skywarder.eu/scs/skyward-boardcore/wikis/Git-Workflow). -* [Miosix Wiki](https://miosix.org/wiki/index.php?title=Main_Page) for the installation. -* [Miosix Doxygen](https://miosix.org/doxygen/doxygen_k2.01/index.html) for the full documentation (classes, constants ecc). -* [ELC Handbooks](https://git.skywarder.eu/docs/elc-internal-reports/tree/master) +If you just want to start messing around, try [this](https://git.skywarder.eu/scs/skyward-boardcore/wikis/Boardcore-Quick-Start). \ No newline at end of file diff --git a/ide/vscode/c_cpp_properties.json b/ide/vscode/c_cpp_properties.json new file mode 100755 index 0000000000000000000000000000000000000000..abf33c87fab5ad668eab706beecadcea033a4e45 --- /dev/null +++ b/ide/vscode/c_cpp_properties.json @@ -0,0 +1,172 @@ +{ + "configurations": [ + { + "name": "stm32f429zi_skyward_homeone", + "includePath": [ + "${workspaceFolder}/libs/miosix-kernel/miosix/config/arch/cortexM4_stm32f4/stm32f429zi_skyward_homeone", + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/cortexM4_stm32f4/stm32f429zi_skyward_homeone", + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/cortexM4_stm32f4/common", + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/common", + "${workspaceFolder}/libs/miosix-kernel/miosix", + "${workspaceFolder}/libs", + "${workspaceFolder}/src/shared", + "${workspaceFolder}/src/tests", + "${workspaceFolder}" + ], + "compilerPath": "/opt/arm-miosix-eabi/bin/arm-miosix-eabi-g++", + + "defines": [ + "DEBUG", + "_ARCH_CORTEXM4_STM32F4", + "_BOARD_STM32F429ZI_SKYWARD_HOMEONE", + "_MIOSIX_BOARDNAME=stm32f429zi_skyward_homeone", + "HSE_VALUE=8000000", + "SYSCLK_FREQ_168MHz=168000000", + "_MIOSIX", + "__cplusplus=201103L" + ], + + "cStandard": "c11", + "cppStandard": "c++11", + + "browse": { + "path" : [ + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/cortexM4_stm32f4/stm32f429zi_skyward_homeone/interfaces-impl", + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/cortexM4_stm32f4/stm32f429zi_skyward_homeone", + "${workspaceFolder}/libs/miosix-kernel/miosix/config/arch/cortexM4_stm32f4/stm32f429zi_skyward_homeone", + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/cortexM4_stm32f4/stm32f429zi_skyward_homeone", + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/cortexM4_stm32f4/common", + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/common", + "${workspaceFolder}/libs/miosix-kernel/miosix/interfaces", + "${workspaceFolder}/libs/miosix-kernel/miosix/kernel", + "${workspaceFolder}/libs/miosix-kernel/miosix/util", + "${workspaceFolder}/libs/miosix-kernel/miosix/e20", + "${workspaceFolder}/libs/miosix-kernel/miosix/filesystem", + "${workspaceFolder}/libs/miosix-kernel/miosix/stdlib_integration", + "${workspaceFolder}/libs/miosix-kernel/miosix/*", + "${workspaceFolder}/libs/mavlink_skyward_lib", + "${workspaceFolder}/libs/tscpp", + "${workspaceFolder}/src/shared", + "${workspaceFolder}/src/tests", + "${workspaceFolder}/*", + "${workspaceFolder}/src", + "${workspaceFolder}/*" + ], + "limitSymbolsToIncludedHeaders": true + } + }, + { + "name": "stm32f429zi_skyward_death_stack", + "includePath": [ + "${workspaceFolder}/libs/miosix-kernel/miosix/config/arch/cortexM4_stm32f4/stm32f429zi_skyward_death_stack", + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/cortexM4_stm32f4/stm32f429zi_skyward_death_stack", + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/cortexM4_stm32f4/common", + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/common", + "${workspaceFolder}/libs/miosix-kernel/miosix", + "${workspaceFolder}/libs", + "${workspaceFolder}/src/shared", + "${workspaceFolder}/src/tests", + "${workspaceFolder}" + ], + "compilerPath": "/opt/arm-miosix-eabi/bin/arm-miosix-eabi-g++", + + "defines": [ + "DEBUG", + "_ARCH_CORTEXM4_STM32F4", + "_BOARD_stm32f429zi_skyward_death_stack", + "_MIOSIX_BOARDNAME=stm32f429zi_skyward_death_stack", + "HSE_VALUE=8000000", + "SYSCLK_FREQ_168MHz=168000000", + "_MIOSIX", + "__cplusplus=201103L" + ], + + "cStandard": "c11", + "cppStandard": "c++11", + + "browse": { + "path" : [ + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/cortexM4_stm32f4/stm32f429zi_skyward_death_stack/interfaces-impl", + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/cortexM4_stm32f4/stm32f429zi_skyward_death_stack", + "${workspaceFolder}/libs/miosix-kernel/miosix/config/arch/cortexM4_stm32f4/stm32f429zi_skyward_death_stack", + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/cortexM4_stm32f4/stm32f429zi_skyward_death_stack", + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/cortexM4_stm32f4/common", + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/common", + "${workspaceFolder}/libs/miosix-kernel/miosix/interfaces", + "${workspaceFolder}/libs/miosix-kernel/miosix/kernel", + "${workspaceFolder}/libs/miosix-kernel/miosix/util", + "${workspaceFolder}/libs/miosix-kernel/miosix/e20", + "${workspaceFolder}/libs/miosix-kernel/miosix/filesystem", + "${workspaceFolder}/libs/miosix-kernel/miosix/stdlib_integration", + "${workspaceFolder}/libs/miosix-kernel/miosix/*", + "${workspaceFolder}/libs/mavlink_skyward_lib", + "${workspaceFolder}/libs/tscpp", + "${workspaceFolder}/src/shared", + "${workspaceFolder}/src/tests", + "${workspaceFolder}/*", + "${workspaceFolder}/src" + ], + "limitSymbolsToIncludedHeaders": true + } + }, + { + "name": "stm32f429zi_skyward_rogallina", + "includePath": [ + "${workspaceFolder}/libs/miosix-kernel/miosix/config/arch/cortexM4_stm32f4/stm32f429zi_skyward_rogallina", + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/cortexM4_stm32f4/stm32f429zi_skyward_rogallina", + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/cortexM4_stm32f4/common", + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/common", + "${workspaceFolder}/libs/miosix-kernel/miosix", + "${workspaceFolder}/libs", + "${workspaceFolder}/src/shared", + "${workspaceFolder}/src/tests", + "${workspaceFolder}" + ], + "compilerPath": "/opt/arm-miosix-eabi/bin/arm-miosix-eabi-g++", + + "defines": [ + "DEBUG", + "_ARCH_CORTEXM4_STM32F4", + "_BOARD_STM32F429ZI_SKYWARD_HOMEONE", + "_MIOSIX_BOARDNAME=stm32f429zi_skyward_homeone", + "HSE_VALUE=8000000", + "SYSCLK_FREQ_168MHz=168000000", + "_MIOSIX", + "__cplusplus=201103L" + ], + + "cStandard": "c11", + "cppStandard": "c++11", + + "browse": { + "path" : [ + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/cortexM4_stm32f4/stm32f429zi_skyward_rogallina/interfaces-impl", + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/cortexM4_stm32f4/stm32f429zi_skyward_rogallina", + "${workspaceFolder}/libs/miosix-kernel/miosix/config/arch/cortexM4_stm32f4/stm32f429zi_skyward_rogallina", + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/cortexM4_stm32f4/stm32f429zi_skyward_rogallina", + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/cortexM4_stm32f4/common", + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/common", + "${workspaceFolder}/libs/miosix-kernel/miosix/interfaces", + "${workspaceFolder}/libs/miosix-kernel/miosix/kernel", + "${workspaceFolder}/libs/miosix-kernel/miosix/util", + "${workspaceFolder}/libs/miosix-kernel/miosix/e20", + "${workspaceFolder}/libs/miosix-kernel/miosix/filesystem", + "${workspaceFolder}/libs/miosix-kernel/miosix/stdlib_integration", + "${workspaceFolder}/libs/miosix-kernel/miosix/*", + "${workspaceFolder}/libs/mavlink_skyward_lib", + "${workspaceFolder}/libs/mxgui", + "${workspaceFolder}/libs/tscpp", + "${workspaceFolder}/libs/*", + "${workspaceFolder}/src/shared", + "${workspaceFolder}/src/tests", + "${workspaceFolder}/*", + "${workspaceFolder}/src", + "${workspaceFolder}/*" + ], + "limitSymbolsToIncludedHeaders": true + } + } + + ], + "version": 4 +} \ No newline at end of file diff --git a/ide/vscode/launch.json b/ide/vscode/launch.json new file mode 100644 index 0000000000000000000000000000000000000000..f07981bfab285ff228026edb5840654b0d08f3f9 --- /dev/null +++ b/ide/vscode/launch.json @@ -0,0 +1,30 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "cwd": "${workspaceRoot}", + "executable": "${workspaceFolder}/bin/${fileBasenameNoExtension}/${fileBasenameNoExtension}.elf", + "name": "ST-LINK V2 (current entrypoint)", + "request": "attach", + "type": "cortex-debug", + "servertype": "openocd", + "device": "STM32F429ZI", + "svdFile": "/home/luca/drive/Skyward/STM32F429.svd", + "configFiles": [ + "/home/luca/test.cfg" + ] + }, + { + "cwd": "${workspaceRoot}", + "executable": "${workspaceFolder}/bin/${fileBasenameNoExtension}/${fileBasenameNoExtension}.elf", + "name": "ST-LINK V1 (current entrypoint)", + "request": "attach", + "type": "cortex-debug", + "servertype": "openocd", + "device": "STM32F429ZI", + "configFiles": [ + "${workspaceFolder}/libs/miosix-kernel/miosix/arch/cortexM4_stm32f4/stm32f429zi_skyward_death_stack/death_stack.cfg" + ] + } + ] +} \ No newline at end of file diff --git a/ide/vscode/tasks.json b/ide/vscode/tasks.json new file mode 100755 index 0000000000000000000000000000000000000000..3afed1afbf7ea2f36e4c773f52487bdfe1f46f34 --- /dev/null +++ b/ide/vscode/tasks.json @@ -0,0 +1,111 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Show Workspace Folder", + "type": "shell", + "windows": { + "command": "echo ${workspaceFolder}" + }, + "problemMatcher": [] + }, + { + "label": "CLEAN", + "type": "shell", + "windows": { + "command": "python sbs -c" + }, + "linux": { + "command": "./sbs -c" + }, + "problemMatcher": "$gcc" + }, + { + "label": "BUILD all", + "type": "shell", + "windows": { + "command": "python sbs" + }, + "linux": { + "command": "./sbs" + }, + "problemMatcher": "$gcc" + }, + { + "label": "BUILD current-entrypoint", + "type": "shell", + "windows": { + "command": "python sbs -v -n -b ${fileBasenameNoExtension}" + }, + "linux": { + "command": "./sbs -v -b ${fileBasenameNoExtension}" + }, + "problemMatcher": "$gcc" + }, + { + "label": "RUN current-entrypoint", + "type": "shell", + "windows": { + "command": "ST-LINK_CLI.exe -P bin/${fileBasenameNoExtension}/${fileBasenameNoExtension}.bin 0x8000000 -V -Rst" + }, + "linux": { + "command": "st-flash write bin/${fileBasenameNoExtension}/${fileBasenameNoExtension}.bin 0x8000000" + }, + "problemMatcher": [] + }, + { + "label": "BUILD+RUN current-entrypoint", + "type": "shell", + "windows": { + "command": "ST-LINK_CLI.exe -P bin/${fileBasenameNoExtension}/${fileBasenameNoExtension}.bin 0x8000000 -V -Rst" + }, + "linux": { + "command": "sleep 1;st-flash write bin/${fileBasenameNoExtension}/${fileBasenameNoExtension}.bin 0x8000000" + }, + "problemMatcher": [], + "dependsOn": [ + "BUILD current-entrypoint" + ], + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "BUILD tests-catch", + "type": "shell", + "windows": { + "command": "python sbs -v -n -b tests-catch" + }, + "linux": { + "command": "./sbs -v -b tests-catch" + }, + "problemMatcher": "$gcc" + }, + { + "label": "RUN tests-catch", + "type": "shell", + "windows": { + "command": "ST-LINK_CLI.exe -P bin/tests-catch/tests-catch.bin 0x8000000 -V" + }, + "linux": { + "command": "st-flash write bin/tests-catch/tests-catch.bin 0x8000000" + }, + "problemMatcher": [] + }, + { + "type": "shell", + "label": "arm-miosix-eabi-g++ build active file", + "command": "/opt/arm-miosix-eabi/bin/arm-miosix-eabi-g++", + "args": [ + "-g", + "${file}", + "-o", + "${fileDirname}/${fileBasenameNoExtension}" + ], + "options": { + "cwd": "/opt/arm-miosix-eabi/bin" + } + } + ] +} \ No newline at end of file diff --git a/libs/miosix-kernel b/libs/miosix-kernel index a89006f621fe7e6937b1102b8a8ef35e5f486f6d..9096c209182fc1c86a1f4f33c9a1546fdad4e0b3 160000 --- a/libs/miosix-kernel +++ b/libs/miosix-kernel @@ -1 +1 @@ -Subproject commit a89006f621fe7e6937b1102b8a8ef35e5f486f6d +Subproject commit 9096c209182fc1c86a1f4f33c9a1546fdad4e0b3 diff --git a/sbs b/sbs index 8a4449230b92485761f59ee3d469187fa9c4fda8..d159ba3a51bb38c2280f2f0853a1aa75da16d187 100755 --- a/sbs +++ b/sbs @@ -4,17 +4,17 @@ # # Copyright (c) 2015-2019 Skyward Experimental Rocketry # Authors: Alain Carlucci, Alvise de'Faveri Tron -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -82,7 +82,7 @@ def printBanner(): print("+----------------------------------|___/-------------------v2.0-+") # -# Colorized output helper functions +# Colorized output helper functions # NOTE: I don't know why but an extra \n is added at the end of each line, # so we have to use replace # @@ -110,23 +110,25 @@ def print_make_output(text): # def configCmdParser(): parser = OptionParser() - parser.add_option("-c", "--clean", help="Run a 'make clean'", dest="clean", + parser.add_option("-c", "--clean", help="Run a 'make clean'", dest="clean", action="store_true") parser.add_option("-b", "--build", help="Build a specific entrypoint or test", dest="board") parser.add_option("-l", "--list", help="List all possible configurations", dest="list", default=False, action='store_true') - parser.add_option("-t", "--all-test", help="Build all tests", dest="all_tests", + parser.add_option("-t", "--all-test", help="Build all tests", dest="all_tests", default=False, action='store_true') - parser.add_option("-e", "--all-entrypoint", help="Build all entrypoints", dest="all_entry", + parser.add_option("-e", "--all-entrypoint", help="Build all entrypoints", dest="all_entry", default=False, action='store_true') - parser.add_option("-g", "--gen-faults", help="Generate fault list header files and exit", - dest="genhdr", default=False, action='store_true') - parser.add_option("-v", "--verbose", help="Print a verbose output", dest="log", +# parser.add_option("-g", "--gen-faults", help="Generate fault list header files and exit", +# dest="genhdr", default=False, action='store_true') + parser.add_option("-v", "--verbose", help="Print a verbose output", dest="log", action="store_true") - parser.add_option("-j", "--jobs", - help="Specifies the number of jobs (commands) to run simultaneously.", + parser.add_option("", "--lint", help="Run the linter", dest="lint", + action="store_true") + parser.add_option("-j", "--jobs", + help="Specifies the number of jobs (commands) to run simultaneously.", type="int", dest="JOBS", default=8) - parser.add_option("-n", "--no-colors", help="Don't print colors in the output", dest="colors", + parser.add_option("-n", "--no-colors", help="Don't print colors in the output", dest="colors", default=True, action="store_false") return parser @@ -153,14 +155,14 @@ def parseConf(path): elif stype == 'srcfiles': srcfiles[i] = [x.strip() for x in conf.get(i, 'Files').split("\n")] elif stype == 'board': - entrypoints[i] = { 'id': conf.get(i, 'BoardId'), + entrypoints[i] = { 'id': conf.get(i, 'BoardId'), 'bin': conf.get(i, 'BinName'), 'defines': conf.get(i, 'Defines'), 'files': [entrypoint_mask % conf.get(i, 'Main').strip()] + [x.strip() for x in conf.get(i, 'Include').split(' ')] - } + } elif stype == 'test': - tests[i] = { 'id': conf.get(i, 'BoardId'), + tests[i] = { 'id': conf.get(i, 'BoardId'), 'bin': conf.get(i, 'BinName'), 'defines': conf.get(i, 'Defines') + " -I" + projconf['TESTS_PATH'], 'files': [test_mask % conf.get(i, 'Main').strip()] + @@ -215,10 +217,10 @@ def build_makefile(template, board, bname): for incl in incllist: rmap["PROJECT_INCLUDES"] += incl.strip() + " " - + for lib in liblist: rmap["PROJECT_LIBS"] += lib.strip() + " " - + for sd in subdirs: rmap["PROJECT_SUBDIRS"] += sd.strip() + " " @@ -232,10 +234,10 @@ def build_makefile(template, board, bname): # # Generate fault counter headers # -def genFaultCounters(): - d1 = "data/fault_list.csv" - d2 = SBS_BASE + "/src/shared/diagnostic/FaultCounterData.h" - os.system("python %s/scripts/gen_fault_headers.py %s %s" %(SBS_BASE,d1,d2)) +#def genFaultCounters(): +# d1 = "data/fault_list.csv" +# d2 = SBS_BASE + "/src/shared/diagnostic/FaultCounterData.h" +# os.system("python %s/scripts/gen_fault_headers.py %s %s" %(SBS_BASE,d1,d2)) # # Import srcfiles groups from another .conf file. @@ -282,9 +284,15 @@ if len(make_template) == 0: # # Fault headers generation -if options.genhdr == True: - genFaultCounters() - exit(0) +# if options.genhdr == True: +# genFaultCounters() +# exit(0) + +# Linter +if options.lint == True: + print("[SBS] Executing linter.sh", flush=True) + os.system("%s/scripts/linter.sh %s" % (projconf['SBS_BASE'], projconf['SRC_PATH']) ) + sys.exit(0) # List if options.list == True: @@ -326,16 +334,16 @@ os.mkdir('build') for i in mainfiles: printout('[SBS] %s %s' % (action[1],i), WHITE, BOLD) - + with open('build/%s' % i, 'w') as f: f.write(build_makefile(make_template, mainfiles[i], i)) # stdout printed only in verbose if options.log == True: - p = subprocess.Popen(['make', '-j', str(options.JOBS), '-f', 'build/%s' % i] + p = subprocess.Popen(['make','-j', str(options.JOBS), '-f', 'build/%s' % i] + cleanparam, universal_newlines=True, - #stdout=subprocess.PIPE, + #stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1) else: @@ -362,4 +370,4 @@ for i in mainfiles: # Clean if options.clean == True: - shutil.rmtree('build',ignore_errors=True, onerror=None) + shutil.rmtree('build',ignore_errors=True, onerror=None) \ No newline at end of file diff --git a/sbs.conf b/sbs.conf index 6f9a8809c1443e5748dbf62b4640e14be9ac24fe..58769fb570789f0dd4485b02f5f62889560abdaf 100644 --- a/sbs.conf +++ b/sbs.conf @@ -51,6 +51,7 @@ #stm32f429zi_skyward_homeone #stm32f401re_nucleo #stm32f103c8_skyward_aldeeran +#stm32f407vg_skyward_tortellino # Syntax of this file: @@ -76,9 +77,9 @@ ENTRY_PATH: src/entrypoints TESTS_PATH: src/tests SRC_PATH: src/shared SBS_BASE: . -PROJECT_INCLUDES: -PROJECT_SUBDIRS: -PROJECT_LIBS: +PROJECT_INCLUDES: +PROJECT_SUBDIRS: +PROJECT_LIBS: #-----------------------------------# # List of Sources # @@ -187,7 +188,7 @@ Main: config-dsgamma Type: board BoardId: stm32f429zi_stm32f4discovery BinName: kernel-testsuite -Include: +Include: Defines: Main: kernel-testsuite @@ -240,7 +241,7 @@ Main: test-hsm Type: test BoardId: stm32f429zi_stm32f4discovery BinName: test-interrupt -Include: +Include: Defines: -DDEBUG Main: test-interrupt @@ -256,7 +257,7 @@ Main: logger/test-logger Type: test BoardId: stm32f429zi_stm32f4discovery BinName: test-kalman-benchmark -Include: +Include: Defines: Main: kalman/test-kalman-benchmark @@ -294,6 +295,14 @@ Include: %shared Defines: Main: drivers/test-mpu9250 +[test-lsm] +Type: test +BoardId: stm32f429zi_skyward_death_stack +BinName: test-lsm +Include: %shared +Defines: +Main: drivers/test-lsm + [test-timer] Type: test BoardId: stm32f429zi_stm32f4discovery @@ -331,7 +340,7 @@ Type: test BoardId: stm32f429zi_stm32f4discovery BinName: test-IMU Include: %shared -Defines: +Defines: Main: drivers/test-IMU #[test-mavlink-multi] diff --git a/scripts/cppcheck_suppressions_list.txt b/scripts/cppcheck_suppressions_list.txt deleted file mode 100644 index 84fbc0df253990712eeec40ac270a53a17228317..0000000000000000000000000000000000000000 --- a/scripts/cppcheck_suppressions_list.txt +++ /dev/null @@ -1,2 +0,0 @@ -*:src/tests/* - diff --git a/scripts/linter.sh b/scripts/linter.sh index 79792ccfa247a75ddccfc2bb96a2db7dccbe98ce..70dd0adb8d12a393cc67d72cc2051353b27c2b22 100755 --- a/scripts/linter.sh +++ b/scripts/linter.sh @@ -1,38 +1,18 @@ #!/bin/bash -declare -i RESULT=0 +echo '[Linter] Executing cppcheck' +cppcheck --template=gcc -q --std=c++11 --enable=all \ + --suppress=unusedFunction --suppress=missingInclude --suppress=noExplicitConstructor \ + "$@" 2>&1 | awk ' + function color(c,s) { + printf("\033[%dm%s\033[0m\n",30+c,s) + } + /warning/ {color(1,$0);next} + /style/ {color(2,$0);next} + /performance/ {color(3,$0);next} + /information/ {color(4,$0);next} + /portability/ {color(5,$0);next} + {print} + ' -function check { - B=$(eval "$3") - A=$? - echo -n "[LINTER] $2... " - - if [ "$1" == "OUT" ]; then - if [ ${#B} -gt 0 ]; then - A=$1 - fi; - fi; - if [ "$A" == "$1" ]; then - echo -e "FAIL\n------------ OUTPUT ------------" - echo "$B" - echo "--------------------------------" - return 1 - else - echo "OK" - return 0 - fi; -} - -#check 0 "Checking for files with lines longer than 80 characters" 'egrep --include="*.cpp" --include="*.h" -nr ".{81}" src/' -check 0 "Checking for files with lines longer than 120 characters" 'egrep --include="*.cpp" --include="*.h" -nr ".{121}" src/' -RESULT+=$? -check OUT "Checking for files having \n\n\n" "grep -HPcrz '(\\r?\\n){4,}' src | egrep -v ':0$' | cut -d ':' -f 1" -check OUT "Checking for files not having the copyright" "grep -rL '* Permission is hereby granted, free of charge' src/" -check OUT "Checking for tabulations instead of spaces" "grep -Pr '\t' src" -check OUT "MMP wants his full name" "grep -rl 'Matteo Piazzolla' src/" -check OUT "Launching cppcheck" "cppcheck --suppressions-list=./scripts/cppcheck_suppressions_list.txt --template=gcc -q --suppress=unusedFunction --suppress=missingInclude --std=c++11 --enable=all src/ 2>&1" -RESULT+=$? -check OUT "Checking for using namespace in header files" "grep -rl 'using namespace' src | egrep '.h(pp)?$'" -RESULT+=$? - -exit 0 +#exit 0 diff --git a/src/shared/diagnostic/NewLogger.h b/src/shared/diagnostic/NewLogger.h new file mode 100644 index 0000000000000000000000000000000000000000..ef14f373559a4e3bbc627670351216d76e17f019 --- /dev/null +++ b/src/shared/diagnostic/NewLogger.h @@ -0,0 +1,177 @@ +/* Copyright (c) 2015-2017 Skyward Experimental Rocketry + * Authors: Luca Erbetta + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef SRC_SHARED_DIAGNOSTIC_NEWLOGGER_H_ +#define SRC_SHARED_DIAGNOSTIC_NEWLOGGER_H_ + +#include <iostream> +#include <string> +#include <sstream> +#include <ctime> +#include <iomanip> + +namespace logging +{ + +enum class LogLevel : int + { + INFO = 0, + NOTIFY = 1, + WARNING = 2, + ERROR = 3, + CRITICAL = 4 +}; + +template <typename T> +std::string hex(T val) +{ + std::stringstream stream; + stream << std::hex << val; + std::string result( stream.str() ); + return result; +} + +class Logger +{ + public: + Logger() : out_stream_(&std::cout) + { + + } + + Logger(std::ostream* ostream) : out_stream_(ostream) + { + + } + + void setOstream(std::ostream* new_ostream) + { + out_stream_ = new_ostream; + } + + template<typename ... Args> + void log(LogLevel level, std::string tag, Args ...args) const + { + if (level < log_level_) + return; + + std::stringstream stream; + stream << getStringPrefix(level) << formatTag(tag) << " "; + _printOnSStream(&stream, args...); + *out_stream_ << stream.str() << std::endl; + } + + template<typename ... Args> + void info(std::string tag, Args ... args) const + { + log(LogLevel::INFO, tag, args...); + } + + template<typename ... Args> + void notify(std::string tag, Args ... args) const + { + log(LogLevel::NOTIFY, tag, args...); + } + + template<typename ... Args> + void warning(std::string tag, Args ... args) const + { + log(LogLevel::WARNING, tag, args...); + } + + template<typename ... Args> + void error(std::string tag, Args ... args) const + { + log(LogLevel::ERROR, tag, args...); + } + + template<typename ... Args> + void critical(std::string tag, Args ... args) const + { + log(LogLevel::CRITICAL, tag, args...); + } + + private: + + std::string getStringPrefix(LogLevel level) const + { + //Text is colored and formatted using Ansi Escape Codes: + //https://en.wikipedia.org/wiki/ANSI_escape_code + + switch (level) + { + case LogLevel::INFO: + return "\033[37m[INFO]\033[0m "; + break; + case LogLevel::NOTIFY: + return "\033[32m[NOTIFY]\033[0m "; + break; + case LogLevel::WARNING: + return "\033[33m[WARNING]\033[0m "; + break; + case LogLevel::ERROR: + return "\033[1;31m[ERROR]\033[0m "; + break; + case LogLevel::CRITICAL: + return "\033[1;41;37m[CRITICAL]\033[0m "; + break; + } + return ""; + } + + static std::string formatTag(std::string tag) + { + static const unsigned int maxlen = 14; + if(tag.length() > maxlen) + tag = tag.substr(0, maxlen); + + tag + tag + ": "; + + //Add trailing whitespace + std::string whitespace(maxlen - tag.length() + 2, ' '); + tag = tag + whitespace; + + return tag; + } + + void _printOnSStream(std::stringstream*) const + { + } + + template<typename FirstArg, typename ... Args> + void _printOnSStream(std::stringstream* stream, const FirstArg& arg, + Args ...args) const + { + *stream << arg; + _printOnSStream(stream, args...); + } + + LogLevel log_level_ = LogLevel::INFO; + std::ostream* out_stream_; +}; + +//Default logger on cout +static const Logger logger; + +} //namespace logger + +#endif /* SRC_SHARED_DIAGNOSTIC_NEWLOGGER_H_ */ diff --git a/src/shared/drivers/BusTemplate.h b/src/shared/drivers/BusTemplate.h index 208e9212cad8e1d26494f820009f3d074c065c61..0652a840bf017fa009fbc6883e85947ac5454318 100644 --- a/src/shared/drivers/BusTemplate.h +++ b/src/shared/drivers/BusTemplate.h @@ -192,6 +192,7 @@ private: getSPIAddr(N)->CR1=SPI_CR1_SSM //No HW cs | SPI_CR1_SSI | SPI_CR1_SPE //SPI enabled + | SPI_CR1_BR_0 | SPI_CR1_BR_1 | SPI_CR1_BR_2 | SPI_CR1_MSTR; diff --git a/src/shared/drivers/memory/FlashController.h b/src/shared/drivers/memory/FlashController.h new file mode 100644 index 0000000000000000000000000000000000000000..7d123ba22167b6572e8310a0c82f082825b43870 --- /dev/null +++ b/src/shared/drivers/memory/FlashController.h @@ -0,0 +1,656 @@ +// +// FlashController.hpp +// FlashController +// +// Created by Matteo Piazzolla on 27/05/17. +// Copyright © 2017 Matteo Piazzolla. All rights reserved. +// + +#ifndef FLASHCONTROLLER_H +#define FLASHCONTROLLER_H + +#include <drivers/memory/FlashDriverInclude.h> + +#include <miosix.h> +#include <Singleton.h> +#include <diagnostic/FaultCounter.h> +#include <diagnostic/NewLogger.h> + +using logging::logger; + +//Forward declaration +namespace testing +{ +namespace flashmemorytests +{ +template<typename > +class FlashTest; +} +} + +namespace flashmemory +{ + +/* + 16 pagine da 256 byte + 1000000 pagine in un banco + 62500 sottosettori + */ +static const uint16_t CONTROLLER_VERSION = 1; +static const uint16_t BOARD_ID = 0xAB; //TBD + +//BLOCK_SIZE deve essere un divisore di SUBSECTOR_SIZE +static const uint32_t BLOCK_SIZE = PAGE_SIZE; + +/** Number of flash data blocks that fit in each sector. + * First block of each sector is reserved for the header. + * In the first sector, the entire first subsector is reserved for the header. + */ +static const uint32_t BLOCKS_PER_SECTOR = SECTOR_SIZE / BLOCK_SIZE; +static const uint32_t BLOCKS_NUM = BLOCKS_PER_SECTOR * SECTORS_NUM; + +static const uint32_t FIRST_BOOT_KEY = 0xF135B007; +static const uint32_t READ_ONLY_KEY = 0x3EAD0271; + +//How many sectors should we attempt to write in before we give up? +static const int MAX_SECTOR_ITERATIONS = 50; + +struct FlashHeader +{ + const uint16_t version = CONTROLLER_VERSION; + const uint16_t board_id = BOARD_ID; + + uint32_t first_boot = FIRST_BOOT_KEY; + uint32_t start_index = 0; + uint32_t read_only = 0; + + bool operator==(const FlashHeader& other) const + { + return memcmp(this, &other, sizeof(FlashHeader)) == 0; + } + + bool operator!=(const FlashHeader& other) const + { + return memcmp(this, &other, sizeof(FlashHeader)) != 0; + } +}; + +static const uint8_t SECTOR_EMPTY = 0x55; +static const uint8_t SECTOR_WRITTEN = 0x44; +static const uint8_t SECTOR_CORRUPT = 0x00; + +struct SectorHeader +{ + uint8_t content = SECTOR_CORRUPT; //Either one of SECTOR_EMPTY, + //SECTOR_WRITTEN, SECTOR_CORRUPT + bool operator==(const SectorHeader& other) const + { + return memcmp(this, &other, sizeof(SectorHeader)) == 0; + } +}; + +static const uint32_t FLASH_BUFFER_SIZE = (BLOCK_SIZE) + - (sizeof(uint32_t) * 2 + sizeof(uint16_t)); + +struct FlashDataBlock +{ + uint32_t id; + uint32_t timestamp; + uint8_t buffer[FLASH_BUFFER_SIZE]; + uint16_t crc; + + bool operator==(const FlashDataBlock& other) const + { + return memcmp(this, &other, sizeof(FlashDataBlock)) == 0; + } + + bool operator!=(const FlashDataBlock& other) const + { + return memcmp(this, &other, sizeof(FlashDataBlock)) != 0; + } +}; + +template<typename MemoryBus> +class FlashController : Singleton<FlashController<MemoryBus>> +{ + friend class Singleton<FlashController<MemoryBus>>; + + template<typename > + friend class testing::flashmemorytests::FlashTest; + + typedef FlashController<MemoryBus> FlashControllerType; + typedef FlashDriver<MemoryBus> FlashDriverType; + + public: + enum class Status + { + WRITE_READY = 0, + READ_ONLY = 1, + INITIALIZATION_ERROR = 2, + WRITE_ERROR = 3 + }; + + void init() + { + status_ = Status::INITIALIZATION_ERROR; + block_index_ = SUBSECTOR_SIZE / BLOCK_SIZE; + + FlashHeader header; + + if (!readFlashHeader(&header)) + { + logger.critical(logtag(), + "Cannot read flash header."); + sFaultCounterMgr->Increment(Fault::F_MASTER_FLASH_CNTRL_FAULT); + return; + } + + if (header.read_only == READ_ONLY_KEY) + { + flash_->setReadOnly(); + status_ = Status::READ_ONLY; + } else + { + if (header.first_boot == FIRST_BOOT_KEY) + { + //Update the first boot key + header.first_boot = 0; + + if (!writeFlashHeader(header, false)) + { + logger.critical(logtag(), + "Could not update first boot value."); + sFaultCounterMgr->Increment(Fault::F_MASTER_FLASH_CNTRL_FAULT); + return; + } + + if (header.start_index >= block_index_) + { + block_index_ = header.start_index; + } + status_ = Status::WRITE_READY; + } else + { + if (recoverFromReboot()) + { + status_ = Status::WRITE_READY; + } else + { + logger.critical(logtag(), "Couldn't find sector to write in."); + sFaultCounterMgr->Increment(Fault::F_MASTER_FLASH_CNTRL_FAULT); + } + } + } + } + + FlashController::Status getStatus() + { + return status_; + } + + uint32_t getBlockIndex() + { + return block_index_; + } + + /** + * @brief Writes a block onto the flash memory. + * + * @param block The block to be written on the flash + */ + bool writeDataBlock(FlashDataBlock* block_buf) + { + + if (status_ != Status::WRITE_READY) + { + logger.error(logtag(), "Writing is not enabled."); + return false; + } + + if (block_index_ >= BLOCKS_NUM) + { + logger.critical(logtag(), "Memory is full."); + sFaultCounterMgr->Increment(Fault::F_MASTER_FLASH_CNTRL_FAULT); + status_ = Status::WRITE_ERROR; + return false; + } + + //Enforce size <= BLOCK_SIZE + size_t size = sizeof(FlashDataBlock) < BLOCK_SIZE ? + sizeof(FlashDataBlock) : BLOCK_SIZE; + + //Beginning of a new sector. Check if we can write in it and update its header + if (block_index_ % BLOCKS_PER_SECTOR == 0) + { + if(!gotoNewSector()){ + logger.critical(logtag(), "Could not find sector to write in."); + status_ = Status::WRITE_ERROR; + sFaultCounterMgr->Increment(Fault::F_MASTER_FLASH_CNTRL_FAULT); + return false; + } + } + + uint8_t result; + flash_->write(&result, block_index_ * BLOCK_SIZE, + reinterpret_cast<uint8_t*>(block_buf), size); + block_index_++; + + return result == RESULT_OK; + } + + static bool readFlashHeader(FlashHeader* header) + { + uint8_t result; + FlashDriverType* flash = Singleton<FlashDriverType>::getInstance(); + flash->read(&result, 0, (uint8_t*) header, sizeof(FlashHeader)); + return result == RESULT_OK; + } + + /** + * Saves (and overwrites) the header in the first address on the memory. + * @param header + */ + static bool writeFlashHeader(const FlashHeader& header, bool erase = true) + { + uint8_t result; + FlashDriverType* flash = Singleton<FlashDriverType>::getInstance(); + if (erase) + { + flash->enableErase(); + flash->eraseSubsector(&result, 0); + if (result != OpResultFlags::RESULT_OK) + { + return false; + } + } + uint32_t size = + sizeof(FlashHeader) > SUBSECTOR_SIZE ? SUBSECTOR_SIZE : + sizeof(FlashHeader); + + flash->writeAndCheck(&result, 0, (uint8_t*) (&header), size); + return result == RESULT_OK; + } + + static bool readBlock(uint32_t block_index, uint8_t* buffer) + { + uint8_t result; + FlashDriverType* flash = Singleton<FlashDriverType>::getInstance(); + + flash->read(&result, block_index * BLOCK_SIZE, buffer, BLOCK_SIZE); + return result == RESULT_OK; + } + + static bool readDataBlock(uint32_t block_index, FlashDataBlock* block) + { + //Flash header & sector headers are not *data* blocks. + if(block_index % BLOCKS_PER_SECTOR == 0 + || block_index < SUBSECTOR_SIZE / BLOCK_SIZE) + { + return false; + } + + readBlock(block_index, reinterpret_cast<uint8_t*>(block)); + return true; + } + + /** + * Saves (and overwrites) the header in the first address on the memory. + * @param header + */ + static bool writeSectorHeader(uint32_t sector, const SectorHeader& header) + { + uint8_t result; + FlashDriverType* flash = Singleton<FlashDriverType>::getInstance(); + + uint32_t size = + sizeof(SectorHeader) > BLOCK_SIZE ? BLOCK_SIZE : sizeof(SectorHeader); + + flash->writeAndCheck(&result, sector * SECTOR_SIZE, (uint8_t*) (&header), + size); + + return result == RESULT_OK; + } + + static bool readSectorHeader(uint32_t sector, SectorHeader* header) + { + return readSectorHeader(sector, (uint8_t*) header); + } + + static bool readSectorHeader(uint32_t sector, uint8_t* header_buff) + { + uint8_t result; + FlashDriverType* flash = Singleton<FlashDriverType>::getInstance(); + flash->read(&result, SECTOR_SIZE * sector, header_buff, + sizeof(SectorHeader)); + return result == RESULT_OK; + } + + static bool format() + { + uint8_t result; + logger.warning(logtag(), "THE MEMORY WILL BE FORMATTED IN 5 SECONDS"); + //Thread::sleep(5000); + logger.info(logtag(), "Erasing..."); + //TODO: Flash leds for a few seconds as a warning + eraseMemory(&result); + if (result != OpResultFlags::RESULT_OK) + { + logger.error(logtag(), "Error: result=", logging::hex(result)); + return false; + } + logger.info(logtag(), "Writing flash header..."); + FlashHeader header = { }; + header.first_boot = FIRST_BOOT_KEY; + //header.magic_word = MAGIC_WORD; + + if (!writeFlashHeader(header, false)) + { + logger.error(logtag(), "Error: result=", logging::hex(result)); + return false; + } + logger.info(logtag(), "Writing sector headers..."); + SectorHeader sheader; + sheader.content = SECTOR_EMPTY; + + for (uint32_t sector = 1; sector < SECTORS_NUM; sector++) + { + if (!writeSectorHeader(sector, sheader)) + { + logger.error(logtag(), "Error: result=", logging::hex(result), + " sector: ", sector); + return false; + } + } + logger.info(logtag(), "Success!"); + + return true; + } + + /** + * @brief Formats the memory. + * Use this only if the filesystem is in a good state: this fuction + * reads every sector header and erases only sector where + * header content != SECTOR_EMPTY + * + * @warning Use this only for testing! + */ + static bool fastFormat() + { + logger.warning(logtag(), "!!!THE MEMORY IS ABOUT TO BE FORMATTED!!!\n"); + + logger.info(logtag(),"Formatting...\n"); + //TODO: Flash leds for a few seconds as a warning + //Always erase the first sector + if(!erase(0, SUBSECTORS_PER_SECTOR)){ + logger.error(logtag(), "Couldn't erase sector 0"); + return false; + } + + //Then erase only the written/broken sectors + uint32_t s; + for (s = 1; s < SECTORS_NUM; s++) + { + SectorHeader header; + bool read_result = readSectorHeader(s, &header); + if (read_result && header.content != SECTOR_EMPTY) + { + //Erase data in this sector + if(!erase(s * SUBSECTORS_PER_SECTOR, (s + 1) * SUBSECTORS_PER_SECTOR)){ + logger.error(logtag(), "Couldn't erase sector ", s); + return false; + } + + //Rewrite the header + SectorHeader sheader; + sheader.content = SECTOR_EMPTY; + if (!writeSectorHeader(s, sheader)) + { + logger.error("Error writing sector header in sector ", s); + return false; + } + }else if(!read_result) + { + logger.error(logtag(), "Error reading sector header in sector ", s); + return false; + } + } + + //Format the memory + FlashHeader header { }; + header.first_boot = FIRST_BOOT_KEY; + + if(!writeFlashHeader(header, false)){ + logger.error(logtag(),"Couldn't write flash header."); + return false; + } + + return true; + } + + static bool eraseMemory(uint8_t* result) + { + return erase(0, SUBSECTORS_NUM); + } + + /** + * Erases the memory between the given subsectors + * Only erases a subsector if data has been written into it, + * to avoid wasting erase cycles. + * + * @brief Erases The memory between the given bounds. + * @param result Erase successful or not + * @param from Address belonging to the first subsector to be erased + * @param to Address belonging to the last subsector to be erased + * @return + */ + static bool erase(uint32_t from_subsector, uint32_t to_subsector) + { + uint8_t result; + FlashDriverType* flash = Singleton<FlashDriverType>::getInstance(); + uint8_t* buffer = new uint8_t[PAGE_SIZE]; + + std::stringstream stream; //To save strings to write in a single log line + stream << "Erasing subsectors: "; + + //Erase from the start of the subsector corresponding to "from" + uint32_t from_addr = from_subsector * SUBSECTOR_SIZE; + + //To the end of the subsector corresponding to "to". + uint32_t to_addr = to_subsector * SUBSECTOR_SIZE; + + //Read memory in blocks of PAGE_SIZE, if a byte is != 0xFF, erase the + //subsector and start reading again from the next one. + while (from_addr < to_addr) + { + flash->read(&result, from_addr, buffer, PAGE_SIZE); + + if (result == OpResultFlags::RESULT_OK) + { + bool erase = false; + for (uint32_t i = 0; i < PAGE_SIZE; i++) + { + erase = buffer[i] != 0xFF; + + if (erase) + break; + } + + if (erase) + { + stream << from_addr / SUBSECTOR_SIZE << ", "; + + flash->enableErase(); + flash->eraseSubsector(&result, from_addr); + if (result == OpResultFlags::RESULT_OK) + { + //Move to the beginning of the next subsector + from_addr = (from_addr / SUBSECTOR_SIZE + 1) * SUBSECTOR_SIZE; + } else + { + logger.error(logtag(), "Error erasing subsector: result=0x", + logging::hex(result)); + break; + } + + } else + { + from_addr = from_addr + PAGE_SIZE; + } + } else + { + logger.error(logtag(), "Error reading memory: result=0x", + logging::hex(result)); + break; + } + } + + stream << "--"; + + logger.info(logtag(), stream.str()); + delete buffer; + + return from_addr >= to_addr; + } + + static bool isMemoryClean(uint8_t* result) + { + FlashDriverType* flash = Singleton<FlashDriverType>::getInstance(); + uint8_t buffer[BLOCK_SIZE]; + + //Check from the start of the subsector corresponding to "from" + uint32_t addr = SUBSECTOR_SIZE; + + bool clean = true; + while (addr < MEMORY_SIZE) + { + if (addr % SECTOR_SIZE == 0) + { + SectorHeader header; + bool read_result = readSectorHeader(addr / SECTOR_SIZE, &header); + if (!read_result || header.content != SECTOR_EMPTY) + { + logger.warning(logtag(), "Wrong sector header @", addr); + clean = false; + } + } else + { + flash->read(result, addr, buffer, BLOCK_SIZE); + if (*result == OpResultFlags::RESULT_OK) + { + for (uint32_t i = 0; i < BLOCK_SIZE; i++) + { + if (buffer[i] != 0xFF) + { + logger.warning(logtag(), "Dirty block @", addr); + clean = false; + break; + } + } + } else + { + logger.warning(logtag(), "Error reading at %d\n", addr); + break; + } + } + + addr = addr + BLOCK_SIZE; + } + + if (clean) + { + logger.info(logtag(), "Memory is clean."); + } + return clean; + } + + private: + + FlashController() + { + flash_ = Singleton<FlashDriverType>::getInstance(); + } + + /** + * @brief Restores the state after a reboot. + * + * Restores the state after a reboot. + * Sequentially search for the first empty sector and update block_index_ + * accordingly + * + * @return Empty sector found or not + */ + bool recoverFromReboot() + { + //Start from the second sector + uint32_t sector = 1; + SectorHeader header; + + do + { + bool r = readSectorHeader(sector, &header); + if (r && header.content == SECTOR_EMPTY) + { + block_index_ = sector * BLOCKS_PER_SECTOR; + return true; + } + } while (++sector < SECTORS_NUM); + + return false; + } + + /** + * Updates block_index_ to point to the next available sector. + * + * @return True if a new sector is found + */ + bool gotoNewSector() + { + uint32_t sector = block_index_ / BLOCKS_PER_SECTOR; + SectorHeader readHeader, writeHeader; + + //Look for an empty sector + bool read_result; + int i = 0; + do + { + read_result = readSectorHeader(sector, &readHeader); + + } while (((read_result && readHeader.content != SECTOR_EMPTY) || !read_result) + && ++sector < SECTORS_NUM && ++i < MAX_SECTOR_ITERATIONS); + + block_index_ = sector * BLOCKS_PER_SECTOR; + + if (read_result && readHeader.content == SECTOR_EMPTY) + { + writeHeader.content = SECTOR_WRITTEN; + + if (!writeSectorHeader(sector, writeHeader)) + { + logger.error(logtag(), "Error writing sector header."); + return false; + } + block_index_++; + } else + { + logger.error(logtag(), "Error: could not find empty sector to write in."); + return false; + } + + return true; + } + + static std::string logtag() + { + return "FlashCTRL"; + } + + FlashDriverType* flash_; + uint32_t block_index_ = 0; + //Value will be changed if initialization is successful + Status status_ = Status::INITIALIZATION_ERROR; + +}; + +} //namespace flashmemory + +#endif /* FLASHCONTROLLER_H */ diff --git a/src/shared/drivers/memory/FlashDriver.h b/src/shared/drivers/memory/FlashDriver.h new file mode 100644 index 0000000000000000000000000000000000000000..2f4f5f70a4a0ecdd540a8851514ebdfcd14b059d --- /dev/null +++ b/src/shared/drivers/memory/FlashDriver.h @@ -0,0 +1,595 @@ + + + +#ifndef FLASHDRIVER_H +#define FLASHDRIVER_H + +#include <miosix.h> +#include <Singleton.h> +#include <diagnostic/NewLogger.h> +#include <string> + +using miosix::Thread; +using miosix::Mode; + +using logging::logger; + +namespace flashmemory { + +static const uint32_t PAGES_PER_SUBSECTOR = 16U; +static const uint32_t PAGES_PER_SECTOR = 256U; +static const uint32_t SUBSECTORS_PER_SECTOR = 16U; + +static const uint32_t SUBSECTORS_NUM = 16384U; +static const uint32_t SECTORS_NUM = 1024U; +static const uint32_t PAGE_SIZE = 256U; + +static const uint32_t SUBSECTOR_SIZE = PAGE_SIZE*PAGES_PER_SUBSECTOR; +static const uint32_t SECTOR_SIZE = PAGE_SIZE*PAGES_PER_SECTOR; + +static const uint32_t BANK_SIZE = SECTOR_SIZE*SECTORS_NUM/2; +static const uint32_t MEMORY_SIZE = SECTOR_SIZE*SECTORS_NUM; + + +/** + * @brief Definitions for results of various flash operations. + * + * Definitions for result flags of various flash operations. + */ +enum OpResultFlags +{ + RESULT_OK = 0x00, + + + RESULT_F_OUT_OF_MEMORY = 0x01, + RESULT_F_CHECK_FAIL = 0x04, + + + //Bits reserved for future use + RESULT_F_UNUSED_1 = 0x08, + RESULT_F_UNUSED_2 = 0x40, + RESULT_F_UNUSED_3 = 0x80, + + + /** + * These bits are in the same positions of the corresponding bits in the + * FLAG STATUS REGISTER + */ + RESULT_F_PROTECTION_ERROR = 0x02, + RESULT_F_PROGRAM_ERROR = 0x10, + RESULT_F_ERASE_ERROR = 0x20 +}; + + +/* + * TODO: + * -Controlla significato bit Vpp nel Flag status register + * -Casi in cui eseguire _write_disable() + * -Velocizzare il clock software per il factory reset + * -Controlla se bisogna davvero leggere due volte il flag_status_register per + * vedere se una scrittura ad un registro e' stata completata + * + */ + +template<typename Bus> +class FlashDriver : Singleton<FlashDriver<Bus>> +{ + typedef Singleton<FlashDriver<Bus>> SingletonType; + + friend class Singleton<FlashDriver<Bus>>; + + template<typename> + friend class FlashDriverTest; + +public: + + /** + * @brief Read n bytes into a buffer, starting from the specified address + * + * @param result + * @param address Starting address + * @param buf Buffer to read data into + * @param size Number of bytes to read + */ + void read(uint8_t *result, uint32_t address, uint8_t* buf, uint32_t size) + { + if(address + size > MEMORY_SIZE){ + *result = RESULT_F_OUT_OF_MEMORY; + return; + } + + uint32_t rsize; + if(address < BANK_SIZE) + rsize = std::min(size, BANK_SIZE - address); + else + rsize = size; + + uint8_t addr_buf[4]; + addrToBuf(addr_buf, address); + + Bus::read(READ, addr_buf, buf, 4, rsize); + + size -= rsize; + + //This means that we reached the end of the first die but we still need + //to read some data. + if(size > 0) + { + address += rsize; + buf += rsize; + + addrToBuf(addr_buf, address); + + //Read the remaining bytes + Bus::read(READ, addr_buf, buf, 4, size); + } + + *result = RESULT_OK; + } + + /** + * @brief Writes data on the flash starting from the specified address. + * + * @param address + * @param data + * @param size + */ + void write(uint8_t *result, uint32_t address, uint8_t* data, uint32_t size) + { + //Do not write outside of the memory + if(address + size > MEMORY_SIZE) + { + *result = RESULT_F_OUT_OF_MEMORY; + return; + } + + uint8_t addr_buf[4], status; + + uint32_t next_page, wsize; + + do { + next_page = (address / 256)*256 + 256; + + wsize = std::min(next_page - address, size); + + addrToBuf(addr_buf, address); + writeEnable(); + Bus::write(PROGRAM_PAGE, addr_buf, data, 4, wsize); + + do { + status = readFlagStatusReg(); + }while((status & FSR_PRG_ERS_CTRL) == 0); + + data += wsize; + size -= wsize; + address = next_page; + + *result = status & (FSR_PROGRAM | FSR_PROTECTION); + }while(size > 0 && *result == RESULT_OK); + + //Clear flag status register if an error occurred + if(*result != RESULT_OK) + { + clearFlagStatusReg(); + + //Manually reset write_enable latch + if((*result & RESULT_F_PROTECTION_ERROR) > 0) + { + writeDisable(); + } + } + } + + /** + * @brief Writes data on the flash starting from the specified address, then + * checks if everything was written correctly. + * + * @param address + * @param data + * @param size + */ + void writeAndCheck(uint8_t *result, uint32_t address, uint8_t* data, + uint32_t size){ //TODO: size_t + write(result,address,data,size); + if(*result == RESULT_OK){ + uint8_t *check = new uint8_t[size]; + read(result, address, check, size); + for(uint32_t i = 0; i < size; i++){ + if(*check++ != *data++){ + //Something was not written/read correctly. + *result = RESULT_F_CHECK_FAIL; + break; + } + } + } + } + + /** + * @brief Writes data on the flash starting from the specified address, only + * if the provided data fits in a single page. + * + * Writes data on the flash starting from the specified address, only + * if the provided data fits in a single page. If too much data is provided + * nothing will be written and result will be set to RESULT_F_OUT_OF_MEMORY + * + * @param result + * @param address + * @param data + * @param size + */ + void programPage(uint8_t *result, uint32_t address, uint8_t* data, uint32_t size) + { + //Do now wrap around a page and do not try to write outside of the memory + uint32_t next_page = (address / 256)*256 + 256; + if(address + size > next_page || address > MEMORY_SIZE) + { + *result = RESULT_F_OUT_OF_MEMORY; + return; + } + + uint8_t addr_buf[4], status; + addrToBuf(addr_buf, address); + + writeEnable(); + Bus::write(PROGRAM_PAGE, addr_buf, data, 4, size); + + do { + status = readFlagStatusReg(); + }while((status & FSR_PRG_ERS_CTRL) == 0); + + *result = status & (FSR_PROGRAM | FSR_PROTECTION); + + //Clear flag status register if an error has been encountered + if(*result != RESULT_OK) + { + clearFlagStatusReg(); + + //Manually reset write_enable latch + if((*result & RESULT_F_PROTECTION_ERROR) > 0) + { + writeDisable(); + } + } + } + + /** + * @brief Enables or disables read only mode + */ + void setReadOnly(bool readonly = true) + { + read_only_ = readonly; + } + + + bool isReadOnly() const + { + return read_only_; + } + + /** + * @brief Enable the next erase operation. Call this before every erase op. + */ + void enableErase() + { + writeEnable(); + } + + /** + * @brief Erases the subsector containing the specified address. For the operation + * to be successful, enable_erase_op must be called before this. + * + * @warning Estimated execution time is slightly less than 1 second. + * + * @param result + * @param address + */ + void eraseSubsector(uint8_t* result, uint32_t address) + { + erase(result, SUBSECTOR_ERASE, address); + } + + /** + * @brief Erases the sector containing the specified address. For the operation + * to be successful, enable_erase_op must be called before this. + * @warning Estimated execution time is about 2 seconds. + * + * @param result + * @param address + */ + void eraseSector(uint8_t* result, uint32_t address) + { + erase(result, SECTOR_ERASE, address); + } + + /** + * @brief Erases the entire die containing the specified address. For the operation + * to be successful, enable_erase_op must be called before this. + * @warning Estimated execution time is more than 4 minutes. + * + * @param result + * @param die: 0 to erase the first die, 1 to erase the second. + */ + void eraseDie(uint8_t* result, uint8_t die) + { + if(die == 0){ + erase(result, DIE_ERASE, 0); + }else if(die == 1){ + erase(result, DIE_ERASE, BANK_SIZE); + } + } + + /** + * Reads id information for this flash memory. + * @param buf Buffer with at least 20 bytes + */ + void readId(uint8_t* buf) + { + Bus::read(READ_ID, buf, 20); + } + + + /** + * @brief Software reset for the memory. All volatile bits are set to their + * default value. + */ + void softReset() + { + Bus::write(RESET_ENABLE); + usleep(5); + Bus::write(RESET_MEMORY); + waitUntilReady(); + } + + /** + * If the flash memory is in a bad state and can't be recovered by any + * other means, run this, then set CONFIG_REGISTER to 0xFFFF. + */ + template <class CS, class MOSI, class CLK> + static void factoryReset() + { + { + miosix::FastInterruptDisableLock dLock; + CS::mode(Mode::OUTPUT); + MOSI::mode(Mode::OUTPUT); + CLK::mode(Mode::OUTPUT); + } + MOSI::high(); + Thread::sleep(20); + + + factoryResetSequence<CS, CLK>(7); + factoryResetSequence<CS, CLK>(13); + factoryResetSequence<CS, CLK>(17); + factoryResetSequence<CS, CLK>(25); + factoryResetSequence<CS, CLK>(33); + + MOSI::low(); + Thread::sleep(50); + MOSI::high(); + factoryResetSequence<CS, CLK>(8); + + MOSI::low(); + CS::low(); + } + +private: + FlashDriver(){ + waitUntilReady(); + + uint8_t buf[20]; + readId(buf); + + if(buf[0] != 0x20) + { + logger.critical(logtag(), "Wrong id on FlashDriver instantiation"); + } + + writeStatusReg(0x00); + + clearFlagStatusReg(); + + + uint16_t config = readConfigReg(); + + //Last bit to 0 to enable 4 byte address mode + if(config != 0xFFFE) writeConfigReg(0xFFFE); + } + + void erase(uint8_t *result, uint8_t erase_cmd, uint32_t address) + { + if(address > MEMORY_SIZE) + { + *result = RESULT_F_OUT_OF_MEMORY; + return; + } + + uint8_t addr_buf[4], status; + addrToBuf(addr_buf, address); + Bus::write(erase_cmd, addr_buf, 4); + + do { + status = readFlagStatusReg(); + }while((status & FSR_PRG_ERS_CTRL) == 0); + + *result = status & (FSR_ERASE | FSR_PROTECTION); + + //Clear flag status register if an error has been encountered + if(*result != RESULT_OK) + { + clearFlagStatusReg(); + + //Manually reset write_enable latch + if((*result & RESULT_F_PROTECTION_ERROR) > 0) + { + writeDisable(); + } + } + } + + /** + * @brief Checks if a program operation is still in progress. + * @param result + */ + bool isBusy() + { + return readFlagStatusReg() & FSR_PRG_ERS_CTRL; + } + + /** + * @brief Waits until the memory is ready to perform a new write/erase op. + * @param result + */ + void waitUntilReady() + { + while((readFlagStatusReg() & FSR_PRG_ERS_CTRL) == 0) + { + + } + } + + uint8_t readStatusReg() + { + uint8_t ret = Bus::read(READ_STATUS_REG); + return ret; + } + + void writeStatusReg(uint8_t val) + { + writeEnable(); + Bus::write(WRITE_STATUS_REG, val); + writeDisable(); + waitUntilReady(); + } + + uint8_t readFlagStatusReg() + { + uint8_t ret = Bus::read(READ_FLAG_STATUS_REG); + return ret; + } + + void clearFlagStatusReg() + { + Bus::write(CLEAR_FLAG_STATUS_REG); + } + + uint16_t readConfigReg() + { + uint8_t buf[2]; + Bus::read(READ_NV_CONFIG_REG, buf, 2); + + uint16_t res = buf[0] | (buf[1] << 8); + + return res; + } + + void writeConfigReg(uint16_t val) + { + /* + * Mask some of the bits of the config register because + * we don't want to change them by accident. + */ + val = val | 0x0FEC; + + uint8_t buf[2]; + buf[0] = val & 0xFF; + buf[1] = (val >> 8) & 0xFF; + + writeEnable(); + Bus::write(WRITE_NV_CONFIG_REG, buf, 2); + writeDisable(); + waitUntilReady(); + } + + void writeEnable() + { + if(!read_only_) { + Bus::write(WRITE_ENABLE); + } + } + + void writeDisable() + { + Bus::write(WRITE_DISABLE); + } + + static void addrToBuf(uint8_t* buf, uint32_t addr) + { + for(int i = 0; i < 4; i++) + { + buf[3 - i] = (addr >> 8*i) & 0xFF; + } + } + + template <class CS, class CLK> + static void factoryResetSequence(int cycles) + { + CS::low(); + Thread::sleep(5); + for(int i = 0; i < cycles; i++) + { + CLK::high(); + Thread::sleep(5); + CLK::low(); + Thread::sleep(5); + } + CS::high(); + Thread::sleep(20); + } + + static std::string logtag() + { + return "FLASH_DRIVER"; + } + + enum Commands + { + READ_ID = 0x9F, + + WRITE_ENABLE = 0x06, + WRITE_DISABLE = 0x04, + + READ_STATUS_REG = 0x05, + WRITE_STATUS_REG = 0x01, + + READ_FLAG_STATUS_REG = 0x70, + CLEAR_FLAG_STATUS_REG = 0x50, + + //non-volatile configuration register + READ_NV_CONFIG_REG = 0xB5, + WRITE_NV_CONFIG_REG = 0xB1, + + //volatile configuration register + WRITE_VOL_CONFIG_REG = 0x85, + READ_VOL_CONFIG_REG = 0x81, + + READ_EXT_ADDRESS_REG = 0xC8, + WRITE_EXT_ADDRESS_REG = 0xC5, + + RESET_ENABLE = 0x66, + RESET_MEMORY = 0x99, + + READ = 0x03, + PROGRAM_PAGE = 0x02, + + SUBSECTOR_ERASE = 0x20, + SECTOR_ERASE = 0xD8, + DIE_ERASE = 0xC4 + }; + + bool read_only_ = false; + + /** + * Flag status register bit definitions + */ + static const uint8_t FSR_ADDRESSING_MODE = 0x01; + static const uint8_t FSR_PROTECTION = 0x02; + static const uint8_t FSR_PROGRAM_SUSPEND = 0x04; + static const uint8_t FSR_VPP = 0x08; + static const uint8_t FSR_PROGRAM = 0x10; + static const uint8_t FSR_ERASE = 0x20; + static const uint8_t FSR_ERASE_SUSPEND = 0x40; + static const uint8_t FSR_PRG_ERS_CTRL = 0x80; +}; + +} +#endif /* FLASHDRIVER_H */ diff --git a/src/shared/drivers/memory/FlashDriverInclude.h b/src/shared/drivers/memory/FlashDriverInclude.h new file mode 100644 index 0000000000000000000000000000000000000000..30e28b85707f6eaa0248c42bedc2f1d5bf5edda6 --- /dev/null +++ b/src/shared/drivers/memory/FlashDriverInclude.h @@ -0,0 +1,38 @@ +/* Copyright (c) 2015-2017 Skyward Experimental Rocketry + * Authors: Luca Erbetta + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef SRC_SHARED_DRIVERS_MEMORY_FLASHDRIVERINCLUDE_H_ +#define SRC_SHARED_DRIVERS_MEMORY_FLASHDRIVERINCLUDE_H_ + + +#ifdef FLASH_TEST + +#include <flashmemory/mockup/FlashDriver.h> + +#else + +#include <drivers/memory/FlashDriver.h> + +#endif /* FLASH_TEST */ + + +#endif /* SRC_SHARED_DRIVERS_MEMORY_FLASHDRIVERINCLUDE_H_ */ diff --git a/src/shared/drivers/memory/MultiFlashController.cpp b/src/shared/drivers/memory/MultiFlashController.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ae0aeef1623ca1874f930f2fd86529d94a7bf9a7 --- /dev/null +++ b/src/shared/drivers/memory/MultiFlashController.cpp @@ -0,0 +1,91 @@ +/* + * MultiFlashController.cpp + * + * Created on: Sep 17, 2017 + * Author: luca + */ + +#include "MultiFlashController.h" +#include <Singleton.h> +#include <diagnostic/NewLogger.h> + +using logging::logger; +/* stub crc class */ + +namespace flashmemory +{ +class CRCHelper +{ + public: + static uint16_t calcCRC16(uint8_t* data, uint32_t size) + { + return 0xABCD; + } +}; + +//miosix::Queue<FlashDataBlock,20> MultiFlashController::block_queue_; + +MultiFlashController::MultiFlashController() + : ActiveObject::ActiveObject() +{ + flash0_ = Singleton<FlashController<MemoryBus0>>::getInstance(); + + //block_queue_ = new miosix::Queue<FlashDataBlock, 5>(); +} + +MultiFlashController::~MultiFlashController() +{ +} + +/** + * @brief Infinite loop that process the block queue. + */ +void MultiFlashController::run() +{ + FlashDataBlock to_flash; + while (true) + { + block_queue_.waitUntilNotEmpty(); + + block_queue_.get(to_flash); + + bool result = true; + + result = result && flash0_->writeDataBlock(&to_flash); + + //TODO: Also write on 2nd and 3rd flash memory + //result = result && flash1_->writeBlock(&to_flash); + //result = result && flash1_->writeBlock(&to_flash); + + if (!result) + { + logger.error(logtag(), "Error writing blocks!"); + } + + } +} + +/** + * @brief Insert into the fixed queue a new FlashDataBlock to save in flash. + * + * @param buffer data do be saved + * @param size the size of the data (must be < FLASH_BUFFER_SIZE) + */ +void MultiFlashController::addData(const uint8_t* buffer, size_t size) +{ + /* limit data to FLASH_BUFFER_SIZE */ + uint16_t safe_size = (size <= FLASH_BUFFER_SIZE ? size : FLASH_BUFFER_SIZE); + FlashDataBlock to_save = { }; + + // create the Flash Data Block. + to_save.id = block_counter++; + to_save.timestamp = miosix::getTick(); + memcpy((uint8_t*) to_save.buffer, (uint8_t*) buffer, safe_size); + + //TODO crc of the whole block with toSave.crc = 0x0000? + to_save.crc = CRCHelper::calcCRC16((uint8_t*) to_save.buffer, safe_size); + + block_queue_.put(to_save); +} + +} //namespace flashmemory diff --git a/src/shared/drivers/memory/MultiFlashController.h b/src/shared/drivers/memory/MultiFlashController.h new file mode 100644 index 0000000000000000000000000000000000000000..9f1d42e0df98ad6e9ab780a1a49e50e7ab9270ca --- /dev/null +++ b/src/shared/drivers/memory/MultiFlashController.h @@ -0,0 +1,73 @@ +/* + * MultiFlashController.h + * + * Created on: Sep 13, 2017 + * Author: luca + */ +#ifndef MULTIFLASHCONTROLLER_H +#define MULTIFLASHCONTROLLER_H + +#include <Common.h> +#include <ActiveObject.h> +#include <drivers/BusTemplate.h> +#include "FlashController.h" + +using miosix::Gpio; + +//Forward declaration +namespace testing +{ +namespace flashmemorytests +{ +template<typename > +class FlashTest; +} +} + +namespace flashmemory +{ + +class MultiFlashController : public Singleton<MultiFlashController>, ActiveObject +{ + friend class Singleton<MultiFlashController>; + + template<typename > + friend class testing::flashmemorytests::FlashTest; + + typedef Gpio<GPIOA_BASE, 5> GpioSck; + typedef Gpio<GPIOA_BASE, 6> GpioMiso; + typedef Gpio<GPIOA_BASE, 7> GpioMosi; + typedef BusSPI<1, GpioMosi, GpioMiso, GpioSck> bus; + + typedef Gpio<GPIOC_BASE, 15> CS_FLASH0; + + public: + typedef ProtocolSPI<bus, CS_FLASH0> MemoryBus0; + + virtual ~MultiFlashController(); + + void addData(const uint8_t* buffer, size_t size); + + protected: + + void run() override; + + private: + MultiFlashController(); + + static std::string logtag() + { + return "MultiFlashCTRL"; + } + + uint32_t block_counter = 0; + miosix::Queue<FlashDataBlock,20> block_queue_; + + FlashController<MemoryBus0>* flash0_; + //FlashController<spi_flash0> flash1; //Change to spi_flash1 + //FlashController<spi_flash0> flash2; //Change to spi_flash2 +}; + +} //namespace flashmemory + +#endif /* MULTIFLASHCONTROLLER_H */ diff --git a/src/shared/sensors/LSM6DS3H.h b/src/shared/sensors/LSM6DS3H.h deleted file mode 100644 index fbc35c321bde7a94201d8d728438d3d7ace4144b..0000000000000000000000000000000000000000 --- a/src/shared/sensors/LSM6DS3H.h +++ /dev/null @@ -1,423 +0,0 @@ -/* LSM6DS3H Driver - * - * Copyright (c) 2018-2019 Skyward Experimental Rocketry - * Authors: Soufiane Machkouk - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#pragma once -#include <drivers/BusTemplate.h> -#include "Common.h" -#include "Sensor.h" -#include "miosix.h" -template <typename BusG> -class LSM6DS3H : public GyroSensor, public AccelSensor -{ -public: - LSM6DS3H(uint8_t accelFullScale, uint16_t gyroFullScale) - { - accelFS = accelFullScale; - gyroFS = gyroFullScale; - } - bool init() - { - selfTest(); - BusG::write(RegMap::CTRL5_C, 0x60); // Rounding read enabled for both accel and gyro - BusG::write(RegMap::CTRL3_C, 0x44 ); // incrementare l'indirizzo ad ogni lettura data LSB @ lower address - fifoConfiguration(); - accelConfiguration(); - gyroConfiguration(); - // TODO - uint8_t XLSamplesToDiscard=2; // - uint8_t gyroSamplesToDiscard=2; //since we set ODR to 416HZ - uint8_t buffer[2]={0, 0}; - BusG::read(RegMap::FIFO_DATA_OUT_L, buffer, XLSamplesToDiscard); - BusG::read(RegMap::FIFO_DATA_OUT_L, buffer, gyroSamplesToDiscard); - return true; - } - void fifoConfiguration() - { - uint8_t continuousMode = 0x36; // ODR is set to 416HZ - BusG::write(RegMap::FIFO_CTRL_REG5, continuousMode); - uint8_t bitToSet= BusG::read(RegMap::FIFO_CTRL_REG2); - bitToSet = bitToSet & 0xcf; - BusG::write(RegMap::FIFO_CTRL_REG2, bitToSet); - uint8_t noDecimation = 0x09; - BusG::write(RegMap::FIFO_CTRL_REG3, noDecimation); // NO decimation for both accel and gyro data - bitToSet = 0x00; - BusG::write(RegMap::FIFO_CTRL_REG5, bitToSet); - uint8_t registerStatus = BusG::read(RegMap::FIFO_STATUS4); - bitToSet = 0x03; - bitToSet = bitToSet & registerStatus; - BusG::write(RegMap::FIFO_STATUS4, bitToSet); - } - bool selfTest() - { - uint8_t whoami = BusG::read(RegMap::WHO_AM_I); - if (whoami != whoami_value) - { - last_error = ERR_NOT_ME; - return false; - } - return (accelSelfTest() & gyroSelfTest()); - } - void accelConfiguration() - { - uint8_t i=0; - uint8_t controlAccel = 0x61; - for (i = 0; i<=3; i++) - { - if ((uint8_t)accelFSMAP[i] == accelFS) - { - controlAccel = controlAccel | (i << 2); - BusG::write(RegMap::CTRL1_XL, controlAccel); // anti-aliasing filter set to 200Hz - // ODR to 416HZ - } - } - - BusG::write(RegMap::CTRL8_XL, 0x00); // no LPF2 - uint8_t enableXYZ = 0x38; - BusG::write(RegMap::CTRL9_XL, enableXYZ); - } - void gyroConfiguration() - { - uint8_t i=0; - uint8_t controlGyro = 0x60; - for(i=0; i<=3; i++) - { - if ((uint16_t)gyroFSMAP[i] == gyroFS) - { - controlGyro = controlGyro | (i << 2); - BusG::write(RegMap::CTRL2_G, controlGyro); - } - } - uint8_t enableXYZ = 0x39; - BusG::write(RegMap::CTRL10_C, enableXYZ); - //HPfilter disabled - } - bool onSimpleUpdate() { return false; } - void sampleUpdate() - { - //gyro data set ---> lettura da 6 byte - BusG::read(FIFO_DATA_OUT_L, datiGyro, 6); - // accel dataset ---> lettura da 6 byte - BusG::read(FIFO_DATA_OUT_L, datiAccel, 6); - Data.gyroXSample = toFloat(datiGyro[0]+datiGyro[1]<<8, gyroFS); - Data.gyroYSample = toFloat(datiGyro[2]+datiGyro[3]<<8, gyroFS); - Data.gyroZSample = toFloat(datiGyro[4]+datiGyro[5]<<8, gyroFS); - Data.accelXSample = toFloat(datiAccel[0]+datiAccel[1]<<8, accelFS); - Data.accelYSample = toFloat(datiAccel[2]+datiAccel[3]<<8, accelFS); - Data.accelZSample = toFloat(datiAccel[4]+datiAccel[5]<<8, accelFS); - - } - - bool accelSelfTest(){ - uint8_t discard; - uint8_t c=0; - uint8_t rxData[2]; - float OUTX_NOST[]={0, 0, 0, 0, 0}; - float outXNoST; - float OUTY_NOST[]={0, 0, 0, 0, 0}; - float outYNoST; - float OUTZ_NOST[]={0, 0, 0, 0, 0}; - float outZNoST; - float OUTX_ST[]={0, 0, 0, 0, 0}; - float outXST; - float OUTY_ST[]={0, 0, 0, 0, 0}; - float outYST; - float OUTZ_ST[]={0, 0, 0, 0, 0}; - float outZST; - BusG::write(CTRL1_XL, 0x30); - BusG::write(CTRL2_G, 0x00); - BusG::write(CTRL3_C, 0x44); - BusG::write(CTRL4_C, 0x00); - BusG::write(CTRL5_C, 0x00); - BusG::write(CTRL6_C, 0x00); - BusG::write(CTRL7_G, 0x00); - BusG::write(CTRL8_XL, 0x00); - BusG::write(CTRL9_XL, 0x38); - BusG::write(CTRL10_C, 0x00); - miosix::Thread::sleep(200); - discard = BusG::read(STATUS_REG); - while ((discard & 0x01) == 0 ){ - discard = BusG::read(STATUS_REG); - } - BusG::read(OUT_X_L_XL, rxData, 2); - BusG::read(OUT_Y_L_XL, rxData, 2); - BusG::read(OUT_Z_L_XL, rxData, 2); - discard = BusG::read(STATUS_REG); - while ( c<=5 ){ - while ((discard & 0x01) == 0 ){ - discard = BusG::read(STATUS_REG); - } - BusG::read(OUT_X_L_XL, rxData, 2); - OUTX_NOST[c] = toFloat(rxData[0]+(rxData[1]<<8), 2); - BusG::read(OUT_Y_L_XL, rxData, 2); - OUTY_NOST[c] = toFloat(rxData[0]+(rxData[1]<<8), 2); - BusG::read(OUT_Z_L_XL, rxData, 2); - OUTZ_NOST[c] = toFloat(rxData[0]+(rxData[1]<<8), 2); - c++; - } - outXNoST = (OUTX_NOST[0]+OUTX_NOST[1]+OUTX_NOST[2]+OUTX_NOST[3]+OUTX_NOST[4])/5; - outYNoST = (OUTY_NOST[0]+OUTY_NOST[1]+OUTY_NOST[2]+OUTY_NOST[3]+OUTY_NOST[4])/5; - outZNoST = (OUTZ_NOST[0]+OUTZ_NOST[1]+OUTZ_NOST[2]+OUTZ_NOST[3]+OUTZ_NOST[4])/5; - BusG::write(CTRL5_C, 0x01); - miosix::Thread::sleep(200); - discard = BusG::read(STATUS_REG); - while ((discard & 0x01) == 0 ){ - discard = BusG::read(STATUS_REG); - } - BusG::read(OUT_X_L_XL, rxData, 2); - BusG::read(OUT_Y_L_XL, rxData, 2); - BusG::read(OUT_Z_L_XL, rxData, 2); - discard = BusG::read(STATUS_REG); - c=0; - while ( c<=5 ){ - while ((discard & 0x01) == 0 ){ - discard = BusG::read(STATUS_REG); - } - BusG::read(OUT_X_L_XL, rxData, 2); - OUTX_ST[c] = toFloat(rxData[0]+(rxData[1]<<8), 2); - BusG::read(OUT_Y_L_XL, rxData, 2); - OUTY_ST[c] = toFloat(rxData[0]+(rxData[1]<<8), 2); - BusG::read(OUT_Z_L_XL, rxData, 2); - OUTZ_ST[c] = toFloat(rxData[0]+(rxData[1]<<8), 2); - c++; - } - outXST = (OUTX_ST[0]+OUTX_ST[1]+OUTX_ST[2]+OUTX_ST[3]+OUTX_ST[4])/5; - outYST = (OUTY_ST[0]+OUTY_ST[1]+OUTY_ST[2]+OUTY_ST[3]+OUTY_ST[4])/5; - outZST = (OUTZ_ST[0]+OUTZ_ST[1]+OUTZ_ST[2]+OUTZ_ST[3]+OUTZ_ST[4])/5; - float maxSTX = max(OUTX_ST); - float maxSTY = max(OUTY_ST); - float maxSTZ = max(OUTZ_ST); - float minSTX = min(OUTX_ST); - float minSTY = min(OUTY_ST); - float minSTZ = min(OUTZ_ST); - if (((uint8_t)(outXST-outXNoST)<=maxSTX) & ((uint8_t)(outXST-outXNoST)>=minSTX)){ - if (((uint8_t)(outYST-outYNoST)<=maxSTY) & ((uint8_t)(outYST-outYNoST)>=minSTY)){ - if (((uint8_t)(outZST-outZNoST)<=maxSTZ) & ((uint8_t)(outZST-outZNoST)>=minSTZ)){ - BusG::write(CTRL1_XL, 0x00); - BusG::write(CTRL5_C, 0x00); - return true; - } - } - } - BusG::write(CTRL1_XL, 0x00); - BusG::write(CTRL5_C, 0x00); - return false; - } - bool gyroSelfTest(){ - uint8_t discard; - uint8_t c=0; - uint8_t rxData[2]; - float OUTX_NOST[]={0, 0, 0, 0, 0}; - float outXNoST; - float OUTY_NOST[]={0, 0, 0, 0, 0}; - float outYNoST; - float OUTZ_NOST[]={0, 0, 0, 0, 0}; - float outZNoST; - float OUTX_ST[]={0, 0, 0, 0, 0}; - float outXST; - float OUTY_ST[]={0, 0, 0, 0, 0}; - float outYST; - float OUTZ_ST[]={0, 0, 0, 0, 0}; - float outZST; - BusG::write(CTRL1_XL, 0x00); - BusG::write(CTRL2_G, 0x5C); - BusG::write(CTRL3_C, 0x44); - BusG::write(CTRL4_C, 0x00); - BusG::write(CTRL5_C, 0x00); - BusG::write(CTRL6_C, 0x00); - BusG::write(CTRL7_G, 0x00); - BusG::write(CTRL8_XL, 0x00); - BusG::write(CTRL9_XL, 0x00); - BusG::write(CTRL10_C, 0x38); - miosix::Thread::sleep(800); - discard = BusG::read(STATUS_REG); - while ((discard & 0x01) == 0 ){ - discard = BusG::read(STATUS_REG); - } - BusG::read(OUT_X_L_G, rxData, 2); - BusG::read(OUT_Y_L_G, rxData, 2); - BusG::read(OUT_Z_L_G, rxData, 2); - discard = BusG::read(STATUS_REG); - while ( c<=5 ){ - while ((discard & 0x02) == 0 ){ - discard = BusG::read(STATUS_REG); - } - BusG::read(OUT_X_L_G, rxData, 2); - OUTX_NOST[c] = toFloat(rxData[0]+(rxData[1]<<8), 2); - BusG::read(OUT_Y_L_G, rxData, 2); - OUTY_NOST[c] = toFloat(rxData[0]+(rxData[1]<<8), 2); - BusG::read(OUT_Z_L_G, rxData, 2); - OUTZ_NOST[c] = toFloat(rxData[0]+(rxData[1]<<8), 2); - c++; - } - outXNoST = (OUTX_NOST[0]+OUTX_NOST[1]+OUTX_NOST[2]+OUTX_NOST[3]+OUTX_NOST[4])/5; - outYNoST = (OUTY_NOST[0]+OUTY_NOST[1]+OUTY_NOST[2]+OUTY_NOST[3]+OUTY_NOST[4])/5; - outZNoST = (OUTZ_NOST[0]+OUTZ_NOST[1]+OUTZ_NOST[2]+OUTZ_NOST[3]+OUTZ_NOST[4])/5; - BusG::write(CTRL5_C, 0x04); - miosix::Thread::sleep(60); - discard = BusG::read(STATUS_REG); - while ((discard & 0x02) == 0 ){ - discard = BusG::read(STATUS_REG); - } - BusG::read(OUT_X_L_G, rxData, 2); - BusG::read(OUT_Y_L_G, rxData, 2); - BusG::read(OUT_Z_L_G, rxData, 2); - discard = BusG::read(STATUS_REG); - while ( c<=5 ){ - while ((discard & 0x02) == 0 ){ - discard = BusG::read(STATUS_REG); - } - BusG::read(OUT_X_L_G, rxData, 2); - OUTX_ST[c] = toFloat(rxData[0]+(rxData[1]<<8), 2); - BusG::read(OUT_Y_L_G, rxData, 2); - OUTY_ST[c] = toFloat(rxData[0]+(rxData[1]<<8), 2); - BusG::read(OUT_Z_L_G, rxData, 2); - OUTZ_ST[c] = toFloat(rxData[0]+(rxData[1]<<8), 2); - c++; - } - outXST = (OUTX_ST[0]+OUTX_ST[1]+OUTX_ST[2]+OUTX_ST[3]+OUTX_ST[4])/5; - outYST = (OUTY_ST[0]+OUTY_ST[1]+OUTY_ST[2]+OUTY_ST[3]+OUTY_ST[4])/5; - outZST = (OUTZ_ST[0]+OUTZ_ST[1]+OUTZ_ST[2]+OUTZ_ST[3]+OUTZ_ST[4])/5; - float maxSTX = max(OUTX_ST); - float maxSTY = max(OUTY_ST); - float maxSTZ = max(OUTZ_ST); - float minSTX = min(OUTX_ST); - float minSTY = min(OUTY_ST); - float minSTZ = min(OUTZ_ST); - if (((uint8_t)(outXST-outXNoST)<=maxSTX) & ((uint8_t)(outXST-outXNoST)>=minSTX)){ - if (((uint8_t)(outYST-outYNoST)<=maxSTY) & ((uint8_t)(outYST-outYNoST)>=minSTY)){ - if (((uint8_t)(outZST-outZNoST)<=maxSTZ) & ((uint8_t)(outZST-outZNoST)>=minSTZ)){ - BusG::write(CTRL2_G, 0x00); - BusG::write(CTRL5_C, 0x00); - return true; - } - } - } - BusG::write(CTRL2_G, 0x00); - BusG::write(CTRL5_C, 0x00); - return false; - - } - float min (float* var){ - uint8_t min=var[0]; - uint8_t i=0; - for(i=0; i<=5; i++){ - if (var[i]<= min){ - min= var[i]; - } - } - return min; - } - float max (float* var){ - uint8_t max=var[0]; - uint8_t i=0; - for(i=0; i<=5; i++){ - if (var[i]>= max){ - max= var[i]; - } - } - return max; - } - - float toFloat(uint16_t var, uint16_t FScale){ - float out; - if(var > 0x7FFF){ - out= var-0xFFFF; - } - else { - out= var; - } - out = out*FScale/0x8000; - return out; - } - uint8_t datiGyro[6] = {0, 0, 0, 0, 0, 0}; - uint8_t datiAccel[6] = {0, 0, 0, 0, 0, 0}; - - struct Samples - { - float gyroXSample; - float gyroYSample; - float gyroZSample; - float accelXSample; - float accelYSample; - float accelZSample; - }; - Samples Data; - -private: - uint8_t accelFS; - uint16_t gyroFS; - constexpr static uint8_t whoami_value = 0x69; - - uint8_t accelFSMAP[4] = {2, 16, 4, 8}; - uint16_t gyroFSMAP[4] = {250, 500, 1000, 2000}; - enum RegMap { - WHO_AM_I = 0x0F, // default value - // FIFO configuration registers - FIFO_CTRL_REG1 = 0x06, - FIFO_CTRL_REG2 = 0x07, - FIFO_CTRL_REG3 = 0x08, - FIFO_CTRL_REG4 = 0x09, - FIFO_CTRL_REG5 = 0x0A, - - // Accelerometer and gyroscope control registers - CTRL1_XL = 0x10, - CTRL2_G = 0x11, - CTRL3_C = 0x12, - CTRL4_C = 0x13, - CTRL5_C = 0x14, - CTRL6_C = 0x15, - CTRL7_G = 0x16, - CTRL8_XL = 0x17, - CTRL9_XL = 0x18, - CTRL10_C = 0x19, - - // Accelerometer output registers - - OUT_X_L_XL = 0x28, - OUT_X_H_XL = 0x29, - OUT_Y_L_XL = 0x2A, - OUT_Y_H_XL = 0x2B, - OUT_Z_L_XL = 0x2C, - OUT_Z_H_XL = 0x2D, - - // gyro data registers - OUT_X_L_G = 0x22, - OUT_X_H_G = 0x23, - OUT_Y_L_G = 0x24, - OUT_Y_H_G = 0x25, - OUT_Z_L_G = 0x26, - OUT_Z_H_G = 0x27, - - // FIFO status registers - FIFO_STATUS1 = 0x3A, - FIFO_STATUS2 = 0x3B, - FIFO_STATUS3 = 0x3C, - FIFO_STATUS4 = 0x3D, - - // FIFO data output registers - FIFO_DATA_OUT_L = 0x3E, - FIFO_DATA_OUT_H = 0x3F, - // STATUS register - STATUS_REG = 0X1E - }; -}; \ No newline at end of file diff --git a/src/shared/sensors/LSM6DS3H/LSM6DS3H.h b/src/shared/sensors/LSM6DS3H/LSM6DS3H.h new file mode 100644 index 0000000000000000000000000000000000000000..117e823c2dd526af27a121dbb8b6a6469f652379 --- /dev/null +++ b/src/shared/sensors/LSM6DS3H/LSM6DS3H.h @@ -0,0 +1,220 @@ +/* LSM6DS3H Driver + * + * Copyright (c) 2019 Skyward Experimental Rocketry + * Authors: Nuno Barcellos + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef LSM6DS3H_H +#define LSM6DS3H_H + +#include <drivers/BusTemplate.h> + +#include "../Sensor.h" +#include "Common.h" +#include "miosix.h" + +/** ********ATTENTION*********** + * @warning Once upon a time, this driver was working, but due to hardware + * problems we were unable to confirm it and test it properly. If you need this, + * you should test it a bit before using it in production code. + */ + +template <typename Bus> +class LSM6DS3H : public GyroSensor, + public AccelSensor, + public CompassSensor, + public TemperatureSensor +{ +#pragma pack(1) + // __extension__ is needed to prevent compiler warnings for anonymous + // structs + typedef union { + __extension__ struct + { + int16_t temp; + int16_t gyro[3]; + int16_t accel[3]; + }; + int16_t buf[8]; + } lsmData_t; + +#pragma pack() + +public: + LSM6DS3H(uint8_t accelFullScale, uint16_t gyroFullScale) + { + accelFS = accelFullScale; + gyroFS = gyroFullScale; + } + + bool init() override + { + // SPI clock frequency up to 10 MHz + // The device is compatible with SPI modes 0 and 3 + + uint8_t whoami = Bus::read(RegMap::WHO_AM_I); + printf("[LSM] expected: %x actual: %x\n", whoami_value, whoami); + if (whoami != whoami_value) + { + last_error = ERR_NOT_ME; + return false; + } + + // Reset device + uint8_t reg = Bus::read(RegMap::CTRL3_C); + Bus::write(reg | 0x01); + + miosix::Thread::sleep(100); + + // clang-format off + uint8_t init_data[][2] = + { + {RegMap::CTRL3_C, 0x44}, // Register address automatically incremented during a multiple + // byte access with a serial interface; LSB @ lower address; + // SPI 4 wire; Output registers not updated until MSB and LSB + // have been read + {RegMap::CTRL1_XL, (uint8_t) (0x70 | (accelFS << 2))}, // Accel ODR to 833 Hz, + // Anti-aliasing filter to 400 hz + // {RegMap::CTRL5_C, 0x60}, // Rounding read enabled for both accel and gyro + {RegMap::CTRL2_G, (uint8_t) (0x70 | (gyroFS << 2))}, // Gyro ODR to 833 Hz + }; + // clang-format on + + for (size_t i = 0; i < sizeof(init_data) / sizeof(init_data[0]); i++) + Bus::write(init_data[i][0], init_data[i][1]); + + return true; + } + + bool selfTest() override { return true; } + + bool onSimpleUpdate() + { + lsmData_t raw_data; + uint8_t buf[20]; + + // Read temp, gyro, accel + Bus::read(RegMap::OUT_TEMP_L, buf, 14); + memcpy(&raw_data.buf, buf, 14); + + mLastTemp = normalizeTemp(raw_data.temp); + + mLastGyro.setX(normalizeGyro(raw_data.gyro[0])); + mLastGyro.setY(normalizeGyro(raw_data.gyro[1])); + mLastGyro.setZ(normalizeGyro(raw_data.gyro[2])); + + mLastAccel.setX(normalizeAccel(raw_data.accel[0])); + mLastAccel.setY(normalizeAccel(raw_data.accel[1])); + mLastAccel.setZ(normalizeAccel(raw_data.accel[2])); + + return true; + } + + // clang-format off + enum gyroFullScale + { + GYRO_FS_250 = 0, + GYRO_FS_500 = 1, + GYRO_FS_1000 = 2, + GYRO_FS_2000 = 3 + }; + + enum accelFullScale + { + ACC_FS_2G = 0, // Do not change this sequence + ACC_FS_4G = 2, + ACC_FS_8G = 3, + ACC_FS_16G = 1 + }; + // clang-format on + +private: + constexpr static uint8_t whoami_value = 0x69; + constexpr static float accelFSMAP[4] = {0.061, 0.488, 0.122, 0.244}; + constexpr static float gyroFSMAP[4] = {8.75, 17.50, 35.0, 70.0}; + uint8_t accelFS; + uint16_t gyroFS; + + inline float normalizeAccel(int16_t val) + { + return static_cast<float>(val) / 1000.0f * accelFSMAP[accelFS] * + EARTH_GRAVITY; // [m/ss] + } + + inline float normalizeGyro(int16_t val) + { + return static_cast<float>(val) / 1000.0f * gyroFSMAP[gyroFS] * + DEGREES_TO_RADIANS; // [rad/s] + } + + inline float normalizeTemp(int16_t val) + { + return static_cast<float>(val) / 16.0f + 25.0f; // [deg C] + } + + enum RegMap + { + WHO_AM_I = 0x0F, // default value + + // Accelerometer and gyroscope control registers + CTRL1_XL = 0x10, + CTRL2_G = 0x11, + CTRL3_C = 0x12, + CTRL4_C = 0x13, + CTRL5_C = 0x14, + CTRL6_C = 0x15, + CTRL7_G = 0x16, + CTRL8_XL = 0x17, + CTRL9_XL = 0x18, + CTRL10_C = 0x19, + + // Temperature output registers + OUT_TEMP_L = 0x20, + OUT_TEMP_H = 0x21, + + // Gyro data registers + OUT_X_L_G = 0x22, + OUT_X_H_G = 0x23, + OUT_Y_L_G = 0x24, + OUT_Y_H_G = 0x25, + OUT_Z_L_G = 0x26, + OUT_Z_H_G = 0x27, + + // Accelerometer output registers + OUT_X_L_XL = 0x28, + OUT_X_H_XL = 0x29, + OUT_Y_L_XL = 0x2A, + OUT_Y_H_XL = 0x2B, + OUT_Z_L_XL = 0x2C, + OUT_Z_H_XL = 0x2D, + + // STATUS register + STATUS_REG = 0X1E + }; +}; + +template <typename Bus> +constexpr float LSM6DS3H<Bus>::accelFSMAP[]; + +template <typename Bus> +constexpr float LSM6DS3H<Bus>::gyroFSMAP[]; + +#endif /* ifndef LSM6DS3H */ diff --git a/src/shared/sensors/LSM6DS3H/LSM6DS3HData.h b/src/shared/sensors/LSM6DS3H/LSM6DS3HData.h new file mode 100644 index 0000000000000000000000000000000000000000..b34c3aa815f6c7ac970c84bac755cb1cb94a4439 --- /dev/null +++ b/src/shared/sensors/LSM6DS3H/LSM6DS3HData.h @@ -0,0 +1,48 @@ +/* Copyright (c) 2019 Skyward Experimental Rocketry + * Authors: Nuno Barcellos + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef SRC_SHARED_SENSORS_LSM6DS3H_LSM6DS3HDATA_H +#define SRC_SHARED_SENSORS_LSM6DS3H_LSM6DS3HDATA_H + +#include <ostream> +#include "math/Vec3.h" + +struct LSM6DS3HData +{ + long long timestamp; + Vec3 accel; + Vec3 gyro; + float temp; + + static std::string header() + { + return "timestamp,acc_x,acc_y,acc_z,gyro_x,gyro_y,gyro_z\n"; + } + + void print(std::ostream& os) const + { + os << timestamp << "," << accel.getX() << "," << accel.getY() << "," + << accel.getZ() << "," << gyro.getX() << "," << gyro.getY() << "," + << gyro.getZ() << "\n"; + } +}; + +#endif /* SRC_SHARED_SENSORS_LSM6DS3H_LSM6DS3HDATA_H */ diff --git a/src/shared/sensors/MPU9250/MPU9250.h b/src/shared/sensors/MPU9250/MPU9250.h index 5c0e8a7adc986e3db6d61d54537ebe8bf07f0e69..23a2edba773f8d90de53954439bb97d2600bca55 100644 --- a/src/shared/sensors/MPU9250/MPU9250.h +++ b/src/shared/sensors/MPU9250/MPU9250.h @@ -113,19 +113,20 @@ public: } bool init() override - { + { + miosix::Thread::sleep(100); uint8_t whoami = Bus::read(REG_WHO_AM_I); - // printf("MPU whoami: expected %x actual %x\n", who_am_i_value_mpu, whoami); - if (whoami != who_am_i_value_mpu) + TRACE("MPU whoami: expected %x actual %x\n", who_am_i_value_mpu, whoami); + + if (whoami != who_am_i_value_mpu && whoami != 0x70) { last_error = ERR_NOT_ME; return false; } - int i = 0; - while(!initMagneto() && i < 10) + + if(!initMagneto()) { - ++i; - miosix::Thread::sleep(5); + TRACE("MPU9250 Magnetometer init failed\n"); } // Initialize MPU9250 diff --git a/src/tests/catch/test-packetqueue.cpp b/src/tests/catch/test-packetqueue.cpp index 3e8c2f71b60f6d53c95d680de839347c952990c3..58e6bc12145070d0c6cfc177276686592d99de87 100644 --- a/src/tests/catch/test-packetqueue.cpp +++ b/src/tests/catch/test-packetqueue.cpp @@ -42,7 +42,7 @@ uint8_t buf[BUF_LEN]; inline bool COMPARE(uint8_t* buf, size_t len, const char* expected) { - int i = 0; + size_t i = 0; while (expected[i] != '\0' && i < len) { CAPTURE(i); @@ -240,7 +240,7 @@ TEST_CASE("PacketQueue tests") COMPARE(pq.buffer.get(1), "abcd"); COMPARE(pq.buffer.get(2), "abcdefg"); - + INFO("Popping first element"); p = pq.pop(); // Should still return first packet REQUIRE(p.msgCount() == 3); @@ -263,7 +263,7 @@ TEST_CASE("PacketQueue tests") REQUIRE(pq.countNotEmpty() == 3); REQUIRE_FALSE(pq.isEmpty()); REQUIRE(pq.isFull()); - + COMPARE(pq.buffer.get(0), "abcd"); COMPARE(pq.buffer.get(1), "abcdefg"); COMPARE(pq.buffer.get(2), "0123456789"); @@ -298,7 +298,7 @@ TEST_CASE("PacketQueue tests") REQUIRE(pq.isEmpty()); REQUIRE(pq.countNotEmpty() == 0); REQUIRE(pq.countReady() == 0); - + INFO("Adding empty message") REQUIRE(pq.put(message_base, 0) == -1); diff --git a/src/tests/drivers/flashmemory/FlashControllerTests.h b/src/tests/drivers/flashmemory/FlashControllerTests.h new file mode 100644 index 0000000000000000000000000000000000000000..ded0da2665f4480efee2406f897df87637992016 --- /dev/null +++ b/src/tests/drivers/flashmemory/FlashControllerTests.h @@ -0,0 +1,686 @@ +#ifndef FLASHCONTROLLERTESTS_H +#define FLASHCONTROLLERTESTS_H + +#include <Common.h> +#include <helper/MultiFlashController.h> +#include <diagnostic/NewLogger.h> +#include <drivers/timer.h> +#include <Tests.h> +#include <vector> +#include <limits> +#include <string> + +using std::vector; +using logging::logger; +using namespace flashmemory; + +namespace testing +{ +namespace flashmemorytests +{ + +template<typename MemorySPI> +class FlashTest : public Test +{ + public: + + FlashTest(MultiFlashController* multi) + : Test(), multi(multi), controller(multi->flash0_), driver( + controller->flash_) + { + + } + protected: + typedef FlashController<MemorySPI> FlashControllerType; + + void controller_setBlockIndex(uint32_t block_index) + { + controller->block_index_ = block_index; + } + + uint32_t controller_getBlockIndex() + { + return controller->block_index_; + } + + bool controller_format() + { +#ifdef FLASH_TEST + return FlashControllerType::format(); +#else + return FlashControllerType::fastFormat(); +#endif + } + + void controller_init() + { + controller->init(); + } + + bool controller_readSectorHeader(uint32_t sector, SectorHeader* header) + { + return FlashControllerType::readSectorHeader(sector, header); + } + + bool controller_readBlock(uint32_t block_index, uint8_t* buffer) + { + return FlashControllerType::readBlock(block_index, buffer); + } + + bool controller_readDataBlock(uint32_t block_index, FlashDataBlock* block) + { + return FlashControllerType::readDataBlock(block_index, block); + } + + bool controller_writeBlock(FlashDataBlock* block) + { + return controller->writeDataBlock(block); + } + + bool controller_writeSectorHeader(uint32_t sector, SectorHeader& sheader) + { + return FlashControllerType::writeSectorHeader(sector, sheader); + } + + MultiFlashController* multi; + FlashController<MemorySPI>* controller; + FlashDriver<MemorySPI>* driver; +}; + +/* + * Copy this to create a new FlashTest + * + + template<typename MemorySPI> + class EmptyFlashTest : public FlashTest<MemorySPI> + { + typedef FlashTest<MemorySPI> Base; + + public: + + EmptyFlashTest(MultiFlashController* multi) + : Base::FlashTest(multi) + { + + } + + bool setup() + { + return true; + } + + bool run() + { + return true; + } + + string name() + { + return "EmptyFlashTest"; + } + }; + + */ + +class CompareFlashHeaderTest : public Test +{ + public: + + CompareFlashHeaderTest() + : Test() + { + + } + + bool run() + { + bool success = true; + FlashHeader header1, header2; + + bool test = header1 == header2; + success = success && test; + logger.info(logtag(), "Compare equal headers: ", (test ? "OK" : "Error")); + + header1.read_only = 54; + + test = header1 == header2; + success = success && (test == false); + logger.info(logtag(), "Compare different headers: ", + (test == false ? "OK" : "Error")); + + return success; + } + + string name() + { + return "CompareFlashHeaderTest"; + } + + private: + static string logtag() + { + return "CompareFHTest"; + } +}; + +template<typename MemorySPI> +class MeasureRebootTimeTest : public FlashTest<MemorySPI> +{ + typedef FlashTest<MemorySPI> Base; + typedef Timer32<2> Timer2; + public: + MeasureRebootTimeTest(MultiFlashController* multi) + : Base::FlashTest(multi) + { + + } + + bool run() + { +#ifdef FLASH_TEST + logger.warning(logtag(), "You are using the mockup memory!"); +#endif + logger.info(logtag(), "Running 100 read repetitions!"); + uint8_t result; + SectorHeader header; + float avg, min = std::numeric_limits<float>::max(), max = 0; + Timer2::start(); + for (int j = 0; j < 100; j++) + { + uint32_t t0 = Timer2::tick(); + for (uint32_t i = 0; i < SECTORS_NUM; i++) + { + Base::driver->read(&result, i * SECTOR_SIZE, (uint8_t*) (&header), + sizeof(SectorHeader)); + if (result != RESULT_OK) + { + break; + } + } + uint32_t t1 = Timer2::tick(); + float t = Timer2::milliSeconds(t1 - t0); + avg += t; + if (t < min) + { + min = t; + } else if (t > max) + { + max = t; + } + } + Timer2::stop(); + avg = avg / 100; + if (result == RESULT_OK) + { + logger.info(logtag(), "Avg time: ", avg, " ms"); + logger.info(logtag(), "Min time: ", min, " ms, Max time: ", max, " ms"); + return true; + } else + { + return false; + } + } + + string name() + { + return "MeasureWorstRebootTimeTest"; + } + + private: + static string logtag() + { + return "RebootTimeTest"; + } +}; + +template<typename MemorySPI> +class FlashHeaderTest : public FlashTest<MemorySPI> +{ + typedef FlashTest<MemorySPI> Base; + //using Base::driver; + //using Base::controller; + + public: + + FlashHeaderTest(MultiFlashController* multi) + : Base::FlashTest(multi) + { + + } + + bool setup() + { + return cleanFirstSubsector(); + } + + bool run() + { + FlashHeader orig_header, read_header; + generateRandomHeader(&orig_header); + orig_header.first_boot = FIRST_BOOT_KEY; + logger.info(logtag(), "--SUBTEST 1: Write & read over clean memory"); + + if (!writeAndReadHeader(orig_header, read_header, false)) + { + return false; + } + if (orig_header != read_header) + { + logger.error(logtag(), "Original & written headers are not the same!"); + return false; + } + logger.info(logtag(), "Success."); + + orig_header.first_boot = 0; + logger.info(logtag(), "--SUBTEST 2: Overwrite first_boot"); + + if (!writeAndReadHeader(orig_header, read_header, false)) + { + return false; + } + if (orig_header != read_header) + { + logger.error(logtag(), "Original & written headers are not the same!"); + return false; + } + logger.info(logtag(), "Success."); + + logger.info(logtag(), "--SUBTEST 3: Write & read over dirty memory"); + FlashHeader dirtywrite_header; + generateRandomHeader(&dirtywrite_header); + + if (Base::FlashControllerType::writeFlashHeader(dirtywrite_header, false)) + { + logger.error( + logtag(), + "Function writeFlashHeader should have returned RESULT_CHECK_FAIL!"); + return false; + } + logger.info(logtag(), "Success."); + + logger.info(logtag(), + "--SUBTEST 4: Write with erase & read over dirty memory"); + generateRandomHeader(&dirtywrite_header); + + if (!writeAndReadHeader(dirtywrite_header, read_header, true)) + { + return false; + } + if (dirtywrite_header != read_header) + { + logger.error(logtag(), "Original & written headers are not the same!"); + return false; + } + logger.info(logtag(), "Success."); + return true; + } + + string name() + { + return "WriteFlashHeaderTest"; + } + + private: + static string logtag() + { + return "WrtFHeaderTest"; + } + + bool cleanFirstSubsector() + { + uint8_t result; + Base::driver->enableErase(); + Base::driver->eraseSubsector(&result, 0); + + return result == RESULT_OK; + } + + void generateRandomHeader(FlashHeader* header) + { + header->start_index = (uint32_t) rand(); + header->read_only = (uint32_t) rand(); + header->first_boot = (uint32_t) rand(); + } + + bool writeAndReadHeader(FlashHeader& write_header, FlashHeader& read_header, + bool erase) + { + if (!Base::FlashControllerType::writeFlashHeader(write_header, erase)) + { + logger.error(logtag(), "Error writing header"); + return false; + } + + if (!Base::FlashControllerType::readFlashHeader(&read_header)) + { + logger.error(logtag(), "Error reading header"); + return false; + } + + return true; + } +}; + +template<typename MemorySPI> +class FlashBlockTest : public FlashTest<MemorySPI> +{ + typedef FlashTest<MemorySPI> Base; + + public: + + FlashBlockTest(MultiFlashController* multi) + : Base::FlashTest(multi) + { + + } + + bool setup() + { + if (Base::controller_format()) + { + Base::controller_init(); + return true; + } + return false; + } + + bool run() + { + logger.info(logtag(), "--SUBTEST 1: Just write and read"); + if (!subtest1()) + return false; + logger.info(logtag(), "Success."); + + logger.info(logtag(), "--SUBTEST 2: Write near the end of a sector"); + if (!subtest2()) + return false; + logger.info(logtag(), "Success."); + + logger.info(logtag(), "--SUBTEST 3: Write at the end of the memory"); + if (!subtest3()) + return false; + logger.info(logtag(), "Success."); + + return true; + } + + string name() + { + return "FlashBlockTest"; + } + + private: + static string logtag() + { + return "FBlockTest"; + } + + bool subtest1() + { + uint32_t block_index = SUBSECTOR_SIZE / BLOCK_SIZE; + + Base::controller_setBlockIndex(block_index); + FlashDataBlock write_block, read_block; + generateDataBlock(&write_block); + + if (!writeBlock(&write_block)) + { + logger.error(logtag(), "Cannot write block"); + return false; + } + + if (!readBlock(block_index, &read_block)) + { + logger.error(logtag(), "Cannot read block"); + return false; + } + + if (read_block != write_block) + { + logger.error(logtag(), "Written & read block do not match!"); + return false; + } + return true; + } + + bool subtest2() + { + uint32_t block_index = SECTOR_SIZE / BLOCK_SIZE - 1; + + Base::controller_setBlockIndex(block_index); + + FlashDataBlock write_blocks[2]; + FlashDataBlock read_blocks[2]; + for (int i = 0; i < 2; i++) + { + generateDataBlock(&write_blocks[i]); + if (!writeBlock(&write_blocks[i])) + { + logger.error(logtag(), "Cannot write block ", i + 1); + return false; + } + } + + SectorHeader sheader; + if (!Base::controller_readSectorHeader(1, &sheader)) + { + logger.error(logtag(), "Cannot read sector header"); + return false; + } + + if (sheader.content != SECTOR_WRITTEN) + { + logger.error(logtag(), "Sector header not correctly written"); + logger.error(logtag(), "Sector header content: 0x%02X; should be: 0x%02X", + sheader.content, SECTOR_WRITTEN); + return false; + } + + if (!readBlock(block_index, &read_blocks[0])) + { + logger.error(logtag(), "Cannot read block 1"); + return false; + } + + if (!readBlock(block_index + 2, &read_blocks[1])) + { + logger.error(logtag(), "Cannot read block 2"); + return false; + } + + if (!compareBlocks(write_blocks, read_blocks, 2)) + { + logger.error(logtag(), "Written & read blocks do not match."); + return false; + } + return true; + } + + bool subtest3() + { + //Last block of the memory + uint32_t block_index = MEMORY_SIZE / BLOCK_SIZE - 1; + Base::controller_setBlockIndex(block_index); + + //Keep the filesystem consistent. + SectorHeader sheader; + sheader.content = SECTOR_WRITTEN; + Base::controller_writeSectorHeader(MEMORY_SIZE / SECTOR_SIZE - 1, sheader); + + FlashDataBlock write_blocks[2]; + FlashDataBlock read_blocks[2]; + + for (int i = 0; i < 2; i++) + { + generateDataBlock(&write_blocks[i]); + } + + if (!writeBlock(&write_blocks[0])) + { + logger.error(logtag(), + "Write block failed on the last available address!"); + return false; + } + + if (writeBlock(&write_blocks[1])) + { + logger.error( + logtag(), + "Write block didn't fail while writing outside of the memory!"); + return false; + } + if (!readBlock(block_index, &read_blocks[0])) + { + logger.error(logtag(), "Cannot read block 1"); + return false; + } + + if (readBlock(block_index + 2, &read_blocks[1])) + { + logger.error( + logtag(), + "readBlock didn't fail while reading outside of the memory!"); + return false; + } + + if (read_blocks[0] != write_blocks[0]) + { + logger.error(logtag(), "Written & read blocks do not match."); + return false; + } + + return true; + } + + bool writeBlock(FlashDataBlock* block) + { + return Base::controller_writeBlock(block); + } + + bool readBlock(uint32_t block_index, FlashDataBlock* block) + { + return Base::controller_readBlock(block_index, (uint8_t*) (block)); + } + + bool compareBlocks(FlashDataBlock* blocks1, FlashDataBlock* blocks2, + size_t num_blocks) + { + for (size_t i = 0; i < num_blocks; i++) + { + if (blocks1[i] != blocks2[i]) + { + return false; + } + } + return true; + } + + void generateDataBlock(FlashDataBlock* generated) + { + generated->id = (uint32_t) rand(); + generated->crc = (uint16_t) rand(); + generated->timestamp = (uint32_t) rand(); + + for (int i = 0; i < FLASH_BUFFER_SIZE; i++) + { + generated->buffer[i] = (uint8_t) (rand() % 256); + } + } +}; + +template<typename MemorySPI> +class MultiFlashCTRLTest : public FlashTest<MemorySPI> +{ + typedef FlashTest<MemorySPI> Base; + + public: + + MultiFlashCTRLTest(MultiFlashController* multi) + : Base::FlashTest(multi) + { + + } + + bool setup() + { + if (!Base::controller_format()) + { + return false; + } + Base::controller_init(); + if (Base::controller->getStatus() + != Base::FlashControllerType::Status::WRITE_READY) + { + logger.error(logtag(), "Error initializing flash controller."); + return false; + } + return true; + } + + bool run() + { + logger.info(logtag(), "--SUBTEST 1: Write @ beginning"); + if (!subtest1()) + return false; + logger.info(logtag(), "Success."); + + return true; + } + + string name() + { + return "EmptyFlashTest"; + } + + private: + bool subtest1() + { + uint32_t index = Base::controller_getBlockIndex(); + + uint8_t rand_buff1[FLASH_BUFFER_SIZE]; + uint8_t rand_buff2[FLASH_BUFFER_SIZE]; + Base::multi->addData(rand_buff1, FLASH_BUFFER_SIZE); + Base::multi->addData(rand_buff2, FLASH_BUFFER_SIZE); + + Thread::sleep(500); //Wait for data to be written + + logger.info(logtag(), "Reading blocks."); + FlashDataBlock read_block; + + if(!Base::controller_readDataBlock(index, &read_block)) + { + logger.error(logtag(), "Error reading first block."); + return false; + } + + if(memcmp(rand_buff1, &(read_block.buffer), FLASH_BUFFER_SIZE) != 0) + { + logger.error(logtag(), "Read block 1 is not the same as written one."); + return false; + } + + if(!Base::controller_readDataBlock(index+1, &read_block)) + { + logger.error(logtag(), "Error reading second block."); + return false; + } + + if(memcmp(rand_buff2, &(read_block.buffer), FLASH_BUFFER_SIZE) != 0) + { + logger.error(logtag(), "Read block 2 is not the same as written one."); + return false; + } + + return true; + } + + static string logtag() + { + return "MultiFlashCTRLTest"; + } +}; + +} //namespace flashmemorytests +} //namespace testing + +#endif /*FLASHCONTROLLERTESTS_H*/ diff --git a/src/tests/drivers/flashmemory/mockup/FlashDriver.h b/src/tests/drivers/flashmemory/mockup/FlashDriver.h new file mode 100644 index 0000000000000000000000000000000000000000..18e94c176aa194a65053d6ae82ded4b63a68c9ad --- /dev/null +++ b/src/tests/drivers/flashmemory/mockup/FlashDriver.h @@ -0,0 +1,598 @@ +#ifndef FLASHDRIVER_H +#define FLASHDRIVER_H + +#include <miosix.h> +#include <Singleton.h> +#include <diagnostic/NewLogger.h> + +using miosix::Thread; +using miosix::Mode; +using logging::logger; + +//Forward declaration +namespace testing +{ +namespace flashmemorytests +{ +template<typename> +class FlashTest; +} +} + +namespace flashmemory +{ + +static const uint32_t PAGES_PER_SUBSECTOR = 4U; +static const uint32_t PAGES_PER_SECTOR = 16U; +static const uint32_t SUBSECTORS_PER_SECTOR = 4U; + +static const uint32_t SUBSECTORS_NUM = 48U; +static const uint32_t SECTORS_NUM = 12U; +static const uint32_t PAGE_SIZE = 256U; + +static const uint32_t SUBSECTOR_SIZE = PAGE_SIZE * PAGES_PER_SUBSECTOR; +static const uint32_t SECTOR_SIZE = PAGE_SIZE * PAGES_PER_SECTOR; + +static const uint32_t BANK_SIZE = SECTOR_SIZE * SECTORS_NUM / 2; +static const uint32_t MEMORY_SIZE = SECTOR_SIZE * SECTORS_NUM; + +/** + * @brief Definitions for results of various flash operations. + * + * Definitions for result flags of various flash operations. + */ + +enum OpResultFlags +{ + RESULT_OK = 0x00, + + + RESULT_F_OUT_OF_MEMORY = 0x01, + RESULT_F_CHECK_FAIL = 0x04, + + + //Bits reserved for future use + RESULT_F_UNUSED_1 = 0x08, + RESULT_F_UNUSED_2 = 0x40, + RESULT_F_UNUSED_3 = 0x80, + + + /** + * These bits are in the same positions of the corresponding bits in the + * FLAG STATUS REGISTER + */ + RESULT_F_PROTECTION_ERROR = 0x02, + RESULT_F_PROGRAM_ERROR = 0x10, + RESULT_F_ERASE_ERROR = 0x20 +}; + +template<typename Bus> +class FlashDriver : Singleton<FlashDriver<Bus>> +{ + typedef Singleton<FlashDriver<Bus>> SingletonType; + + friend class Singleton<FlashDriver<Bus>>; + + template<typename > + friend class FlashDriverTest; + + template<typename > + friend class testing::flashmemorytests::FlashTest; + + public: + + ~FlashDriver() + { + delete fake_memory_; + } + + /** + * @brief Read n bytes into a buffer, starting from the specified address + * + * @param result + * @param address Starting address + * @param buf Buffer to read data into + * @param size Number of bytes to read + */ + void read(uint8_t *result, uint32_t address, uint8_t* buf, uint32_t size) + { + if (address + size > MEMORY_SIZE) + { + logger.error(logtag(), "Read outside of memory bounds addr + size: ", + address + size, " MEMSIZE: ", MEMORY_SIZE); + *result = RESULT_F_OUT_OF_MEMORY; + return; + } + + uint32_t rsize; + if (address < BANK_SIZE) + rsize = std::min(size, BANK_SIZE - address); + else + rsize = size; + + //uint8_t addr_buf[4]; + //addr_to_buf(addr_buf, address); + + fakeRead(address, buf, rsize); + //Bus::read(READ, addr_buf, buf, 4, rsize); + + size -= rsize; + + //This means that we reached the end of the first die but we still need + //to read some data. + if (size > 0) + { + address += rsize; + buf += rsize; + + //addr_to_buf(addr_buf, address); + + //Read the remaining bytes + fakeRead(address, buf, size); + //Bus::read(READ, addr_buf, buf, 4, size); + } + + *result = RESULT_OK; + } + + /** + * @brief Writes data on the flash starting from the specified address. + * + * @param address + * @param data + * @param size + */ + void write(uint8_t *result, uint32_t address, uint8_t* data, uint32_t size) + { + //Do not write outside of the memory + if (address + size > MEMORY_SIZE) + { + *result = RESULT_F_OUT_OF_MEMORY; + return; + } + + *result = RESULT_OK; + + //uint8_t addr_buf[4], status; + + uint32_t next_page, wsize; + + do + { + next_page = (address / 256) * 256 + 256; + + wsize = std::min(next_page - address, size); + + //addr_to_buf(addr_buf, address); + //_write_enable(); + + fakeWrite(address, data, wsize); + /*Bus::write(PROGRAM_PAGE, addr_buf, data, 4, wsize); + + do { + status = _read_flag_status_reg(); + }while((status & FSR_PRG_ERS_CTRL) == 0);*/ + + data += wsize; + size -= wsize; + address = next_page; + + } while (size > 0 && *result == RESULT_OK); + + /*//Clear flag status register if an error occurred + if(*result != RESULT_OK) + { + _clear_flag_status_reg(); + + //Manually reset write_enable latch + if((*result & RESULT_PROTECTION_ERROR) > 0) + { + _write_disable(); + } + }*/ + } + + /** + * @brief Writes data on the flash starting from the specified address, then + * checks if everything was written correctly. + * + * @param address + * @param data + * @param size + */ + void writeAndCheck(uint8_t *result, uint32_t address, uint8_t* data, + uint32_t size) + { //TODO: size_t + write(result, address, data, size); + if (*result == RESULT_OK) + { + uint8_t *check = new uint8_t[size]; + read(result, address, check, size); + for (uint32_t i = 0; i < size; i++) + { + if (*check++ != *data++) + { + //Something was not written/read correctly. + *result = RESULT_F_CHECK_FAIL; + break; + } + } + } + } + + /** + * @brief Writes data on the flash starting from the specified address, only + * if the provided data fits in a single page. + * + * Writes data on the flash starting from the specified address, only + * if the provided data fits in a single page. If too much data is provided + * nothing will be written and result will be set to FLASH_OUT_OF_RANGE. + * + * @param result + * @param address + * @param data + * @param size + */ + void programPage(uint8_t *result, uint32_t address, uint8_t* data, + uint32_t size) + { + /*//Do now wrap around a page and do not try to write outside of the memory + uint32_t next_page = (address / 256)*256 + 256; + if(address + size > next_page || address > MEMORY_SIZE) + { + *result = RESULT_OUT_OF_RNG; + return; + } + + uint8_t addr_buf[4], status; + addr_to_buf(addr_buf, address); + + _write_enable(); + Bus::write(PROGRAM_PAGE, addr_buf, data, 4, size); + + do { + status = _read_flag_status_reg(); + }while((status & FSR_PRG_ERS_CTRL) == 0); + + *result = status & (FSR_PROGRAM | FSR_PROTECTION); + + //Clear flag status register if an error has been encountered + if(*result != RESULT_OK) + { + _clear_flag_status_reg(); + + //Manually reset write_enable latch + if((*result & RESULT_PROTECTION_ERROR) > 0) + { + _write_disable(); + } + }*/ + } + + /** + * @brief Enables or disables read only mode + */ + void setReadOnly(bool readonly = true) + { + read_only_ = readonly; + } + + bool isReadOnly() const + { + return read_only_; + } + + /** + * @brief Enable the next erase operation. Call this before every erase op. + */ + void enableErase() + { + m_erase_enable_ = true; + } + + /** + * @brief Erases the subsector containing the specified address. For the operation + * to be successful, enable_erase_op must be called before this. + * + * @warning Estimated execution time is slightly less than 1 second. + * + * @param result + * @param address + */ + void eraseSubsector(uint8_t* result, uint32_t address) + { + erase(result, SUBSECTOR_ERASE, address); + } + + /** + * @brief Erases the sector containing the specified address. For the operation + * to be successful, enable_erase_op must be called before this. + * @warning Estimated execution time is about 2 seconds. + * + * @param result + * @param address + */ + void eraseSector(uint8_t* result, uint32_t address) + { + erase(result, SECTOR_ERASE, address); + } + + /** + * @brief Erases the entire die containing the specified address. For the operation + * to be successful, enable_erase_op must be called before this. + * @warning Estimated execution time is more than 4 minutes. + * + * @param result + * @param die: 0 to erase the first die, 1 to erase the second. + */ + void eraseDie(uint8_t* result, uint8_t die) + { + if (die == 0) + { + erase(result, DIE_ERASE, 0); + } else if (die == 1) + { + erase(result, DIE_ERASE, BANK_SIZE); + } + } + + /** + * Reads id information for this flash memory. + * @param buf Buffer with at least 20 bytes + */ + void readId(uint8_t* buf) + { + //Bus::read(READ_ID, buf, 20); + } + + /** + * @brief Software reset for the memory. All volatile bits are set to their + * default value. + */ + void softReset() + { + /*Bus::write(RESET_ENABLE); + usleep(5); + Bus::write(RESET_MEMORY); + _wait_until_ready();*/ + } + + private: + + FlashDriver() + { + printf("Mock memory size: %d\n", (int) MEMORY_SIZE); + memset((void*) (fake_memory_), 0xFF, MEMORY_SIZE); + } + + void erase(uint8_t *result, uint8_t erase_cmd, uint32_t address) + { + if (address > MEMORY_SIZE) + { + *result = RESULT_F_OUT_OF_MEMORY; + return; + } + + if (!m_erase_enable_) + { + *result = RESULT_F_ERASE_ERROR; + return; + } + + //uint8_t addr_buf[4], status; + //addr_to_buf(addr_buf, address); + *result = RESULT_OK; + switch (erase_cmd) + { + case SUBSECTOR_ERASE: + address = address / SUBSECTOR_SIZE * SUBSECTOR_SIZE; + fakeErase(address, SUBSECTOR_SIZE); + break; + case SECTOR_ERASE: + address = address / SECTOR_SIZE * SECTOR_SIZE; + fakeErase(address, SECTOR_SIZE); + break; + case DIE_ERASE: + address = address / BANK_SIZE * BANK_SIZE; + fakeErase(address, BANK_SIZE); + break; + default: + *result = RESULT_F_ERASE_ERROR; + } + + m_erase_enable_ = false; + } + + /** + * @brief Checks if a program operation is still in progress. + * @param result + */ + bool isBusy() + { + return false; //_read_flag_status_reg() & FSR_PRG_ERS_CTRL; + } + + /** + * @brief Waits until the memory is ready to perform a new write/erase op. + * @param result + */ + void waitUntilReady() + { + /*while((_read_flag_status_reg() & FSR_PRG_ERS_CTRL) == 0) + { + + }*/ + } + + uint8_t readStatusReg() + { + return 0; + /*uint8_t ret = Bus::read(READ_STATUS_REG); + return ret;*/ + } + + void writeStatusReg(uint8_t val) + { + /*_write_enable(); + Bus::write(WRITE_STATUS_REG, val); + _write_disable(); + _wait_until_ready();*/ + } + + uint8_t readFlagStatusReg() + { + /*uint8_t ret = Bus::read(READ_FLAG_STATUS_REG); + return ret;*/ + return 0; + } + + void clearFlagStatusReg() + { + //Bus::write(CLEAR_FLAG_STATUS_REG); + } + + uint16_t readConfigReg() + { + /*uint8_t buf[2]; + Bus::read(READ_NV_CONFIG_REG, buf, 2); + + uint16_t res = buf[0] | (buf[1] << 8);*/ + + return 0; + } + + void writeConfigReg(uint16_t val) + { + /* + * Mask some of the bits of the config register because + * we don't want to change them by accident. + */ + /*val = val | 0x0FEC; + + uint8_t buf[2]; + buf[0] = val & 0xFF; + buf[1] = (val >> 8) & 0xFF; + + _write_enable(); + Bus::write(WRITE_NV_CONFIG_REG, buf, 2); + _write_disable(); + _wait_until_ready();*/ + } + + void writeEnable() + { + /*if(!m_read_only) { + Bus::write(WRITE_ENABLE); + }*/ + } + + void writeDisable() + { + //Bus::write(WRITE_DISABLE); + } + + static void addrToBuf(uint8_t* buf, uint32_t addr) + { + for (int i = 0; i < 4; i++) + { + buf[3 - i] = (addr >> 8 * i) & 0xFF; + } + } + + void fakeRead(uint32_t address, uint8_t* data, uint32_t size) + { + //printf("Reading %d bytes from %d \n", (int)size, (int)address); + if (address + size > MEMORY_SIZE) + { + return; + } + + memcpy((void*) (data), (void*) (fake_memory_ + address), size); + } + + void fakeWrite(uint32_t address, uint8_t* data, uint32_t size) + { + if (address + size > MEMORY_SIZE) + { + return; + } + + for (uint32_t i = 0; i < size; i++) + { + fake_memory_[address + i] &= data[i]; + } + + //memcpy((void*)(fake_memory_ + address), (void*)(data), size); + } + + void fakeErase(uint32_t address, uint32_t size) + { + if (address + size > MEMORY_SIZE) + { + return; + } + + memset((void*) (fake_memory_ + address), 0xFF, size); + } + + static std::string logtag() + { + return "FlashDriver"; + } + + enum Commands + { + READ_ID = 0x9F, + + WRITE_ENABLE = 0x06, + WRITE_DISABLE = 0x04, + + READ_STATUS_REG = 0x05, + WRITE_STATUS_REG = 0x01, + + READ_FLAG_STATUS_REG = 0x70, + CLEAR_FLAG_STATUS_REG = 0x50, + + //non-volatile configuration register + READ_NV_CONFIG_REG = 0xB5, + WRITE_NV_CONFIG_REG = 0xB1, + + //volatile configuration register + WRITE_VOL_CONFIG_REG = 0x85, + READ_VOL_CONFIG_REG = 0x81, + + READ_EXT_ADDRESS_REG = 0xC8, + WRITE_EXT_ADDRESS_REG = 0xC5, + + RESET_ENABLE = 0x66, + RESET_MEMORY = 0x99, + + READ = 0x03, + PROGRAM_PAGE = 0x02, + + SUBSECTOR_ERASE = 0x20, + SECTOR_ERASE = 0xD8, + DIE_ERASE = 0xC4 + }; + + uint8_t* fake_memory_ = new uint8_t[MEMORY_SIZE]; + bool read_only_ = false; + bool m_erase_enable_ = false; + + /** + * Flag status register bit definitions + */ + static const uint8_t FSR_ADDRESSING_MODE = 0x01; + static const uint8_t FSR_PROTECTION = 0x02; + static const uint8_t FSR_PROGRAM_SUSPEND = 0x04; + static const uint8_t FSR_VPP = 0x08; + static const uint8_t FSR_PROGRAM = 0x10; + static const uint8_t FSR_ERASE = 0x20; + static const uint8_t FSR_ERASE_SUSPEND = 0x40; + static const uint8_t FSR_PRG_ERS_CTRL = 0x80; +}; + +} //namespace flashmemory + + +#endif /* FLASHDRIVER_H */ diff --git a/src/tests/drivers/test-IMU.cpp b/src/tests/drivers/test-IMU.cpp index 06e5aadd8972947e504ddfc34942c0c385c692ce..ee8fe8702c820c816c48b56c0b779329c8e2fad4 100644 --- a/src/tests/drivers/test-IMU.cpp +++ b/src/tests/drivers/test-IMU.cpp @@ -1,4 +1,4 @@ -#include <sensors/LSM6DS3H.h> +#include <sensors/LSM6DS3H/LSM6DS3H.h> // SPI1 typedef miosix::Gpio<GPIOA_BASE, 5> GpioSck; diff --git a/src/tests/drivers/test-lsm.cpp b/src/tests/drivers/test-lsm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..641d490a1510c60bd3ae1fa42ac2a88aeaf4476a --- /dev/null +++ b/src/tests/drivers/test-lsm.cpp @@ -0,0 +1,82 @@ +/* Copyright (c) 2019 Skyward Experimental Rocketry + * Authors: Nuno Barcellos + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <Common.h> +#include <drivers/BusTemplate.h> +#include <interfaces-impl/hwmapping.h> +#include <sensors/LSM6DS3H/LSM6DS3H.h> + +#include <drivers/spi/SensorSpi.h> +#include <sensors/SensorSampling.h> +#include <math/Stats.h> +#include <diagnostic/CpuMeter.h> + +using namespace miosix; +using namespace miosix::interfaces; + +/* SPI1 binding to the sensor */ +typedef BusSPI<1,spi1::mosi,spi1::miso,spi1::sck> busSPI1; +typedef ProtocolSPI<busSPI1,sensors::lsm6ds3h::cs> spiLSM6DS3H0_a; +typedef LSM6DS3H<spiLSM6DS3H0_a> lsm6ds3h_t; + +int main() +{ + SimpleSensorSampler sampler; + spiLSM6DS3H0_a::init(); + + lsm6ds3h_t* lsm6ds3h = new lsm6ds3h_t(3,3); + + if(lsm6ds3h->init()) + { + printf("[LSM6DS3H] Init succeeded\n" ); + sampler.AddSensor(lsm6ds3h); + } + else + { + printf("[LSM6DS3H] Init failed\n"); + + while(!lsm6ds3h->init()) { + printf("[LSM6DS3H] Init failed\n"); + Thread::sleep(1000); + } + + } + + + while(true) + { + sampler.Update(); + + // const Vec3* last_data = lsm6ds3h->gyroDataPtr(); + // printf("%f %f %f\n", last_data->getX(), last_data->getY(), + // last_data->getZ()); + + const Vec3* last_data = lsm6ds3h->accelDataPtr(); + printf("%f %f %f\n", last_data->getX(), last_data->getY(), + last_data->getZ()); + + // const float* last_temp = lsm6ds3h->tempDataPtr(); + // printf("temp: %f\n", *last_temp); + + Thread::sleep(100); + } +} \ No newline at end of file diff --git a/src/tests/drivers/test-piksi.cpp b/src/tests/drivers/test-piksi.cpp index c4a604f5190858001bcd8184c12f33112071ecaf..b4e01bc9e9c3983c7bcb311b4bbf29b12195000e 100644 --- a/src/tests/drivers/test-piksi.cpp +++ b/src/tests/drivers/test-piksi.cpp @@ -57,7 +57,7 @@ int main() << " lon: " << gps.longitude << " h: " << gps.height << " vn: " << gps.velocityNorth << " ve: " << gps.velocityEast << " vd: " << gps.velocityDown << " ns: " << gps.numSatellites - << endl; + << " now: " << now; } catch (...) { diff --git a/src/tests/misc/xbee-bitrate.cpp b/src/tests/misc/xbee-bitrate.cpp index 7fbe56c59121279e97df6df67b1a083158339c41..229fa358f1f54b48fc16f47d6aeee8b5aaf8b239 100644 --- a/src/tests/misc/xbee-bitrate.cpp +++ b/src/tests/misc/xbee-bitrate.cpp @@ -95,7 +95,7 @@ bool sendPacket(uint8_t size) snd_buf[0] = '{'; snd_buf[size-1] = '}'; - + for(int i = 0; i < size - 2; i++) { snd_buf[i+1] = ((snd_cntr + i) % 75 ) + 48; //ASCII char from 0 to z @@ -145,7 +145,7 @@ int main() } results[i] = getTick() - start; } - printf("Results for %d byte packet size:\n"); + printf("Results for %d byte packet size:\n", pkt_size); for(int i = 0; i < PKT_NUM; i++) { printf("%d\n", (int)results[i]);