From 32806da6f5647ac637fa7d48aa9c221b091ab35e Mon Sep 17 00:00:00 2001
From: guowenxue <guowenxue@gmail.com>
Date: Mon, 03 Apr 2023 23:45:44 +0800
Subject: [PATCH] Add ISKBoard_QCTester project

---
 Production/ISKBoard_QCTester/Core/ESP/atcmd.c                                 |  287 +
 Production/ISKBoard_QCTester/Core/Src/stm32l4xx_it.c                          |  293 +
 Production/ISKBoard_QCTester/Core/Board/miscdev.c                             |  172 
 Production/ISKBoard_QCTester/Core/ESP/atcmd.h                                 |   88 
 Production/ISKBoard_QCTester/Core/ESP/at-esp32.h                              |  137 
 Production/ISKBoard_QCTester/.project                                         |   32 
 Production/ISKBoard_QCTester/Core/Board/ringbuf.c                             |  109 
 Production/ISKBoard_QCTester/Core/Src/gpio.c                                  |  130 
 Production/ISKBoard_QCTester/Core/Board/ringbuf.h                             |   58 
 Production/ISKBoard_QCTester/.settings/stm32cubeide.project.prefs             |    4 
 Production/ISKBoard_QCTester/STM32L431RCTX_FLASH.ld                           |  187 +
 Production/ISKBoard_QCTester/Core/Board/comport.c                             |  238 +
 Production/ISKBoard_QCTester/Core/Src/tim.c                                   |  221 +
 Production/ISKBoard_QCTester/Core/Src/usart.c                                 |  409 ++
 Production/ISKBoard_QCTester/Core/Src/adc.c                                   |  163 
 Production/ISKBoard_QCTester/Core/Board/comport.h                             |   45 
 Production/ISKBoard_QCTester/Core/Board/hal_oled.c                            |  280 +
 Production/ISKBoard_QCTester/Core/Src/main.c                                  |  703 ++++
 Production/ISKBoard_QCTester/Core/Board/ws2812b.h                             |   48 
 Production/ISKBoard_QCTester/Core/Board/sht20.h                               |   19 
 Production/ISKBoard_QCTester/Core/Board/sht20.c                               |  211 +
 Production/ISKBoard_QCTester/.settings/language.settings.xml                  |   25 
 Production/ISKBoard_QCTester/Core/Board/ds18b20.h                             |   13 
 Production/ISKBoard_QCTester/Core/Inc/stm32l4xx_it.h                          |   72 
 Production/ISKBoard_QCTester/Core/Src/spi.c                                   |  121 
 Production/ISKBoard_QCTester/Core/Board/ws2812b.c                             |  169 
 Production/ISKBoard_QCTester/Core/Startup/startup_stm32l431rctx.s             |  461 ++
 Production/ISKBoard_QCTester/Core/Board/ds18b20.c                             |  237 +
 Production/ISKBoard_QCTester/Core/Inc/can.h                                   |   62 
 Production/ISKBoard_QCTester/Core/Src/system_stm32l4xx.c                      |  332 +
 Production/ISKBoard_QCTester/ISKBoard_QCTester Debug.launch                   |   79 
 Production/ISKBoard_QCTester/Core/Inc/usart.h                                 |   61 
 Production/ISKBoard_QCTester/Core/Board/isl1208.h                             |   37 
 Production/ISKBoard_QCTester/Core/Board/isl1208.c                             |  329 +
 Production/ISKBoard_QCTester/Core/Board/miscdev.h                             |   80 
 Production/ISKBoard_QCTester/Core/ESP/esp32.h                                 |   32 
 Production/ISKBoard_QCTester/.settings/org.eclipse.cdt.core.prefs             |    6 
 Production/ISKBoard_QCTester/Core/Inc/adc.h                                   |   52 
 Production/ISKBoard_QCTester/Core/Inc/spi.h                                   |   52 
 Production/ISKBoard_QCTester/Core/Inc/gpio.h                                  |   49 
 Production/ISKBoard_QCTester/Core/ESP/esp32.c                                 |  198 +
 Production/ISKBoard_QCTester/ISKBoard_QCTester.ioc                            |  367 ++
 Production/ISKBoard_QCTester/Core/Src/stm32l4xx_hal_msp.c                     |   81 
 Production/ISKBoard_QCTester/Core/Board/font_oled.h                           |  205 +
 Production/ISKBoard_QCTester/Core/Board/i2c_bitbang.c                         |  354 ++
 Production/ISKBoard_QCTester/Core/Board/i2c_bitbang.h                         |   84 
 Production/ISKBoard_QCTester/Core/Src/syscalls.c                              |  176 +
 Production/ISKBoard_QCTester/Core/Board/hal_oled.h                            |   45 
 Production/ISKBoard_QCTester/Core/Board/w25q32.c                              | 1025 +++++
 Production/ISKBoard_QCTester/Core/Board/w25q32.h                              |   60 
 Production/ISKBoard_QCTester/Core/Src/can.c                                   |  293 +
 Production/ISKBoard_QCTester/.cproject                                        |  182 +
 Production/ISKBoard_QCTester/.settings/com.st.stm32cube.ide.mcu.sfrview.prefs |    2 
 Production/ISKBoard_QCTester/Core/Inc/tim.h                                   |   57 
 Production/ISKBoard_QCTester/Core/ESP/logger.h                                |   31 
 Production/ISKBoard_QCTester/Core/Src/sysmem.c                                |   79 
 Production/ISKBoard_QCTester/.mxproject                                       |   37 
 Production/ISKBoard_QCTester/Core/Inc/main.h                                  |  116 
 Production/ISKBoard_QCTester/Core/ESP/at-esp32.c                              |  382 ++
 Production/ISKBoard_QCTester/Core/Inc/stm32l4xx_hal_conf.h                    |  482 ++
 60 files changed, 10,359 insertions(+), 0 deletions(-)

diff --git a/Production/ISKBoard_QCTester/.cproject b/Production/ISKBoard_QCTester/.cproject
new file mode 100644
index 0000000..6230a65
--- /dev/null
+++ b/Production/ISKBoard_QCTester/.cproject
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
+	<storageModule moduleId="org.eclipse.cdt.core.settings">
+		<cconfiguration id="com.st.stm32cube.ide.mcu.gnu.managedbuild.config.exe.debug.88492984">
+			<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.config.exe.debug.88492984" moduleId="org.eclipse.cdt.core.settings" name="Debug">
+				<externalSettings/>
+				<extensions>
+					<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+					<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+				</extensions>
+			</storageModule>
+			<storageModule moduleId="cdtBuildSystem" version="4.0.0">
+				<configuration artifactExtension="elf" artifactName="${ProjName}" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.exe" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.exe,org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.debug" cleanCommand="rm -rf" description="" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.config.exe.debug.88492984" name="Debug" parent="com.st.stm32cube.ide.mcu.gnu.managedbuild.config.exe.debug">
+					<folderInfo id="com.st.stm32cube.ide.mcu.gnu.managedbuild.config.exe.debug.88492984." name="/" resourcePath="">
+						<toolChain id="com.st.stm32cube.ide.mcu.gnu.managedbuild.toolchain.exe.debug.369660556" name="MCU ARM GCC" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.toolchain.exe.debug">
+							<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_mcu.2103933359" name="MCU" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_mcu" useByScannerDiscovery="true" value="STM32L431RCTx" valueType="string"/>
+							<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_cpuid.1757044456" name="CPU" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_cpuid" useByScannerDiscovery="false" value="0" valueType="string"/>
+							<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_coreid.324008652" name="Core" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_coreid" useByScannerDiscovery="false" value="0" valueType="string"/>
+							<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.fpu.1278791425" name="Floating-point unit" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.fpu" useByScannerDiscovery="true" value="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.fpu.value.fpv4-sp-d16" valueType="enumerated"/>
+							<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.floatabi.1485422730" name="Floating-point ABI" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.floatabi" useByScannerDiscovery="true" value="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.floatabi.value.hard" valueType="enumerated"/>
+							<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_board.421993750" name="Board" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_board" useByScannerDiscovery="false" value="genericBoard" valueType="string"/>
+							<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.defaults.1564597215" name="Defaults" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.defaults" useByScannerDiscovery="false" value="com.st.stm32cube.ide.common.services.build.inputs.revA.1.0.6 || Debug || true || Executable || com.st.stm32cube.ide.mcu.gnu.managedbuild.option.toolchain.value.workspace || STM32L431RCTx || 0 || 0 || arm-none-eabi- || ${gnu_tools_for_stm32_compiler_path} || ../Core/Inc | ../Drivers/STM32L4xx_HAL_Driver/Inc | ../Drivers/STM32L4xx_HAL_Driver/Inc/Legacy | ../Drivers/CMSIS/Device/ST/STM32L4xx/Include | ../Drivers/CMSIS/Include ||  ||  || USE_HAL_DRIVER | STM32L431xx ||  || Drivers | Core/Startup | Core ||  ||  || ${workspace_loc:/${ProjName}/STM32L431RCTX_FLASH.ld} || true || NonSecure ||  || secure_nsclib.o ||  || None ||  ||  || " valueType="string"/>
+							<option id="com.st.stm32cube.ide.mcu.debug.option.cpuclock.728278903" name="Cpu clock frequence" superClass="com.st.stm32cube.ide.mcu.debug.option.cpuclock" useByScannerDiscovery="false" value="80" valueType="string"/>
+							<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.targetplatform.602987071" isAbstract="false" osList="all" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.targetplatform"/>
+							<builder buildPath="${workspace_loc:/ISKBoard_BareMetal}/Debug" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.builder.206690691" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="Gnu Make Builder" parallelBuildOn="true" parallelizationNumber="optimal" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.builder"/>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.1852606975" name="MCU GCC Assembler" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler">
+								<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.option.debuglevel.878201567" name="Debug level" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.option.debuglevel" value="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.option.debuglevel.value.g3" valueType="enumerated"/>
+								<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.option.definedsymbols.1112296306" name="Define symbols (-D)" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.option.definedsymbols" valueType="definedSymbols">
+									<listOptionValue builtIn="false" value="DEBUG"/>
+								</option>
+								<inputType id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.input.231010816" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.input"/>
+							</tool>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.859911740" name="MCU GCC Compiler" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler">
+								<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.debuglevel.189626527" name="Debug level" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.debuglevel" useByScannerDiscovery="false" value="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.debuglevel.value.g3" valueType="enumerated"/>
+								<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.optimization.level.1740229019" name="Optimization level" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.optimization.level" useByScannerDiscovery="false"/>
+								<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.definedsymbols.1707283178" name="Define symbols (-D)" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.definedsymbols" useByScannerDiscovery="false" valueType="definedSymbols">
+									<listOptionValue builtIn="false" value="DEBUG"/>
+									<listOptionValue builtIn="false" value="USE_HAL_DRIVER"/>
+									<listOptionValue builtIn="false" value="STM32L431xx"/>
+								</option>
+								<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.includepaths.657546956" name="Include paths (-I)" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.includepaths" useByScannerDiscovery="false" valueType="includePath">
+									<listOptionValue builtIn="false" value="../Core/Inc"/>
+									<listOptionValue builtIn="false" value="../Core/ESP"/>
+									<listOptionValue builtIn="false" value="../Core/Board"/>
+									<listOptionValue builtIn="false" value="../Drivers/STM32L4xx_HAL_Driver/Inc"/>
+									<listOptionValue builtIn="false" value="../Drivers/STM32L4xx_HAL_Driver/Inc/Legacy"/>
+									<listOptionValue builtIn="false" value="../Drivers/CMSIS/Device/ST/STM32L4xx/Include"/>
+									<listOptionValue builtIn="false" value="../Drivers/CMSIS/Include"/>
+								</option>
+								<inputType id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.input.c.1387344889" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.input.c"/>
+							</tool>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.cpp.compiler.1111584846" name="MCU G++ Compiler" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.cpp.compiler">
+								<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.cpp.compiler.option.debuglevel.882978263" name="Debug level" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.cpp.compiler.option.debuglevel" useByScannerDiscovery="false" value="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.cpp.compiler.option.debuglevel.value.g3" valueType="enumerated"/>
+								<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.cpp.compiler.option.optimization.level.2038536387" name="Optimization level" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.cpp.compiler.option.optimization.level" useByScannerDiscovery="false"/>
+							</tool>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.linker.1128790366" name="MCU GCC Linker" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.linker">
+								<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.linker.option.script.1550603345" name="Linker Script (-T)" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.linker.option.script" value="${workspace_loc:/${ProjName}/STM32L431RCTX_FLASH.ld}" valueType="string"/>
+								<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.linker.option.otherflags.1903274547" name="Other flags" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.linker.option.otherflags" useByScannerDiscovery="false" valueType="stringList">
+									<listOptionValue builtIn="false" value="-u _printf_float"/>
+								</option>
+								<inputType id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.linker.input.1436834246" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.linker.input">
+									<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
+									<additionalInput kind="additionalinput" paths="$(LIBS)"/>
+								</inputType>
+							</tool>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.cpp.linker.704802323" name="MCU G++ Linker" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.cpp.linker"/>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.archiver.193635021" name="MCU GCC Archiver" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.archiver"/>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.size.1827847333" name="MCU Size" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.size"/>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objdump.listfile.1443246891" name="MCU Output Converter list file" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objdump.listfile"/>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objcopy.hex.226431864" name="MCU Output Converter Hex" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objcopy.hex"/>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objcopy.binary.1482121916" name="MCU Output Converter Binary" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objcopy.binary"/>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objcopy.verilog.1375124649" name="MCU Output Converter Verilog" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objcopy.verilog"/>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objcopy.srec.1439333328" name="MCU Output Converter Motorola S-rec" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objcopy.srec"/>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objcopy.symbolsrec.747940564" name="MCU Output Converter Motorola S-rec with symbols" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objcopy.symbolsrec"/>
+						</toolChain>
+					</folderInfo>
+					<sourceEntries>
+						<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Core"/>
+						<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Drivers"/>
+					</sourceEntries>
+				</configuration>
+			</storageModule>
+			<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+		</cconfiguration>
+		<cconfiguration id="com.st.stm32cube.ide.mcu.gnu.managedbuild.config.exe.release.602940913">
+			<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.config.exe.release.602940913" moduleId="org.eclipse.cdt.core.settings" name="Release">
+				<externalSettings/>
+				<extensions>
+					<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+					<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+				</extensions>
+			</storageModule>
+			<storageModule moduleId="cdtBuildSystem" version="4.0.0">
+				<configuration artifactExtension="elf" artifactName="${ProjName}" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.exe" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.exe,org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.release" cleanCommand="rm -rf" description="" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.config.exe.release.602940913" name="Release" parent="com.st.stm32cube.ide.mcu.gnu.managedbuild.config.exe.release">
+					<folderInfo id="com.st.stm32cube.ide.mcu.gnu.managedbuild.config.exe.release.602940913." name="/" resourcePath="">
+						<toolChain id="com.st.stm32cube.ide.mcu.gnu.managedbuild.toolchain.exe.release.1761512170" name="MCU ARM GCC" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.toolchain.exe.release">
+							<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_mcu.1475982704" name="MCU" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_mcu" useByScannerDiscovery="true" value="STM32L431RCTx" valueType="string"/>
+							<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_cpuid.1546642591" name="CPU" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_cpuid" useByScannerDiscovery="false" value="0" valueType="string"/>
+							<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_coreid.259727423" name="Core" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_coreid" useByScannerDiscovery="false" value="0" valueType="string"/>
+							<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.fpu.95383444" name="Floating-point unit" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.fpu" useByScannerDiscovery="true" value="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.fpu.value.fpv4-sp-d16" valueType="enumerated"/>
+							<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.floatabi.849128184" name="Floating-point ABI" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.floatabi" useByScannerDiscovery="true" value="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.floatabi.value.hard" valueType="enumerated"/>
+							<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_board.34757845" name="Board" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_board" useByScannerDiscovery="false" value="genericBoard" valueType="string"/>
+							<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.defaults.742851534" name="Defaults" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.defaults" useByScannerDiscovery="false" value="com.st.stm32cube.ide.common.services.build.inputs.revA.1.0.6 || Release || false || Executable || com.st.stm32cube.ide.mcu.gnu.managedbuild.option.toolchain.value.workspace || STM32L431RCTx || 0 || 0 || arm-none-eabi- || ${gnu_tools_for_stm32_compiler_path} || ../Core/Inc | ../Drivers/STM32L4xx_HAL_Driver/Inc | ../Drivers/STM32L4xx_HAL_Driver/Inc/Legacy | ../Drivers/CMSIS/Device/ST/STM32L4xx/Include | ../Drivers/CMSIS/Include ||  ||  || USE_HAL_DRIVER | STM32L431xx ||  || Drivers | Core/Startup | Core ||  ||  || ${workspace_loc:/${ProjName}/STM32L431RCTX_FLASH.ld} || true || NonSecure ||  || secure_nsclib.o ||  || None ||  ||  || " valueType="string"/>
+							<option id="com.st.stm32cube.ide.mcu.debug.option.cpuclock.1053420611" name="Cpu clock frequence" superClass="com.st.stm32cube.ide.mcu.debug.option.cpuclock" useByScannerDiscovery="false" value="80" valueType="string"/>
+							<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.targetplatform.733976577" isAbstract="false" osList="all" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.targetplatform"/>
+							<builder buildPath="${workspace_loc:/ISKBoard_BareMetal}/Release" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.builder.2046493537" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="Gnu Make Builder" parallelBuildOn="true" parallelizationNumber="optimal" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.builder"/>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.2146717068" name="MCU GCC Assembler" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler">
+								<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.option.debuglevel.1445316476" name="Debug level" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.option.debuglevel" value="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.option.debuglevel.value.g0" valueType="enumerated"/>
+								<inputType id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.input.1128139411" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.input"/>
+							</tool>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.342548357" name="MCU GCC Compiler" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler">
+								<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.debuglevel.121420081" name="Debug level" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.debuglevel" useByScannerDiscovery="false" value="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.debuglevel.value.g0" valueType="enumerated"/>
+								<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.optimization.level.1146109265" name="Optimization level" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.optimization.level" useByScannerDiscovery="false" value="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.optimization.level.value.os" valueType="enumerated"/>
+								<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.definedsymbols.995295969" name="Define symbols (-D)" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.definedsymbols" useByScannerDiscovery="false" valueType="definedSymbols">
+									<listOptionValue builtIn="false" value="USE_HAL_DRIVER"/>
+									<listOptionValue builtIn="false" value="STM32L431xx"/>
+								</option>
+								<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.includepaths.1392002630" name="Include paths (-I)" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.option.includepaths" useByScannerDiscovery="false" valueType="includePath">
+									<listOptionValue builtIn="false" value="../Core/Inc"/>
+									<listOptionValue builtIn="false" value="../Drivers/STM32L4xx_HAL_Driver/Inc"/>
+									<listOptionValue builtIn="false" value="../Drivers/STM32L4xx_HAL_Driver/Inc/Legacy"/>
+									<listOptionValue builtIn="false" value="../Drivers/CMSIS/Device/ST/STM32L4xx/Include"/>
+									<listOptionValue builtIn="false" value="../Drivers/CMSIS/Include"/>
+								</option>
+								<inputType id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.input.c.1778697518" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.input.c"/>
+							</tool>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.cpp.compiler.2056636271" name="MCU G++ Compiler" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.cpp.compiler">
+								<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.cpp.compiler.option.debuglevel.642105406" name="Debug level" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.cpp.compiler.option.debuglevel" useByScannerDiscovery="false" value="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.cpp.compiler.option.debuglevel.value.g0" valueType="enumerated"/>
+								<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.cpp.compiler.option.optimization.level.445443123" name="Optimization level" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.cpp.compiler.option.optimization.level" useByScannerDiscovery="false" value="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.cpp.compiler.option.optimization.level.value.os" valueType="enumerated"/>
+							</tool>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.linker.1064547196" name="MCU GCC Linker" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.linker">
+								<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.linker.option.script.1325893916" name="Linker Script (-T)" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.linker.option.script" value="${workspace_loc:/${ProjName}/STM32L431RCTX_FLASH.ld}" valueType="string"/>
+								<inputType id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.linker.input.2010997017" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.linker.input">
+									<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
+									<additionalInput kind="additionalinput" paths="$(LIBS)"/>
+								</inputType>
+							</tool>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.cpp.linker.236732362" name="MCU G++ Linker" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.cpp.linker"/>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.archiver.294540938" name="MCU GCC Archiver" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.archiver"/>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.size.15224461" name="MCU Size" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.size"/>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objdump.listfile.460101160" name="MCU Output Converter list file" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objdump.listfile"/>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objcopy.hex.209396082" name="MCU Output Converter Hex" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objcopy.hex"/>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objcopy.binary.1321642423" name="MCU Output Converter Binary" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objcopy.binary"/>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objcopy.verilog.1741144832" name="MCU Output Converter Verilog" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objcopy.verilog"/>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objcopy.srec.347073685" name="MCU Output Converter Motorola S-rec" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objcopy.srec"/>
+							<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objcopy.symbolsrec.1384506974" name="MCU Output Converter Motorola S-rec with symbols" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.objcopy.symbolsrec"/>
+						</toolChain>
+					</folderInfo>
+					<sourceEntries>
+						<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Core"/>
+						<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="Drivers"/>
+					</sourceEntries>
+				</configuration>
+			</storageModule>
+			<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+		</cconfiguration>
+	</storageModule>
+	<storageModule moduleId="org.eclipse.cdt.core.pathentry"/>
+	<storageModule moduleId="cdtBuildSystem" version="4.0.0">
+		<project id="ISKBoard_BareMetal.null.1382914116" name="ISKBoard_BareMetal"/>
+	</storageModule>
+	<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
+	<storageModule moduleId="scannerConfiguration">
+		<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+		<scannerConfigBuildInfo instanceId="com.st.stm32cube.ide.mcu.gnu.managedbuild.config.exe.release.602940913;com.st.stm32cube.ide.mcu.gnu.managedbuild.config.exe.release.602940913.;com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.342548357;com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.input.c.1778697518">
+			<autodiscovery enabled="false" problemReportingEnabled="true" selectedProfileId=""/>
+		</scannerConfigBuildInfo>
+		<scannerConfigBuildInfo instanceId="com.st.stm32cube.ide.mcu.gnu.managedbuild.config.exe.debug.88492984;com.st.stm32cube.ide.mcu.gnu.managedbuild.config.exe.debug.88492984.;com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.859911740;com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.input.c.1387344889">
+			<autodiscovery enabled="false" problemReportingEnabled="true" selectedProfileId=""/>
+		</scannerConfigBuildInfo>
+	</storageModule>
+	<storageModule moduleId="refreshScope"/>
+	<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
+</cproject>
\ No newline at end of file
diff --git a/Production/ISKBoard_QCTester/.mxproject b/Production/ISKBoard_QCTester/.mxproject
new file mode 100644
index 0000000..9cc521a
--- /dev/null
+++ b/Production/ISKBoard_QCTester/.mxproject
@@ -0,0 +1,37 @@
+[PreviousLibFiles]
+LibFiles=Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_adc.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_adc.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_adc_ex.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_def.h;Drivers\STM32L4xx_HAL_Driver\Inc\Legacy\stm32_hal_legacy.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_rcc.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_rcc_ex.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_bus.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_rcc.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_crs.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_system.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_utils.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_flash.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_flash_ex.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_flash_ramfunc.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_gpio.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_gpio_ex.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_gpio.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_dma.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_dma_ex.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_dma.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_dmamux.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_pwr.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_pwr_ex.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_pwr.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_cortex.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_cortex.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_exti.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_exti.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_can.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_uart.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_usart.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_lpuart.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_uart_ex.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_spi.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_spi_ex.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_tim.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_tim_ex.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_tim.h;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_adc.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_adc_ex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_rcc.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_rcc_ex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_flash.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_flash_ex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_flash_ramfunc.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_gpio.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_dma.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_dma_ex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_pwr.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_pwr_ex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_cortex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_exti.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_can.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_uart.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_uart_ex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_spi.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_spi_ex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_tim.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_tim_ex.c;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_adc.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_adc.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_adc_ex.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_def.h;Drivers\STM32L4xx_HAL_Driver\Inc\Legacy\stm32_hal_legacy.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_rcc.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_rcc_ex.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_bus.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_rcc.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_crs.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_system.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_utils.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_flash.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_flash_ex.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_flash_ramfunc.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_gpio.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_gpio_ex.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_gpio.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_dma.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_dma_ex.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_dma.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_dmamux.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_pwr.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_pwr_ex.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_pwr.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_cortex.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_cortex.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_exti.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_exti.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_can.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_uart.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_usart.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_lpuart.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_uart_ex.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_spi.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_spi_ex.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_tim.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_tim_ex.h;Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_ll_tim.h;Drivers\CMSIS\Device\ST\STM32L4xx\Include\stm32l431xx.h;Drivers\CMSIS\Device\ST\STM32L4xx\Include\stm32l4xx.h;Drivers\CMSIS\Device\ST\STM32L4xx\Include\system_stm32l4xx.h;Drivers\CMSIS\Device\ST\STM32L4xx\Source\Templates\system_stm32l4xx.c;Drivers\CMSIS\Include\cmsis_armcc.h;Drivers\CMSIS\Include\cmsis_armclang.h;Drivers\CMSIS\Include\cmsis_armclang_ltm.h;Drivers\CMSIS\Include\cmsis_compiler.h;Drivers\CMSIS\Include\cmsis_gcc.h;Drivers\CMSIS\Include\cmsis_iccarm.h;Drivers\CMSIS\Include\cmsis_version.h;Drivers\CMSIS\Include\core_armv81mml.h;Drivers\CMSIS\Include\core_armv8mbl.h;Drivers\CMSIS\Include\core_armv8mml.h;Drivers\CMSIS\Include\core_cm0.h;Drivers\CMSIS\Include\core_cm0plus.h;Drivers\CMSIS\Include\core_cm1.h;Drivers\CMSIS\Include\core_cm23.h;Drivers\CMSIS\Include\core_cm3.h;Drivers\CMSIS\Include\core_cm33.h;Drivers\CMSIS\Include\core_cm35p.h;Drivers\CMSIS\Include\core_cm4.h;Drivers\CMSIS\Include\core_cm7.h;Drivers\CMSIS\Include\core_sc000.h;Drivers\CMSIS\Include\core_sc300.h;Drivers\CMSIS\Include\mpu_armv7.h;Drivers\CMSIS\Include\mpu_armv8.h;Drivers\CMSIS\Include\tz_context.h;
+
+[PreviousUsedCubeIDEFiles]
+SourceFiles=Core\Src\main.c;Core\Src\gpio.c;Core\Src\adc.c;Core\Src\can.c;Core\Src\usart.c;Core\Src\spi.c;Core\Src\tim.c;Core\Src\stm32l4xx_it.c;Core\Src\stm32l4xx_hal_msp.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_adc.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_adc_ex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_rcc.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_rcc_ex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_flash.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_flash_ex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_flash_ramfunc.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_gpio.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_dma.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_dma_ex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_pwr.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_pwr_ex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_cortex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_exti.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_can.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_uart.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_uart_ex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_spi.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_spi_ex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_tim.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_tim_ex.c;Drivers\CMSIS\Device\ST\STM32L4xx\Source\Templates\system_stm32l4xx.c;Core\Src\system_stm32l4xx.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_adc.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_adc_ex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_rcc.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_rcc_ex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_flash.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_flash_ex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_flash_ramfunc.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_gpio.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_dma.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_dma_ex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_pwr.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_pwr_ex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_cortex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_exti.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_can.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_uart.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_uart_ex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_spi.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_spi_ex.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_tim.c;Drivers\STM32L4xx_HAL_Driver\Src\stm32l4xx_hal_tim_ex.c;Drivers\CMSIS\Device\ST\STM32L4xx\Source\Templates\system_stm32l4xx.c;Core\Src\system_stm32l4xx.c;;;
+HeaderPath=Drivers\STM32L4xx_HAL_Driver\Inc;Drivers\STM32L4xx_HAL_Driver\Inc\Legacy;Drivers\CMSIS\Device\ST\STM32L4xx\Include;Drivers\CMSIS\Include;Core\Inc;
+CDefines=USE_HAL_DRIVER;STM32L431xx;USE_HAL_DRIVER;USE_HAL_DRIVER;
+
+[PreviousGenFiles]
+AdvancedFolderStructure=true
+HeaderFileListSize=9
+HeaderFiles#0=..\Core\Inc\gpio.h
+HeaderFiles#1=..\Core\Inc\adc.h
+HeaderFiles#2=..\Core\Inc\can.h
+HeaderFiles#3=..\Core\Inc\usart.h
+HeaderFiles#4=..\Core\Inc\spi.h
+HeaderFiles#5=..\Core\Inc\tim.h
+HeaderFiles#6=..\Core\Inc\stm32l4xx_it.h
+HeaderFiles#7=..\Core\Inc\stm32l4xx_hal_conf.h
+HeaderFiles#8=..\Core\Inc\main.h
+HeaderFolderListSize=1
+HeaderPath#0=..\Core\Inc
+HeaderFiles=;
+SourceFileListSize=9
+SourceFiles#0=..\Core\Src\gpio.c
+SourceFiles#1=..\Core\Src\adc.c
+SourceFiles#2=..\Core\Src\can.c
+SourceFiles#3=..\Core\Src\usart.c
+SourceFiles#4=..\Core\Src\spi.c
+SourceFiles#5=..\Core\Src\tim.c
+SourceFiles#6=..\Core\Src\stm32l4xx_it.c
+SourceFiles#7=..\Core\Src\stm32l4xx_hal_msp.c
+SourceFiles#8=..\Core\Src\main.c
+SourceFolderListSize=1
+SourcePath#0=..\Core\Src
+SourceFiles=;
+
diff --git a/Production/ISKBoard_QCTester/.project b/Production/ISKBoard_QCTester/.project
new file mode 100644
index 0000000..3d050a3
--- /dev/null
+++ b/Production/ISKBoard_QCTester/.project
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>ISKBoard_QCTester</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
+			<triggers>clean,full,incremental,</triggers>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
+			<triggers>full,incremental,</triggers>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>com.st.stm32cube.ide.mcu.MCUProjectNature</nature>
+		<nature>com.st.stm32cube.ide.mcu.MCUCubeProjectNature</nature>
+		<nature>org.eclipse.cdt.core.cnature</nature>
+		<nature>com.st.stm32cube.ide.mcu.MCUCubeIdeServicesRevAev2ProjectNature</nature>
+		<nature>com.st.stm32cube.ide.mcu.MCUAdvancedStructureProjectNature</nature>
+		<nature>com.st.stm32cube.ide.mcu.MCUSingleCpuProjectNature</nature>
+		<nature>com.st.stm32cube.ide.mcu.MCURootProjectNature</nature>
+		<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
+		<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
+	</natures>
+</projectDescription>
diff --git a/Production/ISKBoard_QCTester/.settings/com.st.stm32cube.ide.mcu.sfrview.prefs b/Production/ISKBoard_QCTester/.settings/com.st.stm32cube.ide.mcu.sfrview.prefs
new file mode 100644
index 0000000..98a69fc
--- /dev/null
+++ b/Production/ISKBoard_QCTester/.settings/com.st.stm32cube.ide.mcu.sfrview.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+sfrviewstate={"fFavorites"\:{"fLists"\:{}},"fProperties"\:{"fNodeProperties"\:{}}}
diff --git a/Production/ISKBoard_QCTester/.settings/language.settings.xml b/Production/ISKBoard_QCTester/.settings/language.settings.xml
new file mode 100644
index 0000000..f382d93
--- /dev/null
+++ b/Production/ISKBoard_QCTester/.settings/language.settings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project>
+	<configuration id="com.st.stm32cube.ide.mcu.gnu.managedbuild.config.exe.debug.88492984" name="Debug">
+		<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
+			<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
+			<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
+			<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
+			<provider class="com.st.stm32cube.ide.mcu.toolchain.armnone.setup.CrossBuiltinSpecsDetector" console="false" env-hash="-388825680522119720" id="com.st.stm32cube.ide.mcu.toolchain.armnone.setup.CrossBuiltinSpecsDetector" keep-relative-paths="false" name="MCU ARM GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
+				<language-scope id="org.eclipse.cdt.core.gcc"/>
+				<language-scope id="org.eclipse.cdt.core.g++"/>
+			</provider>
+		</extension>
+	</configuration>
+	<configuration id="com.st.stm32cube.ide.mcu.gnu.managedbuild.config.exe.release.602940913" name="Release">
+		<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
+			<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
+			<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
+			<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
+			<provider class="com.st.stm32cube.ide.mcu.toolchain.armnone.setup.CrossBuiltinSpecsDetector" console="false" env-hash="-388825680522119720" id="com.st.stm32cube.ide.mcu.toolchain.armnone.setup.CrossBuiltinSpecsDetector" keep-relative-paths="false" name="MCU ARM GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
+				<language-scope id="org.eclipse.cdt.core.gcc"/>
+				<language-scope id="org.eclipse.cdt.core.g++"/>
+			</provider>
+		</extension>
+	</configuration>
+</project>
\ No newline at end of file
diff --git a/Production/ISKBoard_QCTester/.settings/org.eclipse.cdt.core.prefs b/Production/ISKBoard_QCTester/.settings/org.eclipse.cdt.core.prefs
new file mode 100644
index 0000000..c8ec5df
--- /dev/null
+++ b/Production/ISKBoard_QCTester/.settings/org.eclipse.cdt.core.prefs
@@ -0,0 +1,6 @@
+doxygen/doxygen_new_line_after_brief=true
+doxygen/doxygen_use_brief_tag=false
+doxygen/doxygen_use_javadoc_tags=true
+doxygen/doxygen_use_pre_tag=false
+doxygen/doxygen_use_structural_commands=false
+eclipse.preferences.version=1
diff --git a/Production/ISKBoard_QCTester/.settings/stm32cubeide.project.prefs b/Production/ISKBoard_QCTester/.settings/stm32cubeide.project.prefs
new file mode 100644
index 0000000..5b0eb3d
--- /dev/null
+++ b/Production/ISKBoard_QCTester/.settings/stm32cubeide.project.prefs
@@ -0,0 +1,4 @@
+66BE74F758C12D739921AEA421D593D3=2
+8DF89ED150041C4CBC7CB9A9CAA90856=97D06D5F9E071253BB07AC037B086A77
+DC22A860405A8BF2F2C095E5B6529F12=97D06D5F9E071253BB07AC037B086A77
+eclipse.preferences.version=1
diff --git a/Production/ISKBoard_QCTester/Core/Board/comport.c b/Production/ISKBoard_QCTester/Core/Board/comport.c
new file mode 100644
index 0000000..8d89185
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Board/comport.c
@@ -0,0 +1,238 @@
+/* RS232/TTL/RS485 communication
+ * comport.c
+ *
+ *  Created on: Feb 15, 2023
+ *      Author: Think
+ */
+
+#include "comport.h"
+#include "miscdev.h"
+
+//#define CONFIG_COMPORT_DEBUG
+
+#ifdef  CONFIG_COMPORT_DEBUG
+#define com_print(format,args...) printf(format, ##args)
+#else
+#define com_print(format,args...) do{} while(0)
+#endif
+
+#define ARRAY_SIZE(x)   sizeof(x)/sizeof(x[0])
+
+comport_t  comports[] = {
+		{.fd=-1, .devname="WiFi",  .huart=&huart2   }, // ESP8266(WiFi)/ESP32(WiFi/BLE)
+		{.fd=-1, .devname="RS232", .huart=&huart3   }, // RS232/TTL/MikroBUS
+		{.fd=-1, .devname="RS485", .huart=&hlpuart1 }, // RS485
+};
+
+
+static inline comport_t *comport_find(UART_HandleTypeDef *huart)
+{
+	comport_t              *comport = NULL;
+	int                     i;
+
+	for(i=0; i<ARRAY_SIZE(comports); i++)
+	{
+		if( huart==comports[i].huart )
+		{
+			comport = &comports[i];
+		}
+	}
+
+	return comport;
+}
+
+comport_t *comport_init(UART_HandleTypeDef *huart, uint8_t *rxch, uint8_t *rxbuf, size_t size)
+{
+	comport_t              *comport = NULL;
+
+	if( !huart )
+		return NULL;
+
+	/* Find the corresponding comport in the array */
+	comport = comport_find(huart);
+	if( !comport )
+		return NULL;
+
+	comport->fd = 1;
+	comport->huart = huart;
+	comport->rxch = rxch;
+	rb_init(&comport->rb, rxbuf, size);
+
+	return comport;
+}
+
+
+/* +----------------------------------------+
+ * | printf and interrupt receive functions |
+ * +----------------------------------------+*/
+
+/* printf callback function on USART1 */
+int __io_putchar(int ch)
+{
+	HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
+	return ch;
+}
+
+/* USART interrupt receive callback funciton */
+void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
+{
+	comport_t              *comport = NULL;
+	uint8_t                 byte;
+
+	/* Find the corresponding comport in the array */
+	comport = comport_find(huart);
+	if( !comport )
+	{
+		goto cleanup;
+	}
+
+	/* Check comport receive ring buffer */
+	if( comport->fd<0 || !comport->rb.buffer || ! rb_free_size(&comport->rb) )
+	{
+		goto cleanup;
+	}
+
+	/* Receive and put the byte into receive ring buffer */
+	HAL_UART_Receive_IT(comport->huart, comport->rxch, 1);
+	rb_write(&comport->rb, comport->rxch, 1);
+
+	return ;
+
+cleanup:
+	HAL_UART_Receive_IT(huart, &byte, 1);
+	return ;
+}
+
+/* +------------------------+
+ * | UART/TTL API functions |
+ * +------------------------+*/
+
+comport_t *comport_open(UART_HandleTypeDef *huart)
+{
+	comport_t              *comport = NULL;
+
+	if( !huart )
+		return NULL;
+
+	/* Find the corresponding comport in the array */
+	comport = comport_find(huart);
+	if( comport )
+		comport->fd = 1;
+
+	return comport;
+}
+
+void comport_flush(comport_t *comport)
+{
+	if( !comport || !comport->huart)
+		return;
+
+	if( comport->rb.buffer )
+	{
+		rb_clear( &comport->rb );
+	}
+
+	return;
+}
+
+void comport_term(comport_t *comport)
+{
+	if( !comport || !comport->huart)
+		return;
+
+	comport->fd = -1;
+
+	if( comport->rb.buffer )
+	{
+		rb_clear( &comport->rb );
+	}
+
+	return;
+}
+
+int comport_send(comport_t *comport, char *data, int bytes)
+{
+	if( !comport || !data || bytes<=0 )
+		return -1;
+
+	com_print("Send %d bytes data: %s\r\n", bytes, data);
+	HAL_UART_Transmit(comport->huart, (uint8_t *)data, bytes, 0xFFFF);
+
+	return bytes;
+}
+
+int comport_recv(comport_t *comport, char *buf, int size, uint32_t timeout)
+{
+	uint32_t          tickstart = 0;
+	size_t            bytes = 0; 
+
+	if( !comport || !buf || size<=0 )
+		return -1;
+
+	if( !comport->rb.buffer )
+		return -2;
+
+	tickstart = HAL_GetTick();
+
+	while((HAL_GetTick() - tickstart) < timeout)
+	{
+		bytes = rb_data_size(&comport->rb);
+
+		HAL_Delay( 10 );
+
+		/* comport received data before but not receive in 10ms now, maybe data receive over */
+		if( bytes>0 && rb_data_size(&comport->rb)==bytes )
+		{
+			break;
+		}
+	}
+
+	bytes = rb_data_size(&comport->rb);
+	if( !bytes )
+	{
+		return 0;
+	}
+
+	bytes = bytes>size ? size : bytes;
+	rb_read(&comport->rb, (uint8_t *)buf, bytes);
+	com_print("Receive %d bytes data: %s\r\n", bytes, buf);
+
+	return bytes;
+}
+
+
+/* +------------------------+
+ * |   RS485 API functions  |
+ * +------------------------+*/
+
+/* Direction Pin set to be high level will be send mode, and low level will be receive mode */
+#define rs485_set_dirTx()   HAL_GPIO_WritePin(RS485_Dir_GPIO_Port, RS485_Dir_Pin, GPIO_PIN_SET)
+#define rs485_set_dirRx()   HAL_GPIO_WritePin(RS485_Dir_GPIO_Port, RS485_Dir_Pin, GPIO_PIN_RESET)
+
+comport_t *rs485_open(UART_HandleTypeDef *huart)
+{
+	return comport_find(huart);
+}
+
+void rs485_term(comport_t *comport)
+{
+	return comport_term(comport);
+}
+
+int rs485_send(comport_t *comport, char *data, int bytes)
+{
+	int             rv;
+
+	rs485_set_dirTx();
+	rv = comport_send(comport, data, bytes);
+
+	rs485_set_dirRx();
+	return rv;
+}
+
+int rs485_recv(comport_t *comport, char *buf, int size, uint32_t timeout)
+{
+	rs485_set_dirRx();
+
+	return comport_recv(comport, buf, size, timeout);
+}
diff --git a/Production/ISKBoard_QCTester/Core/Board/comport.h b/Production/ISKBoard_QCTester/Core/Board/comport.h
new file mode 100644
index 0000000..dd8ffb1
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Board/comport.h
@@ -0,0 +1,45 @@
+/*
+ * comport.h
+ *
+ *  Created on: Feb 15, 2023
+ *      Author: Think
+ */
+
+#ifndef INC_COMPORT_H_
+#define INC_COMPORT_H_
+
+/* Library includes. */
+#include "stm32l4xx_hal.h"
+
+
+/* Demo application includes. */
+#include <stdio.h>
+#include "usart.h"
+#include "ringbuf.h"
+
+
+#define COMM_RXBUFSIZE          1024
+
+typedef struct comport_s
+{
+	int                       fd;              /* compatible with Linux API */
+	char                      devname[32];     /* UART device name */
+	UART_HandleTypeDef       *huart;           /* STM32 UART Handler */
+	uint8_t                  *rxch;            /* receive data in interrupt handler */
+	ring_buffer_t             rb;              /* receive data ring buffer */
+} comport_t;
+
+comport_t *comport_init(UART_HandleTypeDef *huart, uint8_t *rxch, uint8_t *rxbuf, size_t size);
+comport_t *comport_open(UART_HandleTypeDef *huart);
+void comport_flush(comport_t *comport);
+extern int comport_send(comport_t *comport, char *data, int bytes);
+extern int comport_recv(comport_t *comport, char *buf, int size, uint32_t timeout);
+extern void comport_term(comport_t *comport);
+
+
+extern comport_t *rs485_open(UART_HandleTypeDef *huart);
+extern int rs485_send(comport_t *comport, char *data, int bytes);
+extern int rs485_recv(comport_t *comport, char *buf, int size, uint32_t timeout);
+extern void rs485_term(comport_t *comport);
+
+#endif /* INC_WIRED_H_ */
diff --git a/Production/ISKBoard_QCTester/Core/Board/ds18b20.c b/Production/ISKBoard_QCTester/Core/Board/ds18b20.c
new file mode 100644
index 0000000..994e606
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Board/ds18b20.c
@@ -0,0 +1,237 @@
+/*
+ * ds18b20.c
+ *
+ *  Created on: 2021年8月17日
+ *      Author: Think
+ */
+#include "main.h"
+#include "miscdev.h"
+
+typedef struct w1_gpio_s
+{
+	GPIO_TypeDef        *group;
+	uint16_t             pin;
+} w1_gpio_t;
+
+static w1_gpio_t   W1Dat =  /* IO pin connected to PA12 */
+{
+		.group = GPIOA,
+		.pin   = GPIO_PIN_12,
+};
+
+#define W1DQ_Input()	   	\
+{	\
+		GPIO_InitTypeDef GPIO_InitStruct = {0}; \
+		GPIO_InitStruct.Pin = W1Dat.pin; \
+		GPIO_InitStruct.Mode = GPIO_MODE_INPUT; \
+		GPIO_InitStruct.Pull = GPIO_PULLUP; \
+		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; \
+		HAL_GPIO_Init(W1Dat.group, &GPIO_InitStruct); \
+}
+
+#define W1DQ_Output()	   	\
+{	\
+		GPIO_InitTypeDef GPIO_InitStruct = {0}; \
+		GPIO_InitStruct.Pin = W1Dat.pin; \
+		GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; \
+		GPIO_InitStruct.Pull = GPIO_NOPULL; \
+		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; \
+		HAL_GPIO_Init(W1Dat.group, &GPIO_InitStruct); \
+}
+
+#define W1DQ_Write(x)	HAL_GPIO_WritePin(W1Dat.group, W1Dat.pin, \
+						(x==1)?GPIO_PIN_SET:GPIO_PIN_RESET)
+
+#define W1DQ_Read() 	HAL_GPIO_ReadPin(W1Dat.group,  W1Dat.pin)
+
+
+/* Master issues reset pulse and DS18B20s respond with presence pulse */
+uint8_t ds18b20_reset(void)
+{
+	uint8_t            rv = 0;
+	uint8_t            retry;
+
+	/* Setup W1 DQ pin as output and high level*/
+	W1DQ_Output();
+	W1DQ_Write(1);
+	delay_us(2);
+
+	/* Reset pulse by pulling the DQ pin low >=480us */
+	W1DQ_Write(0);
+	delay_us(480);
+
+	/* Master releases bus to high. When DS18B20 detects this rising edge, it waits 15µs to 60µs */
+	W1DQ_Write(1);
+	delay_us(60);
+
+	/* Then DS18B20 transmits a presence pulse by pulling the W1 bus low for 60µs to 240µs */
+	while( W1DQ_Read() && retry <240)
+	{
+		retry++;
+		delay_us(1);
+	}
+
+	if(retry >= 240)
+		rv = 1;
+
+	delay_us(240);
+
+	/* Master Rx time must >= 480us */
+	W1DQ_Output();
+	W1DQ_Write(1);
+	delay_us(240);
+
+	return rv;
+}
+
+void ds18b20_write(uint8_t byte)
+{
+	uint8_t            i = 0;
+
+	W1DQ_Output();
+
+	for(i=0; i<8; i++)
+	{
+		/* Write 1: pull low <= 15us, Write 0: pull low 15~60us*/
+		W1DQ_Write(0);
+		delay_us(10);
+
+		/* DS18B20 bit sent by LSB (lower bit first) */
+		if( byte & 0x1 )
+			W1DQ_Write(1);
+		else
+			W1DQ_Write(0);
+
+		/* Write 1/0 slot both >= 60us, hold the level for 60us */
+		delay_us(60);
+
+		/* Release W1 bus to high */
+		W1DQ_Write(1);
+		delay_us(2);
+
+		/* Prepare for next bit */
+		byte >>= 1;
+	}
+}
+
+
+uint8_t ds18b20_read(void)
+{
+	uint8_t            i = 0;
+	uint8_t            byte = 0;
+
+	for(i=0; i<8; i++)
+	{
+		/* Read time slot is initiated by master pulling the W1 bus
+		 * low for minimum of 1µs and then releasing the bus */
+		W1DQ_Output();
+		W1DQ_Write(0);
+		delay_us(2);
+		W1DQ_Write(1);
+		delay_us(2);
+
+		/* After master initiates read time slot, DS18B20 will begin
+		 * transmitting a 1 or 0 on bus */
+		W1DQ_Input();
+
+		/* DS18B20 bit sent by LSB (lower bit first) */
+		if( W1DQ_Read() )
+		{
+			byte |= 1<<i;
+		}
+
+		/* Read slot for >= 60us */
+		delay_us(60);
+
+		/* Release W1 bus to high */
+		W1DQ_Output();
+		W1DQ_Write(1);
+		delay_us(2);
+	}
+
+	return byte;
+}
+
+static inline int ds18b20_start_convert(void)
+{
+	/* Master issues reset pulse and DS18B20s respond with presence pulse */
+	if( 0 != ds18b20_reset() )
+		return -1;
+
+	/* Master issues Skip ROM command */
+	ds18b20_write(0xCC);
+
+	/* Master issues Convert T command. */
+	ds18b20_write(0x44);
+
+	return 0;
+}
+
+static inline int ds18b20_start_read(uint8_t *buf, int bytes)
+{
+	/* Master issues reset pulse and DS18B20s respond with presence pulse */
+	if( 0 != ds18b20_reset() )
+		return -1;
+
+	/* Master issues Skip ROM command */
+	ds18b20_write(0xCC);
+
+	/* Master issues Read Scratchpad command. */
+	ds18b20_write(0xBE);
+
+	buf[0] = ds18b20_read();   /* Temperature LSB */
+	buf[1] = ds18b20_read();   /* Temperature MSB */
+
+	/*Don't care followed 7 bytes data */
+
+	return 0;
+}
+
+int ds18b20_sample(float *temperature)
+{
+	uint8_t               byte[2];
+	uint8_t               sign;
+	uint16_t              temp;
+	static uint8_t        firstin = 1;
+
+	if( !temperature )
+		return -1;
+
+	if( 0 != ds18b20_start_convert() )
+		return -2;
+
+	/* First sample need 750ms delay */
+	if( firstin )
+	{
+		HAL_Delay(750);
+		firstin = 0;
+	}
+
+	if( 0 != ds18b20_start_read(byte, 2) )
+		return -3;
+
+	/* Temperature byte[0] is LSB, byte[1] is MSB, total 16 bit:
+	 * Byte[0]:  bit[3:0]: decimal bits,  bit[7:4]: integer bits
+	 * bYTE[1]:  bit[2:0]: integer bits,  bit[7:3]: sign bits
+	 */
+    if( byte[1]> 0x7 ) /* bit[7:3] is 1 */
+    {
+    	temp = ~(byte[1]<<8|byte[0]) + 1;  //补码
+    	sign=0; //温度为负
+    }
+    else
+    {
+    	sign=1;//温度为正
+    	temp = byte[1]<<8 | byte[0];
+    }
+
+   /* byte[1]的低三位和byte[0]的高四位组成温度值的整数部分,
+    * 而byte[0]的低四位为小数精度部分,且精度系数为0.0625 */
+    *temperature = (temp>>4) + (temp&0xF)*0.0625 ;
+    if( !sign )
+    {
+    	*temperature = -*temperature;
+    }
+
+	return 0;
+}
diff --git a/Production/ISKBoard_QCTester/Core/Board/ds18b20.h b/Production/ISKBoard_QCTester/Core/Board/ds18b20.h
new file mode 100644
index 0000000..0658cc3
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Board/ds18b20.h
@@ -0,0 +1,13 @@
+/*
+ * ds18b20.h
+ *
+ *  Created on: Dec 31, 2022
+ *      Author: Think
+ */
+
+#ifndef INC_DS18B20_H_
+#define INC_DS18B20_H_
+
+extern int ds18b20_sample(float *temperature);
+
+#endif /* INC_DS18B20_H_ */
diff --git a/Production/ISKBoard_QCTester/Core/Board/font_oled.h b/Production/ISKBoard_QCTester/Core/Board/font_oled.h
new file mode 100644
index 0000000..52c06d4
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Board/font_oled.h
@@ -0,0 +1,205 @@
+#ifndef __FONT_OLED_H
+#define __FONT_OLED_H 	  
+
+#include <stdint.h>
+
+/************************************ ASCII font size 6x8 ************************************/
+static const uint8_t F6x8[][6] =
+{
+	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00},// sp
+	{0x00, 0x00, 0x00, 0x2f, 0x00, 0x00},// !
+	{0x00, 0x00, 0x07, 0x00, 0x07, 0x00},// "
+	{0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14},// #
+	{0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12},// $
+	{0x00, 0x62, 0x64, 0x08, 0x13, 0x23},// %
+	{0x00, 0x36, 0x49, 0x55, 0x22, 0x50},// &
+	{0x00, 0x00, 0x05, 0x03, 0x00, 0x00},// '
+	{0x00, 0x00, 0x1c, 0x22, 0x41, 0x00},// (
+	{0x00, 0x00, 0x41, 0x22, 0x1c, 0x00},// )
+	{0x00, 0x14, 0x08, 0x3E, 0x08, 0x14},// *
+	{0x00, 0x08, 0x08, 0x3E, 0x08, 0x08},// +
+	{0x00, 0x00, 0x00, 0xA0, 0x60, 0x00},// ,
+	{0x00, 0x08, 0x08, 0x08, 0x08, 0x08},// -
+	{0x00, 0x00, 0x60, 0x60, 0x00, 0x00},// .
+	{0x00, 0x20, 0x10, 0x08, 0x04, 0x02},// /
+	{0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E},// 0
+	{0x00, 0x00, 0x42, 0x7F, 0x40, 0x00},// 1
+	{0x00, 0x42, 0x61, 0x51, 0x49, 0x46},// 2
+	{0x00, 0x21, 0x41, 0x45, 0x4B, 0x31},// 3
+	{0x00, 0x18, 0x14, 0x12, 0x7F, 0x10},// 4
+	{0x00, 0x27, 0x45, 0x45, 0x45, 0x39},// 5
+	{0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30},// 6
+	{0x00, 0x01, 0x71, 0x09, 0x05, 0x03},// 7
+	{0x00, 0x36, 0x49, 0x49, 0x49, 0x36},// 8
+	{0x00, 0x06, 0x49, 0x49, 0x29, 0x1E},// 9
+	{0x00, 0x00, 0x36, 0x36, 0x00, 0x00},// :
+	{0x00, 0x00, 0x56, 0x36, 0x00, 0x00},// ;
+	{0x00, 0x08, 0x14, 0x22, 0x41, 0x00},// <
+	{0x00, 0x14, 0x14, 0x14, 0x14, 0x14},// =
+	{0x00, 0x00, 0x41, 0x22, 0x14, 0x08},// >
+	{0x00, 0x02, 0x01, 0x51, 0x09, 0x06},// ?
+	{0x00, 0x32, 0x49, 0x59, 0x51, 0x3E},// @
+	{0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C},// A
+	{0x00, 0x7F, 0x49, 0x49, 0x49, 0x36},// B
+	{0x00, 0x3E, 0x41, 0x41, 0x41, 0x22},// C
+	{0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C},// D
+	{0x00, 0x7F, 0x49, 0x49, 0x49, 0x41},// E
+	{0x00, 0x7F, 0x09, 0x09, 0x09, 0x01},// F
+	{0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A},// G
+	{0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F},// H
+	{0x00, 0x00, 0x41, 0x7F, 0x41, 0x00},// I
+	{0x00, 0x20, 0x40, 0x41, 0x3F, 0x01},// J
+	{0x00, 0x7F, 0x08, 0x14, 0x22, 0x41},// K
+	{0x00, 0x7F, 0x40, 0x40, 0x40, 0x40},// L
+	{0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F},// M
+	{0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F},// N
+	{0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E},// O
+	{0x00, 0x7F, 0x09, 0x09, 0x09, 0x06},// P
+	{0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E},// Q
+	{0x00, 0x7F, 0x09, 0x19, 0x29, 0x46},// R
+	{0x00, 0x46, 0x49, 0x49, 0x49, 0x31},// S
+	{0x00, 0x01, 0x01, 0x7F, 0x01, 0x01},// T
+	{0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F},// U
+	{0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F},// V
+	{0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F},// W
+	{0x00, 0x63, 0x14, 0x08, 0x14, 0x63},// X
+	{0x00, 0x07, 0x08, 0x70, 0x08, 0x07},// Y
+	{0x00, 0x61, 0x51, 0x49, 0x45, 0x43},// Z
+	{0x00, 0x00, 0x7F, 0x41, 0x41, 0x00},// [
+	{0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55},// 55
+	{0x00, 0x00, 0x41, 0x41, 0x7F, 0x00},// ]
+	{0x00, 0x04, 0x02, 0x01, 0x02, 0x04},// ^
+	{0x00, 0x40, 0x40, 0x40, 0x40, 0x40},// _
+	{0x00, 0x00, 0x01, 0x02, 0x04, 0x00},// '
+	{0x00, 0x20, 0x54, 0x54, 0x54, 0x78},// a
+	{0x00, 0x7F, 0x48, 0x44, 0x44, 0x38},// b
+	{0x00, 0x38, 0x44, 0x44, 0x44, 0x20},// c
+	{0x00, 0x38, 0x44, 0x44, 0x48, 0x7F},// d
+	{0x00, 0x38, 0x54, 0x54, 0x54, 0x18},// e
+	{0x00, 0x08, 0x7E, 0x09, 0x01, 0x02},// f
+	{0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C},// g
+	{0x00, 0x7F, 0x08, 0x04, 0x04, 0x78},// h
+	{0x00, 0x00, 0x44, 0x7D, 0x40, 0x00},// i
+	{0x00, 0x40, 0x80, 0x84, 0x7D, 0x00},// j
+	{0x00, 0x7F, 0x10, 0x28, 0x44, 0x00},// k
+	{0x00, 0x00, 0x41, 0x7F, 0x40, 0x00},// l
+	{0x00, 0x7C, 0x04, 0x18, 0x04, 0x78},// m
+	{0x00, 0x7C, 0x08, 0x04, 0x04, 0x78},// n
+	{0x00, 0x38, 0x44, 0x44, 0x44, 0x38},// o
+	{0x00, 0xFC, 0x24, 0x24, 0x24, 0x18},// p
+	{0x00, 0x18, 0x24, 0x24, 0x18, 0xFC},// q
+	{0x00, 0x7C, 0x08, 0x04, 0x04, 0x08},// r
+	{0x00, 0x48, 0x54, 0x54, 0x54, 0x20},// s
+	{0x00, 0x04, 0x3F, 0x44, 0x40, 0x20},// t
+	{0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C},// u
+	{0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C},// v
+	{0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C},// w
+	{0x00, 0x44, 0x28, 0x10, 0x28, 0x44},// x
+	{0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C},// y
+	{0x00, 0x44, 0x64, 0x54, 0x4C, 0x44},// z
+	{0x14, 0x14, 0x14, 0x14, 0x14, 0x14},// horiz lines
+};
+
+/************************************ ASCII font size 8x16 ************************************/
+static const uint8_t F8X16[]=
+{
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//  0
+	0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00,//! 1
+	0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//" 2
+	0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00,//# 3
+	0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00,//$ 4
+	0xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00,//% 5
+	0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10,//& 6
+	0x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//' 7
+	0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00,//( 8
+	0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00,//) 9
+	0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00,//* 10
+	0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00,//+ 11
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00,//, 12
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,//- 13
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00,//. 14
+	0x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00,/// 15
+	0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00,//0 16
+	0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//1 17
+	0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00,//2 18
+	0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00,//3 19
+	0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00,//4 20
+	0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00,//5 21
+	0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00,//6 22
+	0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,//7 23
+	0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00,//8 24
+	0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00,//9 25
+	0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,//: 26
+	0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00,//; 27
+	0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00,//< 28
+	0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00,//= 29
+	0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00,//> 30
+	0x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00,//? 31
+	0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00,//@ 32
+	0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20,//A 33
+	0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00,//B 34
+	0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00,//C 35
+	0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00,//D 36
+	0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00,//E 37
+	0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00,//F 38
+	0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00,//G 39
+	0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20,//H 40
+	0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//I 41
+	0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00,//J 42
+	0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00,//K 43
+	0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00,//L 44
+	0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00,//M 45
+	0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00,//N 46
+	0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00,//O 47
+	0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00,//P 48
+	0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00,//Q 49
+	0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20,//R 50
+	0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00,//S 51
+	0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//T 52
+	0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//U 53
+	0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00,//V 54
+	0xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00,//W 55
+	0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20,//X 56
+	0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//Y 57
+	0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00,//Z 58
+	0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00,//[ 59
+	0x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00,//\ 60
+	0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00,//] 61
+	0x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//^ 62
+	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,//_ 63
+	0x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//` 64
+	0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20,//a 65
+	0x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00,//b 66
+	0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00,//c 67
+	0x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20,//d 68
+	0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00,//e 69
+	0x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//f 70
+	0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00,//g 71
+	0x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//h 72
+	0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//i 73
+	0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,//j 74
+	0x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00,//k 75
+	0x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//l 76
+	0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F,//m 77
+	0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//n 78
+	0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//o 79
+	0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00,//p 80
+	0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80,//q 81
+	0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00,//r 82
+	0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00,//s 83
+	0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00,//t 84
+	0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20,//u 85
+	0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00,//v 86
+	0x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00,//w 87
+	0x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00,//x 88
+	0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00,//y 89
+	0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00,//z 90
+	0x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40,//{ 91
+	0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,//| 92
+	0x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00,//} 93
+    0x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//~ 94
+};
+
+#endif
+
+
diff --git a/Production/ISKBoard_QCTester/Core/Board/hal_oled.c b/Production/ISKBoard_QCTester/Core/Board/hal_oled.c
new file mode 100644
index 0000000..3e76b62
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Board/hal_oled.c
@@ -0,0 +1,280 @@
+/**********************************************************************
+*   Copyright: (C)2021 LingYun IoT System Studio <www.weike-iot.com>
+*      Author: GuoWenxue<guowenxue@gmail.com> QQ: 281143292
+* Description: BearKE1 NB-IoT Board OLED Hardware Abstract Layer source code
+*
+*   ChangeLog:
+*        Version    Date       Author            Description
+*        V1.0.0  2021.08.10  GuoWenxue      Release initial version
+***********************************************************************/
+
+#include "hal_oled.h"
+
+enum
+{
+	OLED_CMD = 0,
+	OLED_DATA,
+};
+
+static void IIC_Write_Command(uint8_t command)
+{
+	/* Send I2C start signal */
+	I2C_StartCondition(OLED_I2CBUS);
+	
+	/* Send OLED I2C slave write address */
+	I2C_SendAddress(OLED_I2CBUS, I2C_WR);
+	
+	/* send command value */
+	I2C_WriteByte(OLED_I2CBUS, 0x00);
+	I2C_WriteByte(OLED_I2CBUS, command);
+	
+	/* Send I2C stop signal */
+	I2C_StopCondition(OLED_I2CBUS);
+}
+
+static void IIC_Write_Data(uint8_t data)
+{
+	/* Send I2C start signal */
+	I2C_StartCondition(OLED_I2CBUS);
+
+	/* Send OLED I2C slave write address */
+	I2C_SendAddress(OLED_I2CBUS, I2C_WR);
+
+	/* send data value */
+	I2C_WriteByte(OLED_I2CBUS, 0x40);
+	I2C_WriteByte(OLED_I2CBUS, data);
+	
+	/* Send I2C stop signal */
+	I2C_StopCondition(OLED_I2CBUS);
+}
+
+
+ /* OLED write a byte command data or command */
+void OLED_WR_Byte(uint8_t data, uint8_t type)
+{
+	if( OLED_CMD==type )
+	{
+		IIC_Write_Command(data);
+	}
+	else
+	{
+		IIC_Write_Data(data);
+	}
+}
+
+/*
+ *+-------------------------------------------------+
+ *|        OLED initial/control function API        |
+ *+-------------------------------------------------+
+ */ 
+
+/* Turn OLED display onʾ */
+void OLED_Display_On(void)
+{
+	OLED_WR_Byte(0X8D, OLED_CMD);  //SET DCDC command
+	OLED_WR_Byte(0X14, OLED_CMD);  //DCDC ON
+	OLED_WR_Byte(0XAF, OLED_CMD);  //DISPLAY ON
+}
+
+/* Turn OLED display off */
+void OLED_Display_Off(void)
+{
+	OLED_WR_Byte(0X8D, OLED_CMD);  //SET DCDC command
+	OLED_WR_Byte(0X10, OLED_CMD);  //DCDC OFF
+	OLED_WR_Byte(0XAE, OLED_CMD);  //DISPLAY OFF
+}		   		
+
+/* Clear OLED, it will be black */
+void OLED_Clear(void)  
+{  
+	uint8_t   i, j;		 
+	
+	/* update display */
+	for(i=0;i<8;i++)  
+	{  
+		OLED_WR_Byte (0xb0+i, OLED_CMD);    // set page address: 0~7
+		OLED_WR_Byte (0x00, OLED_CMD);      // set display address, column address lower bytes;ַ
+		OLED_WR_Byte (0x10, OLED_CMD);      // set display address, column address higher bytes;
+				
+		for(j=0; j<128; j++)
+			OLED_WR_Byte(0, OLED_DATA); 
+	} 
+	
+	OLED_Set_Pos(0, 0);
+}
+
+void OLED_On(void)  
+{  
+	uint8_t   i, j;	
+	
+	/* update display */
+	for(i=0; i<8; i++)  
+	{  
+		OLED_WR_Byte (0xb0+i, OLED_CMD);    // set page address: 0~7
+		OLED_WR_Byte (0x00, OLED_CMD);      // set display address, row address lower bytes;ַ
+		OLED_WR_Byte (0x10, OLED_CMD);      // set display address, row address higher bytes;
+		
+		for(j=0; j<128; j++)
+			OLED_WR_Byte(1, OLED_DATA); 
+	} 
+}
+
+void OLED_Init(void)
+{ 	
+	if( i2c_init(OLED_I2CBUS, OLED_CHIPADDR) )
+	{
+		return ;
+	}
+	
+	HAL_Delay(200);
+	
+	OLED_WR_Byte(0xAE,OLED_CMD); // Disable
+	
+	OLED_WR_Byte(0x40,OLED_CMD); //---set low column address
+	OLED_WR_Byte(0xB0,OLED_CMD); //---set high column address
+
+	OLED_WR_Byte(0xC8,OLED_CMD);
+
+	OLED_WR_Byte(0x81,OLED_CMD);
+	OLED_WR_Byte(0xff,OLED_CMD);
+
+	OLED_WR_Byte(0xa1,OLED_CMD);
+
+	OLED_WR_Byte(0xa6,OLED_CMD);
+	
+	OLED_WR_Byte(0xa8,OLED_CMD);
+	OLED_WR_Byte(0x1f,OLED_CMD);
+	
+	OLED_WR_Byte(0xd3,OLED_CMD);
+	OLED_WR_Byte(0x00,OLED_CMD);
+	
+	OLED_WR_Byte(0xd5,OLED_CMD);
+	OLED_WR_Byte(0xf0,OLED_CMD);
+	
+	OLED_WR_Byte(0xd9,OLED_CMD);
+	OLED_WR_Byte(0x22,OLED_CMD);
+	
+	OLED_WR_Byte(0xda,OLED_CMD);
+	OLED_WR_Byte(0x02,OLED_CMD);
+	
+	OLED_WR_Byte(0xdb,OLED_CMD);
+	OLED_WR_Byte(0x49,OLED_CMD);
+	
+	OLED_WR_Byte(0x8d,OLED_CMD);
+	OLED_WR_Byte(0x14,OLED_CMD);
+	
+	OLED_WR_Byte(0xaf,OLED_CMD);
+	
+	OLED_Clear();
+}  
+
+/*
+ *+-------------------------------------------------+
+ *|          OLED display function API              |
+ *+-------------------------------------------------+
+ */ 
+
+/* set OLED display position */
+void OLED_Set_Pos(uint8_t x, uint8_t y) 
+{ 	
+	OLED_WR_Byte(0xb0+y, OLED_CMD);
+	OLED_WR_Byte(((x&0xf0)>>4)|0x10, OLED_CMD);
+	OLED_WR_Byte((x&0x0f), OLED_CMD); 
+}  
+
+/* show a character on OLED as $Char_Size */
+void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr, uint8_t char_Size)
+{  
+	uint8_t c=0,i=0;	
+	
+	c=chr-' ';  // get offset value
+
+	if( x>X_WIDTH-1 )
+	{
+		x=0;
+		y=y+2;
+	}
+	
+	if(char_Size ==16)
+	{
+		OLED_Set_Pos(x,y);	
+		
+		for(i=0; i<8; i++)
+			OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);
+		
+		OLED_Set_Pos(x,y+1);
+		
+		for(i=0;i<8;i++)
+			OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);
+	}
+	else 
+	{	
+		OLED_Set_Pos(x,y);
+		for(i=0;i<6;i++)
+			OLED_WR_Byte(F6x8[c][i],OLED_DATA);
+	}
+}
+
+/* show a string on OLED  */
+void OLED_ShowString(uint8_t x, uint8_t y, char *chr, uint8_t font_size)
+{
+	uint8_t 	j=0;
+	
+	while( chr[j]!='\0' )
+	{
+		OLED_ShowChar(x, y, chr[j], font_size);
+		
+		x+=8;
+		
+		if(x>120)
+		{	
+			x=0;
+			y+=2;
+		}
+		
+		j++;
+	}
+}
+
+/* Show Chinese on OLED */
+void OLED_ShowChinese(uint8_t (*Hzk)[32], uint8_t x, uint8_t y, uint8_t no)
+{      			    
+	uint8_t 		t,adder=0;
+	
+	OLED_Set_Pos(x, y);	
+	for(t=0;t<16;t++)
+	{
+		OLED_WR_Byte(Hzk[2*no][t],OLED_DATA);
+		adder+=1;
+	}	
+	
+	OLED_Set_Pos(x,y+1);	
+	for(t=0;t<16;t++)
+	{	
+		OLED_WR_Byte(Hzk[2*no+1][t],OLED_DATA);
+		adder+=1;
+	}					
+}
+
+/* Show BMP images(128x64) on OLED, x: 0~127  y:0~7 */
+void OLED_DrawBMP(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t *BMP)
+{ 	
+	uint32_t 	    j=0;
+	uint8_t 		x,y;
+  
+	if( y1%8==0 )
+		y = y1/8;      
+	else
+		y = y1/8+1;
+	
+	for(y=y0; y<y1; y++)
+	{
+		OLED_Set_Pos(x0, y);
+		for(x=x0;x<x1;x++)
+		{    
+			OLED_WR_Byte(BMP[j++],OLED_DATA);	 
+		}
+	}
+} 
+
+
diff --git a/Production/ISKBoard_QCTester/Core/Board/hal_oled.h b/Production/ISKBoard_QCTester/Core/Board/hal_oled.h
new file mode 100644
index 0000000..89086b7
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Board/hal_oled.h
@@ -0,0 +1,45 @@
+/*
+ * oled.h
+ *
+ *  Created on: 2023年1月20日
+ *      Author: Wenxue
+ */
+
+#ifndef INC_HAL_OLED_H_
+#define INC_HAL_OLED_H_
+
+#include "stm32l4xx_hal.h"
+#include "i2c_bitbang.h"
+#include "font_oled.h"
+
+#define OLED_I2CBUS                I2CBUS0   /* OLED on GPIO I2C bus0 */
+#define OLED_CHIPADDR              0x3C      /* OLED chip address */
+
+#define X_WIDTH 		           128
+#define Y_WIDTH 		           64
+
+#define OLED_FONT16                16
+#define OLED_FONT8                 8
+
+/*
+ *+-------------------------------------------------+
+ *|        OLED initial/control function API        |
+ *+-------------------------------------------------+
+ */
+void OLED_Init(void);
+void OLED_On(void);
+void OLED_Clear(void);
+void OLED_Display_On(void);
+void OLED_Display_Off(void);
+
+/*
+ *+-------------------------------------------------+
+ *|          OLED display function API              |
+ *+-------------------------------------------------+
+ */
+void OLED_Set_Pos(uint8_t x, uint8_t y);
+void OLED_ShowNum(uint8_t x,uint8_t y,uint32_t num,uint8_t len,uint8_t size);
+void OLED_ShowString(uint8_t x,uint8_t y, char *p,uint8_t font_size);
+
+
+#endif /* INC_HAL_OLED_H_ */
diff --git a/Production/ISKBoard_QCTester/Core/Board/i2c_bitbang.c b/Production/ISKBoard_QCTester/Core/Board/i2c_bitbang.c
new file mode 100644
index 0000000..2d615e6
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Board/i2c_bitbang.c
@@ -0,0 +1,354 @@
+/*
+ * i2c_bitbang.c
+ *
+ *  Created on: Jan 19, 2023
+ *      Author: Wenxue
+ */
+
+
+#include "i2c_bitbang.h"
+
+i2c_bus_t i2c_bus[I2CBUSMAX] =
+{
+    /* Bus   ChipAddr       SCL_Pin              SDA_Pin      */
+    {I2CBUS0, 0x00, {GPIOB, GPIO_PIN_6 }, {GPIOB, GPIO_PIN_7 } }, /* SHT20, RTC, OLED */
+	{I2CBUS1, 0x00, {GPIOB, GPIO_PIN_10}, {GPIOB, GPIO_PIN_11} }, /* MikroBUS I2C interface */
+};
+
+
+enum
+{
+	LOW,
+	HIGH,
+};
+
+
+/*+--------------------------+
+ *|    Low level GPIO API    |
+ *+--------------------------+*/
+
+static inline void SDA_IN(uint8_t bus)
+{
+	GPIO_InitTypeDef GPIO_InitStruct = {0};
+
+	GPIO_InitStruct.Pin = i2c_bus[bus].sda.pin;
+	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
+	GPIO_InitStruct.Pull = GPIO_PULLUP;
+	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
+	HAL_GPIO_Init(i2c_bus[bus].sda.group, &GPIO_InitStruct);
+}
+
+static inline void SDA_OUT(uint8_t bus)
+{
+	GPIO_InitTypeDef GPIO_InitStruct = {0};
+
+	GPIO_InitStruct.Pin = i2c_bus[bus].sda.pin;
+	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
+	GPIO_InitStruct.Pull = GPIO_PULLUP;
+	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
+	HAL_GPIO_Init(i2c_bus[bus].sda.group, &GPIO_InitStruct);
+}
+
+static inline void SCL_OUT(uint8_t bus)
+{
+	GPIO_InitTypeDef GPIO_InitStruct = {0};
+
+	GPIO_InitStruct.Pin = i2c_bus[bus].scl.pin;
+	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
+	GPIO_InitStruct.Pull = GPIO_PULLUP;
+	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
+	HAL_GPIO_Init(i2c_bus[bus].scl.group, &GPIO_InitStruct);
+}
+
+static inline void SCL_SET(uint8_t bus, int level)
+{
+	HAL_GPIO_WritePin(i2c_bus[bus].scl.group, i2c_bus[bus].scl.pin, level?GPIO_PIN_SET:GPIO_PIN_RESET);
+}
+
+static inline void SDA_SET(uint8_t bus, int level)
+{
+	HAL_GPIO_WritePin(i2c_bus[bus].sda.group, i2c_bus[bus].sda.pin, level?GPIO_PIN_SET:GPIO_PIN_RESET);
+}
+
+static inline GPIO_PinState SCL_GET(uint8_t bus)
+{
+	return HAL_GPIO_ReadPin(i2c_bus[bus].scl.group, i2c_bus[bus].scl.pin);
+}
+
+static inline GPIO_PinState SDA_GET(uint8_t bus)
+{
+	return HAL_GPIO_ReadPin(i2c_bus[bus].sda.group, i2c_bus[bus].sda.pin);
+}
+
+
+/*+--------------------------+
+ *|  I2C Clock Sequence API  |
+ *+--------------------------+*/
+
+/* */
+#if 1
+#include "miscdev.h"  /* delay_us() implement by TIM6 */
+#else
+static inline void delay_us(uint32_t us)
+{
+	uint32_t      i, j;
+	for(j=0; j<450; j++)// 450 ~500
+		for(i = 0; i < us; i++)
+		{
+			__NOP();
+		}
+}
+#endif
+
+
+void I2C_StartCondition(uint8_t bus)
+{
+	SDA_OUT(bus);
+	SCL_OUT(bus);
+
+	/* Start Condition Clock Sequence:
+	          ______
+     SCL:           |___
+            _____
+     SDA:        |_____
+	*/
+	SDA_SET(bus, HIGH);
+	SCL_SET(bus, HIGH);
+	delay_us(1);
+
+	SDA_SET(bus, LOW);
+	delay_us(1);  // hold time start condition (t_HD;STA)
+	SCL_SET(bus, LOW);
+	delay_us(1);
+}
+
+void I2C_StopCondition(uint8_t bus)
+{
+	SDA_OUT(bus);
+
+	/* SCL Stop Condition Clock Sequence:
+				_______
+      SCL:   ___|
+                   _____
+      SDA:   _____|
+	*/
+	SCL_SET(bus, LOW);
+	SDA_SET(bus, LOW);
+	delay_us(1);
+
+	SCL_SET(bus, HIGH);
+	SDA_SET(bus, HIGH);
+	delay_us(1);
+}
+
+void I2C_Ack(uint8_t bus, uint8_t ack)
+{
+	if(ACK_NONE == ack)
+		return ;
+
+	SCL_SET(bus, LOW);
+	SDA_OUT(bus);
+
+	if( ACK==ack )
+		SDA_SET(bus, LOW);
+	else if( NAK==ack )
+		SDA_SET(bus, HIGH);
+
+	delay_us(1);
+
+	SCL_SET(bus, HIGH);
+	delay_us(1);
+	SCL_SET(bus, LOW);
+}
+
+uint8_t I2C_WriteByte(uint8_t bus, uint8_t txByte)
+{
+	uint8_t error = NO_ERROR;
+	uint8_t mask;
+
+	SDA_OUT(bus);
+	SCL_SET(bus, LOW);
+
+	/* start send 8 bits data */
+	for(mask=0x80; mask>0; mask>>=1)
+	{
+		if((mask & txByte) == 0)
+			SDA_SET(bus, LOW);
+		else
+			SDA_SET(bus, HIGH);
+
+		delay_us(1);	// data set-up time (t_SU;DAT)
+		SCL_SET(bus, HIGH);
+		delay_us(1);	// SCL high time (t_HIGH)
+		SCL_SET(bus, LOW);
+		delay_us(1);	// data hold time(t_HD;DAT)
+	}
+
+	/* Wait for ACK/NAK */
+	SDA_IN(bus);
+	SCL_SET(bus, HIGH); // clk #9 for ack
+	delay_us(1); // data set-up time (t_SU;DAT)
+
+	/* High level means NAK */
+	if( SDA_GET(bus) )
+		error = ACK_ERROR;
+
+	SCL_SET(bus, LOW);
+
+	delay_us(1);
+
+	return error;
+}
+
+static inline uint8_t I2C_WaitWhileClockStreching(uint8_t bus, uint8_t timeout)
+{
+	uint8_t error = NO_ERROR;
+
+	while( SCL_GET(bus)== 0)
+	{
+		if(timeout-- == 0)
+			return TIMEOUT_ERROR;
+
+		delay_us(10);
+	}
+	return error;
+}
+
+uint8_t I2C_ReadByte(uint8_t bus, uint8_t *rxByte, uint8_t ack, uint32_t timeout)
+{
+//==============================================================================
+	uint8_t error = NO_ERROR;
+	uint8_t mask;
+
+	*rxByte = 0x00;
+	SDA_IN(bus);
+
+	/* start read 8 bits data */
+	for(mask = 0x80; mask > 0; mask >>= 1)
+	{
+		SCL_SET(bus, HIGH);  // start clock on SCL-line
+		delay_us(1);         // clock set-up time (t_SU;CLK)
+
+		// wait while clock streching
+		error = I2C_WaitWhileClockStreching(bus, timeout);
+
+		delay_us(1); // SCL high time (t_HIGH)
+
+		if( SDA_GET(bus) )
+			*rxByte |= mask; // read bit
+
+		SCL_SET(bus, LOW);
+		delay_us(1); // data hold time(t_HD;DAT)
+	}
+
+	I2C_Ack(bus, ack);
+  return error;
+}
+
+
+uint8_t I2C_SendAddress(uint8_t bus, uint8_t dir)
+{
+	uint8_t       addr;
+	uint8_t       rv;
+
+	if( I2C_WR == dir )
+	{
+		addr = W_ADDR(i2c_bus[bus].addr);
+	}
+	else
+	{
+		addr = R_ADDR(i2c_bus[bus].addr);
+	}
+
+	rv=I2C_WriteByte(bus, addr);
+
+	return rv;
+}
+
+int i2c_init(uint8_t bus, uint8_t addr)
+{
+	if( bus >= I2CBUSMAX )
+	{
+		return -1;
+	}
+
+#if 0
+	/* I2C bus used already */
+	if( 0x00 != i2c_bus[bus].addr )
+	{
+		return -2;
+	}
+#endif
+
+	/* Set I2C slave address */
+	i2c_bus[bus].addr = addr;
+
+	return 0;
+}
+
+int i2c_term(uint8_t bus)
+{
+	if( bus >= I2CBUSMAX )
+	{
+		return -1;
+	}
+
+	/* Set I2C slave address */
+	i2c_bus[bus].addr = 0x0;
+
+	return 0;
+}
+
+int i2c_read(uint8_t bus, uint8_t *buf, int size)
+{
+  int       i;
+  int       rv = NO_ERROR;
+	uint8_t   byte;
+
+  I2C_StartCondition(bus);
+
+  if( NO_ERROR != (rv=I2C_SendAddress(bus, I2C_RD) ) )
+  {
+    goto OUT;
+  }
+
+  for (i=0; i<size; i++)
+  {
+     if( !I2C_ReadByte(bus, &byte, ACK, I2C_CLK_STRETCH_TIMEOUT) )
+			 buf[i] = byte;
+		 else
+			 goto OUT;
+  }
+
+OUT:
+  I2C_StopCondition(bus);
+  return rv;
+}
+
+int i2c_write(uint8_t bus, uint8_t *data, int bytes)
+{
+  int       i;
+  int       rv = NO_ERROR;
+
+	if(!data)
+		return PARM_ERROR;
+
+  I2C_StartCondition(bus);
+
+  if( NO_ERROR != (rv=I2C_SendAddress(bus, I2C_WR)) )
+  {
+    goto OUT;
+  }
+
+  for (i=0; i<bytes; i++)
+  {
+    if( NO_ERROR != (rv=I2C_WriteByte(bus, data[i])) )
+    {
+      break;
+    }
+  }
+
+OUT:
+  I2C_StopCondition(bus);
+  return rv;
+}
diff --git a/Production/ISKBoard_QCTester/Core/Board/i2c_bitbang.h b/Production/ISKBoard_QCTester/Core/Board/i2c_bitbang.h
new file mode 100644
index 0000000..8ffea73
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Board/i2c_bitbang.h
@@ -0,0 +1,84 @@
+/*
+ * i2c_bitbang.h
+ *
+ *  Created on: Jan 19, 2023
+ *      Author: Wenxue
+ */
+
+#ifndef INC_I2C_BITBANG_H_
+#define INC_I2C_BITBANG_H_
+
+#include "stm32l4xx_hal.h"
+
+#define I2C_CLK_STRETCH_TIMEOUT	  6000
+
+typedef enum i2c_num_e
+{
+	I2CBUS0,
+	I2CBUS1,
+	I2CBUSMAX,
+} i2c_num_t;
+
+typedef struct i2c_gpio_s
+{
+	GPIO_TypeDef        *group; /* GPIO group */
+	uint16_t             pin;   /* GPIO pin   */
+} i2c_gpio_t;
+
+typedef struct i2c_bus_s
+{
+	uint8_t             bus;  /* Bus number   */
+	uint8_t             addr; /* Chip 7bits address */
+	i2c_gpio_t          scl;  /* SCL GPIO pin */
+	i2c_gpio_t          sda;  /* SDA GPIO pin */
+} i2c_bus_t;
+
+
+/*
+ *+---------------------+
+ *|  I2C API functions  |
+ *+---------------------+
+ */
+
+/* I2C bus error number */
+enum{
+  NO_ERROR       = 0x00, // no error
+  ACK_ERROR      = 0x01, // no acknowledgment error
+  CHECKSUM_ERROR = 0x02, // checksum mismatch error
+  TIMEOUT_ERROR  = 0x04, // timeout error
+  PARM_ERROR     = 0x80, // parameter out of range error
+};
+
+extern int i2c_init(uint8_t bus, uint8_t addr);
+
+extern int i2c_term(uint8_t bus);
+
+extern int i2c_read(uint8_t bus, uint8_t *buf, int size);
+
+extern int i2c_write(uint8_t bus, uint8_t *data, int bytes);
+
+extern void I2C_StartCondition(uint8_t bus);
+
+extern void I2C_StopCondition(uint8_t bus);
+
+/* I2C从设备芯片器件读写方向位器件地址 */
+#define R_ADDR(x)     ((x)<<1 | 1) /* I2C地址最低位为1表示读 */
+#define W_ADDR(x)     ((x)<<1 )    /* I2C地址最低位为0表示写 */
+enum
+{
+	I2C_WR,	 /* 写方向 */
+	I2C_RD,  /* 读方向 */
+};
+extern uint8_t I2C_SendAddress(uint8_t bus, uint8_t dir);
+
+extern uint8_t I2C_WriteByte(uint8_t bus, uint8_t txByte);
+
+enum
+{
+	ACK_NONE, /* 不回ACK */
+	ACK,      /* 回ACK */
+	NAK,      /* 回NAK */
+};
+extern uint8_t I2C_ReadByte(uint8_t bus, uint8_t *rxByte, uint8_t ack, uint32_t timeout);
+
+#endif /* INC_I2C_BITBANG_H_ */
diff --git a/Production/ISKBoard_QCTester/Core/Board/isl1208.c b/Production/ISKBoard_QCTester/Core/Board/isl1208.c
new file mode 100644
index 0000000..92b57f0
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Board/isl1208.c
@@ -0,0 +1,329 @@
+/*
+ * sht20.c
+ *
+ *  Created on: Jan 19, 2023
+ *      Author: Wenxue
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "isl1208.h"
+
+/*+--------------------------------------+
+ *|      RTC Chipset ISL1208 Register    |
+ *+--------------------------------------+*/
+/* rtc section */
+#define ISL1208_REG_SC			0x00
+#define ISL1208_REG_MN			0x01
+#define ISL1208_REG_HR			0x02
+#define ISL1208_REG_HR_MIL     (1<<7)   /* 24h/12h mode */
+#define ISL1208_REG_HR_PM      (1<<5)   /* PM/AM bit in 12h mode */
+#define ISL1208_REG_DT			0x03
+#define ISL1208_REG_MO			0x04
+#define ISL1208_REG_YR			0x05
+#define ISL1208_REG_DW			0x06
+#define ISL1208_RTC_SECTION_LEN	7 /* RTC Section register */
+#define REGS_RTC_SR_LEN			8 /* RTC Section and status register  */
+
+/* control/status section */
+#define ISL1208_REG_SR			0x07
+#define ISL1208_REG_SR_ARST    (1<<7)   /* auto reset */
+#define ISL1208_REG_SR_XTOSCB  (1<<6)   /* crystal oscillator */
+#define ISL1208_REG_SR_WRTC    (1<<4)   /* write rtc */
+#define ISL1208_REG_SR_EVT     (1<<3)   /* event */
+#define ISL1208_REG_SR_ALM     (1<<2)   /* alarm */
+#define ISL1208_REG_SR_BAT     (1<<1)   /* battery */
+#define ISL1208_REG_SR_RTCF    (1<<0)   /* rtc fail */
+#define ISL1208_REG_INT			0x08
+#define ISL1208_REG_INT_ALME   (1<<6)   /* alarm enable */
+#define ISL1208_REG_INT_IM     (1<<7)   /* interrupt/alarm mode */
+#define ISL1219_REG_EV			0x09
+#define ISL1219_REG_EV_EVEN    (1<<4)   /* event detection enable */
+#define ISL1219_REG_EV_EVIENB  (1<<7)   /* event in pull-up disable */
+#define ISL1208_REG_ATR			0x0a
+#define ISL1208_REG_DTR			0x0b
+
+/* alarm section */
+#define ISL1208_REG_SCA			0x0c
+#define ISL1208_REG_MNA			0x0d
+#define ISL1208_REG_HRA			0x0e
+#define ISL1208_REG_DTA			0x0f
+#define ISL1208_REG_MOA			0x10
+#define ISL1208_REG_DWA			0x11
+#define ISL1208_ALARM_SECTION_LEN 6
+
+/* user section */
+#define ISL1208_REG_USR1		0x12
+#define ISL1208_REG_USR2		0x13
+#define ISL1208_USR_SECTION_LEN	2
+
+#define ISL1208_REGS_MAX		0x13
+
+const char *weekday[7]={"Sun.","Mon.","Tue.","Wed.","Thu.","Fri.","Sat." };
+
+#define CONFIG_RTC_DEBUG
+
+#ifdef  CONFIG_RTC_DEBUG
+#define rtc_print(format,args...) printf(format, ##args)
+#else
+#define rtc_print(format,args...) do{} while(0)
+#endif
+
+#ifdef CONFIG_RTC_DEBUG
+__attribute__((unused)) static void dump_buf(const char *prompt, uint8_t *buf, uint32_t size)
+{
+	int      i;
+	
+	if(!buf)
+		return ;
+	
+	if(prompt)
+		printf("%s\r\n", prompt);
+	
+	for(i=0; i<size; i++)
+		printf("%02x ", buf[i]);
+	
+	printf("\r\n");
+}
+#endif
+	
+
+/*
+ *+----------------------------------+
+ *|     ISL1208 Low level API        |
+ *+----------------------------------+
+ */
+
+static int isl1208_i2c_read_regs(uint8_t regaddr, uint8_t *regs, uint8_t len)
+{
+	uint8_t        i;
+	int            rv = 0;
+	uint8_t        byte;
+	uint8_t        ack;
+	
+	if( !regs || len<=0 )
+	{
+		rtc_print("ISL1208: Invalid input arguments\r\n");
+		return -PARM_ERROR;
+	}
+
+	I2C_StartCondition(ISL1208_I2CBUS);
+	
+	if( NO_ERROR != (rv=I2C_SendAddress(ISL1208_I2CBUS, I2C_WR)) )
+	{
+		rtc_print("ISL1208: Send chipset address[W] failure: rv=0x%02x\r\n", rv);
+		rv = -rv;
+		goto OUT;
+	}
+	
+	if( NO_ERROR != (rv=I2C_WriteByte(ISL1208_I2CBUS, regaddr) ) )
+	{
+		rtc_print("ISL1208: Set register[0x%02x] failure: rv=0x%02x\r\n", regaddr, rv);
+		rv = -rv;
+		goto OUT;
+	}
+	
+	I2C_StartCondition(ISL1208_I2CBUS);
+	
+	if( NO_ERROR != (rv=I2C_SendAddress( ISL1208_I2CBUS, I2C_RD ) ) )
+	{
+		rtc_print("ISL1208: Send chipset address[W] failure: rv=0x%02x\r\n", rv);
+		rv = -rv;
+		goto OUT;
+	}
+	
+	for (i=0; i<len; i++)
+	{
+		 if( i == (len-1) )
+			 ack = ACK_NONE;
+		 else
+			 ack = ACK; 
+		 
+		 rv = I2C_ReadByte(ISL1208_I2CBUS, &byte, ack, I2C_CLK_STRETCH_TIMEOUT);
+		 if( NO_ERROR != rv )
+		 {
+			 rtc_print("ISL1208: Read register data failure: rv=0x%02x\r\n", rv);	
+			 rv = -rv;
+			 goto OUT;
+		 }
+		 
+		 regs[i] = byte;
+	}
+
+OUT:
+	I2C_StopCondition(ISL1208_I2CBUS);
+	return rv;
+}
+
+static int isl1208_i2c_write_regs(uint8_t regaddr, uint8_t *regs, uint8_t len)
+{
+	uint8_t        i;
+	int            rv = 0;
+
+	I2C_StartCondition(ISL1208_I2CBUS);
+	
+	if( NO_ERROR != (rv=I2C_SendAddress(ISL1208_I2CBUS, I2C_WR ) ) )
+	{
+		rtc_print("ISL1208: Send chipset address[W] failure: rv=0x%02x\r\n", rv);
+		rv = -rv;
+		goto OUT;
+	}
+	
+	if( NO_ERROR != (rv=I2C_WriteByte(ISL1208_I2CBUS, regaddr) ) )
+	{
+		rtc_print("ISL1208: Set register[0x%02x] failure: rv=0x%02x\r\n", regaddr, rv);
+		rv = -rv;
+		goto OUT;
+	}
+
+	for (i=0; i<len; i++)
+	{
+		rv = I2C_WriteByte(ISL1208_I2CBUS, regs[i]);
+		if( NO_ERROR != rv )
+		{
+			rtc_print("ISL1208: Write register data failure: rv=0x%02x\r\n", rv);
+			rv = -rv;
+			goto OUT;
+		}
+	}		
+
+	rv = 0;
+	
+OUT:
+	I2C_StopCondition(ISL1208_I2CBUS);
+	return rv;
+}
+
+
+#define bcd2bin(x)    (((x) & 0x0f) + ((x) >> 4) * 10)
+#define bin2bcd(x)    ((((x) / 10) << 4) + (x) % 10)
+
+int set_rtc_time(rtc_time_t tm)
+{
+	int                     rv;
+	uint8_t                 regs[ISL1208_RTC_SECTION_LEN] = { 0, };
+	uint8_t                 sr;
+
+	regs[ISL1208_REG_SC] = bin2bcd(tm.tm_sec);
+	regs[ISL1208_REG_MN] = bin2bcd(tm.tm_min);
+	regs[ISL1208_REG_HR] = bin2bcd(tm.tm_hour) | ISL1208_REG_HR_MIL;
+	
+	regs[ISL1208_REG_DT] = bin2bcd(tm.tm_mday);
+	regs[ISL1208_REG_MO] = bin2bcd(tm.tm_mon);
+	regs[ISL1208_REG_YR] = bin2bcd(tm.tm_year - 2000);
+	//regs[ISL1208_REG_DW] = bin2bcd(tm.tm_wday & 7);
+	
+	if( i2c_init(ISL1208_I2CBUS, ISL1208_CHIPADDR) )
+	{
+		rtc_print("ISL1208: Initial I2C bus failure!\r\n");
+		return -2;
+	}
+
+	rv = isl1208_i2c_read_regs(ISL1208_REG_SR, &sr, 1);
+	if( rv < 0 )
+	{
+		rtc_print("ISL1208: read Status Register failure, rv=%d!\r\n", rv);
+		rv = -3;
+		goto OUT;
+	}
+	sr |= ISL1208_REG_SR_WRTC;
+	rv = isl1208_i2c_write_regs(ISL1208_REG_SR, &sr, 1);
+	if( rv < 0 )
+	{
+		rtc_print("ISL1208: Set Status Register WRTC failure, rv=%d!\r\n", rv);
+		rv = -4;
+		goto OUT;
+	}	
+
+	rv=isl1208_i2c_write_regs(ISL1208_REG_SC, regs, ISL1208_RTC_SECTION_LEN);
+	if(rv < 0 )
+	{
+		rtc_print("ISL1208: Set RTC section registeres failure, rv=%d!\r\n", rv);
+		rv = -5;
+		goto OUT;
+	}	
+	
+	sr &= (~ISL1208_REG_SR_WRTC);
+	rv = isl1208_i2c_write_regs(ISL1208_REG_SR, &sr, 1);
+	if( rv < 0 )
+	{
+		rtc_print("ISL1208: Clear Status Register WRTC failure, rv=%d!\r\n", rv);
+		rv = -6;
+		goto OUT;
+	}		
+	
+OUT:
+	i2c_term(ISL1208_I2CBUS);
+	return rv;
+}
+
+int get_rtc_time(rtc_time_t *tm)
+{
+	int									rv = 0;
+	uint8_t 							regs[REGS_RTC_SR_LEN] = { 0, };
+	
+	if( !tm )
+	{
+		rtc_print("ISL1208: Invalid input arugments!\r\n");
+		return -1;
+	}
+	
+	if( i2c_init(ISL1208_I2CBUS, ISL1208_CHIPADDR) )
+	{
+		rtc_print("ISL1208: Initial I2C bus failure!\r\n");
+		return -2;
+	}
+
+	rv = isl1208_i2c_read_regs(ISL1208_REG_SC, regs, REGS_RTC_SR_LEN);
+	if (rv < 0)
+	{
+		rtc_print("ISL1208: read RTC_SECTION and SR registeres failure, rv=%d!\r\n", rv);
+		rv = -3;
+		goto OUT;
+	}
+	
+	if( regs[ISL1208_REG_SR] & ISL1208_REG_SR_RTCF )
+	{
+		rtc_print("ISL1208: Initialize RTC time after power failure!\r\n");
+		rv = -4;
+		goto OUT;
+	}
+
+	tm->tm_sec = bcd2bin(regs[ISL1208_REG_SC]);
+	tm->tm_min = bcd2bin(regs[ISL1208_REG_MN]);
+	
+	{
+		const uint8_t _hr = regs[ISL1208_REG_HR];
+			
+		if (_hr & ISL1208_REG_HR_MIL)  /* 24H */
+		{
+			tm->tm_hour = bcd2bin(_hr & 0x3f);
+		}
+		else  /* 12H */
+		{
+			tm->tm_hour = bcd2bin(_hr & 0x1f);
+			if (_hr & ISL1208_REG_HR_PM)
+				tm->tm_hour += 12;
+		}
+	}
+		
+	tm->tm_mday = bcd2bin(regs[ISL1208_REG_DT]);
+	tm->tm_mon  = bcd2bin(regs[ISL1208_REG_MO]); 
+	tm->tm_year = bcd2bin(regs[ISL1208_REG_YR]) + 2000;
+	tm->tm_wday = bcd2bin(regs[ISL1208_REG_DW]);
+	
+OUT:
+	i2c_term(ISL1208_I2CBUS);
+	return rv;
+}
+
+void print_rtc_time(void)
+{
+	rtc_time_t tm;
+
+	if( get_rtc_time(&tm) < 0 )
+		return ;
+
+	printf("%04d-%02d-%02d %02d:%02d:%02d %s\r\n", tm.tm_year, tm.tm_mon, tm.tm_mday,
+		tm.tm_hour, tm.tm_min, tm.tm_sec, weekday[tm.tm_wday]);
+}
diff --git a/Production/ISKBoard_QCTester/Core/Board/isl1208.h b/Production/ISKBoard_QCTester/Core/Board/isl1208.h
new file mode 100644
index 0000000..043398e
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Board/isl1208.h
@@ -0,0 +1,37 @@
+/*
+ * isl1208.h
+ *
+ *  Created on: Jan 19, 2023
+ *      Author: Wenxue
+ */
+
+#ifndef INC_ISL1208_H_
+#define INC_ISL1208_H_
+
+#include "i2c_bitbang.h"
+
+#define ISL1208_I2CBUS           I2CBUS0   /* ISL1208 on GPIO I2C bus0 */
+#define ISL1208_CHIPADDR         0x6F      /* ISL1208 7-Bits Chip address */
+
+typedef struct rtc_time_s
+{
+	int       tm_sec;   /* [0 ~ 59/60 ]  */
+	int       tm_min;   /* [0 ~ 59 ]     */
+	int       tm_hour;  /* [0 ~ 23 ]     */
+
+	int       tm_mday;  /* [1 ~ 31]      */
+	int       tm_mon;   /* [1 ~ 12]      */
+	int       tm_year;  /* [ 2000~2099 ] */
+
+	int	      tm_wday;  /* [0 ~ 6 ]      */
+} rtc_time_t;
+
+extern const char    *weekday[7];
+
+extern int set_rtc_time(rtc_time_t tm);
+
+extern int get_rtc_time(rtc_time_t *tm);
+
+extern void print_rtc_time(void);
+
+#endif /* INC_ISL1208_H_ */
diff --git a/Production/ISKBoard_QCTester/Core/Board/miscdev.c b/Production/ISKBoard_QCTester/Core/Board/miscdev.c
new file mode 100644
index 0000000..822e0d6
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Board/miscdev.c
@@ -0,0 +1,172 @@
+/*
+ * miscdev.c
+ *
+ *  Created on: Jan 1, 2023
+ *      Author: Think
+ */
+#include <miscdev.h>
+#include "stm32l4xx_hal.h"
+#include "main.h"
+#include "adc.h"
+#include "tim.h"
+#include "miscdev.h"
+
+
+/*
+ *+----------------------+
+ *|     GPIO Led API     |
+ *+----------------------+
+ */
+
+gpio_t     leds[LedMax] =
+{
+	{ "RedLed",   LedRed_GPIO_Port,   LedRed_Pin},
+	{ "GreenLed", LedGreen_GPIO_Port, LedGreen_Pin},
+	{ "BlueLed",  LedBlue_GPIO_Port,  LedBlue_Pin},
+};
+
+void turn_led(lednum_t which, status_t status)
+{
+	GPIO_PinState        level;
+
+	if( which >= LedMax )
+		return ;
+
+	level = status==OFF ? GPIO_PIN_SET : GPIO_PIN_RESET;
+
+	HAL_GPIO_WritePin(leds[which].group, leds[which].pin, level);
+}
+
+void blink_led(lednum_t which, uint32_t interval)
+{
+	turn_led(which, ON);
+	HAL_Delay(interval);
+
+	turn_led(which, OFF);
+	HAL_Delay(interval);
+}
+
+/*
+ *+----------------------+
+ *|    GPIO Relay API    |
+ *+----------------------+
+ */
+
+void turn_relay(status_t status)
+{
+	GPIO_PinState        level;
+
+	level = status==OFF ? GPIO_PIN_RESET : GPIO_PIN_SET;
+	HAL_GPIO_WritePin(Relay_GPIO_Port, Relay_Pin, level);
+}
+
+
+/*
+ *+----------------------+
+ *|     GPIO Key API     |
+ *+----------------------+
+ */
+
+void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
+{
+	static unsigned char   status[3];
+
+	if( Key1_Pin == GPIO_Pin )
+	{
+		status[0] ^= 1;
+		turn_led(LedRed, status[0]);
+	}
+	else if( Key2_Pin == GPIO_Pin )
+	{
+		status[1] ^= 1;
+		turn_led(LedGreen, status[1]);
+	}
+	if( Key3_Pin == GPIO_Pin )
+	{
+		status[2] ^= 1;
+		turn_led(LedBlue, status[2]);
+	}
+}
+
+
+/*
+ *+----------------------------+
+ *| ADC noisy & lux sensor API |
+ *+----------------------------+
+ */
+
+int adc_sample_lux_noisy(uint32_t *lux, uint32_t *noisy)
+{
+    uint8_t            i;
+    uint32_t           timeout = 0xffffff;
+
+    for(i=0; i<ADCCHN_MAX; i++)
+    {
+            HAL_ADC_Start(&hadc1);
+
+            HAL_ADC_PollForConversion(&hadc1, timeout);
+
+            if( ADCCHN_NOISY == i )
+            {
+                *noisy = HAL_ADC_GetValue(&hadc1);
+            }
+            else if( ADCCHN_LUX == i )
+            {
+                *lux = HAL_ADC_GetValue(&hadc1);
+            }
+
+            HAL_Delay(10);
+    }
+
+    HAL_ADC_Stop(&hadc1);
+
+    return 0;
+}
+
+
+/*
+ *+----------------------------+
+ *  Timer Buzzer/delay API     |
+ *+----------------------------+
+ */
+
+void beep_start(uint16_t times, uint16_t interval)
+{
+    while( times-- )
+    {
+        /* Start buzzer */
+        if (HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_4) != HAL_OK)
+        {
+            /* Starting Error */
+            Error_Handler();
+        }
+
+        HAL_Delay(interval);
+
+        /* Stop buzzer */
+        if (HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_4) != HAL_OK)
+        {
+            /* Starting Error */
+            Error_Handler();
+        }
+
+        HAL_Delay(interval);
+    }
+}
+
+/* Max to 60000 us*/
+void delay_us(uint16_t us)
+{
+    uint16_t differ = 60000-us;
+
+    HAL_TIM_Base_Start(&htim6);
+
+    __HAL_TIM_SET_COUNTER(&htim6, differ);
+
+    while( differ < 60000 )
+    {
+        differ=__HAL_TIM_GET_COUNTER(&htim6);
+    }
+
+    HAL_TIM_Base_Stop(&htim6);
+}
diff --git a/Production/ISKBoard_QCTester/Core/Board/miscdev.h b/Production/ISKBoard_QCTester/Core/Board/miscdev.h
new file mode 100644
index 0000000..97b9efd
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Board/miscdev.h
@@ -0,0 +1,80 @@
+/*
+ * led.h
+ *
+ *  Created on: Jan 4, 2023
+ *      Author: Think
+ */
+
+#ifndef INC_MISCDEV_H_
+#define INC_MISCDEV_H_
+
+#include "stm32l431xx.h"
+
+typedef enum
+{
+	OFF,
+	ON,
+} status_t;
+
+/*
+ *+----------------------+
+ *|     GPIO Led API     |
+ *+----------------------+
+ */
+
+typedef enum
+{
+	LedRed,
+	LedGreen,
+	LedBlue,
+	LedMax,
+} lednum_t;
+
+typedef struct gpio_s
+{
+	const char          *name;
+	GPIO_TypeDef        *group;
+	uint16_t             pin;
+} gpio_t;
+
+extern gpio_t     leds[LedMax] ;
+
+extern void turn_led(lednum_t which, status_t status);
+
+extern void blink_led(lednum_t which, uint32_t interval);
+
+/*
+ *+----------------------+
+ *|    GPIO Relay API    |
+ *+----------------------+
+ */
+
+extern void turn_relay(status_t status);
+
+/*
+ *+----------------------------+
+ *| ADC noisy & lux sensor API |
+ *+----------------------------+
+ */
+
+enum
+{
+    ADCCHN_NOISY,
+    ADCCHN_LUX,
+    ADCCHN_MAX,
+};
+extern int adc_sample_lux_noisy(uint32_t *lux, uint32_t *noisy);
+
+/*
+ *+----------------------------+
+ *  Timer Buzzer/delay API     |
+ *+----------------------------+
+ */
+
+extern void beep_start(uint16_t times, uint16_t interval);
+
+/* Max to 60000 us, DS18B20 driver depends on it */
+extern void delay_us(uint16_t us);
+
+
+#endif /* INC_LED_H_ */
diff --git a/Production/ISKBoard_QCTester/Core/Board/ringbuf.c b/Production/ISKBoard_QCTester/Core/Board/ringbuf.c
new file mode 100644
index 0000000..16fd897
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Board/ringbuf.c
@@ -0,0 +1,109 @@
+/*
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+	Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+	Based on ringbuffer.c by Patrick Prasse (patrick.prasse@gmx.net). Code
+	has been modified by Telenor and Gemalto.
+
+ */
+
+
+#include <string.h>
+#include <assert.h>
+#include "ringbuf.h"
+
+void rb_init (ring_buffer_t *rb, uint8_t *buf, int size)
+{
+    memset (rb, 0, sizeof (ring_buffer_t));
+    rb->rd_pointer = 0;
+    rb->wr_pointer = 0;
+    rb->buffer= buf;
+    memset(buf, 0, size);
+    rb->size = size;
+}
+
+void rb_clear (ring_buffer_t *rb)
+{
+    memset(rb->buffer,0,rb->size);
+    rb->rd_pointer=0;
+    rb->wr_pointer=0;
+}
+
+int rb_data_size (ring_buffer_t *rb)
+{
+    return ((rb->wr_pointer - rb->rd_pointer) & (rb->size-1));
+}
+
+int rb_free_size (ring_buffer_t *rb)
+{
+    return (rb->size - 1 - rb_data_size(rb));
+}
+
+int rb_write (ring_buffer_t *rb, uint8_t *buf, int len)
+{
+    int total;
+    int i;
+
+    /* total = len = min(space, len) */
+    total = rb_free_size(rb);
+    if(len > total)
+        len = total;
+    else
+        total = len;
+
+    i = rb->wr_pointer;
+    if(i + len > rb->size)
+    {
+        memcpy(rb->buffer + i, buf, rb->size - i);
+        buf += rb->size - i;
+        len -= rb->size - i;
+        i = 0;
+    }
+    memcpy(rb->buffer + i, buf, len);
+    rb->wr_pointer = i + len;
+    return total;
+}
+
+int rb_read (ring_buffer_t *rb, uint8_t *buf, int max)
+{
+    int total;
+    int i;
+    /* total = len = min(used, len) */
+    total = rb_data_size(rb);
+
+    if(max > total)
+        max = total;
+    else
+        total = max;
+
+    i = rb->rd_pointer;
+    if(i + max > rb->size)
+    {
+        memcpy(buf, rb->buffer + i, rb->size - i);
+        buf += rb->size - i;
+        max -= rb->size - i;
+        i = 0;
+    }
+    memcpy(buf, rb->buffer + i, max);
+    rb->rd_pointer = i + max;
+
+    return total;
+}
+
+uint8_t rb_peek(ring_buffer_t *rb, int index)
+{
+	assert(index < rb_data_size(rb));
+
+	return rb->buffer[((rb->rd_pointer + index) % rb->size)];
+}
diff --git a/Production/ISKBoard_QCTester/Core/Board/ringbuf.h b/Production/ISKBoard_QCTester/Core/Board/ringbuf.h
new file mode 100644
index 0000000..abae66d
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Board/ringbuf.h
@@ -0,0 +1,58 @@
+/*
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+	Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+	Based on ringbuffer.h by Patrick Prasse (patrick.prasse@gmx.net). Code
+	has been modified by Telenor and Gemalto.
+
+ */
+
+#ifndef __RINGBUF_H_
+#define __RINGBUF_H_
+
+#include <sys/types.h>
+#include <stdint.h>
+
+typedef struct ring_buffer_s {
+	uint8_t *buffer;
+    int wr_pointer;
+    int rd_pointer;
+    int size;
+} ring_buffer_t;
+
+/* Initial the ring buffer */
+void rb_init (ring_buffer_t *ring, uint8_t *buf, int size) ;
+
+/* Clear the ring buffer data  */
+void rb_clear (ring_buffer_t *rb) ;
+
+/* Get ring buffer left free size  */
+int rb_free_size (ring_buffer_t *rb);
+
+/* Get data size in the ring buffer  */
+int rb_data_size (ring_buffer_t *rb);
+
+/* Read $max bytes data from ring buffer $rb to $buf */
+int rb_read (ring_buffer_t *rb, uint8_t *buf, int max);
+
+/*  Description: Write $len bytes data in $buf into ring buffer $rb 
+ * Return Value: The actual written into ring buffer data size, if ring buffer 
+ * left space size small than $len, then only part of the data be written into.
+ */
+int rb_write (ring_buffer_t *rb, uint8_t *buf, int len) ;
+
+/* Read a specify $index byte data in ring buffer $rb  */
+uint8_t rb_peek(ring_buffer_t *rb, int index);
+
+#endif /* __RINGBUF_H_ */
diff --git a/Production/ISKBoard_QCTester/Core/Board/sht20.c b/Production/ISKBoard_QCTester/Core/Board/sht20.c
new file mode 100644
index 0000000..912763e
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Board/sht20.c
@@ -0,0 +1,211 @@
+/*
+ * sht20.c
+ *
+ *  Created on: Jan 19, 2023
+ *      Author: Wenxue
+ */
+
+#include <sht20.h>
+#include <stdio.h>
+#include <string.h>
+
+#define delay_ms(x)		HAL_Delay(x)
+
+enum
+{
+	TRIG_T_MEASUREMENT_HM    = 0xE3, // command trig. temp meas. hold master
+	TRIG_RH_MEASUREMENT_HM   = 0xE5, // command trig. humidity meas. hold master
+	TRIG_T_MEASUREMENT_POLL  = 0xF3, // command trig. temp meas. no hold master
+	TRIG_RH_MEASUREMENT_POLL = 0xF5, // command trig. humidity meas. no hold master
+	USER_REG_W               = 0xE6, // command writing user register
+	USER_REG_R               = 0xE7, // command reading user register
+	SOFT_RESET               = 0xFE  // command soft reset
+};
+
+
+//#define CONFIG_SHT2X_DEBUG
+
+#ifdef CONFIG_SHT2X_DEBUG
+static inline void dump_buf(uint8_t *buf, uint32_t size)
+{
+	int      i;
+	
+	if(!buf)
+		return ;
+	
+	for(i=0; i<size; i++)
+		printf("%02x ", buf[i]);
+	
+	printf("\r\n");
+}
+#endif
+
+#ifdef CONFIG_SHT2X_DEBUG
+#define sht2x_print(format,args...) printf(format, ##args)
+#else
+#define sht2x_print(format,args...) do{} while(0);
+#endif
+	
+	
+static int sht2x_measure_value(uint8_t command, uint16_t *val);
+static int sht2x_softreset(void);
+
+
+uint32_t sht20_sample_TrH(uint32_t *temperature, uint32_t *humdity)
+{
+	uint16_t       sT;
+	uint16_t       sRH;
+	uint32_t       T;
+	uint32_t       RH;
+	uint32_t       TRH=TRH_FAIL_VAL;
+
+	i2c_term(SHT2X_I2CBUS);
+
+	if( i2c_init(SHT2X_I2CBUS, SHT2X_CHIPADDR) )
+	{
+		sht2x_print("SHT20 initial I2C bus failed.\r\n");
+		return TRH;
+	}
+
+	if( sht2x_softreset() )
+	{
+		sht2x_print("SHT20 soft reset failed.\r\n");
+		goto OUT;
+	}
+
+	sht2x_print("Start to measurement temperature...\r\n");
+	if( sht2x_measure_value(TRIG_T_MEASUREMENT_POLL, &sT) < 0 )
+	{
+		sht2x_print("I2C measurement failed\r\n");
+		goto OUT;
+	}
+  
+	T=(uint32_t)(100*(float)((175.72*sT)/65536-46.85));
+	sht2x_print("Measure temperature: %lu.%lu\r\n", T/100, T%100);
+	if( temperature )
+		*temperature=T;
+
+	TRH = ((T/100)<<8|(T%100))<<16 | 0xFFFF;
+  
+	sht2x_print("Start to measurement relative humidity...\r\n");
+	if( sht2x_measure_value(TRIG_RH_MEASUREMENT_POLL, &sRH) < 0 )
+	{
+		goto OUT;
+	}
+
+	RH=(uint32_t)(100*(float)((125*sRH)/65535-6.0));
+	sht2x_print("Measure relative humidity: %lu.%lu%%\r\n", RH/100, RH%100);
+	if( humdity )
+		*humdity=RH;
+	
+	TRH &=  0xFFFF0000 | ((uint16_t)RH/100)<<8 | ((uint16_t)RH%100) ;
+
+OUT:  
+	i2c_term(SHT2X_I2CBUS);
+	return TRH;
+}
+
+int sht2x_softreset(void)
+{
+	uint8_t           command = SOFT_RESET;
+	int               rv = 0;
+	
+	sht2x_print("Start soft reset sht2x\r\n");
+	rv=i2c_write(SHT2X_I2CBUS, &command, 1);
+	
+	if( rv )
+	{
+		sht2x_print("SHT2X send soft reset command 0x%0x failure: rv=0x%02x\r\n", command, rv);
+		return -rv;
+	}
+	
+	delay_ms(15);
+	return 0;
+}
+
+static int sht2x_checkcrc(uint8_t *data, uint8_t bytes, uint8_t checksum)
+{
+  uint8_t crc = 0;	
+  uint8_t i;
+  uint8_t bit;
+  
+  //calculates 8-Bit checksum with given polynomial
+  for (i=0; i<bytes; ++i)
+  { 
+    crc ^= (data[i]);
+    for (bit=8; bit>0; --bit)
+    { 
+      if (crc & 0x80) 
+        crc = (crc << 1) ^ 0x0131; //POLYNOMIAL;
+      else 
+        crc = (crc << 1);
+    }
+  }
+
+  if (crc != checksum)
+  {
+    return -1;
+  }
+  else 
+  {
+    return 0;
+  }
+}
+
+static int sht2x_measure_value(uint8_t command, uint16_t *val)
+{
+	uint8_t        buf[3];    /* I2C������buffer */
+	int            count=4;    /* �ܹ����Ĵ��� */
+  
+	if( !val )
+	{
+		sht2x_print("SHT2X invalid input arguments\r\n");
+		return -1;
+	}
+	
+	if(TRIG_T_MEASUREMENT_POLL!=command && TRIG_RH_MEASUREMENT_POLL !=command )
+	{
+		sht2x_print("SHT2X unsupport command: 0x%0x\r\n", command);
+		return -2;
+	}
+
+	if( NO_ERROR != i2c_write(SHT2X_I2CBUS, &command, 1) )
+	{
+		sht2x_print("SHT2X send measure command 0x%0x failure\r\n", command);
+		return -3;
+	}
+  
+	if(TRIG_T_MEASUREMENT_POLL == command)
+		delay_ms(85);
+	else
+		delay_ms(29);
+
+	while(count--)
+	{
+		memset(buf, 0, 3);
+
+		if( !i2c_read(SHT2X_I2CBUS, buf, 3) )
+		{
+			break;
+		}
+		delay_ms(5);
+	}
+
+	if( sht2x_checkcrc(buf, 2, buf[2])< 0 )
+	{
+#ifdef CONFIG_SHT2X_DEBUG		
+		sht2x_print("Measurement data checksum failure:\r\n");
+		dump_buf(buf, 3);
+#endif
+		return -4;
+	}
+
+	if(TRIG_T_MEASUREMENT_POLL == command)
+		*val = buf[0]<<8|(buf[1]&0xFC); //14bits(1111 1100)
+	else
+		*val = buf[0]<<8|(buf[1]&0xFF); //12bits(1100 0000)
+
+	sht2x_print("Measurement temperature value: 0x%04x\r\n", *val);
+  
+  return 0;
+}
diff --git a/Production/ISKBoard_QCTester/Core/Board/sht20.h b/Production/ISKBoard_QCTester/Core/Board/sht20.h
new file mode 100644
index 0000000..403f189
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Board/sht20.h
@@ -0,0 +1,19 @@
+/*
+ * sht20.h
+ *
+ *  Created on: Jan 19, 2023
+ *      Author: Wenxue
+ */
+
+#ifndef INC_SHT20_H_
+#define INC_SHT20_H_
+
+#include "i2c_bitbang.h"
+
+#define SHT2X_I2CBUS           I2CBUS0   /* SHT20 on GPIO I2C bus0 */
+#define SHT2X_CHIPADDR         0x40      /* SHT20 7-Bits Chip address */
+
+#define TRH_FAIL_VAL      0xFFFFFFFF
+extern uint32_t sht20_sample_TrH(uint32_t *temperature, uint32_t *humdity);
+
+#endif /* INC_SHT20_H_ */
diff --git a/Production/ISKBoard_QCTester/Core/Board/w25q32.c b/Production/ISKBoard_QCTester/Core/Board/w25q32.c
new file mode 100644
index 0000000..8b49319
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Board/w25q32.c
@@ -0,0 +1,1025 @@
+
+#include <stdio.h>
+#include "w25q32.h"
+#include "spi.h"
+
+/*+------------------------------+
+ *|     Debug print macro        |
+ *+------------------------------+*/
+
+#define CONFIG_W25Q_DEBUG
+
+#ifdef  CONFIG_W25Q_DEBUG
+#define w25q_print(format,args...) printf(format, ##args)
+#else
+#define w25q_print(format,args...) do{} while(0)
+#endif
+
+/*+------------------------------+
+ *|   SPI Hardware definition    |
+ *+------------------------------+*/
+
+#define W25Q_SPI                   &hspi1
+#define W25Q_CS_GPIO               SPI1_CS_GPIO_Port
+#define W25Q_CS_PIN                SPI1_CS_Pin
+#define DUMMY_BYTE                 0xFF
+
+#define w25q_delay(delay)          HAL_Delay(delay)
+#define w25q_cs_enable()           HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_RESET)
+#define w25q_cs_disable()          HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_SET)
+
+/*+------------------------------+
+ *|    W25Q Serial SPI Flash     |
+ *+------------------------------+*/
+
+/* Winbond -- w25x "page" are 256, "sectors" are 4KiB, "blocks" are 64K */
+
+#define MB                  0x100000
+#define W25Q_PGESIZE        256
+#define W25Q_SECSIZE        4096
+#define W25Q_BLKSIZE        65536
+#define W25Q_NPGES(size)    ( ((size)*MB) / W25Q_PGESIZE )
+#define W25Q_NSECS(size)    ( ((size)*MB) / W25Q_SECSIZE )
+#define W25Q_NBLKS(size)    ( ((size)*MB) / W25Q_BLKSIZE )
+
+#define INFO(_name, _jedecid, _npages, _nsectors, _nblocks) \
+        .name       = _name, \
+        .id         = _jedecid, \
+        .pagesize   = W25Q_PGESIZE, \
+        .npages     = _npages, \
+        .sectorsize = W25Q_SECSIZE, \
+        .nsectors   = _nsectors, \
+        .blocksize  = W25Q_BLKSIZE, \
+        .nblocks    = _nblocks,
+
+#define ARRAY_SIZE(x)    (sizeof(x)/sizeof(x[0]))
+
+flash_info_t w25q_ids[] =
+{
+    /* 1MB */
+    { INFO("w25q80bl",  0xef4014, W25Q_NPGES(1), W25Q_NSECS(1), W25Q_NBLKS(1)) },
+    { INFO("w25q80",    0xef5014, W25Q_NPGES(1), W25Q_NSECS(1), W25Q_NBLKS(1)) },
+
+    /* 2MB */
+    { INFO("w25q16cl",  0xef4015, W25Q_NPGES(2), W25Q_NSECS(2), W25Q_NBLKS(2)) },
+    { INFO("w25q16dw",  0xef6015, W25Q_NPGES(2), W25Q_NSECS(2), W25Q_NBLKS(2)) },
+
+    /* 4MB */
+    { INFO("w25q32",    0xef4016, W25Q_NPGES(4), W25Q_NSECS(4), W25Q_NBLKS(4)) },
+    { INFO("w25q32dw",  0xef6016, W25Q_NPGES(4), W25Q_NSECS(4), W25Q_NBLKS(4)) },
+    { INFO("w25q32jv",  0xef7016, W25Q_NPGES(4), W25Q_NSECS(4), W25Q_NBLKS(4)) },
+    { INFO("w25q32jwm", 0xef8016, W25Q_NPGES(4), W25Q_NSECS(4), W25Q_NBLKS(4)) },
+
+    /* 8MB */
+    { INFO("w25q64cv",  0xef4017, W25Q_NPGES(8), W25Q_NSECS(8), W25Q_NBLKS(8)) },
+    { INFO("w25q64dw",  0xef6017, W25Q_NPGES(8), W25Q_NSECS(8), W25Q_NBLKS(8)) },
+    { INFO("w25q64jv",  0xef7017, W25Q_NPGES(8), W25Q_NSECS(8), W25Q_NBLKS(8)) },
+
+    /* 16MB */
+    { INFO("w25q128",   0xef4018, W25Q_NPGES(16), W25Q_NSECS(16), W25Q_NBLKS(16)) },
+    { INFO("w25q128fw", 0xef6018, W25Q_NPGES(16), W25Q_NSECS(16), W25Q_NBLKS(16)) },
+    { INFO("w25q128jv", 0xef7018, W25Q_NPGES(16), W25Q_NSECS(16), W25Q_NBLKS(16)) },
+
+    /* 32MB */
+    { INFO("w25q256",   0xef4019, W25Q_NPGES(32), W25Q_NSECS(32), W25Q_NBLKS(32)) },
+    { INFO("w25q256fw", 0xef6019, W25Q_NPGES(32), W25Q_NSECS(32), W25Q_NBLKS(32)) },
+    { INFO("w25q256jw", 0xef7019, W25Q_NPGES(32), W25Q_NSECS(32), W25Q_NBLKS(32)) },
+};
+
+/*+------------------------------+
+ *|   W25Q Serial Flash Driver   |
+ *+------------------------------+*/
+
+flash_info_t   *w25q;
+
+static inline uint8_t w25q_spi_xfer(uint8_t txbyte)
+{
+    uint8_t rxbyte = 0x00;
+
+    HAL_SPI_TransmitReceive(W25Q_SPI, &txbyte, &rxbyte, 1, 0xFFFF);
+
+    return rxbyte;
+}
+
+void w25q_Reset(void)
+{
+    w25q_cs_enable();
+
+    w25q_spi_xfer(0x66);  // enable reset
+    w25q_spi_xfer(0x99);  // reset
+
+    w25q_cs_disable();
+}
+
+uint16_t w25q_ReadID(void)
+{
+    uint16_t ID = 0x0;
+
+    w25q_cs_enable();
+
+    w25q_spi_xfer(0x90);
+    w25q_spi_xfer(DUMMY_BYTE);
+    w25q_spi_xfer(DUMMY_BYTE);
+    w25q_spi_xfer(0x00);
+
+    ID |= w25q_spi_xfer(DUMMY_BYTE)<<8; /* Manufacturer ID */
+    ID |= w25q_spi_xfer(DUMMY_BYTE);    /* Device ID */
+
+    w25q_cs_disable();
+
+    return ID;
+}
+
+uint32_t w25q_ReadJedecID(void)
+{
+    uint32_t ID = 0x0;
+
+    w25q_cs_enable();
+
+    w25q_spi_xfer(0x9F);
+
+    ID |= w25q_spi_xfer(DUMMY_BYTE)<<16; /* Manufacturer ID */
+    ID |= w25q_spi_xfer(DUMMY_BYTE)<<8;  /* Device ID15~ID8 */
+    ID |= w25q_spi_xfer(DUMMY_BYTE)<<0;  /* Device ID7~ID0  */
+
+    w25q_cs_disable();
+
+    return ID;
+}
+
+int w25q_Init(void)
+{
+    uint16_t    chipID;
+    uint32_t    jedecID;
+    int         i, found = 0;
+    int         rv = 0;
+
+    w25q_Reset();
+
+    chipID = w25q_ReadID();
+    if( 0x00 == chipID )
+    {
+    	rv = -2;
+    	goto cleanup;
+    }
+    w25q_print("W25Q device ID: %04x\r\n", chipID);
+
+    jedecID = w25q_ReadJedecID();
+    if( 0x00 == jedecID )
+    {
+    	rv = -3;
+    	goto cleanup;
+    }
+    w25q_print("W25Q JEDEC  ID: %lx\r\n", jedecID);
+
+    for(i=0; i<ARRAY_SIZE(w25q_ids); i++)
+    {
+        if( w25q_ids[i].id == jedecID )
+        {
+            found = 1;
+            w25q = &w25q_ids[i];
+        }
+    }
+
+    rv = -4;
+
+cleanup:
+    if( found )
+    {
+    	w25q_print("Dectec Norflash %s, Capacity %lu KB\r\n", w25q->name, w25q->blocksize*w25q->nblocks/1024);
+    	return 0;
+    }
+
+	w25q_print("ERROR: Can not found W25Q chipset by JDEDEC\r\n");
+	return rv;
+}
+
+static inline void w25q_WriteEnable(void)
+{
+    w25q_cs_enable();
+    w25q_spi_xfer(0x06);
+    w25q_cs_disable();
+}
+
+static inline void w25q_WriteDisable(void)
+{
+    w25q_cs_enable();
+    w25q_spi_xfer(0x04);
+    w25q_cs_disable();
+
+    w25q_delay(1);
+}
+
+static inline uint8_t w25q_ReadSR(int reg)
+{
+    uint8_t status = 0;
+
+    w25q_cs_enable();
+
+    if (REG_SR1 == reg)
+    {
+        w25q_spi_xfer(0x05);
+        status = w25q_spi_xfer(DUMMY_BYTE);
+    }
+    else if (REG_SR2 == reg)
+    {
+        w25q_spi_xfer(0x35);
+        status = w25q_spi_xfer(DUMMY_BYTE);
+    }
+    else if (REG_SR3 == reg)
+    {
+        w25q_spi_xfer(0x15);
+        status = w25q_spi_xfer(DUMMY_BYTE);
+    }
+
+    w25q_cs_disable();
+    return status;
+}
+
+static inline void w25q_WriteSR(int reg, uint8_t data)
+{
+    w25q_cs_enable();
+
+    if (REG_SR1 == reg)
+    {
+        w25q_spi_xfer(0x01);
+    }
+    else if (REG_SR2 == reg)
+    {
+        w25q_spi_xfer(0x31);
+    }
+    else if (REG_SR2 == reg)
+    {
+        w25q_spi_xfer(0x11);
+    }
+
+    w25q_spi_xfer(data);
+    w25q_cs_disable();
+
+    return ;
+}
+
+/* Status Register[1] bit[0]: Erase/Write In Progress (BUSY) */
+void w25q_Wait4Ready(void)
+{
+	uint8_t         regval;
+
+    do
+    {
+    	w25q_delay(1);
+    	regval = w25q_ReadSR(REG_SR1);
+    } while ( !(regval&0x01) );
+
+	return ;
+}
+
+
+void w25q_EraseChip(void)
+{
+	w25q_WriteEnable();
+
+	w25q_cs_enable();
+	w25q_spi_xfer(0xC7);
+	w25q_cs_disable();
+
+	w25q_Wait4Ready();
+	w25q_print("chip erase %s done!\r\n", w25q->name);
+
+	return ;
+}
+
+#if 0
+
+//###################################################################################################################
+void w25q_EraseChip(void)
+{
+    while (w25q.Lock == 1)
+        w25q_delay(1);
+    w25q.Lock = 1;
+#if (_w25q_DEBUG == 1)
+    uint32_t StartTime = HAL_GetTick();
+    printf("w25q EraseChip Begin...\r\n");
+#endif
+    w25q_WriteEnable();
+    HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_RESET);
+    w25q_spi_xfer(0xC7);
+    HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_SET);
+    w25q_WaitForWriteEnd();
+#if (_w25q_DEBUG == 1)
+    printf("w25q EraseBlock done after %d ms!\r\n", HAL_GetTick() - StartTime);
+#endif
+    w25q_delay(10);
+    w25q.Lock = 0;
+}
+//###################################################################################################################
+void w25q_EraseSector(uint32_t SectorAddr)
+{
+    while (w25q.Lock == 1)
+        w25q_delay(1);
+    w25q.Lock = 1;
+#if (_w25q_DEBUG == 1)
+    uint32_t StartTime = HAL_GetTick();
+    printf("w25q EraseSector %d Begin...\r\n", SectorAddr);
+#endif
+    w25q_WaitForWriteEnd();
+    SectorAddr = SectorAddr * w25q.SectorSize;
+    w25q_WriteEnable();
+    HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_RESET);
+    if (w25q.ID >= W25Q256)
+    {
+        w25q_spi_xfer(0x21);
+        w25q_spi_xfer((SectorAddr & 0xFF000000) >> 24);
+    }
+    else
+    {
+        w25q_spi_xfer(0x20);
+    }
+    w25q_spi_xfer((SectorAddr & 0xFF0000) >> 16);
+    w25q_spi_xfer((SectorAddr & 0xFF00) >> 8);
+    w25q_spi_xfer(SectorAddr & 0xFF);
+    HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_SET);
+    w25q_WaitForWriteEnd();
+#if (_w25q_DEBUG == 1)
+    printf("w25q EraseSector done after %d ms\r\n", HAL_GetTick() - StartTime);
+#endif
+    w25q_delay(1);
+    w25q.Lock = 0;
+}
+//###################################################################################################################
+void w25q_EraseBlock(uint32_t BlockAddr)
+{
+    while (w25q.Lock == 1)
+        w25q_delay(1);
+    w25q.Lock = 1;
+#if (_w25q_DEBUG == 1)
+    printf("w25q EraseBlock %d Begin...\r\n", BlockAddr);
+    w25q_delay(100);
+    uint32_t StartTime = HAL_GetTick();
+#endif
+    w25q_WaitForWriteEnd();
+    BlockAddr = BlockAddr * w25q.SectorSize * 16;
+    w25q_WriteEnable();
+    HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_RESET);
+    if (w25q.ID >= W25Q256)
+    {
+        w25q_spi_xfer(0xDC);
+        w25q_spi_xfer((BlockAddr & 0xFF000000) >> 24);
+    }
+    else
+    {
+        w25q_spi_xfer(0xD8);
+    }
+    w25q_spi_xfer((BlockAddr & 0xFF0000) >> 16);
+    w25q_spi_xfer((BlockAddr & 0xFF00) >> 8);
+    w25q_spi_xfer(BlockAddr & 0xFF);
+    HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_SET);
+    w25q_WaitForWriteEnd();
+#if (_w25q_DEBUG == 1)
+    printf("w25q EraseBlock done after %d ms\r\n", HAL_GetTick() - StartTime);
+    w25q_delay(100);
+#endif
+    w25q_delay(1);
+    w25q.Lock = 0;
+}
+//###################################################################################################################
+uint32_t w25q_PageToSector(uint32_t PageAddress)
+{
+    return ((PageAddress * w25q.PageSize) / w25q.SectorSize);
+}
+//###################################################################################################################
+uint32_t w25q_PageToBlock(uint32_t PageAddress)
+{
+    return ((PageAddress * w25q.PageSize) / w25q.BlockSize);
+}
+//###################################################################################################################
+uint32_t w25q_SectorToBlock(uint32_t SectorAddress)
+{
+    return ((SectorAddress * w25q.SectorSize) / w25q.BlockSize);
+}
+//###################################################################################################################
+uint32_t w25q_SectorToPage(uint32_t SectorAddress)
+{
+    return (SectorAddress * w25q.SectorSize) / w25q.PageSize;
+}
+//###################################################################################################################
+uint32_t w25q_BlockToPage(uint32_t BlockAddress)
+{
+    return (BlockAddress * w25q.BlockSize) / w25q.PageSize;
+}
+//###################################################################################################################
+bool w25q_IsEmptyPage(uint32_t Page_Address, uint32_t OffsetInByte, uint32_t NumByteToCheck_up_to_PageSize)
+{
+    while (w25q.Lock == 1)
+        w25q_delay(1);
+    w25q.Lock = 1;
+    if (((NumByteToCheck_up_to_PageSize + OffsetInByte) > w25q.PageSize) || (NumByteToCheck_up_to_PageSize == 0))
+        NumByteToCheck_up_to_PageSize = w25q.PageSize - OffsetInByte;
+#if (_w25q_DEBUG == 1)
+    printf("w25q CheckPage:%d, Offset:%d, Bytes:%d begin...\r\n", Page_Address, OffsetInByte, NumByteToCheck_up_to_PageSize);
+    w25q_delay(100);
+    uint32_t StartTime = HAL_GetTick();
+#endif
+    uint8_t pBuffer[32];
+    uint32_t WorkAddress;
+    uint32_t i;
+    for (i = OffsetInByte; i < w25q.PageSize; i += sizeof(pBuffer))
+    {
+        HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_RESET);
+        WorkAddress = (i + Page_Address * w25q.PageSize);
+        if (w25q.ID >= W25Q256)
+        {
+            w25q_spi_xfer(0x0C);
+            w25q_spi_xfer((WorkAddress & 0xFF000000) >> 24);
+        }
+        else
+        {
+            w25q_spi_xfer(0x0B);
+        }
+        w25q_spi_xfer((WorkAddress & 0xFF0000) >> 16);
+        w25q_spi_xfer((WorkAddress & 0xFF00) >> 8);
+        w25q_spi_xfer(WorkAddress & 0xFF);
+        w25q_spi_xfer(0);
+        HAL_SPI_Receive(&_W25q_spi_xfer, pBuffer, sizeof(pBuffer), 100);
+        HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_SET);
+        for (uint8_t x = 0; x < sizeof(pBuffer); x++)
+        {
+            if (pBuffer[x] != 0xFF)
+                goto NOT_EMPTY;
+        }
+    }
+    if ((w25q.PageSize + OffsetInByte) % sizeof(pBuffer) != 0)
+    {
+        i -= sizeof(pBuffer);
+        for (; i < w25q.PageSize; i++)
+        {
+            HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_RESET);
+            WorkAddress = (i + Page_Address * w25q.PageSize);
+            w25q_spi_xfer(0x0B);
+            if (w25q.ID >= W25Q256)
+            {
+                w25q_spi_xfer(0x0C);
+                w25q_spi_xfer((WorkAddress & 0xFF000000) >> 24);
+            }
+            else
+            {
+                w25q_spi_xfer(0x0B);
+            }
+            w25q_spi_xfer((WorkAddress & 0xFF0000) >> 16);
+            w25q_spi_xfer((WorkAddress & 0xFF00) >> 8);
+            w25q_spi_xfer(WorkAddress & 0xFF);
+            w25q_spi_xfer(0);
+            HAL_SPI_Receive(&_W25q_spi_xfer, pBuffer, 1, 100);
+            HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_SET);
+            if (pBuffer[0] != 0xFF)
+                goto NOT_EMPTY;
+        }
+    }
+#if (_w25q_DEBUG == 1)
+    printf("w25q CheckPage is Empty in %d ms\r\n", HAL_GetTick() - StartTime);
+    w25q_delay(100);
+#endif
+    w25q.Lock = 0;
+    return true;
+NOT_EMPTY:
+#if (_w25q_DEBUG == 1)
+    printf("w25q CheckPage is Not Empty in %d ms\r\n", HAL_GetTick() - StartTime);
+    w25q_delay(100);
+#endif
+    w25q.Lock = 0;
+    return false;
+}
+//###################################################################################################################
+bool w25q_IsEmptySector(uint32_t Sector_Address, uint32_t OffsetInByte, uint32_t NumByteToCheck_up_to_SectorSize)
+{
+    while (w25q.Lock == 1)
+        w25q_delay(1);
+    w25q.Lock = 1;
+    if ((NumByteToCheck_up_to_SectorSize > w25q.SectorSize) || (NumByteToCheck_up_to_SectorSize == 0))
+        NumByteToCheck_up_to_SectorSize = w25q.SectorSize;
+#if (_w25q_DEBUG == 1)
+    printf("w25q CheckSector:%d, Offset:%d, Bytes:%d begin...\r\n", Sector_Address, OffsetInByte, NumByteToCheck_up_to_SectorSize);
+    w25q_delay(100);
+    uint32_t StartTime = HAL_GetTick();
+#endif
+    uint8_t pBuffer[32];
+    uint32_t WorkAddress;
+    uint32_t i;
+    for (i = OffsetInByte; i < w25q.SectorSize; i += sizeof(pBuffer))
+    {
+        HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_RESET);
+        WorkAddress = (i + Sector_Address * w25q.SectorSize);
+        if (w25q.ID >= W25Q256)
+        {
+            w25q_spi_xfer(0x0C);
+            w25q_spi_xfer((WorkAddress & 0xFF000000) >> 24);
+        }
+        else
+        {
+            w25q_spi_xfer(0x0B);
+        }
+        w25q_spi_xfer((WorkAddress & 0xFF0000) >> 16);
+        w25q_spi_xfer((WorkAddress & 0xFF00) >> 8);
+        w25q_spi_xfer(WorkAddress & 0xFF);
+        w25q_spi_xfer(0);
+        HAL_SPI_Receive(&_W25q_spi_xfer, pBuffer, sizeof(pBuffer), 100);
+        HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_SET);
+        for (uint8_t x = 0; x < sizeof(pBuffer); x++)
+        {
+            if (pBuffer[x] != 0xFF)
+                goto NOT_EMPTY;
+        }
+    }
+    if ((w25q.SectorSize + OffsetInByte) % sizeof(pBuffer) != 0)
+    {
+        i -= sizeof(pBuffer);
+        for (; i < w25q.SectorSize; i++)
+        {
+            HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_RESET);
+            WorkAddress = (i + Sector_Address * w25q.SectorSize);
+            if (w25q.ID >= W25Q256)
+            {
+                w25q_spi_xfer(0x0C);
+                w25q_spi_xfer((WorkAddress & 0xFF000000) >> 24);
+            }
+            else
+            {
+                w25q_spi_xfer(0x0B);
+            }
+            w25q_spi_xfer((WorkAddress & 0xFF0000) >> 16);
+            w25q_spi_xfer((WorkAddress & 0xFF00) >> 8);
+            w25q_spi_xfer(WorkAddress & 0xFF);
+            w25q_spi_xfer(0);
+            HAL_SPI_Receive(&_W25q_spi_xfer, pBuffer, 1, 100);
+            HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_SET);
+            if (pBuffer[0] != 0xFF)
+                goto NOT_EMPTY;
+        }
+    }
+#if (_w25q_DEBUG == 1)
+    printf("w25q CheckSector is Empty in %d ms\r\n", HAL_GetTick() - StartTime);
+    w25q_delay(100);
+#endif
+    w25q.Lock = 0;
+    return true;
+NOT_EMPTY:
+#if (_w25q_DEBUG == 1)
+    printf("w25q CheckSector is Not Empty in %d ms\r\n", HAL_GetTick() - StartTime);
+    w25q_delay(100);
+#endif
+    w25q.Lock = 0;
+    return false;
+}
+//###################################################################################################################
+bool w25q_IsEmptyBlock(uint32_t Block_Address, uint32_t OffsetInByte, uint32_t NumByteToCheck_up_to_BlockSize)
+{
+    while (w25q.Lock == 1)
+        w25q_delay(1);
+    w25q.Lock = 1;
+    if ((NumByteToCheck_up_to_BlockSize > w25q.BlockSize) || (NumByteToCheck_up_to_BlockSize == 0))
+        NumByteToCheck_up_to_BlockSize = w25q.BlockSize;
+#if (_w25q_DEBUG == 1)
+    printf("w25q CheckBlock:%d, Offset:%d, Bytes:%d begin...\r\n", Block_Address, OffsetInByte, NumByteToCheck_up_to_BlockSize);
+    w25q_delay(100);
+    uint32_t StartTime = HAL_GetTick();
+#endif
+    uint8_t pBuffer[32];
+    uint32_t WorkAddress;
+    uint32_t i;
+    for (i = OffsetInByte; i < w25q.BlockSize; i += sizeof(pBuffer))
+    {
+        HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_RESET);
+        WorkAddress = (i + Block_Address * w25q.BlockSize);
+
+        if (w25q.ID >= W25Q256)
+        {
+            w25q_spi_xfer(0x0C);
+            w25q_spi_xfer((WorkAddress & 0xFF000000) >> 24);
+        }
+        else
+        {
+            w25q_spi_xfer(0x0B);
+        }
+        w25q_spi_xfer((WorkAddress & 0xFF0000) >> 16);
+        w25q_spi_xfer((WorkAddress & 0xFF00) >> 8);
+        w25q_spi_xfer(WorkAddress & 0xFF);
+        w25q_spi_xfer(0);
+        HAL_SPI_Receive(&_W25q_spi_xfer, pBuffer, sizeof(pBuffer), 100);
+        HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_SET);
+        for (uint8_t x = 0; x < sizeof(pBuffer); x++)
+        {
+            if (pBuffer[x] != 0xFF)
+                goto NOT_EMPTY;
+        }
+    }
+    if ((w25q.BlockSize + OffsetInByte) % sizeof(pBuffer) != 0)
+    {
+        i -= sizeof(pBuffer);
+        for (; i < w25q.BlockSize; i++)
+        {
+            HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_RESET);
+            WorkAddress = (i + Block_Address * w25q.BlockSize);
+
+            if (w25q.ID >= W25Q256)
+            {
+                w25q_spi_xfer(0x0C);
+                w25q_spi_xfer((WorkAddress & 0xFF000000) >> 24);
+            }
+            else
+            {
+                w25q_spi_xfer(0x0B);
+            }
+            w25q_spi_xfer((WorkAddress & 0xFF0000) >> 16);
+            w25q_spi_xfer((WorkAddress & 0xFF00) >> 8);
+            w25q_spi_xfer(WorkAddress & 0xFF);
+            w25q_spi_xfer(0);
+            HAL_SPI_Receive(&_W25q_spi_xfer, pBuffer, 1, 100);
+            HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_SET);
+            if (pBuffer[0] != 0xFF)
+                goto NOT_EMPTY;
+        }
+    }
+#if (_w25q_DEBUG == 1)
+    printf("w25q CheckBlock is Empty in %d ms\r\n", HAL_GetTick() - StartTime);
+    w25q_delay(100);
+#endif
+    w25q.Lock = 0;
+    return true;
+NOT_EMPTY:
+#if (_w25q_DEBUG == 1)
+    printf("w25q CheckBlock is Not Empty in %d ms\r\n", HAL_GetTick() - StartTime);
+    w25q_delay(100);
+#endif
+    w25q.Lock = 0;
+    return false;
+}
+//###################################################################################################################
+void w25q_WriteByte(uint8_t pBuffer, uint32_t WriteAddr_inBytes)
+{
+    while (w25q.Lock == 1)
+        w25q_delay(1);
+    w25q.Lock = 1;
+#if (_w25q_DEBUG == 1)
+    uint32_t StartTime = HAL_GetTick();
+    printf("w25q WriteByte 0x%02X at address %d begin...", pBuffer, WriteAddr_inBytes);
+#endif
+    w25q_WaitForWriteEnd();
+    w25q_WriteEnable();
+    HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_RESET);
+
+    if (w25q.ID >= W25Q256)
+    {
+        w25q_spi_xfer(0x12);
+        w25q_spi_xfer((WriteAddr_inBytes & 0xFF000000) >> 24);
+    }
+    else
+    {
+        w25q_spi_xfer(0x02);
+    }
+    w25q_spi_xfer((WriteAddr_inBytes & 0xFF0000) >> 16);
+    w25q_spi_xfer((WriteAddr_inBytes & 0xFF00) >> 8);
+    w25q_spi_xfer(WriteAddr_inBytes & 0xFF);
+    w25q_spi_xfer(pBuffer);
+    HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_SET);
+    w25q_WaitForWriteEnd();
+#if (_w25q_DEBUG == 1)
+    printf("w25q WriteByte done after %d ms\r\n", HAL_GetTick() - StartTime);
+#endif
+    w25q.Lock = 0;
+}
+//###################################################################################################################
+void w25q_WritePage(uint8_t *pBuffer, uint32_t Page_Address, uint32_t OffsetInByte, uint32_t NumByteToWrite_up_to_PageSize)
+{
+    while (w25q.Lock == 1)
+        w25q_delay(1);
+    w25q.Lock = 1;
+    if (((NumByteToWrite_up_to_PageSize + OffsetInByte) > w25q.PageSize) || (NumByteToWrite_up_to_PageSize == 0))
+        NumByteToWrite_up_to_PageSize = w25q.PageSize - OffsetInByte;
+    if ((OffsetInByte + NumByteToWrite_up_to_PageSize) > w25q.PageSize)
+        NumByteToWrite_up_to_PageSize = w25q.PageSize - OffsetInByte;
+#if (_w25q_DEBUG == 1)
+    printf("w25q WritePage:%d, Offset:%d ,Writes %d Bytes, begin...\r\n", Page_Address, OffsetInByte, NumByteToWrite_up_to_PageSize);
+    w25q_delay(100);
+    uint32_t StartTime = HAL_GetTick();
+#endif
+    w25q_WaitForWriteEnd();
+    w25q_WriteEnable();
+    HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_RESET);
+    Page_Address = (Page_Address * w25q.PageSize) + OffsetInByte;
+    if (w25q.ID >= W25Q256)
+    {
+        w25q_spi_xfer(0x12);
+        w25q_spi_xfer((Page_Address & 0xFF000000) >> 24);
+    }
+    else
+    {
+        w25q_spi_xfer(0x02);
+    }
+    w25q_spi_xfer((Page_Address & 0xFF0000) >> 16);
+    w25q_spi_xfer((Page_Address & 0xFF00) >> 8);
+    w25q_spi_xfer(Page_Address & 0xFF);
+    HAL_SPI_Transmit(&_W25q_spi_xfer, pBuffer, NumByteToWrite_up_to_PageSize, 100);
+    HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_SET);
+    w25q_WaitForWriteEnd();
+#if (_w25q_DEBUG == 1)
+    StartTime = HAL_GetTick() - StartTime;
+    for (uint32_t i = 0; i < NumByteToWrite_up_to_PageSize; i++)
+    {
+        if ((i % 8 == 0) && (i > 2))
+        {
+            printf("\r\n");
+            w25q_delay(10);
+        }
+        printf("0x%02X,", pBuffer[i]);
+    }
+    printf("\r\n");
+    printf("w25q WritePage done after %d ms\r\n", StartTime);
+    w25q_delay(100);
+#endif
+    w25q_delay(1);
+    w25q.Lock = 0;
+}
+//###################################################################################################################
+void w25q_WriteSector(uint8_t *pBuffer, uint32_t Sector_Address, uint32_t OffsetInByte, uint32_t NumByteToWrite_up_to_SectorSize)
+{
+    if ((NumByteToWrite_up_to_SectorSize > w25q.SectorSize) || (NumByteToWrite_up_to_SectorSize == 0))
+        NumByteToWrite_up_to_SectorSize = w25q.SectorSize;
+#if (_w25q_DEBUG == 1)
+    printf("+++w25q WriteSector:%d, Offset:%d ,Write %d Bytes, begin...\r\n", Sector_Address, OffsetInByte, NumByteToWrite_up_to_SectorSize);
+    w25q_delay(100);
+#endif
+    if (OffsetInByte >= w25q.SectorSize)
+    {
+#if (_w25q_DEBUG == 1)
+        printf("---w25q WriteSector Faild!\r\n");
+        w25q_delay(100);
+#endif
+        return;
+    }
+    uint32_t StartPage;
+    int32_t BytesToWrite;
+    uint32_t LocalOffset;
+    if ((OffsetInByte + NumByteToWrite_up_to_SectorSize) > w25q.SectorSize)
+        BytesToWrite = w25q.SectorSize - OffsetInByte;
+    else
+        BytesToWrite = NumByteToWrite_up_to_SectorSize;
+    StartPage = w25q_SectorToPage(Sector_Address) + (OffsetInByte / w25q.PageSize);
+    LocalOffset = OffsetInByte % w25q.PageSize;
+    do
+    {
+        w25q_WritePage(pBuffer, StartPage, LocalOffset, BytesToWrite);
+        StartPage++;
+        BytesToWrite -= w25q.PageSize - LocalOffset;
+        pBuffer += w25q.PageSize - LocalOffset;
+        LocalOffset = 0;
+    } while (BytesToWrite > 0);
+#if (_w25q_DEBUG == 1)
+    printf("---w25q WriteSector Done\r\n");
+    w25q_delay(100);
+#endif
+}
+//###################################################################################################################
+void w25q_WriteBlock(uint8_t *pBuffer, uint32_t Block_Address, uint32_t OffsetInByte, uint32_t NumByteToWrite_up_to_BlockSize)
+{
+    if ((NumByteToWrite_up_to_BlockSize > w25q.BlockSize) || (NumByteToWrite_up_to_BlockSize == 0))
+        NumByteToWrite_up_to_BlockSize = w25q.BlockSize;
+#if (_w25q_DEBUG == 1)
+    printf("+++w25q WriteBlock:%d, Offset:%d ,Write %d Bytes, begin...\r\n", Block_Address, OffsetInByte, NumByteToWrite_up_to_BlockSize);
+    w25q_delay(100);
+#endif
+    if (OffsetInByte >= w25q.BlockSize)
+    {
+#if (_w25q_DEBUG == 1)
+        printf("---w25q WriteBlock Faild!\r\n");
+        w25q_delay(100);
+#endif
+        return;
+    }
+    uint32_t StartPage;
+    int32_t BytesToWrite;
+    uint32_t LocalOffset;
+    if ((OffsetInByte + NumByteToWrite_up_to_BlockSize) > w25q.BlockSize)
+        BytesToWrite = w25q.BlockSize - OffsetInByte;
+    else
+        BytesToWrite = NumByteToWrite_up_to_BlockSize;
+    StartPage = w25q_BlockToPage(Block_Address) + (OffsetInByte / w25q.PageSize);
+    LocalOffset = OffsetInByte % w25q.PageSize;
+    do
+    {
+        w25q_WritePage(pBuffer, StartPage, LocalOffset, BytesToWrite);
+        StartPage++;
+        BytesToWrite -= w25q.PageSize - LocalOffset;
+        pBuffer += w25q.PageSize - LocalOffset;
+        LocalOffset = 0;
+    } while (BytesToWrite > 0);
+#if (_w25q_DEBUG == 1)
+    printf("---w25q WriteBlock Done\r\n");
+    w25q_delay(100);
+#endif
+}
+//###################################################################################################################
+void w25q_ReadByte(uint8_t *pBuffer, uint32_t Bytes_Address)
+{
+    while (w25q.Lock == 1)
+        w25q_delay(1);
+    w25q.Lock = 1;
+#if (_w25q_DEBUG == 1)
+    uint32_t StartTime = HAL_GetTick();
+    printf("w25q ReadByte at address %d begin...\r\n", Bytes_Address);
+#endif
+    HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_RESET);
+
+    if (w25q.ID >= W25Q256)
+    {
+        w25q_spi_xfer(0x0C);
+        w25q_spi_xfer((Bytes_Address & 0xFF000000) >> 24);
+    }
+    else
+    {
+        w25q_spi_xfer(0x0B);
+    }
+    w25q_spi_xfer((Bytes_Address & 0xFF0000) >> 16);
+    w25q_spi_xfer((Bytes_Address & 0xFF00) >> 8);
+    w25q_spi_xfer(Bytes_Address & 0xFF);
+    w25q_spi_xfer(0);
+    *pBuffer = w25q_spi_xfer(DUMMY_BYTE);
+    HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_SET);
+#if (_w25q_DEBUG == 1)
+    printf("w25q ReadByte 0x%02X done after %d ms\r\n", *pBuffer, HAL_GetTick() - StartTime);
+#endif
+    w25q.Lock = 0;
+}
+//###################################################################################################################
+void w25q_ReadBytes(uint8_t *pBuffer, uint32_t ReadAddr, uint32_t NumByteToRead)
+{
+    while (w25q.Lock == 1)
+        w25q_delay(1);
+    w25q.Lock = 1;
+#if (_w25q_DEBUG == 1)
+    uint32_t StartTime = HAL_GetTick();
+    printf("w25q ReadBytes at Address:%d, %d Bytes  begin...\r\n", ReadAddr, NumByteToRead);
+#endif
+    HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_RESET);
+
+    if (w25q.ID >= W25Q256)
+    {
+        w25q_spi_xfer(0x0C);
+        w25q_spi_xfer((ReadAddr & 0xFF000000) >> 24);
+    }
+    else
+    {
+        w25q_spi_xfer(0x0B);
+    }
+    w25q_spi_xfer((ReadAddr & 0xFF0000) >> 16);
+    w25q_spi_xfer((ReadAddr & 0xFF00) >> 8);
+    w25q_spi_xfer(ReadAddr & 0xFF);
+    w25q_spi_xfer(0);
+    HAL_SPI_Receive(&_W25q_spi_xfer, pBuffer, NumByteToRead, 2000);
+    HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_SET);
+#if (_w25q_DEBUG == 1)
+    StartTime = HAL_GetTick() - StartTime;
+    for (uint32_t i = 0; i < NumByteToRead; i++)
+    {
+        if ((i % 8 == 0) && (i > 2))
+        {
+            printf("\r\n");
+            w25q_delay(10);
+        }
+        printf("0x%02X,", pBuffer[i]);
+    }
+    printf("\r\n");
+    printf("w25q ReadBytes done after %d ms\r\n", StartTime);
+    w25q_delay(100);
+#endif
+    w25q_delay(1);
+    w25q.Lock = 0;
+}
+//###################################################################################################################
+void w25q_ReadPage(uint8_t *pBuffer, uint32_t Page_Address, uint32_t OffsetInByte, uint32_t NumByteToRead_up_to_PageSize)
+{
+    while (w25q.Lock == 1)
+        w25q_delay(1);
+    w25q.Lock = 1;
+    if ((NumByteToRead_up_to_PageSize > w25q.PageSize) || (NumByteToRead_up_to_PageSize == 0))
+        NumByteToRead_up_to_PageSize = w25q.PageSize;
+    if ((OffsetInByte + NumByteToRead_up_to_PageSize) > w25q.PageSize)
+        NumByteToRead_up_to_PageSize = w25q.PageSize - OffsetInByte;
+#if (_w25q_DEBUG == 1)
+    printf("w25q ReadPage:%d, Offset:%d ,Read %d Bytes, begin...\r\n", Page_Address, OffsetInByte, NumByteToRead_up_to_PageSize);
+    w25q_delay(100);
+    uint32_t StartTime = HAL_GetTick();
+#endif
+    Page_Address = Page_Address * w25q.PageSize + OffsetInByte;
+    HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_RESET);
+    if (w25q.ID >= W25Q256)
+    {
+        w25q_spi_xfer(0x0C);
+        w25q_spi_xfer((Page_Address & 0xFF000000) >> 24);
+    }
+    else
+    {
+        w25q_spi_xfer(0x0B);
+    }
+    w25q_spi_xfer((Page_Address & 0xFF0000) >> 16);
+    w25q_spi_xfer((Page_Address & 0xFF00) >> 8);
+    w25q_spi_xfer(Page_Address & 0xFF);
+    w25q_spi_xfer(0);
+    HAL_SPI_Receive(&_W25q_spi_xfer, pBuffer, NumByteToRead_up_to_PageSize, 100);
+    HAL_GPIO_WritePin(W25Q_CS_GPIO, W25Q_CS_PIN, GPIO_PIN_SET);
+#if (_w25q_DEBUG == 1)
+    StartTime = HAL_GetTick() - StartTime;
+    for (uint32_t i = 0; i < NumByteToRead_up_to_PageSize; i++)
+    {
+        if ((i % 8 == 0) && (i > 2))
+        {
+            printf("\r\n");
+            w25q_delay(10);
+        }
+        printf("0x%02X,", pBuffer[i]);
+    }
+    printf("\r\n");
+    printf("w25q ReadPage done after %d ms\r\n", StartTime);
+    w25q_delay(100);
+#endif
+    w25q_delay(1);
+    w25q.Lock = 0;
+}
+//###################################################################################################################
+void w25q_ReadSector(uint8_t *pBuffer, uint32_t Sector_Address, uint32_t OffsetInByte, uint32_t NumByteToRead_up_to_SectorSize)
+{
+    if ((NumByteToRead_up_to_SectorSize > w25q.SectorSize) || (NumByteToRead_up_to_SectorSize == 0))
+        NumByteToRead_up_to_SectorSize = w25q.SectorSize;
+#if (_w25q_DEBUG == 1)
+    printf("+++w25q ReadSector:%d, Offset:%d ,Read %d Bytes, begin...\r\n", Sector_Address, OffsetInByte, NumByteToRead_up_to_SectorSize);
+    w25q_delay(100);
+#endif
+    if (OffsetInByte >= w25q.SectorSize)
+    {
+#if (_w25q_DEBUG == 1)
+        printf("---w25q ReadSector Faild!\r\n");
+        w25q_delay(100);
+#endif
+        return;
+    }
+    uint32_t StartPage;
+    int32_t BytesToRead;
+    uint32_t LocalOffset;
+    if ((OffsetInByte + NumByteToRead_up_to_SectorSize) > w25q.SectorSize)
+        BytesToRead = w25q.SectorSize - OffsetInByte;
+    else
+        BytesToRead = NumByteToRead_up_to_SectorSize;
+    StartPage = w25q_SectorToPage(Sector_Address) + (OffsetInByte / w25q.PageSize);
+    LocalOffset = OffsetInByte % w25q.PageSize;
+    do
+    {
+        w25q_ReadPage(pBuffer, StartPage, LocalOffset, BytesToRead);
+        StartPage++;
+        BytesToRead -= w25q.PageSize - LocalOffset;
+        pBuffer += w25q.PageSize - LocalOffset;
+        LocalOffset = 0;
+    } while (BytesToRead > 0);
+#if (_w25q_DEBUG == 1)
+    printf("---w25q ReadSector Done\r\n");
+    w25q_delay(100);
+#endif
+}
+//###################################################################################################################
+void w25q_ReadBlock(uint8_t *pBuffer, uint32_t Block_Address, uint32_t OffsetInByte, uint32_t NumByteToRead_up_to_BlockSize)
+{
+    if ((NumByteToRead_up_to_BlockSize > w25q.BlockSize) || (NumByteToRead_up_to_BlockSize == 0))
+        NumByteToRead_up_to_BlockSize = w25q.BlockSize;
+#if (_w25q_DEBUG == 1)
+    printf("+++w25q ReadBlock:%d, Offset:%d ,Read %d Bytes, begin...\r\n", Block_Address, OffsetInByte, NumByteToRead_up_to_BlockSize);
+    w25q_delay(100);
+#endif
+    if (OffsetInByte >= w25q.BlockSize)
+    {
+#if (_w25q_DEBUG == 1)
+        printf("w25q ReadBlock Faild!\r\n");
+        w25q_delay(100);
+#endif
+        return;
+    }
+    uint32_t StartPage;
+    int32_t BytesToRead;
+    uint32_t LocalOffset;
+    if ((OffsetInByte + NumByteToRead_up_to_BlockSize) > w25q.BlockSize)
+        BytesToRead = w25q.BlockSize - OffsetInByte;
+    else
+        BytesToRead = NumByteToRead_up_to_BlockSize;
+    StartPage = w25q_BlockToPage(Block_Address) + (OffsetInByte / w25q.PageSize);
+    LocalOffset = OffsetInByte % w25q.PageSize;
+    do
+    {
+        w25q_ReadPage(pBuffer, StartPage, LocalOffset, BytesToRead);
+        StartPage++;
+        BytesToRead -= w25q.PageSize - LocalOffset;
+        pBuffer += w25q.PageSize - LocalOffset;
+        LocalOffset = 0;
+    } while (BytesToRead > 0);
+#if (_w25q_DEBUG == 1)
+    printf("---w25q ReadBlock Done\r\n");
+    w25q_delay(100);
+#endif
+}
+
+#endif
diff --git a/Production/ISKBoard_QCTester/Core/Board/w25q32.h b/Production/ISKBoard_QCTester/Core/Board/w25q32.h
new file mode 100644
index 0000000..cb87ed8
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Board/w25q32.h
@@ -0,0 +1,60 @@
+#ifndef _w25Q32_H
+#define _W25Q32_H
+
+#include "stm32l4xx_hal.h"
+
+typedef enum
+{
+	W25Q80,
+	W25Q16,
+	W25Q32,
+} w25q_chip_t;
+
+typedef enum
+{
+	REG_SR1=1,
+	REG_SR2,
+	REG_SR3,
+} w25q_sreg_t;
+
+typedef struct flash_info_s
+{
+	char        *name;       /* Chip name */
+	uint32_t     id;         /* JEDEC ID  */
+	uint32_t     pagesize;   /* page size */
+	uint32_t     npages;     /* page count */
+	uint32_t     sectorsize; /* sector size */
+	uint32_t     nsectors;   /* sector count */
+	uint32_t     blocksize;  /* block size */
+	uint32_t     nblocks;    /* block count */
+} flash_info_t;
+
+extern flash_info_t   *w25q;
+
+int w25q_Init(void);
+void w25q_EraseChip(void);
+void w25q_EraseSector(uint32_t SectorAddr);
+void w25q_EraseBlock(uint32_t BlockAddr);
+
+uint32_t w25q_PageToSector(uint32_t PageAddress);
+uint32_t w25q_PageToBlock(uint32_t PageAddress);
+uint32_t w25q_SectorToBlock(uint32_t SectorAddress);
+uint32_t w25q_SectorToPage(uint32_t SectorAddress);
+uint32_t w25q_BlockToPage(uint32_t BlockAddress);
+
+int w25q_IsEmptyPage(uint32_t Page_Address, uint32_t OffsetInByte, uint32_t NumByteToCheck_up_to_PageSize);
+int w25q_IsEmptySector(uint32_t Sector_Address, uint32_t OffsetInByte, uint32_t NumByteToCheck_up_to_SectorSize);
+int w25q_IsEmptyBlock(uint32_t Block_Address, uint32_t OffsetInByte, uint32_t NumByteToCheck_up_to_BlockSize);
+
+void w25q_WriteByte(uint8_t pBuffer, uint32_t Bytes_Address);
+void w25q_WritePage(uint8_t *pBuffer, uint32_t Page_Address, uint32_t OffsetInByte, uint32_t NumByteToWrite_up_to_PageSize);
+void w25q_WriteSector(uint8_t *pBuffer, uint32_t Sector_Address, uint32_t OffsetInByte, uint32_t NumByteToWrite_up_to_SectorSize);
+void w25q_WriteBlock(uint8_t *pBuffer, uint32_t Block_Address, uint32_t OffsetInByte, uint32_t NumByteToWrite_up_to_BlockSize);
+
+void w25q_ReadByte(uint8_t *pBuffer, uint32_t Bytes_Address);
+void w25q_ReadBytes(uint8_t *pBuffer, uint32_t ReadAddr, uint32_t NumByteToRead);
+void w25q_ReadPage(uint8_t *pBuffer, uint32_t Page_Address, uint32_t OffsetInByte, uint32_t NumByteToRead_up_to_PageSize);
+void w25q_ReadSector(uint8_t *pBuffer, uint32_t Sector_Address, uint32_t OffsetInByte, uint32_t NumByteToRead_up_to_SectorSize);
+void w25q_ReadBlock(uint8_t *pBuffer, uint32_t Block_Address, uint32_t OffsetInByte, uint32_t NumByteToRead_up_to_BlockSize);
+
+#endif
diff --git a/Production/ISKBoard_QCTester/Core/Board/ws2812b.c b/Production/ISKBoard_QCTester/Core/Board/ws2812b.c
new file mode 100644
index 0000000..e43f639
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Board/ws2812b.c
@@ -0,0 +1,169 @@
+/* Reference: https://blog.csdn.net/Lennon8_8/article/details/108980808
+ * ws2812b.c
+ *
+ *  Created on: 2023年2月13日
+ *      Author: Think
+ */
+#include <string.h>
+#include <stdint.h>
+#include "ws2812b.h"
+#include "miscdev.h"
+
+static color_t g_ColorPanel[WS2812_NUM];
+
+typedef struct ws_gpio_s
+{
+    GPIO_TypeDef        *group;
+    uint16_t             pin;
+} ws_gpio_t;
+
+static ws_gpio_t   ws_gpio =  /* IO pin connected to PC2 */
+{
+        .group = GPIOC,
+        .pin   = GPIO_PIN_2,
+};
+
+#define ws_setpin(x) if(!x) ws_gpio.group->BRR=ws_gpio.pin; else ws_gpio.group->BSRR=ws_gpio.pin
+
+/*
+ * 1 NOP = 1/80MHz = 12.5ns => 100ns = 8 * NOP
+ *
+ * */
+#define delay_200ns() { __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP(); \
+					    __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP(); }
+
+void ws2812b_init(void)
+{
+	GPIO_InitTypeDef GPIO_InitStruct = {0};
+
+	GPIO_InitStruct.Pin = ws_gpio.pin;
+	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; 
+	GPIO_InitStruct.Pull = GPIO_NOPULL; 
+	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; 
+
+	HAL_GPIO_Init(ws_gpio.group, &GPIO_InitStruct); 
+
+	ws2812b_reset();
+
+	return ;
+}
+
+static inline void ws2812b_WriteByte(uint8_t byte)
+{
+	int      i;
+
+	for(i=0; i<8; i++)
+	{
+		if(byte & 0x80)
+		{
+			/* Sequence Send 1: T1H: 580ns~1.6us  T1L: 200ns~420ns:
+			 *
+			 * +--------------+         +
+			 * |<-   T1H    ->|<- T1L ->|
+			 * |              +---------+
+			 */
+			ws_setpin(1); // T1H
+			delay_us(1);
+
+			ws_setpin(0); // T1L
+			delay_200ns(); //200ns test okay
+		}
+		else
+		{
+			/* Sequence Send 0: T0H: 220ns~380ns  T0L: 580ns~1.6ns:
+			 *
+			 * +---------+              +
+			 * |<- T0H ->|<-    T0L   ->| 
+			 * |         +--------------+
+			 */
+			ws_setpin(1);  // T0H
+			delay_200ns(); //200ns test okay
+
+			ws_setpin(0); // T0L
+			delay_us(1);
+		}
+
+		byte<<=1;
+	}
+
+	return ;
+}
+
+void ws2812b_reset(void)
+{
+	/* Sequence Reset: Treset >280us:
+	 *
+	 * +              +
+	 * |<-  Treset  ->| 
+	 * +--------------+
+	 */
+	ws_setpin(0);
+	delay_us(300);
+
+	memset(g_ColorPanel, 0, sizeof(g_ColorPanel));
+
+	return ;
+}
+
+static inline void ws2812b_SendColor(color_t color)
+{
+	ws2812b_WriteByte(color.pixel.green);
+	ws2812b_WriteByte(color.pixel.red);
+	ws2812b_WriteByte(color.pixel.blue);
+
+	return ;
+}
+
+static inline void ws2812b_Refresh(void)
+{
+	int      i;
+
+	for(i=0; i<WS2812_NUM; i++)
+	{
+		ws2812b_SendColor(g_ColorPanel[i]);
+	}
+
+	return ;
+}
+
+static inline int ws2812b_SetPixel(int which, color_t color)
+{
+	if( which<0 || which>=WS2812_NUM )
+		return -1;
+
+	g_ColorPanel[which].pixel.red   = color.pixel.red;
+	g_ColorPanel[which].pixel.green = color.pixel.green;
+	g_ColorPanel[which].pixel.blue  = color.pixel.blue;
+
+	return 0;
+}
+
+int ws2812b_turn(int which, color_t color)
+{
+	if( which<0 || which>=WS2812_NUM )
+		return -1;
+
+	ws2812b_SetPixel(which, color);
+	ws2812b_Refresh();
+
+	return 0;
+}
+
+void ws2812b_blink(void)
+{
+	int      c, i;
+	color_t  colors[4] = {[0].data=PIXEL_B, [1].data=PIXEL_R, [2].data=PIXEL_G, [3].data=PIXEL_W};
+	color_t  color_off = {.data = PIXEL_OFF};
+
+	for(c=0; c<4; c++) /* color */
+	{
+		for(i=0; i<WS2812_NUM; i++)
+		{
+			ws2812b_turn(i, colors[c]);
+			HAL_Delay(200);
+			ws2812b_turn(i, (color_t)color_off);
+			HAL_Delay(200);
+		}
+	}
+}
+
diff --git a/Production/ISKBoard_QCTester/Core/Board/ws2812b.h b/Production/ISKBoard_QCTester/Core/Board/ws2812b.h
new file mode 100644
index 0000000..6366c5a
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Board/ws2812b.h
@@ -0,0 +1,48 @@
+/*
+ * ws2812b.h
+ *
+ *  Created on: Jan 6, 2023
+ *      Author: weihh
+ */
+
+#ifndef INC_WS2812B_H_
+#define INC_WS2812B_H_
+
+#include "stm32l4xx_hal.h"
+
+#define WS2812_NUM	3
+
+#define PIXEL_OFF     0          /* off   */
+#define PIXEL_B       (0xFF<<0)  /* blue  */
+#define PIXEL_R       (0xFF<<8)  /* red   */
+#define PIXEL_G       (0xFF<<16) /* green */
+#define PIXEL_W       (0xFFFFFF) /* white */
+
+/* Composition of 24 bits data: GRB888
+ * +----+----+----+----+----+----+----+----+----+
+ * | G7 | ...| G0 | R7 | ...| R0 | B7 | ...| B0 |
+ * +----+----+----+----+----+----+----+----+----+
+ */
+typedef struct pixel_s
+{
+	uint32_t    blue:8;
+	uint32_t    red:8;
+	uint32_t    green:8;
+} pixel_t;
+
+typedef union color_u
+{
+	pixel_t    pixel;
+	uint32_t   data;
+} color_t;
+
+
+extern void ws2812b_init(void);
+
+extern void ws2812b_reset(void);
+
+extern int ws2812b_turn(int which, color_t color);
+
+extern void ws2812b_blink(void);
+
+#endif /* INC_WS2812B_H_ */
diff --git a/Production/ISKBoard_QCTester/Core/ESP/at-esp32.c b/Production/ISKBoard_QCTester/Core/ESP/at-esp32.c
new file mode 100644
index 0000000..c81ec4a
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/ESP/at-esp32.c
@@ -0,0 +1,382 @@
+/*
+ * Copyright (c) 2022 Avnet
+ * Author: Guo Wenxue <wenxue.guo@avnet.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GPL license.
+ */
+
+#include "at-esp32.h"
+
+/*+------------------------+
+ *|    Baisc AT command    |
+ *+------------------------+*/
+
+static inline int check_at_ready(comport_t *comport)
+{
+    int             times = 6;
+    int             ready = 0;
+
+    while( times-- )
+    {
+        if( 0 == send_atcmd_check_ok(comport, "AT", 500) )
+        {
+            ready = 1;
+            break;
+        }
+    }
+
+    return ready;
+}
+
+/* AT command: AT+RST */
+int esp32_softreset(comport_t *comport)
+{
+    int             rv;
+
+    rv = send_atcmd(comport, "AT+RST", 5000, "ready", AT_ERRSTR, NULL, 0);
+    if( rv < 0)
+    {
+        log_error("send AT command to reset ESP32 failed, rv=%d\n", rv);
+        return -1;
+    }
+
+    if( check_at_ready(comport) )
+    {
+        log_info("send AT to reset ESP32 and AT command ready\n");
+        return 0;
+    }
+    else
+    {
+        log_info("send AT to reset ESP32 but AT command not ready\n");
+        return -3;
+    }
+}
+
+/* AT command: AT+RESTORE */
+int esp32_restore(comport_t *comport)
+{
+    int             rv;
+
+    //rv = send_atcmd_check_ok(comport, "AT+RESTORE", 5000);
+    rv = send_atcmd(comport, "AT+RESTORE", 5000, "ready", AT_ERRSTR, NULL, 0);
+    if( rv < 0)
+    {
+        log_error("send AT command to restore ESP32 failed, rv=%d\n", rv);
+        return -1;
+    }
+
+    if( check_at_ready(comport) )
+    {
+        log_info("send AT command to resstore ESP32 ready\n");
+        return 0;
+    }
+    else
+    {
+        log_error("send AT command to restore ESP32 but AT not ready\n");
+        return -3;
+    }
+}
+
+/* AT command: ATE1 or ATE0 */
+int esp32_set_echo(comport_t *comport, int enable)
+{
+    char           *at = enable? "ATE1" : "ATE0";
+
+    return send_atcmd_check_ok(comport, at, 500);
+}
+
+/* AT command: AT+GMR */
+int esp32_get_firmware(comport_t *comport, char *version, int size)
+{
+    if( !version || size<=0 )
+        return -1;
+
+    return send_atcmd_check_value(comport, "AT+GMR", 2000, version, size);
+}
+
+/* AT command: AT+SYSSTORE */
+int esp32_set_sysstore(comport_t *comport, int enable)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    snprintf(at, sizeof(at), "AT+SYSSTORE=%d", enable?1:0);
+
+    return send_atcmd_check_ok(comport, at, 1000);
+}
+
+/*+------------------------+
+ *|    WiFi AT command     |
+ *+------------------------+*/
+
+/* AT command: AT+CWMODE */
+int esp32_set_wmode(comport_t *comport, workmode_t mode, int autoconn)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    snprintf(at, sizeof(at), "AT+CWMODE=%d,%d", mode, autoconn?1:0);
+    return send_atcmd_check_ok(comport, at, 1500);
+}
+
+/* AT command: AT+CIPSTAMAC/CIPAPMAC */
+int esp32_get_macaddr(comport_t *comport, workmode_t mode, char *mac)
+{
+    if( !mac )
+        return -1;
+
+    if( MODE_SOFTAP == mode )
+        return send_atcmd_check_request(comport, "AT+CIPAPMAC?", 3000, mac, MAC_LEN);
+    else
+        return send_atcmd_check_request(comport, "AT+CIPSTAMAC?", 3000, mac, MAC_LEN);
+}
+
+/* AT command: AT+CIPSTA/AT+CIPAP  */
+int esp32_set_ipaddr(comport_t *comport, workmode_t mode, char *ip, char *gateway)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    if( !ip || !gateway )
+        return -1;
+
+    if( MODE_SOFTAP == mode )
+        snprintf(at, sizeof(at), "AT+CIPAP=\"%s\",\"%s\"", ip, gateway);
+    else
+        snprintf(at, sizeof(at), "AT+CIPSTA=\"%s\",\"%s\"", ip, gateway);
+
+    return send_atcmd_check_ok(comport, at, 2000);
+}
+
+/* AT command: AT+CIPSTA/AT+CIPAP  */
+int esp32_get_ipaddr(comport_t *comport, workmode_t mode, char *ip, char *gateway)
+{
+    char           *at = MODE_SOFTAP==mode? "AT+CIPAP?" : "AT+CIPSTA?";
+    char            buf[ATCMD_REPLY_LEN];
+    int             rv;
+
+    if( !ip || !gateway )
+        return -1;
+
+    rv = send_atcmd_check_value(comport, at, 3000, buf, sizeof(buf));
+    if( rv < 0)
+    {
+        log_error("AT command query IP address failed, rv=%d\n", rv);
+        return rv;
+    }
+
+    rv = parser_request_value(buf, "ip", ip, IP_LEN);
+    if( rv < 0 )
+    {
+        log_error("Parser query IP address failed, rv=%d\n", rv);
+        return rv;
+    }
+
+    rv = parser_request_value(buf, "gateway", gateway, IP_LEN);
+    if( rv < 0 )
+    {
+        log_error("Parser query gateway address failed, rv=%d\n", rv);
+        return rv;
+    }
+
+    return 0;
+}
+
+/* AT command: AT+CWDHCP */
+int esp32_set_dhcp(comport_t *comport, workmode_t mode, int enable)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    snprintf(at, sizeof(at), "AT+CWDHCP=%d,%d", enable?1:0, mode);
+    return send_atcmd_check_ok(comport, at, 1500);
+}
+
+/* AT command: AT+CWAUTOCONN */
+int esp32_set_autoconn(comport_t *comport, int enable)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    snprintf(at, sizeof(at), "AT+CWAUTOCONN=%d", enable?1:0);
+    return send_atcmd_check_ok(comport, at, 1500);
+}
+
+/* AT command: AT+CWLAP */
+int esp32_list_ap(comport_t *comport, char *buf, int size)
+{
+    if( !buf || size<=0 )
+        return -1;
+
+    return send_atcmd_check_value(comport, "AT+CWLAP", 8000, buf, size);
+}
+
+/* AT command: AT+CWJAP */
+int esp32_connect_ap(comport_t *comport, char *ssid, char *pwd)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    if( !ssid || !pwd )
+        return -1;
+
+    snprintf(at, sizeof(at), "AT+CWJAP=\"%s\",\"%s\"", ssid, pwd);
+    return send_atcmd_check_ok(comport, at, 15000);
+}
+
+/* AT command: AT+CWQAP */
+int esp32_disconn_ap(comport_t *comport)
+{
+    return send_atcmd_check_ok(comport, "AT+CWQAP", 5000);
+}
+
+
+/* AT command: AT+CWSTATE?  status value:
+ * - 0: ESP32 station has not started any Wi-Fi connection.
+ * - 1: ESP32 station has connected to an AP, but does not get an IPv4 address yet.
+ * - 2: ESP32 station has connected to an AP, and got an IPv4 address.
+ * - 3: ESP32 station is in Wi-Fi connecting or reconnecting state.
+ * - 4: ESP32 station is in Wi-Fi disconnected state
+ */
+int esp32_join_status(comport_t *comport, int *status, char *ssid)
+{
+    char            buf[ATCMD_REPLY_LEN];
+    int             rv;
+
+    if( !status || !ssid )
+        return -1;
+
+    rv = send_atcmd_check_request(comport, "AT+CWSTATE?", 3000, buf, sizeof(buf));
+    if( rv < 0)
+    {
+        log_error("AT command query join status failed, rv=%d\n", rv);
+        return rv;
+    }
+
+    sscanf(buf, "%d,%s", status, ssid);
+
+    return 0;
+}
+
+/* AT command:  AT+PING */
+int esp32_ping(comport_t *comport, char *host, int timeout)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    if( !host )
+        return -1;
+
+    snprintf(at, sizeof(at), "AT+PING=\"%s\"", host);
+    return send_atcmd_check_ok(comport, at, timeout);
+}
+
+/* AT command:  AT+CWSAP */
+int esp32_set_softap(comport_t *comport, char *ssid, char *pwd, int channel, encmode_t encmode)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    if( !ssid || !pwd )
+        return -1;
+
+    snprintf(at, sizeof(at), "AT+CWSAP=\"%s\",\"%s\",%d,%d", ssid, pwd, channel, encmode);
+    return send_atcmd_check_ok(comport, at, 5000);
+}
+
+/* AT command: AT+CWLIF */
+int esp32_list_client(comport_t *comport, char *buf, int size)
+{
+    if( !buf || size<=0 )
+        return -1;
+
+    return send_atcmd_check_value(comport, "AT+CWLIF", 8000, buf, size);
+}
+
+/*+------------------------+
+ *|   Socket AT command    |
+ *+------------------------+*/
+
+/* AT command: AT+CIPMUX */
+int esp32_set_socket_mux(comport_t *comport, int enable)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    snprintf(at, sizeof(at), "AT+CIPMUX=%d", enable?1:0);
+
+    return send_atcmd_check_ok(comport, at, 1500);
+}
+
+/* AT command: AT+CIPSERVERMAXCONN */
+int esp32_set_socket_clients(comport_t *comport, int max)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    if( max <= 0 )
+        return -1;
+
+    snprintf(at, sizeof(at), "AT+CIPSERVERMAXCONN=%d", max);
+
+    return send_atcmd_check_ok(comport, at, 1500);
+}
+
+/* AT command: AT+CIPSTO, timeout unit second */
+int esp32_set_socket_timeout(comport_t *comport, int timeout)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    if( timeout <= 0 )
+        return -1;
+
+    snprintf(at, sizeof(at), "AT+CIPSTO=%d", timeout);
+
+    return send_atcmd_check_ok(comport, at, 1500);
+}
+
+/* AT command: AT+CIPSERVER */
+int esp32_set_tcp_server(comport_t *comport, int port)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    if( port <= 0 )
+        return -1;
+
+    snprintf(at, sizeof(at), "AT+CIPSERVER=1,%d,\"TCP\"", port);
+
+    return send_atcmd_check_ok(comport, at, 1500);
+}
+
+/* AT command: AT+CIPSERVER */
+int esp32_del_tcp_server(comport_t *comport, int port)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    if( port <= 0 )
+        return -1;
+
+    snprintf(at, sizeof(at), "AT+CIPSERVER=0,%d,\"TCP\"", port);
+
+    return send_atcmd_check_ok(comport, at, 1500);
+}
+
+/* AT command: AT+CIPSTART */
+int esp32_set_tcp_client(comport_t *comport, int mux, char *host, int port)
+{
+    char            at[ATCMD_LEN]={'\0'};
+    char            buf[ATCMD_REPLY_LEN]={'\0'};
+    int             keepalive = 60; /* unit second */
+    int             rv,linkid = 0;
+
+    if( !host || port <= 0 )
+        return -1;
+
+    rv = esp32_set_socket_mux(comport, mux);
+    if(rv < 0)
+        return rv;
+
+    if( mux )
+        snprintf(at, sizeof(at), "AT+CIPSTART=1,\"TCP\",\"%s\",%d,%d", host, port, keepalive);
+    else
+        snprintf(at, sizeof(at), "AT+CIPSTART=\"TCP\",\"%s\",%d,%d", host, port, keepalive);
+
+    rv = send_atcmd_check_value(comport, at, 1500, buf, sizeof(buf));
+    if(rv < 0)
+        return rv;
+
+    sscanf(buf, "%d,", &linkid);
+
+    return linkid;
+}
diff --git a/Production/ISKBoard_QCTester/Core/ESP/at-esp32.h b/Production/ISKBoard_QCTester/Core/ESP/at-esp32.h
new file mode 100644
index 0000000..3074a33
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/ESP/at-esp32.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2022 Avnet
+ * Author: Guo Wenxue <wenxue.guo@avnet.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GPL license.
+ */
+
+#ifndef  _AT_ESP32_H_
+#define  _AT_ESP32_H_
+
+#include "atcmd.h"
+
+#if 0
+enum
+{
+    DISABLE = 0,    /* common disable */
+    ENABLE,         /* common enable  */
+};
+#endif
+
+#define MAC_LEN     18 /* 11:22:33:44:55:66 */
+#define IP_LEN      16 /* 255.255.255.255 */
+
+/*+------------------------+
+ *|    Baisc AT command    |
+ *+------------------------+*/
+
+/* AT command: AT+RST */
+extern int esp32_softreset(comport_t *comport);
+
+/* AT command: AT+RESTORE */
+extern int esp32_restore(comport_t *comport);
+
+/* AT command: ATE1 or ATE0 */
+extern int esp32_set_echo(comport_t *comport, int enable);
+
+/* AT command: AT+GMR */
+extern int esp32_get_firmware(comport_t *comport, char *version, int size);
+
+/* AT command: AT+SYSSTORE */
+extern int esp32_set_sysstore(comport_t *comport, int enable);
+
+
+/*+------------------------+
+ *|    WiFi AT command     |
+ *+------------------------+*/
+typedef enum
+{
+    MODE_DISABLE=0, /* Wi-Fi RF will be disabled */
+    MODE_STATION,   /* Station mode */
+    MODE_SOFTAP,    /* SoftAP mode */
+    MODE_WDS,       /* Wireless Distribution System: Both Station & SoftAP mode */
+} workmode_t;
+
+/* AT command: AT+CWMODE */
+extern int esp32_set_wmode(comport_t *comport, workmode_t mode, int autoconn);
+
+/* AT command: AT+CWDHCP */
+extern int esp32_set_dhcp(comport_t *comport, workmode_t mode, int enable);
+
+/* AT command: AT+CWAUTOCONN */
+extern int esp32_set_autoconn(comport_t *comport, int enable);
+
+/* AT command: AT+CIPSTAMAC/CIPAPMAC */
+extern int esp32_get_macaddr(comport_t *comport, workmode_t mode, char *mac);
+
+/* AT command: AT+CIPSTA/CIPAP */
+extern int esp32_get_ipaddr(comport_t *comport, workmode_t mode, char *ip, char *gateway);
+
+/* AT command: AT+CIPSTA/AT+CIPAP  */
+extern int esp32_set_ipaddr(comport_t *comport, workmode_t mode, char *ip, char *gateway);
+
+/* AT command: AT+CWLAP */
+extern int esp32_list_ap(comport_t *comport, char *buf, int size);
+
+/* AT command: AT+CWJAP */
+extern int esp32_connect_ap(comport_t *comport, char *ssid, char *pwd);
+
+/* AT command: AT+CWQAP */
+extern int esp32_disconn_ap(comport_t *comport);
+
+/* AT command: AT+CWSTATE? , status value:
+ * - 0: ESP32 station has not started any Wi-Fi connection.
+ * - 1: ESP32 station has connected to an AP, but does not get an IPv4 address yet.
+ * - 2: ESP32 station has connected to an AP, and got an IPv4 address.
+ * - 3: ESP32 station is in Wi-Fi connecting or reconnecting state.
+ * - 4: ESP32 station is in Wi-Fi disconnected state
+ */
+extern int esp32_join_status(comport_t *comport, int *status, char *ssid);
+
+/* AT command:  AT+PING */
+extern int esp32_ping(comport_t *comport, char *host, int timeout);
+
+/* AT command:  AT+CWSAP */
+typedef enum
+{
+    MODE_OPEN,
+    /* WEP not support */
+    MODE_WPAPSK = 2,
+    MODE_WPA2PSK,
+    MODE_WPA_WPA2PSK,
+} encmode_t;
+extern int esp32_set_softap(comport_t *comport, char *ssid, char *pwd, int channel, encmode_t encmode);
+
+/* AT command:  AT+CWLIF */
+extern int esp32_list_client(comport_t *comport, char *buf, int size);
+
+
+/*+------------------------+
+ *|   Socket AT command    |
+ *+------------------------+*/
+
+/* AT command: CIPMUX */
+extern int esp32_set_socket_mux(comport_t *comport, int enable);
+
+/* AT command: AT+CIPSERVERMAXCONN */
+extern int esp32_set_socket_clients(comport_t *comport, int max);
+
+/* AT command: AT+CIPSTO, timeout unit second */
+extern int esp32_set_socket_timeout(comport_t *comport, int timeout);
+
+/* AT command: AT+CIPSERVER */
+extern int esp32_set_tcp_server(comport_t *comport, int port);
+
+/* AT command: AT+CIPSERVER */
+extern int esp32_del_tcp_server(comport_t *comport, int port);
+
+/* AT command: AT+CIPSTART */
+extern int esp32_set_tcp_client(comport_t *comport, int mux, char *host, int port);
+
+/*+------------------------+
+ *|     BLE AT command     |
+ *+------------------------+*/
+// RFU
+
+#endif   /* ----- #ifndef _AT_ESP32_H_  ----- */
diff --git a/Production/ISKBoard_QCTester/Core/ESP/atcmd.c b/Production/ISKBoard_QCTester/Core/ESP/atcmd.c
new file mode 100644
index 0000000..a67f16d
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/ESP/atcmd.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2022 Avnet
+ * Author: Guo Wenxue <wenxue.guo@avnet.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GPL license.
+ */
+
+#include <ctype.h>
+#include <string.h>
+
+#include "atcmd.h"
+
+/*  Description: this function used to send an AT command from serial port and wait for reply message from
+ *               the communication module, and it will return once get expet/error string or timeout.
+ *
+ *    Arugments:
+ *         $comport: the serial port which connected to GPRS/3G/4G/NB-IoT/WiFi/BLE/Zigbee/LoRa...
+ *              $at: the AT command need to be sent, without "\r\n"
+ *         $timeout: wait for module reply for AT command timeout value, unit micro seconds(ms)
+ *          $exepct: the expect string reply from communication module
+ *           $error: the error string reply from communication module
+ *           $reply: the module reply message output buffer
+ *            $size: the output buffer ($reply) size
+ *
+ * Return value: <0: Function error   0: Got expect string  1: Got error string   2: Receive until timeout
+ *
+ */
+
+int send_atcmd(comport_t *comport, char *at, unsigned long timeout, char *expect, char *error, char *reply, int size)
+{
+    int                    rv = 0;
+    int                    res = ATRES_TIMEOUT;
+    int                    bytes = 0;
+    char                   buf[ATCMD_REPLY_LEN] = {'\0'};
+    char                   atcmd[ATCMD_LEN] = {'\0'};
+
+    if( !comport || !at )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+    if( comport->fd <= 0 )
+    {
+        log_error("comport[%s] not opened\n", comport->devname);
+        return -2;
+    }
+
+    /* flushes receive stream buffer */
+    //xStreamBufferReset(comport->xRxStreamBuffer);
+
+    snprintf(atcmd, sizeof(atcmd), "%s%s", at, AT_SUFFIX);
+    rv=comport_send( comport, atcmd, strlen(atcmd) );
+    if(rv < 0)
+    {
+        log_error("send command \"%s\" to \"%s\" failed, rv=%d\n", at, comport->devname, rv);
+        return -3;
+    }
+
+    memset( buf, 0, sizeof(buf) );
+    rv=comport_recv( comport, buf, sizeof(buf), timeout);
+    if(rv < 0)
+    {
+        log_error("Wait for command \'%s\' reply failed, rv=%d\n", at, rv);
+        return -4;
+    }
+    else if(rv == 0)
+    {
+        log_error("Wait for command \'%s\' reply timeout\n", at);
+        return ATRES_TIMEOUT;
+    }
+
+    if( error && strstr(buf, error) )
+    {
+        log_debug("send command \"%s\" and got reply \"ERROR\"\n", at);
+        return ATRES_ERROR;
+    }
+
+    if( expect && strstr(buf, expect) )
+    {
+        log_debug("send command \"%s\" and got reply \"OK\"\n", at);
+        res = ATRES_EXPECT;
+    }
+
+    if( reply && size>0 )
+    {
+        bytes = strlen(buf)>size ? size : strlen(buf);
+        memset(reply, 0, size);
+        strncpy(reply, buf, bytes);
+
+        log_debug("copy out command \"%s\" reply message: \n%s", at, reply);
+    }
+
+    return res;
+}
+
+
+/*
+ *  Description: Send AT command which will only reply by "OK" or "ERROR", such as AT:
+ *                 Reply:   \r\nOK\r\n
+ * Return Value: 0: OK     -X: Failure
+ */
+int send_atcmd_check_ok(comport_t *comport, char *at, unsigned long timeout)
+{
+    int                     rv;
+
+    if( !comport || !at )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+    rv=send_atcmd(comport, at, timeout, AT_OKSTR, AT_ERRSTR, NULL, 0);
+    if( ATRES_EXPECT == rv )
+    {
+        return 0;
+    }
+    else
+    {
+        return -2;
+    }
+}
+
+
+/*
+ *  Description: Send AT command which will reply by a value directly in a single line, such as AT+CGMM:
+ *                  Reply:   \r\nEC20F\r\nOK\r\n
+ *
+ * Return Value: 0: OK     -X: Failure
+ */
+int send_atcmd_check_value(comport_t *comport, char *at, unsigned long timeout, char *reply, int size)
+{
+    int                     rv, len;
+    char                    buf[ATCMD_REPLY_LEN];
+    char                   *ptr_start = buf;
+    char                   *ptr_end;
+
+    if( !comport || !at || !reply || size<=0 )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+    rv = send_atcmd(comport, at, timeout, AT_OKSTR, AT_ERRSTR, buf, ATCMD_REPLY_LEN);
+    if( ATRES_EXPECT != rv )
+    {
+    	log_error("send AT command \"%s\" failed, rv=%d\n", at, rv);
+        return -2;
+    }
+
+    /* Skip the echo back command line */
+    if( !strncmp(buf, at, strlen(at)) )
+    {
+        ptr_start=strchr(buf, '\n');
+        if( !ptr_start )
+        {
+            log_error("reply message got wrong\n");
+            return -3;
+        }
+
+        ptr_start++;   /* skip '\n'  */
+    }
+
+    /* find end reply string "\r\nOK\r\n"  */
+    ptr_end = strstr(ptr_start, AT_OKSTR);
+    if( ptr_end )
+    {
+        len = ptr_end - ptr_start;
+    }
+    else
+    {
+        len = strlen(buf) - (ptr_start-buf);
+    }
+
+    memset(reply, 0, size);
+
+    len = len>size ? size : len;
+    memcpy(reply, ptr_start, len);
+
+    return 0;
+}
+
+/*
+ *  Description: Parser the $value from $key like "xxx: " line, such as AT+CSQ:
+ *                  Reply:   \r\n+CSQ: 26,99\r\nOK\r\n
+ *
+ * Return Value: 0: OK     -X: Failure
+ */
+int parser_request_value(char *buf, char *key, char *value, int size)
+{
+    char                   *ptr;
+    int                    i = 0;
+
+    if( !buf || !key || !value || size<=0 )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+    ptr = strstr(buf, key);
+    if( !ptr )
+    {
+        log_debug("Not found key \"%s\" in %s\n", key, buf);
+        return -2;
+    }
+
+    ptr=strchr(ptr, ':');  /* found ':' before the value */
+    if( !ptr )
+    {
+        log_debug("Not found ':' before value\n");
+        return -3;
+    }
+    ptr++;   /* skip ':'  */
+
+    if( *ptr == '\"' ) /* skip " */
+        ptr++;
+
+    memset(value, 0, size);
+    while(*ptr!='\r' && i<size-1)
+    {
+        if( !isspace(*(uint8_t *)ptr) && *ptr!='\"') /* skip space,\r,\n ...  */
+            value[i++] = *ptr;
+        ptr ++;
+    }
+
+    ptr++; /* skip  */
+
+    return 0;
+}
+
+/*
+ *  Description: Send AT command which will reply by the value  with a prefix "+CMD: " line, such as AT+CSQ:
+ *                  Reply:   \r\n+CSQ: 26,99\r\nOK\r\n
+ *
+ * Return Value: 0: OK     -X: Failure
+ */
+int send_atcmd_check_request(comport_t *comport, char *at, unsigned long timeout, char *reply, int size)
+{
+    int                     i = 0;
+    int                     rv;
+    char                    buf[ATCMD_REPLY_LEN];
+    char                   *ptr;
+
+    if( !comport || !at || !reply || size<=0 )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+    rv = send_atcmd(comport, at, timeout, AT_OKSTR, AT_ERRSTR, buf, ATCMD_REPLY_LEN);
+    if( ATRES_EXPECT != rv )
+    {
+        return -2;
+    }
+
+    ptr=strchr(buf, '+');  /* found '+' before the value */
+    if( !ptr )
+    {
+        log_error("reply message got wrong\n");
+        return -3;
+    }
+    ptr++;   /* skip '+'  */
+
+
+    ptr=strchr(buf, ':');  /* found ':' before the value */
+    if( !ptr )
+    {
+        log_error("reply message got wrong\n");
+        return -3;
+    }
+    ptr++;   /* skip ':'  */
+
+    if( *ptr == '\"' ) /* skip " */
+        ptr++;
+
+    memset(reply, 0, size);
+    while(*ptr!='\r' && i<size-1)
+    {
+        if( !isspace(*(uint8_t *)ptr) && *ptr!='\"') /* skip space,\r,\n ...  */
+            reply[i++] = *ptr;
+        ptr ++;
+    }
+
+    return 0;
+}
+
diff --git a/Production/ISKBoard_QCTester/Core/ESP/atcmd.h b/Production/ISKBoard_QCTester/Core/ESP/atcmd.h
new file mode 100644
index 0000000..23b3cee
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/ESP/atcmd.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2022 Avnet
+ * Author: Guo Wenxue <wenxue.guo@avnet.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GPL license.
+ */
+
+#ifndef  _ATCMD_H_
+#define  _ATCMD_H_
+
+#include "comport.h"
+#include "logger.h"
+
+/* AT command common reply message max length  */
+#define ATCMD_REPLY_LEN          512 /* Max AT command reply message length */
+#define ATCMD_LEN                128 /* Max AT command length */
+
+/* AT command reply message got expect or error string */
+#define AT_OKSTR                 "\r\nOK\r\n"     /* expect string always be OK */
+#define AT_ERRSTR                "\r\nERROR\r\n"  /* error string always be ERROR */
+
+/* AT command should be end by $AT_SUFFIX */
+#define AT_SUFFIX                "\r\n"
+
+/* send_atcmd() return value status */
+enum
+{
+    ATRES_EXPECT,   /* AT command reply got expect string, such as "OK\r\n" */
+    ATRES_ERROR,    /* AT command reply got error string, such as "ERROR\r\n" */
+    ATRES_TIMEOUT,  /* AT command not get error/expect string, receive until timeout */
+};
+
+/*  Description: this function used to send an AT command from serial port and wait for reply message from
+ *               the communication module, and it will return once get expect/error string or timeout.
+ *
+ *    Arugments:
+ *         $comport: the serial port which connected to GPRS/3G/4G/NB-IoT/WiFi/BLE/Zigbee/LoRa...
+ *              $at: the AT command need to be sent, without "\r\n"
+ *         $timeout: wait for module reply for AT command timeout value, unit micro seconds(ms)
+ *          $exepct: the expect string reply from communication module
+ *           $error: the error string reply from communication module
+ *           $reply: the module reply message output buffer
+ *            $size: the output buffer ($reply) size
+ *
+ * Return value: <0: Function error   0: Got expect string  1: Got error string   2: Receive until timeout
+ *
+ */
+int send_atcmd(comport_t *comport, char *at, unsigned long timeout, char *expect, char *error, char *reply, int size);
+
+
+/*
+ *  Description: Send AT command which will only reply by "OK" or "ERROR", such as AT:
+ *                  Reply:   \r\nOK\r\n
+ *
+ * Return Value:  0: OK     -X: ERROR
+ */
+int send_atcmd_check_ok(comport_t *comport, char *at, unsigned long timeout);
+
+
+/*
+ *  Description: Send AT command which will reply by a value directly in a single line, such as AT+CGMM:
+ *                  Reply:   \r\nEC20F\r\nOK\r\n
+ *
+ * Return Value:  0: OK     -X: ERROR
+ */
+int send_atcmd_check_value(comport_t *comport, char *at, unsigned long timeout, char *reply, int size);
+
+/*
+ *  Description: Parser the $value from $key like "xxx: " line, such as AT+CSQ:
+ *                  Reply:   \r\n+CSQ: 26,99\r\nOK\r\n
+ *
+ * Return Value: 0: OK     -X: Failure
+ */
+int parser_request_value(char *buf, char *key, char *value, int size);
+
+
+/*
+ *  Description: Send AT command which will reply by the value  with a prefix "+CMD: " line, such as AT+CSQ:
+ *                  Reply:   \r\n+CSQ: 26,99\r\nOK\r\n
+ *
+ * Return Value:  0: OK     -X: ERROR
+ */
+int send_atcmd_check_request(comport_t *comport, char *at, unsigned long timeout, char *repy, int size);
+
+
+#endif   /* ----- #ifndef _ATCMD_H_  ----- */
+
diff --git a/Production/ISKBoard_QCTester/Core/ESP/esp32.c b/Production/ISKBoard_QCTester/Core/ESP/esp32.c
new file mode 100644
index 0000000..a2e9df8
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/ESP/esp32.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2022 Avnet
+ * Author: Guo Wenxue <wenxue.guo@avnet.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GPL license.
+ */
+
+#include <string.h>
+#include "esp32.h"
+
+int esp32_hwreset(comport_t *comport)
+{
+	uint32_t        tickstart = 0;
+	int             bytes;
+
+	/* Clear the receive buffer */
+	rb_clear(&comport->rb);
+
+	/* Reset WiFi module by hardware GPIO */
+	HAL_GPIO_WritePin(WiFiRst_GPIO_Port, WiFiRst_Pin, GPIO_PIN_RESET);
+	HAL_Delay(500);
+	HAL_GPIO_WritePin(WiFiRst_GPIO_Port, WiFiRst_Pin, GPIO_PIN_SET);
+
+	/* Check module bootup information */
+	tickstart = HAL_GetTick();
+	while((HAL_GetTick() - tickstart) < 5000)
+	{
+		bytes = rb_data_size(&comport->rb);
+
+		HAL_Delay( 100 );
+
+		/* comport received data before but not receive in 100ms now, maybe data receive over */
+		if( bytes>0 && rb_data_size(&comport->rb)==bytes )
+		{
+			break;
+		}
+	}
+
+	/* WiFi module maybe not present */
+	if( rb_data_size(&comport->rb) <= 0 )
+	{
+		printf("ERROR: WiFi module maybe not present\r\n");
+		return -1;
+	}
+
+	/* Wait for 'ready' and cleanup */
+	HAL_Delay(500);
+	rb_clear(&comport->rb);
+	return 0;
+}
+
+int esp32_init_module(comport_t *comport)
+{
+    int             rv;
+    char            version[256] = {0};
+
+
+    if( !comport )
+        return -1;
+
+    esp32_hwreset(comport);
+
+    esp32_set_echo(comport, DISABLE);
+
+    //esp32_set_sysstore(comport, ENABLE);
+
+    rv = esp32_get_firmware(comport, version, sizeof(version));
+    if( rv < 0)
+    {
+        log_error("Query ESP32 firmware version failed: %d\n", rv);
+        return rv;
+    }
+
+    log_info("ESP32 firmware version:\n%s\n", version);
+
+    return 0;
+}
+
+int esp32_setup_softap(comport_t *comport, char *ssid, char *pwd)
+{
+    esp32_set_wmode(comport, MODE_SOFTAP, ENABLE);
+
+    esp32_set_ipaddr(comport, MODE_SOFTAP, DEF_SOFTAP_IPADDR, DEF_SOFTAP_IPADDR);
+
+    esp32_set_dhcp(comport, MODE_SOFTAP, ENABLE);
+
+    esp32_set_softap(comport, ssid, pwd, 11, MODE_WPA2PSK);
+
+    return 0;
+}
+
+int esp32_join_network(comport_t *comport, char *ssid, char *pwd)
+{
+    int             rv, status = 0;
+    int             times=10;
+    char            buf[128] = {0};
+
+    if( !comport )
+        return -1;
+
+    esp32_join_status(comport, &status, buf);
+    if( 2==status && !strcmp(buf, ssid) )
+    {
+        log_info("ESP32 connected to \"%s\" already\n", ssid);
+        return 0;
+    }
+
+    esp32_set_wmode(comport, MODE_STATION, ENABLE);
+
+    esp32_set_dhcp(comport, MODE_STATION, ENABLE);
+
+    rv = esp32_connect_ap(comport, ssid, pwd);
+    if( rv < 0 )
+    {
+        log_error("connect to AP \"%s\" failed, rv=%d", ssid, rv);
+        return rv;
+    }
+
+    while(times--)
+    {
+        rv = esp32_join_status(comport, &status, buf);
+        if( 2 == status )
+        {
+            log_info("ESP32 connected to \"%s\" already\n", ssid);
+            return 0;
+        }
+        rv = -3;
+    }
+
+    return rv;
+}
+
+
+int esp32_check_network(comport_t *comport)
+{
+    int             rv, times=5;
+    char            ip[IP_LEN] = {0};
+    char            gateway[IP_LEN] = {0};
+
+    memset(ip, 0, sizeof(ip));
+    memset(gateway, 0, sizeof(gateway));
+    rv = esp32_get_ipaddr(comport, MODE_STATION, ip, gateway);
+    if( rv<0 )
+        return rv;
+
+    if( !strlen(ip) || !strlen(gateway) )
+        return -3;
+
+    log_info("IP address: %s, netmask: %s\n", ip, gateway);
+
+    while( times-- )
+    {
+        if( !(rv=esp32_ping(comport, gateway, 5000)) )
+        {
+            rv = 0;
+            break;
+        }
+    }
+
+    return 0;
+}
+
+int esp32_setup_tcp_server(comport_t *comport, int port)
+{
+    int             rv;
+
+    rv = esp32_set_socket_mux(comport, ENABLE);
+    if(rv<0)
+        return rv;
+
+    rv = esp32_set_socket_clients(comport, 2);
+    if(rv<0)
+        return rv;
+
+    rv = esp32_set_tcp_server(comport, port);
+    if(rv<0)
+        return rv;
+
+    rv = esp32_set_socket_timeout(comport, 600);
+    if(rv<0)
+        return rv;
+
+    return 0;
+}
+
+int esp32_setup_tcp_client(comport_t *comport, char *host, int port)
+{
+    int             rv;
+
+    rv = esp32_set_tcp_client(comport, DISABLE, host, port);
+    if(rv<0)
+        return rv;
+
+
+    return 0;
+}
+
diff --git a/Production/ISKBoard_QCTester/Core/ESP/esp32.h b/Production/ISKBoard_QCTester/Core/ESP/esp32.h
new file mode 100644
index 0000000..cf7523d
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/ESP/esp32.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2022 Avnet
+ * Author: Guo Wenxue <wenxue.guo@avnet.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GPL license.
+ */
+
+#ifndef  _ESP32_H_
+#define  _ESP32_H_
+
+#include "at-esp32.h"
+
+#define DEF_SOFTAP_IPADDR    "192.168.8.1"
+#define DEF_SOFTAP_SSID      "Router_ESP32"
+#define DEF_SOFTAP_PWD       "12345678"
+
+extern int esp32_hwreset(comport_t *comport);
+
+extern int esp32_init_module(comport_t *comport);
+
+extern int esp32_setup_softap(comport_t *comport, char *ssid, char *pwd);
+
+extern int esp32_join_network(comport_t *comport, char *ssid, char *pwd);
+
+extern int esp32_check_network(comport_t *comport);
+
+extern int esp32_setup_tcp_server(comport_t *comport, int port);
+
+extern int esp32_setup_tcp_client(comport_t *comport, char *host, int port);
+
+#endif   /* ----- #ifndef _ESP32_H_  ----- */
diff --git a/Production/ISKBoard_QCTester/Core/ESP/logger.h b/Production/ISKBoard_QCTester/Core/ESP/logger.h
new file mode 100644
index 0000000..fc88705
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/ESP/logger.h
@@ -0,0 +1,31 @@
+/*
+ * logger.h
+ *
+ *  Created on: 2023年2月20日
+ *      Author: Think
+ */
+
+#ifndef SRC_ESP_LOGGER_H_
+#define SRC_ESP_LOGGER_H_
+
+//#define CONFIG_ESP_DEBUG
+
+#ifdef  CONFIG_ESP_DEBUG
+
+#define log_error(format,args...) printf(format, ##args)
+#define log_info(format,args...)  printf(format, ##args)
+#define log_debug(format,args...)  printf(format, ##args)
+#define log_trace(format,args...)  printf(format, ##args)
+
+
+#else
+
+#define log_error(format,args...) do{} while(0)
+#define log_info(format,args...) do{} while(0)
+#define log_debug(format,args...) do{} while(0)
+#define log_trace(format,args...) do{} while(0)
+
+#endif
+
+
+#endif /* SRC_ESP_LOGGER_H_ */
diff --git a/Production/ISKBoard_QCTester/Core/Inc/adc.h b/Production/ISKBoard_QCTester/Core/Inc/adc.h
new file mode 100644
index 0000000..830535e
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Inc/adc.h
@@ -0,0 +1,52 @@
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file    adc.h
+  * @brief   This file contains all the function prototypes for
+  *          the adc.c file
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2023 STMicroelectronics.
+  * All rights reserved.
+  *
+  * This software is licensed under terms that can be found in the LICENSE file
+  * in the root directory of this software component.
+  * If no LICENSE file comes with this software, it is provided AS-IS.
+  *
+  ******************************************************************************
+  */
+/* USER CODE END Header */
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __ADC_H__
+#define __ADC_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "main.h"
+
+/* USER CODE BEGIN Includes */
+
+/* USER CODE END Includes */
+
+extern ADC_HandleTypeDef hadc1;
+
+/* USER CODE BEGIN Private defines */
+
+/* USER CODE END Private defines */
+
+void MX_ADC1_Init(void);
+
+/* USER CODE BEGIN Prototypes */
+
+/* USER CODE END Prototypes */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ADC_H__ */
+
diff --git a/Production/ISKBoard_QCTester/Core/Inc/can.h b/Production/ISKBoard_QCTester/Core/Inc/can.h
new file mode 100644
index 0000000..0d4b12c
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Inc/can.h
@@ -0,0 +1,62 @@
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file    can.h
+  * @brief   This file contains all the function prototypes for
+  *          the can.c file
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2023 STMicroelectronics.
+  * All rights reserved.
+  *
+  * This software is licensed under terms that can be found in the LICENSE file
+  * in the root directory of this software component.
+  * If no LICENSE file comes with this software, it is provided AS-IS.
+  *
+  ******************************************************************************
+  */
+/* USER CODE END Header */
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __CAN_H__
+#define __CAN_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "main.h"
+
+/* USER CODE BEGIN Includes */
+#include "usart.h"
+#include "ringbuf.h"
+/* USER CODE END Includes */
+
+extern CAN_HandleTypeDef hcan1;
+
+/* USER CODE BEGIN Private defines */
+typedef struct can_s
+{
+	int							fd;/* compatible with Linux API, not used */
+	ring_buffer_t               xRxBuffer; /* receive data ring buffer */
+} can_t;
+
+extern can_t can;
+/* USER CODE END Private defines */
+
+void MX_CAN1_Init(void);
+
+/* USER CODE BEGIN Prototypes */
+extern can_t *can_init(CAN_HandleTypeDef *hcan);
+extern int can_send(can_t *pcan, char *data, int bytes);
+extern int can_recv(can_t *pcan, char *buf, int size, uint32_t timeout);
+extern void can_term(can_t *pcan);
+/* USER CODE END Prototypes */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CAN_H__ */
+
diff --git a/Production/ISKBoard_QCTester/Core/Inc/gpio.h b/Production/ISKBoard_QCTester/Core/Inc/gpio.h
new file mode 100644
index 0000000..6c03ac7
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Inc/gpio.h
@@ -0,0 +1,49 @@
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file    gpio.h
+  * @brief   This file contains all the function prototypes for
+  *          the gpio.c file
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2023 STMicroelectronics.
+  * All rights reserved.
+  *
+  * This software is licensed under terms that can be found in the LICENSE file
+  * in the root directory of this software component.
+  * If no LICENSE file comes with this software, it is provided AS-IS.
+  *
+  ******************************************************************************
+  */
+/* USER CODE END Header */
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __GPIO_H__
+#define __GPIO_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "main.h"
+
+/* USER CODE BEGIN Includes */
+
+/* USER CODE END Includes */
+
+/* USER CODE BEGIN Private defines */
+
+/* USER CODE END Private defines */
+
+void MX_GPIO_Init(void);
+
+/* USER CODE BEGIN Prototypes */
+
+/* USER CODE END Prototypes */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /*__ GPIO_H__ */
+
diff --git a/Production/ISKBoard_QCTester/Core/Inc/main.h b/Production/ISKBoard_QCTester/Core/Inc/main.h
new file mode 100644
index 0000000..875a44d
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Inc/main.h
@@ -0,0 +1,116 @@
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file           : main.h
+  * @brief          : Header for main.c file.
+  *                   This file contains the common defines of the application.
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2023 STMicroelectronics.
+  * All rights reserved.
+  *
+  * This software is licensed under terms that can be found in the LICENSE file
+  * in the root directory of this software component.
+  * If no LICENSE file comes with this software, it is provided AS-IS.
+  *
+  ******************************************************************************
+  */
+/* USER CODE END Header */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __MAIN_H
+#define __MAIN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32l4xx_hal.h"
+
+/* Private includes ----------------------------------------------------------*/
+/* USER CODE BEGIN Includes */
+
+/* USER CODE END Includes */
+
+/* Exported types ------------------------------------------------------------*/
+/* USER CODE BEGIN ET */
+
+/* USER CODE END ET */
+
+/* Exported constants --------------------------------------------------------*/
+/* USER CODE BEGIN EC */
+
+/* USER CODE END EC */
+
+/* Exported macro ------------------------------------------------------------*/
+/* USER CODE BEGIN EM */
+
+/* USER CODE END EM */
+
+/* Exported functions prototypes ---------------------------------------------*/
+void Error_Handler(void);
+
+/* USER CODE BEGIN EFP */
+
+/* USER CODE END EFP */
+
+/* Private defines -----------------------------------------------------------*/
+#define StripLights_Pin GPIO_PIN_2
+#define StripLights_GPIO_Port GPIOC
+#define mikrobus_an_Pin GPIO_PIN_3
+#define mikrobus_an_GPIO_Port GPIOC
+#define mikrobus_pwm_Pin GPIO_PIN_1
+#define mikrobus_pwm_GPIO_Port GPIOA
+#define SPI1_CS_Pin GPIO_PIN_4
+#define SPI1_CS_GPIO_Port GPIOA
+#define LedBlue_Pin GPIO_PIN_2
+#define LedBlue_GPIO_Port GPIOB
+#define mikrobus_scl_Pin GPIO_PIN_10
+#define mikrobus_scl_GPIO_Port GPIOB
+#define mikrobus_sda_Pin GPIO_PIN_11
+#define mikrobus_sda_GPIO_Port GPIOB
+#define Key1_Pin GPIO_PIN_12
+#define Key1_GPIO_Port GPIOB
+#define Key1_EXTI_IRQn EXTI15_10_IRQn
+#define Key2_Pin GPIO_PIN_13
+#define Key2_GPIO_Port GPIOB
+#define Key2_EXTI_IRQn EXTI15_10_IRQn
+#define Key3_Pin GPIO_PIN_14
+#define Key3_GPIO_Port GPIOB
+#define Key3_EXTI_IRQn EXTI15_10_IRQn
+#define LedGreen_Pin GPIO_PIN_6
+#define LedGreen_GPIO_Port GPIOC
+#define RS485_Dir_Pin GPIO_PIN_7
+#define RS485_Dir_GPIO_Port GPIOC
+#define CAN_STB_Pin GPIO_PIN_8
+#define CAN_STB_GPIO_Port GPIOC
+#define LedRed_Pin GPIO_PIN_9
+#define LedRed_GPIO_Port GPIOC
+#define mikrobus_cs_Pin GPIO_PIN_15
+#define mikrobus_cs_GPIO_Port GPIOA
+#define mikrobus_sck_Pin GPIO_PIN_10
+#define mikrobus_sck_GPIO_Port GPIOC
+#define mikrobus_miso_Pin GPIO_PIN_11
+#define mikrobus_miso_GPIO_Port GPIOC
+#define mikrobus_mosi_Pin GPIO_PIN_12
+#define mikrobus_mosi_GPIO_Port GPIOC
+#define Relay_Pin GPIO_PIN_2
+#define Relay_GPIO_Port GPIOD
+#define WiFiRst_Pin GPIO_PIN_3
+#define WiFiRst_GPIO_Port GPIOB
+#define mikrobus_int_Pin GPIO_PIN_4
+#define mikrobus_int_GPIO_Port GPIOB
+#define mikrobus_rst_Pin GPIO_PIN_5
+#define mikrobus_rst_GPIO_Port GPIOB
+
+/* USER CODE BEGIN Private defines */
+
+/* USER CODE END Private defines */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MAIN_H */
diff --git a/Production/ISKBoard_QCTester/Core/Inc/spi.h b/Production/ISKBoard_QCTester/Core/Inc/spi.h
new file mode 100644
index 0000000..f40913c
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Inc/spi.h
@@ -0,0 +1,52 @@
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file    spi.h
+  * @brief   This file contains all the function prototypes for
+  *          the spi.c file
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2023 STMicroelectronics.
+  * All rights reserved.
+  *
+  * This software is licensed under terms that can be found in the LICENSE file
+  * in the root directory of this software component.
+  * If no LICENSE file comes with this software, it is provided AS-IS.
+  *
+  ******************************************************************************
+  */
+/* USER CODE END Header */
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __SPI_H__
+#define __SPI_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "main.h"
+
+/* USER CODE BEGIN Includes */
+
+/* USER CODE END Includes */
+
+extern SPI_HandleTypeDef hspi1;
+
+/* USER CODE BEGIN Private defines */
+
+/* USER CODE END Private defines */
+
+void MX_SPI1_Init(void);
+
+/* USER CODE BEGIN Prototypes */
+
+/* USER CODE END Prototypes */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SPI_H__ */
+
diff --git a/Production/ISKBoard_QCTester/Core/Inc/stm32l4xx_hal_conf.h b/Production/ISKBoard_QCTester/Core/Inc/stm32l4xx_hal_conf.h
new file mode 100644
index 0000000..1a5590b
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Inc/stm32l4xx_hal_conf.h
@@ -0,0 +1,482 @@
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file    stm32l4xx_hal_conf.h
+  * @author  MCD Application Team
+  * @brief   HAL configuration template file.
+  *          This file should be copied to the application folder and renamed
+  *          to stm32l4xx_hal_conf.h.
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2017 STMicroelectronics.
+  * All rights reserved.
+  *
+  * This software is licensed under terms that can be found in the LICENSE file
+  * in the root directory of this software component.
+  * If no LICENSE file comes with this software, it is provided AS-IS.
+  *
+  ******************************************************************************
+  */
+/* USER CODE END Header */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef STM32L4xx_HAL_CONF_H
+#define STM32L4xx_HAL_CONF_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Exported types ------------------------------------------------------------*/
+/* Exported constants --------------------------------------------------------*/
+
+/* ########################## Module Selection ############################## */
+/**
+  * @brief This is the list of modules to be used in the HAL driver
+  */
+#define HAL_MODULE_ENABLED
+#define HAL_ADC_MODULE_ENABLED
+/*#define HAL_CRYP_MODULE_ENABLED   */
+#define HAL_CAN_MODULE_ENABLED
+/*#define HAL_COMP_MODULE_ENABLED   */
+/*#define HAL_I2C_MODULE_ENABLED   */
+/*#define HAL_CRC_MODULE_ENABLED   */
+/*#define HAL_CRYP_MODULE_ENABLED   */
+/*#define HAL_DAC_MODULE_ENABLED   */
+/*#define HAL_DCMI_MODULE_ENABLED   */
+/*#define HAL_DMA2D_MODULE_ENABLED   */
+/*#define HAL_DFSDM_MODULE_ENABLED   */
+/*#define HAL_DSI_MODULE_ENABLED   */
+/*#define HAL_FIREWALL_MODULE_ENABLED   */
+/*#define HAL_GFXMMU_MODULE_ENABLED   */
+/*#define HAL_HCD_MODULE_ENABLED   */
+/*#define HAL_HASH_MODULE_ENABLED   */
+/*#define HAL_I2S_MODULE_ENABLED   */
+/*#define HAL_IRDA_MODULE_ENABLED   */
+/*#define HAL_IWDG_MODULE_ENABLED   */
+/*#define HAL_LTDC_MODULE_ENABLED   */
+/*#define HAL_LCD_MODULE_ENABLED   */
+/*#define HAL_LPTIM_MODULE_ENABLED   */
+/*#define HAL_MMC_MODULE_ENABLED   */
+/*#define HAL_NAND_MODULE_ENABLED   */
+/*#define HAL_NOR_MODULE_ENABLED   */
+/*#define HAL_OPAMP_MODULE_ENABLED   */
+/*#define HAL_OSPI_MODULE_ENABLED   */
+/*#define HAL_OSPI_MODULE_ENABLED   */
+/*#define HAL_PCD_MODULE_ENABLED   */
+/*#define HAL_PKA_MODULE_ENABLED   */
+/*#define HAL_QSPI_MODULE_ENABLED   */
+/*#define HAL_QSPI_MODULE_ENABLED   */
+/*#define HAL_RNG_MODULE_ENABLED   */
+/*#define HAL_RTC_MODULE_ENABLED   */
+/*#define HAL_SAI_MODULE_ENABLED   */
+/*#define HAL_SD_MODULE_ENABLED   */
+/*#define HAL_SMBUS_MODULE_ENABLED   */
+/*#define HAL_SMARTCARD_MODULE_ENABLED   */
+#define HAL_SPI_MODULE_ENABLED
+/*#define HAL_SRAM_MODULE_ENABLED   */
+/*#define HAL_SWPMI_MODULE_ENABLED   */
+#define HAL_TIM_MODULE_ENABLED
+/*#define HAL_TSC_MODULE_ENABLED   */
+#define HAL_UART_MODULE_ENABLED
+/*#define HAL_USART_MODULE_ENABLED   */
+/*#define HAL_WWDG_MODULE_ENABLED   */
+/*#define HAL_EXTI_MODULE_ENABLED   */
+/*#define HAL_PSSI_MODULE_ENABLED   */
+#define HAL_GPIO_MODULE_ENABLED
+#define HAL_EXTI_MODULE_ENABLED
+#define HAL_DMA_MODULE_ENABLED
+#define HAL_RCC_MODULE_ENABLED
+#define HAL_FLASH_MODULE_ENABLED
+#define HAL_PWR_MODULE_ENABLED
+#define HAL_CORTEX_MODULE_ENABLED
+
+/* ########################## Oscillator Values adaptation ####################*/
+/**
+  * @brief Adjust the value of External High Speed oscillator (HSE) used in your application.
+  *        This value is used by the RCC HAL module to compute the system frequency
+  *        (when HSE is used as system clock source, directly or through the PLL).
+  */
+#if !defined  (HSE_VALUE)
+  #define HSE_VALUE    ((uint32_t)8000000U) /*!< Value of the External oscillator in Hz */
+#endif /* HSE_VALUE */
+
+#if !defined  (HSE_STARTUP_TIMEOUT)
+  #define HSE_STARTUP_TIMEOUT    ((uint32_t)100U)   /*!< Time out for HSE start up, in ms */
+#endif /* HSE_STARTUP_TIMEOUT */
+
+/**
+  * @brief Internal Multiple Speed oscillator (MSI) default value.
+  *        This value is the default MSI range value after Reset.
+  */
+#if !defined  (MSI_VALUE)
+  #define MSI_VALUE    ((uint32_t)4000000U) /*!< Value of the Internal oscillator in Hz*/
+#endif /* MSI_VALUE */
+/**
+  * @brief Internal High Speed oscillator (HSI) value.
+  *        This value is used by the RCC HAL module to compute the system frequency
+  *        (when HSI is used as system clock source, directly or through the PLL).
+  */
+#if !defined  (HSI_VALUE)
+  #define HSI_VALUE    ((uint32_t)16000000U) /*!< Value of the Internal oscillator in Hz*/
+#endif /* HSI_VALUE */
+
+/**
+  * @brief Internal High Speed oscillator (HSI48) value for USB FS, SDMMC and RNG.
+  *        This internal oscillator is mainly dedicated to provide a high precision clock to
+  *        the USB peripheral by means of a special Clock Recovery System (CRS) circuitry.
+  *        When the CRS is not used, the HSI48 RC oscillator runs on it default frequency
+  *        which is subject to manufacturing process variations.
+  */
+#if !defined  (HSI48_VALUE)
+ #define HSI48_VALUE   ((uint32_t)48000000U) /*!< Value of the Internal High Speed oscillator for USB FS/SDMMC/RNG in Hz.
+                                              The real value my vary depending on manufacturing process variations.*/
+#endif /* HSI48_VALUE */
+
+/**
+  * @brief Internal Low Speed oscillator (LSI) value.
+  */
+#if !defined  (LSI_VALUE)
+ #define LSI_VALUE  32000U       /*!< LSI Typical Value in Hz*/
+#endif /* LSI_VALUE */                      /*!< Value of the Internal Low Speed oscillator in Hz
+                                             The real value may vary depending on the variations
+                                             in voltage and temperature.*/
+
+/**
+  * @brief External Low Speed oscillator (LSE) value.
+  *        This value is used by the UART, RTC HAL module to compute the system frequency
+  */
+#if !defined  (LSE_VALUE)
+  #define LSE_VALUE    32768U /*!< Value of the External oscillator in Hz*/
+#endif /* LSE_VALUE */
+
+#if !defined  (LSE_STARTUP_TIMEOUT)
+  #define LSE_STARTUP_TIMEOUT    5000U   /*!< Time out for LSE start up, in ms */
+#endif /* HSE_STARTUP_TIMEOUT */
+
+/**
+  * @brief External clock source for SAI1 peripheral
+  *        This value is used by the RCC HAL module to compute the SAI1 & SAI2 clock source
+  *        frequency.
+  */
+#if !defined  (EXTERNAL_SAI1_CLOCK_VALUE)
+  #define EXTERNAL_SAI1_CLOCK_VALUE    2097000U /*!< Value of the SAI1 External clock source in Hz*/
+#endif /* EXTERNAL_SAI1_CLOCK_VALUE */
+
+/**
+  * @brief External clock source for SAI2 peripheral
+  *        This value is used by the RCC HAL module to compute the SAI1 & SAI2 clock source
+  *        frequency.
+  */
+#if !defined  (EXTERNAL_SAI2_CLOCK_VALUE)
+  #define EXTERNAL_SAI2_CLOCK_VALUE    48000U /*!< Value of the SAI2 External clock source in Hz*/
+#endif /* EXTERNAL_SAI2_CLOCK_VALUE */
+
+/* Tip: To avoid modifying this file each time you need to use different HSE,
+   ===  you can define the HSE value in your toolchain compiler preprocessor. */
+
+/* ########################### System Configuration ######################### */
+/**
+  * @brief This is the HAL system configuration section
+  */
+
+#define  VDD_VALUE					  3300U /*!< Value of VDD in mv */
+#define  TICK_INT_PRIORITY            15U    /*!< tick interrupt priority */
+#define  USE_RTOS                     0U
+#define  PREFETCH_ENABLE              0U
+#define  INSTRUCTION_CACHE_ENABLE     1U
+#define  DATA_CACHE_ENABLE            1U
+
+/* ########################## Assert Selection ############################## */
+/**
+  * @brief Uncomment the line below to expanse the "assert_param" macro in the
+  *        HAL drivers code
+  */
+/* #define USE_FULL_ASSERT    1U */
+
+/* ################## Register callback feature configuration ############### */
+/**
+  * @brief Set below the peripheral configuration  to "1U" to add the support
+  *        of HAL callback registration/deregistration feature for the HAL
+  *        driver(s). This allows user application to provide specific callback
+  *        functions thanks to HAL_PPP_RegisterCallback() rather than overwriting
+  *        the default weak callback functions (see each stm32l4xx_hal_ppp.h file
+  *        for possible callback identifiers defined in HAL_PPP_CallbackIDTypeDef
+  *        for each PPP peripheral).
+  */
+#define USE_HAL_ADC_REGISTER_CALLBACKS        0U
+#define USE_HAL_CAN_REGISTER_CALLBACKS        0U
+#define USE_HAL_COMP_REGISTER_CALLBACKS       0U
+#define USE_HAL_CRYP_REGISTER_CALLBACKS       0U
+#define USE_HAL_DAC_REGISTER_CALLBACKS        0U
+#define USE_HAL_DCMI_REGISTER_CALLBACKS       0U
+#define USE_HAL_DFSDM_REGISTER_CALLBACKS      0U
+#define USE_HAL_DMA2D_REGISTER_CALLBACKS      0U
+#define USE_HAL_DSI_REGISTER_CALLBACKS        0U
+#define USE_HAL_GFXMMU_REGISTER_CALLBACKS     0U
+#define USE_HAL_HASH_REGISTER_CALLBACKS       0U
+#define USE_HAL_HCD_REGISTER_CALLBACKS        0U
+#define USE_HAL_I2C_REGISTER_CALLBACKS        0U
+#define USE_HAL_IRDA_REGISTER_CALLBACKS       0U
+#define USE_HAL_LPTIM_REGISTER_CALLBACKS      0U
+#define USE_HAL_LTDC_REGISTER_CALLBACKS       0U
+#define USE_HAL_MMC_REGISTER_CALLBACKS        0U
+#define USE_HAL_OPAMP_REGISTER_CALLBACKS      0U
+#define USE_HAL_OSPI_REGISTER_CALLBACKS       0U
+#define USE_HAL_PCD_REGISTER_CALLBACKS        0U
+#define USE_HAL_QSPI_REGISTER_CALLBACKS       0U
+#define USE_HAL_RNG_REGISTER_CALLBACKS        0U
+#define USE_HAL_RTC_REGISTER_CALLBACKS        0U
+#define USE_HAL_SAI_REGISTER_CALLBACKS        0U
+#define USE_HAL_SD_REGISTER_CALLBACKS         0U
+#define USE_HAL_SMARTCARD_REGISTER_CALLBACKS  0U
+#define USE_HAL_SMBUS_REGISTER_CALLBACKS      0U
+#define USE_HAL_SPI_REGISTER_CALLBACKS        0U
+#define USE_HAL_SWPMI_REGISTER_CALLBACKS      0U
+#define USE_HAL_TIM_REGISTER_CALLBACKS        0U
+#define USE_HAL_TSC_REGISTER_CALLBACKS        0U
+#define USE_HAL_UART_REGISTER_CALLBACKS       0U
+#define USE_HAL_USART_REGISTER_CALLBACKS      0U
+#define USE_HAL_WWDG_REGISTER_CALLBACKS       0U
+
+/* ################## SPI peripheral configuration ########################## */
+
+/* CRC FEATURE: Use to activate CRC feature inside HAL SPI Driver
+ * Activated: CRC code is present inside driver
+ * Deactivated: CRC code cleaned from driver
+ */
+
+#define USE_SPI_CRC                   0U
+
+/* Includes ------------------------------------------------------------------*/
+/**
+  * @brief Include module's header file
+  */
+
+#ifdef HAL_RCC_MODULE_ENABLED
+  #include "stm32l4xx_hal_rcc.h"
+#endif /* HAL_RCC_MODULE_ENABLED */
+
+#ifdef HAL_GPIO_MODULE_ENABLED
+  #include "stm32l4xx_hal_gpio.h"
+#endif /* HAL_GPIO_MODULE_ENABLED */
+
+#ifdef HAL_DMA_MODULE_ENABLED
+  #include "stm32l4xx_hal_dma.h"
+#endif /* HAL_DMA_MODULE_ENABLED */
+
+#ifdef HAL_DFSDM_MODULE_ENABLED
+  #include "stm32l4xx_hal_dfsdm.h"
+#endif /* HAL_DFSDM_MODULE_ENABLED */
+
+#ifdef HAL_CORTEX_MODULE_ENABLED
+  #include "stm32l4xx_hal_cortex.h"
+#endif /* HAL_CORTEX_MODULE_ENABLED */
+
+#ifdef HAL_ADC_MODULE_ENABLED
+  #include "stm32l4xx_hal_adc.h"
+#endif /* HAL_ADC_MODULE_ENABLED */
+
+#ifdef HAL_CAN_MODULE_ENABLED
+  #include "stm32l4xx_hal_can.h"
+#endif /* HAL_CAN_MODULE_ENABLED */
+
+#ifdef HAL_CAN_LEGACY_MODULE_ENABLED
+  #include "Legacy/stm32l4xx_hal_can_legacy.h"
+#endif /* HAL_CAN_LEGACY_MODULE_ENABLED */
+
+#ifdef HAL_COMP_MODULE_ENABLED
+  #include "stm32l4xx_hal_comp.h"
+#endif /* HAL_COMP_MODULE_ENABLED */
+
+#ifdef HAL_CRC_MODULE_ENABLED
+  #include "stm32l4xx_hal_crc.h"
+#endif /* HAL_CRC_MODULE_ENABLED */
+
+#ifdef HAL_CRYP_MODULE_ENABLED
+  #include "stm32l4xx_hal_cryp.h"
+#endif /* HAL_CRYP_MODULE_ENABLED */
+
+#ifdef HAL_DAC_MODULE_ENABLED
+  #include "stm32l4xx_hal_dac.h"
+#endif /* HAL_DAC_MODULE_ENABLED */
+
+#ifdef HAL_DCMI_MODULE_ENABLED
+  #include "stm32l4xx_hal_dcmi.h"
+#endif /* HAL_DCMI_MODULE_ENABLED */
+
+#ifdef HAL_DMA2D_MODULE_ENABLED
+  #include "stm32l4xx_hal_dma2d.h"
+#endif /* HAL_DMA2D_MODULE_ENABLED */
+
+#ifdef HAL_DSI_MODULE_ENABLED
+  #include "stm32l4xx_hal_dsi.h"
+#endif /* HAL_DSI_MODULE_ENABLED */
+
+#ifdef HAL_EXTI_MODULE_ENABLED
+  #include "stm32l4xx_hal_exti.h"
+#endif /* HAL_EXTI_MODULE_ENABLED */
+
+#ifdef HAL_GFXMMU_MODULE_ENABLED
+  #include "stm32l4xx_hal_gfxmmu.h"
+#endif /* HAL_GFXMMU_MODULE_ENABLED */
+
+#ifdef HAL_FIREWALL_MODULE_ENABLED
+  #include "stm32l4xx_hal_firewall.h"
+#endif /* HAL_FIREWALL_MODULE_ENABLED */
+
+#ifdef HAL_FLASH_MODULE_ENABLED
+  #include "stm32l4xx_hal_flash.h"
+#endif /* HAL_FLASH_MODULE_ENABLED */
+
+#ifdef HAL_HASH_MODULE_ENABLED
+  #include "stm32l4xx_hal_hash.h"
+#endif /* HAL_HASH_MODULE_ENABLED */
+
+#ifdef HAL_HCD_MODULE_ENABLED
+  #include "stm32l4xx_hal_hcd.h"
+#endif /* HAL_HCD_MODULE_ENABLED */
+
+#ifdef HAL_I2C_MODULE_ENABLED
+  #include "stm32l4xx_hal_i2c.h"
+#endif /* HAL_I2C_MODULE_ENABLED */
+
+#ifdef HAL_IRDA_MODULE_ENABLED
+  #include "stm32l4xx_hal_irda.h"
+#endif /* HAL_IRDA_MODULE_ENABLED */
+
+#ifdef HAL_IWDG_MODULE_ENABLED
+  #include "stm32l4xx_hal_iwdg.h"
+#endif /* HAL_IWDG_MODULE_ENABLED */
+
+#ifdef HAL_LCD_MODULE_ENABLED
+  #include "stm32l4xx_hal_lcd.h"
+#endif /* HAL_LCD_MODULE_ENABLED */
+
+#ifdef HAL_LPTIM_MODULE_ENABLED
+  #include "stm32l4xx_hal_lptim.h"
+#endif /* HAL_LPTIM_MODULE_ENABLED */
+
+#ifdef HAL_LTDC_MODULE_ENABLED
+  #include "stm32l4xx_hal_ltdc.h"
+#endif /* HAL_LTDC_MODULE_ENABLED */
+
+#ifdef HAL_MMC_MODULE_ENABLED
+  #include "stm32l4xx_hal_mmc.h"
+#endif /* HAL_MMC_MODULE_ENABLED */
+
+#ifdef HAL_NAND_MODULE_ENABLED
+  #include "stm32l4xx_hal_nand.h"
+#endif /* HAL_NAND_MODULE_ENABLED */
+
+#ifdef HAL_NOR_MODULE_ENABLED
+  #include "stm32l4xx_hal_nor.h"
+#endif /* HAL_NOR_MODULE_ENABLED */
+
+#ifdef HAL_OPAMP_MODULE_ENABLED
+  #include "stm32l4xx_hal_opamp.h"
+#endif /* HAL_OPAMP_MODULE_ENABLED */
+
+#ifdef HAL_OSPI_MODULE_ENABLED
+  #include "stm32l4xx_hal_ospi.h"
+#endif /* HAL_OSPI_MODULE_ENABLED */
+
+#ifdef HAL_PCD_MODULE_ENABLED
+  #include "stm32l4xx_hal_pcd.h"
+#endif /* HAL_PCD_MODULE_ENABLED */
+
+#ifdef HAL_PKA_MODULE_ENABLED
+  #include "stm32l4xx_hal_pka.h"
+#endif /* HAL_PKA_MODULE_ENABLED */
+
+#ifdef HAL_PSSI_MODULE_ENABLED
+  #include "stm32l4xx_hal_pssi.h"
+#endif /* HAL_PSSI_MODULE_ENABLED */
+
+#ifdef HAL_PWR_MODULE_ENABLED
+  #include "stm32l4xx_hal_pwr.h"
+#endif /* HAL_PWR_MODULE_ENABLED */
+
+#ifdef HAL_QSPI_MODULE_ENABLED
+  #include "stm32l4xx_hal_qspi.h"
+#endif /* HAL_QSPI_MODULE_ENABLED */
+
+#ifdef HAL_RNG_MODULE_ENABLED
+  #include "stm32l4xx_hal_rng.h"
+#endif /* HAL_RNG_MODULE_ENABLED */
+
+#ifdef HAL_RTC_MODULE_ENABLED
+  #include "stm32l4xx_hal_rtc.h"
+#endif /* HAL_RTC_MODULE_ENABLED */
+
+#ifdef HAL_SAI_MODULE_ENABLED
+  #include "stm32l4xx_hal_sai.h"
+#endif /* HAL_SAI_MODULE_ENABLED */
+
+#ifdef HAL_SD_MODULE_ENABLED
+  #include "stm32l4xx_hal_sd.h"
+#endif /* HAL_SD_MODULE_ENABLED */
+
+#ifdef HAL_SMARTCARD_MODULE_ENABLED
+  #include "stm32l4xx_hal_smartcard.h"
+#endif /* HAL_SMARTCARD_MODULE_ENABLED */
+
+#ifdef HAL_SMBUS_MODULE_ENABLED
+  #include "stm32l4xx_hal_smbus.h"
+#endif /* HAL_SMBUS_MODULE_ENABLED */
+
+#ifdef HAL_SPI_MODULE_ENABLED
+  #include "stm32l4xx_hal_spi.h"
+#endif /* HAL_SPI_MODULE_ENABLED */
+
+#ifdef HAL_SRAM_MODULE_ENABLED
+  #include "stm32l4xx_hal_sram.h"
+#endif /* HAL_SRAM_MODULE_ENABLED */
+
+#ifdef HAL_SWPMI_MODULE_ENABLED
+  #include "stm32l4xx_hal_swpmi.h"
+#endif /* HAL_SWPMI_MODULE_ENABLED */
+
+#ifdef HAL_TIM_MODULE_ENABLED
+  #include "stm32l4xx_hal_tim.h"
+#endif /* HAL_TIM_MODULE_ENABLED */
+
+#ifdef HAL_TSC_MODULE_ENABLED
+  #include "stm32l4xx_hal_tsc.h"
+#endif /* HAL_TSC_MODULE_ENABLED */
+
+#ifdef HAL_UART_MODULE_ENABLED
+  #include "stm32l4xx_hal_uart.h"
+#endif /* HAL_UART_MODULE_ENABLED */
+
+#ifdef HAL_USART_MODULE_ENABLED
+  #include "stm32l4xx_hal_usart.h"
+#endif /* HAL_USART_MODULE_ENABLED */
+
+#ifdef HAL_WWDG_MODULE_ENABLED
+  #include "stm32l4xx_hal_wwdg.h"
+#endif /* HAL_WWDG_MODULE_ENABLED */
+
+/* Exported macro ------------------------------------------------------------*/
+#ifdef  USE_FULL_ASSERT
+/**
+  * @brief  The assert_param macro is used for function's parameters check.
+  * @param  expr If expr is false, it calls assert_failed function
+  *         which reports the name of the source file and the source
+  *         line number of the call that failed.
+  *         If expr is true, it returns no value.
+  * @retval None
+  */
+  #define assert_param(expr) ((expr) ? (void)0U : assert_failed((uint8_t *)__FILE__, __LINE__))
+/* Exported functions ------------------------------------------------------- */
+  void assert_failed(uint8_t *file, uint32_t line);
+#else
+  #define assert_param(expr) ((void)0U)
+#endif /* USE_FULL_ASSERT */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* STM32L4xx_HAL_CONF_H */
diff --git a/Production/ISKBoard_QCTester/Core/Inc/stm32l4xx_it.h b/Production/ISKBoard_QCTester/Core/Inc/stm32l4xx_it.h
new file mode 100644
index 0000000..b192203
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Inc/stm32l4xx_it.h
@@ -0,0 +1,72 @@
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file    stm32l4xx_it.h
+  * @brief   This file contains the headers of the interrupt handlers.
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2023 STMicroelectronics.
+  * All rights reserved.
+  *
+  * This software is licensed under terms that can be found in the LICENSE file
+  * in the root directory of this software component.
+  * If no LICENSE file comes with this software, it is provided AS-IS.
+  *
+ ******************************************************************************
+  */
+/* USER CODE END Header */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32L4xx_IT_H
+#define __STM32L4xx_IT_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Private includes ----------------------------------------------------------*/
+/* USER CODE BEGIN Includes */
+
+/* USER CODE END Includes */
+
+/* Exported types ------------------------------------------------------------*/
+/* USER CODE BEGIN ET */
+
+/* USER CODE END ET */
+
+/* Exported constants --------------------------------------------------------*/
+/* USER CODE BEGIN EC */
+
+/* USER CODE END EC */
+
+/* Exported macro ------------------------------------------------------------*/
+/* USER CODE BEGIN EM */
+
+/* USER CODE END EM */
+
+/* Exported functions prototypes ---------------------------------------------*/
+void NMI_Handler(void);
+void HardFault_Handler(void);
+void MemManage_Handler(void);
+void BusFault_Handler(void);
+void UsageFault_Handler(void);
+void SVC_Handler(void);
+void DebugMon_Handler(void);
+void PendSV_Handler(void);
+void SysTick_Handler(void);
+void CAN1_RX0_IRQHandler(void);
+void USART1_IRQHandler(void);
+void USART2_IRQHandler(void);
+void USART3_IRQHandler(void);
+void EXTI15_10_IRQHandler(void);
+void LPUART1_IRQHandler(void);
+/* USER CODE BEGIN EFP */
+
+/* USER CODE END EFP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __STM32L4xx_IT_H */
diff --git a/Production/ISKBoard_QCTester/Core/Inc/tim.h b/Production/ISKBoard_QCTester/Core/Inc/tim.h
new file mode 100644
index 0000000..f996b33
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Inc/tim.h
@@ -0,0 +1,57 @@
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file    tim.h
+  * @brief   This file contains all the function prototypes for
+  *          the tim.c file
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2023 STMicroelectronics.
+  * All rights reserved.
+  *
+  * This software is licensed under terms that can be found in the LICENSE file
+  * in the root directory of this software component.
+  * If no LICENSE file comes with this software, it is provided AS-IS.
+  *
+  ******************************************************************************
+  */
+/* USER CODE END Header */
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __TIM_H__
+#define __TIM_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "main.h"
+
+/* USER CODE BEGIN Includes */
+
+/* USER CODE END Includes */
+
+extern TIM_HandleTypeDef htim1;
+
+extern TIM_HandleTypeDef htim6;
+
+/* USER CODE BEGIN Private defines */
+
+/* USER CODE END Private defines */
+
+void MX_TIM1_Init(void);
+void MX_TIM6_Init(void);
+
+void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim);
+
+/* USER CODE BEGIN Prototypes */
+
+/* USER CODE END Prototypes */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TIM_H__ */
+
diff --git a/Production/ISKBoard_QCTester/Core/Inc/usart.h b/Production/ISKBoard_QCTester/Core/Inc/usart.h
new file mode 100644
index 0000000..38bfd4f
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Inc/usart.h
@@ -0,0 +1,61 @@
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file    usart.h
+  * @brief   This file contains all the function prototypes for
+  *          the usart.c file
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2023 STMicroelectronics.
+  * All rights reserved.
+  *
+  * This software is licensed under terms that can be found in the LICENSE file
+  * in the root directory of this software component.
+  * If no LICENSE file comes with this software, it is provided AS-IS.
+  *
+  ******************************************************************************
+  */
+/* USER CODE END Header */
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __USART_H__
+#define __USART_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "main.h"
+
+/* USER CODE BEGIN Includes */
+
+/* USER CODE END Includes */
+
+extern UART_HandleTypeDef hlpuart1;
+
+extern UART_HandleTypeDef huart1;
+
+extern UART_HandleTypeDef huart2;
+
+extern UART_HandleTypeDef huart3;
+
+/* USER CODE BEGIN Private defines */
+
+/* USER CODE END Private defines */
+
+void MX_LPUART1_UART_Init(void);
+void MX_USART1_UART_Init(void);
+void MX_USART2_UART_Init(void);
+void MX_USART3_UART_Init(void);
+
+/* USER CODE BEGIN Prototypes */
+
+/* USER CODE END Prototypes */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __USART_H__ */
+
diff --git a/Production/ISKBoard_QCTester/Core/Src/adc.c b/Production/ISKBoard_QCTester/Core/Src/adc.c
new file mode 100644
index 0000000..cb66379
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Src/adc.c
@@ -0,0 +1,163 @@
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file    adc.c
+  * @brief   This file provides code for the configuration
+  *          of the ADC instances.
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2023 STMicroelectronics.
+  * All rights reserved.
+  *
+  * This software is licensed under terms that can be found in the LICENSE file
+  * in the root directory of this software component.
+  * If no LICENSE file comes with this software, it is provided AS-IS.
+  *
+  ******************************************************************************
+  */
+/* USER CODE END Header */
+/* Includes ------------------------------------------------------------------*/
+#include "adc.h"
+
+/* USER CODE BEGIN 0 */
+
+/* USER CODE END 0 */
+
+ADC_HandleTypeDef hadc1;
+
+/* ADC1 init function */
+void MX_ADC1_Init(void)
+{
+
+  /* USER CODE BEGIN ADC1_Init 0 */
+
+  /* USER CODE END ADC1_Init 0 */
+
+  ADC_ChannelConfTypeDef sConfig = {0};
+
+  /* USER CODE BEGIN ADC1_Init 1 */
+
+  /* USER CODE END ADC1_Init 1 */
+
+  /** Common config
+  */
+  hadc1.Instance = ADC1;
+  hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
+  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
+  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
+  hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
+  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
+  hadc1.Init.LowPowerAutoWait = DISABLE;
+  hadc1.Init.ContinuousConvMode = ENABLE;
+  hadc1.Init.NbrOfConversion = 2;
+  hadc1.Init.DiscontinuousConvMode = DISABLE;
+  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
+  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
+  hadc1.Init.DMAContinuousRequests = DISABLE;
+  hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
+  hadc1.Init.OversamplingMode = DISABLE;
+  if (HAL_ADC_Init(&hadc1) != HAL_OK)
+  {
+    Error_Handler();
+  }
+
+  /** Configure Regular Channel
+  */
+  sConfig.Channel = ADC_CHANNEL_15;
+  sConfig.Rank = ADC_REGULAR_RANK_1;
+  sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5;
+  sConfig.SingleDiff = ADC_SINGLE_ENDED;
+  sConfig.OffsetNumber = ADC_OFFSET_NONE;
+  sConfig.Offset = 0;
+  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
+  {
+    Error_Handler();
+  }
+
+  /** Configure Regular Channel
+  */
+  sConfig.Channel = ADC_CHANNEL_16;
+  sConfig.Rank = ADC_REGULAR_RANK_2;
+  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
+  {
+    Error_Handler();
+  }
+  /* USER CODE BEGIN ADC1_Init 2 */
+
+  /* USER CODE END ADC1_Init 2 */
+
+}
+
+void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
+{
+
+  GPIO_InitTypeDef GPIO_InitStruct = {0};
+  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
+  if(adcHandle->Instance==ADC1)
+  {
+  /* USER CODE BEGIN ADC1_MspInit 0 */
+
+  /* USER CODE END ADC1_MspInit 0 */
+
+  /** Initializes the peripherals clock
+  */
+    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
+    PeriphClkInit.AdcClockSelection = RCC_ADCCLKSOURCE_PLLSAI1;
+    PeriphClkInit.PLLSAI1.PLLSAI1Source = RCC_PLLSOURCE_HSE;
+    PeriphClkInit.PLLSAI1.PLLSAI1M = 1;
+    PeriphClkInit.PLLSAI1.PLLSAI1N = 9;
+    PeriphClkInit.PLLSAI1.PLLSAI1P = RCC_PLLP_DIV7;
+    PeriphClkInit.PLLSAI1.PLLSAI1Q = RCC_PLLQ_DIV2;
+    PeriphClkInit.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV6;
+    PeriphClkInit.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_ADC1CLK;
+    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
+    {
+      Error_Handler();
+    }
+
+    /* ADC1 clock enable */
+    __HAL_RCC_ADC_CLK_ENABLE();
+
+    __HAL_RCC_GPIOB_CLK_ENABLE();
+    /**ADC1 GPIO Configuration
+    PB0     ------> ADC1_IN15
+    PB1     ------> ADC1_IN16
+    */
+    GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;
+    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG_ADC_CONTROL;
+    GPIO_InitStruct.Pull = GPIO_NOPULL;
+    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
+
+  /* USER CODE BEGIN ADC1_MspInit 1 */
+
+  /* USER CODE END ADC1_MspInit 1 */
+  }
+}
+
+void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
+{
+
+  if(adcHandle->Instance==ADC1)
+  {
+  /* USER CODE BEGIN ADC1_MspDeInit 0 */
+
+  /* USER CODE END ADC1_MspDeInit 0 */
+    /* Peripheral clock disable */
+    __HAL_RCC_ADC_CLK_DISABLE();
+
+    /**ADC1 GPIO Configuration
+    PB0     ------> ADC1_IN15
+    PB1     ------> ADC1_IN16
+    */
+    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_0|GPIO_PIN_1);
+
+  /* USER CODE BEGIN ADC1_MspDeInit 1 */
+
+  /* USER CODE END ADC1_MspDeInit 1 */
+  }
+}
+
+/* USER CODE BEGIN 1 */
+
+/* USER CODE END 1 */
diff --git a/Production/ISKBoard_QCTester/Core/Src/can.c b/Production/ISKBoard_QCTester/Core/Src/can.c
new file mode 100644
index 0000000..35d65ce
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Src/can.c
@@ -0,0 +1,293 @@
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file    can.c
+  * @brief   This file provides code for the configuration
+  *          of the CAN instances.
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2023 STMicroelectronics.
+  * All rights reserved.
+  *
+  * This software is licensed under terms that can be found in the LICENSE file
+  * in the root directory of this software component.
+  * If no LICENSE file comes with this software, it is provided AS-IS.
+  *
+  ******************************************************************************
+  */
+/* USER CODE END Header */
+/* Includes ------------------------------------------------------------------*/
+#include "can.h"
+
+/* USER CODE BEGIN 0 */
+#include "comport.h"
+
+//#define CONFIG_CAN_DEBUG
+#ifdef  CONFIG_CAN_DEBUG
+#define can_print(format,args...) printf(format, ##args)
+#else
+#define can_print(format,args...) do{} while(0)
+#endif
+/* USER CODE END 0 */
+
+CAN_HandleTypeDef hcan1;
+
+/* CAN1 init function */
+void MX_CAN1_Init(void)
+{
+
+  /* USER CODE BEGIN CAN1_Init 0 */
+	CAN_FilterTypeDef can1FilterConfig;
+  /* USER CODE END CAN1_Init 0 */
+
+  /* USER CODE BEGIN CAN1_Init 1 */
+
+  /* USER CODE END CAN1_Init 1 */
+  hcan1.Instance = CAN1;
+  hcan1.Init.Prescaler = 10;
+  hcan1.Init.Mode = CAN_MODE_NORMAL;
+  hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
+  hcan1.Init.TimeSeg1 = CAN_BS1_9TQ;
+  hcan1.Init.TimeSeg2 = CAN_BS2_6TQ;
+  hcan1.Init.TimeTriggeredMode = DISABLE;
+  hcan1.Init.AutoBusOff = DISABLE;
+  hcan1.Init.AutoWakeUp = DISABLE;
+  hcan1.Init.AutoRetransmission = DISABLE;
+  hcan1.Init.ReceiveFifoLocked = DISABLE;
+  hcan1.Init.TransmitFifoPriority = DISABLE;
+  if (HAL_CAN_Init(&hcan1) != HAL_OK)
+  {
+    Error_Handler();
+  }
+  /* USER CODE BEGIN CAN1_Init 2 */
+  can1FilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
+  can1FilterConfig.FilterIdHigh = 0x0000;
+  can1FilterConfig.FilterIdLow = 0x0000;
+  can1FilterConfig.FilterMaskIdHigh = 0x0000;
+  can1FilterConfig.FilterMaskIdLow = 0x0000;
+  can1FilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
+  can1FilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;
+  can1FilterConfig.FilterBank = 0;
+  can1FilterConfig.SlaveStartFilterBank = 13;
+  can1FilterConfig.FilterActivation = CAN_FILTER_ENABLE; //使能过滤�????
+  if(HAL_CAN_ConfigFilter(&hcan1, &can1FilterConfig) != HAL_OK)
+  {
+	  Error_Handler();
+  }
+  if(HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
+  {
+	  Error_Handler();
+  }
+  can_init(&hcan1);
+  /* USER CODE END CAN1_Init 2 */
+
+}
+
+void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)
+{
+
+  GPIO_InitTypeDef GPIO_InitStruct = {0};
+  if(canHandle->Instance==CAN1)
+  {
+  /* USER CODE BEGIN CAN1_MspInit 0 */
+
+  /* USER CODE END CAN1_MspInit 0 */
+    /* CAN1 clock enable */
+    __HAL_RCC_CAN1_CLK_ENABLE();
+
+    __HAL_RCC_GPIOB_CLK_ENABLE();
+    /**CAN1 GPIO Configuration
+    PB8     ------> CAN1_RX
+    PB9     ------> CAN1_TX
+    */
+    GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;
+    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+    GPIO_InitStruct.Pull = GPIO_NOPULL;
+    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+    GPIO_InitStruct.Alternate = GPIO_AF9_CAN1;
+    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
+
+    /* CAN1 interrupt Init */
+    HAL_NVIC_SetPriority(CAN1_RX0_IRQn, 1, 0);
+    HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);
+  /* USER CODE BEGIN CAN1_MspInit 1 */
+
+  /* USER CODE END CAN1_MspInit 1 */
+  }
+}
+
+void HAL_CAN_MspDeInit(CAN_HandleTypeDef* canHandle)
+{
+
+  if(canHandle->Instance==CAN1)
+  {
+  /* USER CODE BEGIN CAN1_MspDeInit 0 */
+
+  /* USER CODE END CAN1_MspDeInit 0 */
+    /* Peripheral clock disable */
+    __HAL_RCC_CAN1_CLK_DISABLE();
+
+    /**CAN1 GPIO Configuration
+    PB8     ------> CAN1_RX
+    PB9     ------> CAN1_TX
+    */
+    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_8|GPIO_PIN_9);
+
+    /* CAN1 interrupt Deinit */
+    HAL_NVIC_DisableIRQ(CAN1_RX0_IRQn);
+  /* USER CODE BEGIN CAN1_MspDeInit 1 */
+
+  /* USER CODE END CAN1_MspDeInit 1 */
+  }
+}
+
+/* USER CODE BEGIN 1 */
+can_t can={-1, NULL};
+uint8_t aRxData[COMM_RXBUFSIZE];//不能太小
+
+CAN_RxHeaderTypeDef 		hCAN1_RxHeader; //接收报文定义结构�???
+void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
+{
+	can_t              		*cans = &can;
+	uint8_t 	 			i;
+
+	/* Check can receive ringbuffer */
+	if( cans->fd<0 || !cans->xRxBuffer.buffer || !rb_free_size(&cans->xRxBuffer) )
+	{
+		return;
+	}
+
+	/* Put the receive byte data into Ringbuffer */
+	if(HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &hCAN1_RxHeader, aRxData) == HAL_OK)
+	{
+		can_print("\r\nGet Rx Message Success!!\r\n");
+		can_print("[ID]:0x%lX [DLC]:%ld [Data]:", hCAN1_RxHeader.StdId, hCAN1_RxHeader.DLC);
+
+	    for(i=0; i<hCAN1_RxHeader.DLC; i++)
+	        can_print("%X ", aRxData[i]);
+	    can_print("\r\n");
+	}
+	else
+	{
+		can_print("\r\nGet Rx Message error!!\r\n");
+		return;
+	}
+	rb_write(&cans->xRxBuffer, aRxData, 8);
+}
+
+can_t *can_init(CAN_HandleTypeDef *hcan)
+{
+	can_t              *cans = &can;
+	rb_init(&can.xRxBuffer, aRxData, sizeof(aRxData));
+	// 触发唤醒的字节数,流缓冲区中必须存在不少该字节数的数据才能唤醒等待数据的任务
+	//(设置为0时,将按照如1的方式处理,设置的数比buffer_size还大时,创建无效�???
+
+	if( !cans )
+	{
+		return NULL;
+	}
+
+	can.fd = 3;
+
+	if (HAL_CAN_Start(&hcan1) != HAL_OK)    //�?启CAN
+	{
+		can_print("start can Error_Handler ....\r\n");
+		Error_Handler();
+    }
+
+	return cans;
+}
+
+void can_term(can_t *pcan)
+{
+	if( !pcan )
+		return;
+
+	pcan->fd = -1;
+
+	if( pcan->xRxBuffer.buffer )
+	{
+		rb_clear(&pcan->xRxBuffer);
+	}
+
+	return;
+}
+/*
+ * @brief: CAN Send Message.
+ * @param: "TxData[]" stored the message of ready to send, which length must between 0 and 8.
+ * @param: "length" stored the number of the data (one data is 8 bit) of ready to send.
+ * @retval: Tx_Error: send error; other: the mailbox which has been used, this parameter can be a CAN_TX_MAILBOX0,
+ * 																						   CAN_TX_MAILBOX1,
+ * 																						   CAN_TX_MAILBOX2.
+ */
+
+CAN_TxHeaderTypeDef 	 	Tx_pHeader;     //发�?�报文定义结构体
+int can_send(can_t *pcan, char *data, int bytes)
+{
+	if( !pcan || !data || bytes<=0 )
+	{
+		return -1;
+	}
+
+	//发�?�函数的改写
+	uint32_t TxMailboxNumber = 0x00000000U;    // 存储本次发�?�所使用邮箱的邮箱号
+
+	Tx_pHeader.StdId = 0x122;    /*!标准ID。参数�?�只能是 0 �??? 0x7FF(二进制的11�???1) */
+	Tx_pHeader.ExtId = 0x0000;      /*!扩展ID。参数�?�只能是 0 �??? 0x1FFFFFFF(二进制的29�???1)*/
+	Tx_pHeader.IDE = CAN_ID_STD;    /*!IDE。HAL库编程时,标准帧填写CAN_ID_STD,扩展帧填写CAN_ID_EXT */
+	Tx_pHeader.RTR = CAN_RTR_DATA;  /*!RTR。HAL库编程时,数据帧填写CAN_RTR_DATA,遥控帧填写CAN_RTR_REMOTE */
+	Tx_pHeader.DLC = bytes;    /*!DLC。数据长度,参数值只能是 0 �??? 8 */
+	Tx_pHeader.TransmitGlobalTime = DISABLE;
+
+	//�??? Tx 邮箱中增加一个消�???,并且�???活对应的传输请求
+	if(HAL_CAN_AddTxMessage(&hcan1, &Tx_pHeader, (uint8_t *)data, &TxMailboxNumber) != HAL_OK)
+	{
+		return -1;
+	}
+	return 0;
+}
+
+int can_recv(can_t *pcan, char *buf, int size, uint32_t timeout)
+{
+	uint32_t          tickstart = 0;
+	size_t            bytes = 0;
+
+	if( !pcan || !buf || size<=0 )
+		return -1;
+
+	if( !pcan->xRxBuffer.buffer )
+		return -2;
+
+	tickstart = HAL_GetTick();
+
+//	bytes = rb_data_size(&can->xRxBuffer);
+	can_print("bytes is %d\r\n", bytes);
+
+	while((HAL_GetTick() - tickstart) < timeout)
+	{
+		bytes = rb_data_size(&pcan->xRxBuffer);
+		HAL_Delay(10);
+
+		/* can received data before but not receive in 10ms now, maybe data receive over */
+		if( bytes>0 && rb_data_size(&pcan->xRxBuffer)==bytes )
+		{
+			break;
+		}
+	}
+
+	bytes = rb_data_size( &pcan->xRxBuffer );
+	if( !bytes )
+	{
+		return 0;
+	}
+	bytes = bytes>size ? size : bytes;
+
+	can_print("bytes=%d size=%d\r\n", bytes, size);
+
+	rb_read(&pcan->xRxBuffer, (uint8_t *)buf, bytes);
+
+	return bytes;
+}
+
+/* USER CODE END 1 */
diff --git a/Production/ISKBoard_QCTester/Core/Src/gpio.c b/Production/ISKBoard_QCTester/Core/Src/gpio.c
new file mode 100644
index 0000000..8705f71
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Src/gpio.c
@@ -0,0 +1,130 @@
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file    gpio.c
+  * @brief   This file provides code for the configuration
+  *          of all used GPIO pins.
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2023 STMicroelectronics.
+  * All rights reserved.
+  *
+  * This software is licensed under terms that can be found in the LICENSE file
+  * in the root directory of this software component.
+  * If no LICENSE file comes with this software, it is provided AS-IS.
+  *
+  ******************************************************************************
+  */
+/* USER CODE END Header */
+
+/* Includes ------------------------------------------------------------------*/
+#include "gpio.h"
+
+/* USER CODE BEGIN 0 */
+
+/* USER CODE END 0 */
+
+/*----------------------------------------------------------------------------*/
+/* Configure GPIO                                                             */
+/*----------------------------------------------------------------------------*/
+/* USER CODE BEGIN 1 */
+
+/* USER CODE END 1 */
+
+/** Configure pins as
+        * Analog
+        * Input
+        * Output
+        * EVENT_OUT
+        * EXTI
+*/
+void MX_GPIO_Init(void)
+{
+
+  GPIO_InitTypeDef GPIO_InitStruct = {0};
+
+  /* GPIO Ports Clock Enable */
+  __HAL_RCC_GPIOH_CLK_ENABLE();
+  __HAL_RCC_GPIOC_CLK_ENABLE();
+  __HAL_RCC_GPIOA_CLK_ENABLE();
+  __HAL_RCC_GPIOB_CLK_ENABLE();
+  __HAL_RCC_GPIOD_CLK_ENABLE();
+
+  /*Configure GPIO pin Output Level */
+  HAL_GPIO_WritePin(GPIOC, StripLights_Pin|mikrobus_an_Pin|RS485_Dir_Pin|CAN_STB_Pin
+                          |mikrobus_sck_Pin|mikrobus_miso_Pin|mikrobus_mosi_Pin, GPIO_PIN_RESET);
+
+  /*Configure GPIO pin Output Level */
+  HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);
+
+  /*Configure GPIO pin Output Level */
+  HAL_GPIO_WritePin(GPIOB, LedBlue_Pin|WiFiRst_Pin, GPIO_PIN_SET);
+
+  /*Configure GPIO pin Output Level */
+  HAL_GPIO_WritePin(GPIOB, mikrobus_scl_Pin|mikrobus_sda_Pin|mikrobus_int_Pin|mikrobus_rst_Pin, GPIO_PIN_RESET);
+
+  /*Configure GPIO pin Output Level */
+  HAL_GPIO_WritePin(GPIOC, LedGreen_Pin|LedRed_Pin, GPIO_PIN_SET);
+
+  /*Configure GPIO pin Output Level */
+  HAL_GPIO_WritePin(mikrobus_cs_GPIO_Port, mikrobus_cs_Pin, GPIO_PIN_RESET);
+
+  /*Configure GPIO pin Output Level */
+  HAL_GPIO_WritePin(Relay_GPIO_Port, Relay_Pin, GPIO_PIN_RESET);
+
+  /*Configure GPIO pins : PCPin PCPin PCPin PCPin
+                           PCPin PCPin PCPin PCPin
+                           PCPin */
+  GPIO_InitStruct.Pin = StripLights_Pin|mikrobus_an_Pin|LedGreen_Pin|RS485_Dir_Pin
+                          |CAN_STB_Pin|LedRed_Pin|mikrobus_sck_Pin|mikrobus_miso_Pin
+                          |mikrobus_mosi_Pin;
+  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
+  GPIO_InitStruct.Pull = GPIO_NOPULL;
+  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
+  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
+
+  /*Configure GPIO pin : PtPin */
+  GPIO_InitStruct.Pin = mikrobus_pwm_Pin;
+  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
+  GPIO_InitStruct.Pull = GPIO_NOPULL;
+  HAL_GPIO_Init(mikrobus_pwm_GPIO_Port, &GPIO_InitStruct);
+
+  /*Configure GPIO pins : PAPin PAPin */
+  GPIO_InitStruct.Pin = SPI1_CS_Pin|mikrobus_cs_Pin;
+  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
+  GPIO_InitStruct.Pull = GPIO_NOPULL;
+  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
+  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+
+  /*Configure GPIO pins : PBPin PBPin PBPin PBPin
+                           PBPin PBPin */
+  GPIO_InitStruct.Pin = LedBlue_Pin|mikrobus_scl_Pin|mikrobus_sda_Pin|WiFiRst_Pin
+                          |mikrobus_int_Pin|mikrobus_rst_Pin;
+  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
+  GPIO_InitStruct.Pull = GPIO_NOPULL;
+  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
+  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
+
+  /*Configure GPIO pins : PBPin PBPin PBPin */
+  GPIO_InitStruct.Pin = Key1_Pin|Key2_Pin|Key3_Pin;
+  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
+  GPIO_InitStruct.Pull = GPIO_NOPULL;
+  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
+
+  /*Configure GPIO pin : PtPin */
+  GPIO_InitStruct.Pin = Relay_Pin;
+  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
+  GPIO_InitStruct.Pull = GPIO_NOPULL;
+  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
+  HAL_GPIO_Init(Relay_GPIO_Port, &GPIO_InitStruct);
+
+  /* EXTI interrupt init*/
+  HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0);
+  HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
+
+}
+
+/* USER CODE BEGIN 2 */
+
+/* USER CODE END 2 */
diff --git a/Production/ISKBoard_QCTester/Core/Src/main.c b/Production/ISKBoard_QCTester/Core/Src/main.c
new file mode 100644
index 0000000..d02d1dd
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Src/main.c
@@ -0,0 +1,703 @@
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file           : main.c
+  * @brief          : Main program body
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2023 STMicroelectronics.
+  * All rights reserved.
+  *
+  * This software is licensed under terms that can be found in the LICENSE file
+  * in the root directory of this software component.
+  * If no LICENSE file comes with this software, it is provided AS-IS.
+  *
+  ******************************************************************************
+  */
+/* USER CODE END Header */
+/* Includes ------------------------------------------------------------------*/
+#include "main.h"
+#include "adc.h"
+#include "can.h"
+#include "usart.h"
+#include "spi.h"
+#include "tim.h"
+#include "gpio.h"
+
+/* Private includes ----------------------------------------------------------*/
+/* USER CODE BEGIN Includes */
+#include <string.h>
+#include "miscdev.h"
+#include "ds18b20.h"
+#include "sht20.h"
+#include "isl1208.h"
+#include "ws2812b.h"
+#include "w25q32.h"
+#include "comport.h"
+#include "hal_oled.h"
+#include "esp32.h"
+/* USER CODE END Includes */
+
+/* Private typedef -----------------------------------------------------------*/
+/* USER CODE BEGIN PTD */
+
+/* USER CODE END PTD */
+
+/* Private define ------------------------------------------------------------*/
+/* USER CODE BEGIN PD */
+
+/* USER CODE END PD */
+
+/* Private macro -------------------------------------------------------------*/
+/* USER CODE BEGIN PM */
+
+/* USER CODE END PM */
+
+/* Private variables ---------------------------------------------------------*/
+
+/* USER CODE BEGIN PV */
+
+/* USER CODE END PV */
+
+/* Private function prototypes -----------------------------------------------*/
+void SystemClock_Config(void);
+/* USER CODE BEGIN PFP */
+
+/* USER CODE END PFP */
+
+/* Private user code ---------------------------------------------------------*/
+/* USER CODE BEGIN 0 */
+#define POS_X       5
+#define POS_Y       1
+#define SHOWTIME    800
+#define BLOCK_TIME		0XFFFFFFFF
+
+static inline void oled_show(char *msg)
+{
+	OLED_Init();
+	OLED_ShowString(POS_X, POS_Y, msg, OLED_FONT16);
+	HAL_Delay(SHOWTIME);
+}
+
+
+int test_beep(void);
+int test_relay(void);
+int test_led(void);
+int test_wifi(void);
+int test_adc(void);
+int test_spiflash(void);
+int test_ds18b20(void);
+int test_sht20(void);
+int test_rtc(void);
+int test_usart3(void);
+int test_can(void);
+int test_rs485(void);
+int test_mikrobus(void);
+
+/* USER CODE END 0 */
+
+/**
+  * @brief  The application entry point.
+  * @retval int
+  */
+int main(void)
+{
+  /* USER CODE BEGIN 1 */
+    rtc_time_t       tm = { .tm_year=2023, .tm_mon=4, .tm_mday=2, .tm_hour=6, .tm_min=6, .tm_sec=6 };
+  /* USER CODE END 1 */
+
+  /* MCU Configuration--------------------------------------------------------*/
+
+  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
+  HAL_Init();
+
+  /* USER CODE BEGIN Init */
+
+  /* USER CODE END Init */
+
+  /* Configure the system clock */
+  SystemClock_Config();
+
+  /* USER CODE BEGIN SysInit */
+
+  /* USER CODE END SysInit */
+
+  /* Initialize all configured peripherals */
+  MX_GPIO_Init();
+  MX_USART2_UART_Init();
+  MX_USART3_UART_Init();
+  MX_USART1_UART_Init();
+  MX_LPUART1_UART_Init();
+  MX_ADC1_Init();
+  MX_TIM6_Init();
+  MX_TIM1_Init();
+  MX_SPI1_Init();
+  MX_CAN1_Init();
+  /* USER CODE BEGIN 2 */
+
+  printf("Welcome to ISKBoard v1.0\r\n");
+
+  OLED_Init();
+  ws2812b_init();
+  set_rtc_time(tm);
+
+  test_beep();
+  test_relay();
+
+  /* USER CODE END 2 */
+
+  /* Infinite loop */
+  /* USER CODE BEGIN WHILE */
+  while (1)
+  {
+	  test_mikrobus();
+
+	  test_spiflash();
+	  test_rtc();
+	  test_wifi();
+
+	  test_adc();
+	  test_ds18b20();
+	  test_sht20();
+
+	  test_usart3();
+	  test_can();
+	  test_rs485();
+
+	  test_led();
+    /* USER CODE END WHILE */
+
+    /* USER CODE BEGIN 3 */
+  }
+  /* USER CODE END 3 */
+}
+
+/**
+  * @brief System Clock Configuration
+  * @retval None
+  */
+void SystemClock_Config(void)
+{
+  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
+  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
+
+  /** Configure the main internal regulator output voltage
+  */
+  if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
+  {
+    Error_Handler();
+  }
+
+  /** Initializes the RCC Oscillators according to the specified parameters
+  * in the RCC_OscInitTypeDef structure.
+  */
+  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
+  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
+  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
+  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
+  RCC_OscInitStruct.PLL.PLLM = 1;
+  RCC_OscInitStruct.PLL.PLLN = 20;
+  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
+  RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
+  RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
+  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
+  {
+    Error_Handler();
+  }
+
+  /** Initializes the CPU, AHB and APB buses clocks
+  */
+  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
+                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
+  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
+  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
+  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
+  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
+
+  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
+  {
+    Error_Handler();
+  }
+}
+
+/* USER CODE BEGIN 4 */
+
+int test_beep(void)
+{
+	printf("Buzzer                    [TEST]\r\n");
+	oled_show("Beep [TEST]");
+	beep_start(2, 300);
+
+	printf("\r\n");
+	return 0;
+}
+
+int test_relay(void)
+{
+	printf("Relay                     [TEST]\r\n");
+	oled_show("Relay [TEST]");
+
+	turn_relay(ON);
+	HAL_Delay(800);
+	turn_relay(OFF);
+
+	printf("\r\n");
+	return 0;
+}
+
+#define MIKROBUS_PIN_GROUP      5
+static gpio_t  mikrobus_pins[MIKROBUS_PIN_GROUP][2] =
+{
+		{ {"AN", mikrobus_an_GPIO_Port, mikrobus_an_Pin}, {"RST", mikrobus_rst_GPIO_Port, mikrobus_rst_Pin} },
+		{ {"CS", mikrobus_cs_GPIO_Port, mikrobus_cs_Pin}, {"SCK", mikrobus_sck_GPIO_Port, mikrobus_sck_Pin} },
+		{ {"MISO", mikrobus_miso_GPIO_Port, mikrobus_miso_Pin}, {"MOSI", mikrobus_mosi_GPIO_Port, mikrobus_mosi_Pin} },
+		{ {"PWM", mikrobus_pwm_GPIO_Port, mikrobus_pwm_Pin}, {"INT", mikrobus_int_GPIO_Port, mikrobus_int_Pin} },
+		/* RX3/TX3 initial as comport before, and test as comport but not GPIO mode */
+		{ {"SCL", mikrobus_scl_GPIO_Port, mikrobus_scl_Pin}, {"SDA", mikrobus_sda_GPIO_Port, mikrobus_sda_Pin} },
+};
+
+#define PIN_OUTPUT         1
+#define PIN_INPUT          0
+
+void set_gpio_mode(gpio_t pin, int mode)
+{
+	GPIO_InitTypeDef    GPIO_InitStruct = {0};
+
+	if( PIN_INPUT == mode )
+	{
+		GPIO_InitStruct.Pin = pin.pin;
+		GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
+		GPIO_InitStruct.Pull = GPIO_NOPULL;
+		HAL_GPIO_Init(pin.group, &GPIO_InitStruct);
+	}
+	else
+	{
+		GPIO_InitStruct.Pin = pin.pin;
+		GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
+		GPIO_InitStruct.Pull = GPIO_NOPULL;
+		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
+		HAL_GPIO_Init(pin.group, &GPIO_InitStruct);
+	}
+
+}
+
+int comport_test(UART_HandleTypeDef *huart)
+{
+	comport_t          *comport = NULL;
+	char                buf[32] = { 0 };
+	int                 rv = 0;
+
+	comport = comport_open(huart);
+	if( !comport )
+	{
+		//printf("%s() comport open failed\r\n", __func__);
+		return -2;
+	}
+
+	comport_flush(comport);
+	comport_send(comport, "ISKBoard", strlen("ISKBoard"));
+	comport_recv(comport, buf, sizeof(buf), 50);
+//	printf("%s() receive: %s\r\n", __func__, buf);
+
+	if( strcmp(buf, "ISKBoard") )
+	{
+		rv = -3;
+		goto cleanup;
+	}
+
+cleanup:
+	comport_term(comport);
+	return rv;
+}
+
+int rs485_test(UART_HandleTypeDef *huart)
+{
+	comport_t          *comport_rs485 = NULL;
+	char                buf[32] = { 0 };
+	int                 rv = 0;
+
+	comport_rs485 = comport_open(huart);
+	if( !comport_rs485 )
+	{
+		//printf("%s() comport open failed\r\n", __func__);
+		return -2;
+	}
+
+	comport_flush(comport_rs485);
+	rs485_recv(comport_rs485, buf, sizeof(buf), BLOCK_TIME);
+//	printf("%s() receive: %s\r\n", __func__, buf);
+
+	if( !strstr(buf, "PC2ISKBoard-RS485") )
+	{
+		rv = -3;
+		goto cleanup;
+	}else
+	{
+		rs485_send(comport_rs485, "ISKBoard2PC-RS485", strlen("ISKBoard2PC-RS485"));
+	}
+
+cleanup:
+	rs485_term(comport_rs485);
+	return rv;
+}
+
+int can_test(can_t *pcan)
+{
+	char                rx_data_buf[8] = { 0 };
+	char 				test_data_buf[8] = {0x01, 0x02, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
+	int                 rv = 0;
+
+	if( !pcan )
+	{
+		//printf("%s() CAN open failed\r\n", __func__);
+		return -2;
+	}
+
+	if(can_recv(pcan, rx_data_buf, sizeof(rx_data_buf), BLOCK_TIME) > 0)
+	{
+#if 0	//echo CAN recv 8bytes data
+		printf("CAN RX DATA:");
+		for(int i=0; i<sizeof(rx_data_buf); i++)
+		{
+			printf("0x%x ", rx_data_buf[i]);
+		}
+		printf("\r\n");
+#endif
+		if( !memcmp(test_data_buf, rx_data_buf, sizeof(test_data_buf)) )
+		{
+			can_send(pcan, test_data_buf, sizeof(test_data_buf));
+		}else
+		{
+			rv = -3;
+			goto cleanup;
+		}
+	}else
+	{
+		rv = -4;
+		goto cleanup;
+	}
+
+cleanup:
+	return rv;
+}
+
+int test_mikrobus(void)
+{
+	int                 i = 0, rv=0;
+	int                 failed = 0;
+
+
+	for(i=0; i<MIKROBUS_PIN_GROUP; i++)
+	{
+		/* Set pin as output/input mode */
+		set_gpio_mode(mikrobus_pins[i][0], PIN_OUTPUT);
+		set_gpio_mode(mikrobus_pins[i][1], PIN_INPUT);
+
+		HAL_GPIO_WritePin(mikrobus_pins[i][0].group, mikrobus_pins[i][0].pin, GPIO_PIN_SET);
+		if( GPIO_PIN_SET != HAL_GPIO_ReadPin(mikrobus_pins[i][1].group, mikrobus_pins[i][1].pin) )
+		{
+			failed = 1;
+			break;
+		}
+
+		HAL_GPIO_WritePin(mikrobus_pins[i][0].group, mikrobus_pins[i][0].pin, GPIO_PIN_RESET);
+		if( GPIO_PIN_RESET != HAL_GPIO_ReadPin(mikrobus_pins[i][1].group, mikrobus_pins[i][1].pin) )
+		{
+			failed = 1;
+			break;
+		}
+
+		/* Set pin as output mode */
+		set_gpio_mode(mikrobus_pins[i][1], PIN_OUTPUT);
+		set_gpio_mode(mikrobus_pins[i][0], PIN_INPUT);
+
+		HAL_GPIO_WritePin(mikrobus_pins[i][1].group, mikrobus_pins[i][1].pin, GPIO_PIN_SET);
+		if( GPIO_PIN_SET != HAL_GPIO_ReadPin(mikrobus_pins[i][0].group, mikrobus_pins[i][0].pin) )
+		{
+			failed = 1;
+			break;
+		}
+
+		HAL_GPIO_WritePin(mikrobus_pins[i][1].group, mikrobus_pins[i][1].pin, GPIO_PIN_RESET);
+		if( GPIO_PIN_RESET != HAL_GPIO_ReadPin(mikrobus_pins[i][0].group, mikrobus_pins[i][0].pin) )
+		{
+			failed = 1;
+			break;
+		}
+		printf("MikroBUS %4s<->%4s test [OKAY]\r\n", mikrobus_pins[i][0].name, mikrobus_pins[i][1].name);
+	}
+
+	if( failed )
+	{
+		printf("MikroBUS %4s<->%4s test [FAIL]\r\n", mikrobus_pins[i][0].name, mikrobus_pins[i][1].name);
+		rv = -1;
+		goto cleanup;
+	}
+
+	if( comport_test(&huart3) < 0 )
+		printf("MikroBUS UART test        [FAIL]\r\n");
+	else
+		printf("MikroBUS UART test        [OKAY]\r\n");
+
+cleanup:
+	if( rv )
+		oled_show("MikroBUS [FAIL]");
+	else
+		oled_show("MikroBUS [OK]");
+
+	printf("\r\n");
+	return 0;
+}
+
+int test_led(void)
+{
+	oled_show("LED [TEST]");
+	printf("RGB Led                   [TEST]\r\n");
+
+	blink_led(LedRed, 200);
+	blink_led(LedGreen, 200);
+	blink_led(LedBlue, 200);
+
+	printf("Strip Lights              [TEST]\r\n");
+	ws2812b_blink();
+
+	printf("\r\n");
+	return 0;
+}
+
+int test_spiflash(void)
+{
+	int              rv;
+	char             oledstr[32] = {0};
+
+	rv = w25q_Init();
+	printf("SPI Flash W25Q32     test [%s]\r\n", rv?"FAIL":"OKAY");
+	snprintf(oledstr, sizeof(oledstr), "W25Q: %s", rv?"FAIL":"OK");
+	oled_show(oledstr);
+
+	printf("\r\n");
+	return 0;
+}
+
+
+int test_wifi(void)
+{
+	static int         init = 0;
+	static comport_t  *comport = NULL;
+	int                rv;
+	char               oledstr[32] = {0};
+	char               version[256] = {0};
+
+	if( !init )
+	{
+		comport = comport_open(&huart2);
+		if( !comport )
+			return -1;
+
+		esp32_hwreset(comport);
+
+		esp32_set_echo(comport, DISABLE);
+
+	    rv = esp32_get_firmware(comport, version, sizeof(version));
+	    if( rv < 0)
+	    {
+	        printf("Query ESP32 firmware version failed: %d\r\n", rv);
+	        return rv;
+	    }
+
+	    printf("ESP32 firmware version:\r\n%s\r\n", version);
+	    init = 1;
+	}
+
+	rv = send_atcmd_check_ok(comport, "AT", 500);
+
+	printf("WiFi module AT test       [%s]\r\n", rv?"FAIL":"OKAY");
+	snprintf(oledstr, sizeof(oledstr), "WiFi: %s", rv?"FAIL":"OK");
+	oled_show(oledstr);
+
+	printf("\r\n");
+	return 0;
+}
+
+int test_adc(void)
+{
+	char             oledstr[32] = {0};
+    uint32_t         lux, noisy;
+
+  	adc_sample_lux_noisy(&lux, &noisy);
+  	printf("Lux: %lu Noisy: %lu\r\n", lux, noisy);
+
+  	printf("ADC(Lux/Noisy) test       [OKAY]\r\n");
+  	snprintf(oledstr, sizeof(oledstr), "lux:%lu db:%lu", lux, noisy);
+  	oled_show(oledstr);
+
+  	printf("\r\n");
+  	return 0;
+}
+
+int test_ds18b20(void)
+{
+	int              rv;
+    float            temperature_f = 0.0;
+	char             oledstr[32] = {0};
+
+    rv = ds18b20_sample(&temperature_f);
+    if( rv < 0 )
+    {
+    	printf("DS18B20 test              [FAIL]\r\n");
+      	oled_show("DS18B20: [FAIL]");
+    	return -1;
+    }
+
+    printf("DS18B20 sample temperature: %.3f\'C\r\n", temperature_f);
+    printf("DS18B20 test              [OKAY]\r\n");
+
+  	snprintf(oledstr, sizeof(oledstr), "DS18B20: %.3f", temperature_f);
+  	oled_show(oledstr);
+
+  	printf("\r\n");
+  	return 0;
+}
+
+int test_sht20(void)
+{
+    uint32_t         TrH, temperature, humdity;
+	char             oledstr[32] = {0};
+
+	TrH = sht20_sample_TrH(&temperature, &humdity);
+	if( TRH_FAIL_VAL == TrH)
+	{
+		printf("SHT20 test                [FAIL]\r\n");
+      	oled_show("SHT20: [FAIL]");
+    	return -1;
+
+	}
+
+	printf("SHT20 sample temperature: %lu.%02lu\'C humidity: %lu.%02lu%%\r\n",
+			temperature/100, temperature%100, humdity/100, humdity%100);
+
+	printf("SHT20 test                [OKAY]\r\n");
+
+	snprintf(oledstr, sizeof(oledstr), "SHT20: %lu.%02lu\'C", temperature/100, temperature%100);
+  	oled_show(oledstr);
+
+	snprintf(oledstr, sizeof(oledstr), "SHT20: %lu.%02lu%%", humdity/100, humdity%100);
+  	oled_show(oledstr);
+
+  	printf("\r\n");
+  	return 0;
+}
+
+int test_rtc(void)
+{
+	char             oledstr[32] = {0};
+	rtc_time_t       tm;
+
+	if( get_rtc_time(&tm) < 0 )
+		return -1;
+
+	printf("RTC: %04d-%02d-%02d %02d:%02d:%02d %s\r\n", tm.tm_year, tm.tm_mon, tm.tm_mday,
+		tm.tm_hour, tm.tm_min, tm.tm_sec, weekday[tm.tm_wday]);
+
+	printf("RTC ISL1208 test          [OKAY]\r\n");
+
+	snprintf(oledstr, sizeof(oledstr), "RTC: %04d-%02d-%02d", tm.tm_year, tm.tm_mon, tm.tm_mday);
+  	oled_show(oledstr);
+
+	snprintf(oledstr, sizeof(oledstr), "RTC: %02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec);
+  	oled_show(oledstr);
+
+  	printf("\r\n");
+	return 0;
+}
+
+int test_usart3(void)
+{
+	printf("Please switch USART3 to RS232.\r\n");
+
+	if( comport_test(&huart3) < 0 )
+	{
+		printf("USART3 RS232 test         [FAIL]\r\n");
+		oled_show("RS232  [FAIL]");
+	}
+	else
+	{
+		printf("USART3 RS232 test         [OKAY]\r\n");
+		oled_show("RS232  [OK]");
+	}
+
+  	printf("\r\n");
+  	return 0;
+}
+
+int test_can(void)
+{
+	printf("Please send {0x01, 0x02, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F} CAN frame data in PC.\r\n");
+	if( can_test(&can) < 0 )
+	{
+		printf("CAN test                  [FAIL]\r\n");
+		oled_show("CAN  [FAIL]");
+	}
+	else
+	{
+		printf("CAN test                  [OKAY]\r\n");
+		oled_show("CAN  [OK]");
+	}
+
+  	printf("\r\n");
+  	return 0;
+}
+
+int test_rs485(void)
+{
+	printf("Please send {PC2ISKBoard-RS485} rs485 data in PC.\r\n");
+	if( rs485_test(&hlpuart1) < 0 )
+	{
+		printf("RS485 test                [FAIL]\r\n");
+		oled_show("RS485  [FAIL]");
+	}
+	else
+	{
+		printf("RS485 test                [OKAY]\r\n");
+		oled_show("RS485  [OK]");
+	}
+
+  	printf("\r\n");
+  	return 0;
+}
+
+
+/* USER CODE END 4 */
+
+/**
+  * @brief  This function is executed in case of error occurrence.
+  * @retval None
+  */
+void Error_Handler(void)
+{
+  /* USER CODE BEGIN Error_Handler_Debug */
+  /* User can add his own implementation to report the HAL error return state */
+  __disable_irq();
+  while (1)
+  {
+  }
+  /* USER CODE END Error_Handler_Debug */
+}
+
+#ifdef  USE_FULL_ASSERT
+/**
+  * @brief  Reports the name of the source file and the source line number
+  *         where the assert_param error has occurred.
+  * @param  file: pointer to the source file name
+  * @param  line: assert_param error line source number
+  * @retval None
+  */
+void assert_failed(uint8_t *file, uint32_t line)
+{
+  /* USER CODE BEGIN 6 */
+  /* User can add his own implementation to report the file name and line number,
+     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
+  /* USER CODE END 6 */
+}
+#endif /* USE_FULL_ASSERT */
diff --git a/Production/ISKBoard_QCTester/Core/Src/spi.c b/Production/ISKBoard_QCTester/Core/Src/spi.c
new file mode 100644
index 0000000..e9b654d
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Src/spi.c
@@ -0,0 +1,121 @@
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file    spi.c
+  * @brief   This file provides code for the configuration
+  *          of the SPI instances.
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2023 STMicroelectronics.
+  * All rights reserved.
+  *
+  * This software is licensed under terms that can be found in the LICENSE file
+  * in the root directory of this software component.
+  * If no LICENSE file comes with this software, it is provided AS-IS.
+  *
+  ******************************************************************************
+  */
+/* USER CODE END Header */
+/* Includes ------------------------------------------------------------------*/
+#include "spi.h"
+
+/* USER CODE BEGIN 0 */
+
+/* USER CODE END 0 */
+
+SPI_HandleTypeDef hspi1;
+
+/* SPI1 init function */
+void MX_SPI1_Init(void)
+{
+
+  /* USER CODE BEGIN SPI1_Init 0 */
+
+  /* USER CODE END SPI1_Init 0 */
+
+  /* USER CODE BEGIN SPI1_Init 1 */
+
+  /* USER CODE END SPI1_Init 1 */
+  hspi1.Instance = SPI1;
+  hspi1.Init.Mode = SPI_MODE_MASTER;
+  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
+  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
+  hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
+  hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
+  hspi1.Init.NSS = SPI_NSS_SOFT;
+  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
+  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
+  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
+  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
+  hspi1.Init.CRCPolynomial = 7;
+  hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
+  hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
+  if (HAL_SPI_Init(&hspi1) != HAL_OK)
+  {
+    Error_Handler();
+  }
+  /* USER CODE BEGIN SPI1_Init 2 */
+
+  /* USER CODE END SPI1_Init 2 */
+
+}
+
+void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
+{
+
+  GPIO_InitTypeDef GPIO_InitStruct = {0};
+  if(spiHandle->Instance==SPI1)
+  {
+  /* USER CODE BEGIN SPI1_MspInit 0 */
+
+  /* USER CODE END SPI1_MspInit 0 */
+    /* SPI1 clock enable */
+    __HAL_RCC_SPI1_CLK_ENABLE();
+
+    __HAL_RCC_GPIOA_CLK_ENABLE();
+    /**SPI1 GPIO Configuration
+    PA5     ------> SPI1_SCK
+    PA6     ------> SPI1_MISO
+    PA7     ------> SPI1_MOSI
+    */
+    GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
+    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+    GPIO_InitStruct.Pull = GPIO_NOPULL;
+    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+    GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
+    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+
+  /* USER CODE BEGIN SPI1_MspInit 1 */
+
+  /* USER CODE END SPI1_MspInit 1 */
+  }
+}
+
+void HAL_SPI_MspDeInit(SPI_HandleTypeDef* spiHandle)
+{
+
+  if(spiHandle->Instance==SPI1)
+  {
+  /* USER CODE BEGIN SPI1_MspDeInit 0 */
+
+  /* USER CODE END SPI1_MspDeInit 0 */
+    /* Peripheral clock disable */
+    __HAL_RCC_SPI1_CLK_DISABLE();
+
+    /**SPI1 GPIO Configuration
+    PA5     ------> SPI1_SCK
+    PA6     ------> SPI1_MISO
+    PA7     ------> SPI1_MOSI
+    */
+    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7);
+
+  /* USER CODE BEGIN SPI1_MspDeInit 1 */
+
+  /* USER CODE END SPI1_MspDeInit 1 */
+  }
+}
+
+/* USER CODE BEGIN 1 */
+
+/* USER CODE END 1 */
diff --git a/Production/ISKBoard_QCTester/Core/Src/stm32l4xx_hal_msp.c b/Production/ISKBoard_QCTester/Core/Src/stm32l4xx_hal_msp.c
new file mode 100644
index 0000000..e4be098
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Src/stm32l4xx_hal_msp.c
@@ -0,0 +1,81 @@
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file         stm32l4xx_hal_msp.c
+  * @brief        This file provides code for the MSP Initialization
+  *               and de-Initialization codes.
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2023 STMicroelectronics.
+  * All rights reserved.
+  *
+  * This software is licensed under terms that can be found in the LICENSE file
+  * in the root directory of this software component.
+  * If no LICENSE file comes with this software, it is provided AS-IS.
+  *
+  ******************************************************************************
+  */
+/* USER CODE END Header */
+
+/* Includes ------------------------------------------------------------------*/
+#include "main.h"
+/* USER CODE BEGIN Includes */
+
+/* USER CODE END Includes */
+
+/* Private typedef -----------------------------------------------------------*/
+/* USER CODE BEGIN TD */
+
+/* USER CODE END TD */
+
+/* Private define ------------------------------------------------------------*/
+/* USER CODE BEGIN Define */
+
+/* USER CODE END Define */
+
+/* Private macro -------------------------------------------------------------*/
+/* USER CODE BEGIN Macro */
+
+/* USER CODE END Macro */
+
+/* Private variables ---------------------------------------------------------*/
+/* USER CODE BEGIN PV */
+
+/* USER CODE END PV */
+
+/* Private function prototypes -----------------------------------------------*/
+/* USER CODE BEGIN PFP */
+
+/* USER CODE END PFP */
+
+/* External functions --------------------------------------------------------*/
+/* USER CODE BEGIN ExternalFunctions */
+
+/* USER CODE END ExternalFunctions */
+
+/* USER CODE BEGIN 0 */
+
+/* USER CODE END 0 */
+/**
+  * Initializes the Global MSP.
+  */
+void HAL_MspInit(void)
+{
+  /* USER CODE BEGIN MspInit 0 */
+
+  /* USER CODE END MspInit 0 */
+
+  __HAL_RCC_SYSCFG_CLK_ENABLE();
+  __HAL_RCC_PWR_CLK_ENABLE();
+
+  /* System interrupt init*/
+
+  /* USER CODE BEGIN MspInit 1 */
+
+  /* USER CODE END MspInit 1 */
+}
+
+/* USER CODE BEGIN 1 */
+
+/* USER CODE END 1 */
diff --git a/Production/ISKBoard_QCTester/Core/Src/stm32l4xx_it.c b/Production/ISKBoard_QCTester/Core/Src/stm32l4xx_it.c
new file mode 100644
index 0000000..b63788d
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Src/stm32l4xx_it.c
@@ -0,0 +1,293 @@
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file    stm32l4xx_it.c
+  * @brief   Interrupt Service Routines.
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2023 STMicroelectronics.
+  * All rights reserved.
+  *
+  * This software is licensed under terms that can be found in the LICENSE file
+  * in the root directory of this software component.
+  * If no LICENSE file comes with this software, it is provided AS-IS.
+  *
+  ******************************************************************************
+  */
+/* USER CODE END Header */
+
+/* Includes ------------------------------------------------------------------*/
+#include "main.h"
+#include "stm32l4xx_it.h"
+/* Private includes ----------------------------------------------------------*/
+/* USER CODE BEGIN Includes */
+/* USER CODE END Includes */
+
+/* Private typedef -----------------------------------------------------------*/
+/* USER CODE BEGIN TD */
+
+/* USER CODE END TD */
+
+/* Private define ------------------------------------------------------------*/
+/* USER CODE BEGIN PD */
+
+/* USER CODE END PD */
+
+/* Private macro -------------------------------------------------------------*/
+/* USER CODE BEGIN PM */
+
+/* USER CODE END PM */
+
+/* Private variables ---------------------------------------------------------*/
+/* USER CODE BEGIN PV */
+
+/* USER CODE END PV */
+
+/* Private function prototypes -----------------------------------------------*/
+/* USER CODE BEGIN PFP */
+
+/* USER CODE END PFP */
+
+/* Private user code ---------------------------------------------------------*/
+/* USER CODE BEGIN 0 */
+
+/* USER CODE END 0 */
+
+/* External variables --------------------------------------------------------*/
+extern CAN_HandleTypeDef hcan1;
+extern UART_HandleTypeDef hlpuart1;
+extern UART_HandleTypeDef huart1;
+extern UART_HandleTypeDef huart2;
+extern UART_HandleTypeDef huart3;
+/* USER CODE BEGIN EV */
+
+/* USER CODE END EV */
+
+/******************************************************************************/
+/*           Cortex-M4 Processor Interruption and Exception Handlers          */
+/******************************************************************************/
+/**
+  * @brief This function handles Non maskable interrupt.
+  */
+void NMI_Handler(void)
+{
+  /* USER CODE BEGIN NonMaskableInt_IRQn 0 */
+
+  /* USER CODE END NonMaskableInt_IRQn 0 */
+  /* USER CODE BEGIN NonMaskableInt_IRQn 1 */
+  while (1)
+  {
+  }
+  /* USER CODE END NonMaskableInt_IRQn 1 */
+}
+
+/**
+  * @brief This function handles Hard fault interrupt.
+  */
+void HardFault_Handler(void)
+{
+  /* USER CODE BEGIN HardFault_IRQn 0 */
+
+  /* USER CODE END HardFault_IRQn 0 */
+  while (1)
+  {
+    /* USER CODE BEGIN W1_HardFault_IRQn 0 */
+    /* USER CODE END W1_HardFault_IRQn 0 */
+  }
+}
+
+/**
+  * @brief This function handles Memory management fault.
+  */
+void MemManage_Handler(void)
+{
+  /* USER CODE BEGIN MemoryManagement_IRQn 0 */
+
+  /* USER CODE END MemoryManagement_IRQn 0 */
+  while (1)
+  {
+    /* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
+    /* USER CODE END W1_MemoryManagement_IRQn 0 */
+  }
+}
+
+/**
+  * @brief This function handles Prefetch fault, memory access fault.
+  */
+void BusFault_Handler(void)
+{
+  /* USER CODE BEGIN BusFault_IRQn 0 */
+
+  /* USER CODE END BusFault_IRQn 0 */
+  while (1)
+  {
+    /* USER CODE BEGIN W1_BusFault_IRQn 0 */
+    /* USER CODE END W1_BusFault_IRQn 0 */
+  }
+}
+
+/**
+  * @brief This function handles Undefined instruction or illegal state.
+  */
+void UsageFault_Handler(void)
+{
+  /* USER CODE BEGIN UsageFault_IRQn 0 */
+
+  /* USER CODE END UsageFault_IRQn 0 */
+  while (1)
+  {
+    /* USER CODE BEGIN W1_UsageFault_IRQn 0 */
+    /* USER CODE END W1_UsageFault_IRQn 0 */
+  }
+}
+
+/**
+  * @brief This function handles System service call via SWI instruction.
+  */
+void SVC_Handler(void)
+{
+  /* USER CODE BEGIN SVCall_IRQn 0 */
+
+  /* USER CODE END SVCall_IRQn 0 */
+  /* USER CODE BEGIN SVCall_IRQn 1 */
+
+  /* USER CODE END SVCall_IRQn 1 */
+}
+
+/**
+  * @brief This function handles Debug monitor.
+  */
+void DebugMon_Handler(void)
+{
+  /* USER CODE BEGIN DebugMonitor_IRQn 0 */
+
+  /* USER CODE END DebugMonitor_IRQn 0 */
+  /* USER CODE BEGIN DebugMonitor_IRQn 1 */
+
+  /* USER CODE END DebugMonitor_IRQn 1 */
+}
+
+/**
+  * @brief This function handles Pendable request for system service.
+  */
+void PendSV_Handler(void)
+{
+  /* USER CODE BEGIN PendSV_IRQn 0 */
+
+  /* USER CODE END PendSV_IRQn 0 */
+  /* USER CODE BEGIN PendSV_IRQn 1 */
+
+  /* USER CODE END PendSV_IRQn 1 */
+}
+
+/**
+  * @brief This function handles System tick timer.
+  */
+void SysTick_Handler(void)
+{
+  /* USER CODE BEGIN SysTick_IRQn 0 */
+
+  /* USER CODE END SysTick_IRQn 0 */
+  HAL_IncTick();
+  /* USER CODE BEGIN SysTick_IRQn 1 */
+
+  /* USER CODE END SysTick_IRQn 1 */
+}
+
+/******************************************************************************/
+/* STM32L4xx Peripheral Interrupt Handlers                                    */
+/* Add here the Interrupt Handlers for the used peripherals.                  */
+/* For the available peripheral interrupt handler names,                      */
+/* please refer to the startup file (startup_stm32l4xx.s).                    */
+/******************************************************************************/
+
+/**
+  * @brief This function handles CAN1 RX0 interrupt.
+  */
+void CAN1_RX0_IRQHandler(void)
+{
+  /* USER CODE BEGIN CAN1_RX0_IRQn 0 */
+
+  /* USER CODE END CAN1_RX0_IRQn 0 */
+  HAL_CAN_IRQHandler(&hcan1);
+  /* USER CODE BEGIN CAN1_RX0_IRQn 1 */
+
+  /* USER CODE END CAN1_RX0_IRQn 1 */
+}
+
+/**
+  * @brief This function handles USART1 global interrupt.
+  */
+void USART1_IRQHandler(void)
+{
+  /* USER CODE BEGIN USART1_IRQn 0 */
+
+  /* USER CODE END USART1_IRQn 0 */
+  HAL_UART_IRQHandler(&huart1);
+  /* USER CODE BEGIN USART1_IRQn 1 */
+
+  /* USER CODE END USART1_IRQn 1 */
+}
+
+/**
+  * @brief This function handles USART2 global interrupt.
+  */
+void USART2_IRQHandler(void)
+{
+  /* USER CODE BEGIN USART2_IRQn 0 */
+
+  /* USER CODE END USART2_IRQn 0 */
+  HAL_UART_IRQHandler(&huart2);
+  /* USER CODE BEGIN USART2_IRQn 1 */
+
+  /* USER CODE END USART2_IRQn 1 */
+}
+
+/**
+  * @brief This function handles USART3 global interrupt.
+  */
+void USART3_IRQHandler(void)
+{
+  /* USER CODE BEGIN USART3_IRQn 0 */
+
+  /* USER CODE END USART3_IRQn 0 */
+  HAL_UART_IRQHandler(&huart3);
+  /* USER CODE BEGIN USART3_IRQn 1 */
+
+  /* USER CODE END USART3_IRQn 1 */
+}
+
+/**
+  * @brief This function handles EXTI line[15:10] interrupts.
+  */
+void EXTI15_10_IRQHandler(void)
+{
+  /* USER CODE BEGIN EXTI15_10_IRQn 0 */
+
+  /* USER CODE END EXTI15_10_IRQn 0 */
+  HAL_GPIO_EXTI_IRQHandler(Key1_Pin);
+  HAL_GPIO_EXTI_IRQHandler(Key2_Pin);
+  HAL_GPIO_EXTI_IRQHandler(Key3_Pin);
+  /* USER CODE BEGIN EXTI15_10_IRQn 1 */
+
+  /* USER CODE END EXTI15_10_IRQn 1 */
+}
+
+/**
+  * @brief This function handles LPUART1 global interrupt.
+  */
+void LPUART1_IRQHandler(void)
+{
+  /* USER CODE BEGIN LPUART1_IRQn 0 */
+
+  /* USER CODE END LPUART1_IRQn 0 */
+  HAL_UART_IRQHandler(&hlpuart1);
+  /* USER CODE BEGIN LPUART1_IRQn 1 */
+
+  /* USER CODE END LPUART1_IRQn 1 */
+}
+
+/* USER CODE BEGIN 1 */
+
+/* USER CODE END 1 */
diff --git a/Production/ISKBoard_QCTester/Core/Src/syscalls.c b/Production/ISKBoard_QCTester/Core/Src/syscalls.c
new file mode 100644
index 0000000..f4278b7
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Src/syscalls.c
@@ -0,0 +1,176 @@
+/**
+ ******************************************************************************
+ * @file      syscalls.c
+ * @author    Auto-generated by STM32CubeIDE
+ * @brief     STM32CubeIDE Minimal System calls file
+ *
+ *            For more information about which c-functions
+ *            need which of these lowlevel functions
+ *            please consult the Newlib libc-manual
+ ******************************************************************************
+ * @attention
+ *
+ * Copyright (c) 2020-2022 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software is licensed under terms that can be found in the LICENSE file
+ * in the root directory of this software component.
+ * If no LICENSE file comes with this software, it is provided AS-IS.
+ *
+ ******************************************************************************
+ */
+
+/* Includes */
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/times.h>
+
+
+/* Variables */
+extern int __io_putchar(int ch) __attribute__((weak));
+extern int __io_getchar(void) __attribute__((weak));
+
+
+char *__env[1] = { 0 };
+char **environ = __env;
+
+
+/* Functions */
+void initialise_monitor_handles()
+{
+}
+
+int _getpid(void)
+{
+  return 1;
+}
+
+int _kill(int pid, int sig)
+{
+  (void)pid;
+  (void)sig;
+  errno = EINVAL;
+  return -1;
+}
+
+void _exit (int status)
+{
+  _kill(status, -1);
+  while (1) {}    /* Make sure we hang here */
+}
+
+__attribute__((weak)) int _read(int file, char *ptr, int len)
+{
+  (void)file;
+  int DataIdx;
+
+  for (DataIdx = 0; DataIdx < len; DataIdx++)
+  {
+    *ptr++ = __io_getchar();
+  }
+
+  return len;
+}
+
+__attribute__((weak)) int _write(int file, char *ptr, int len)
+{
+  (void)file;
+  int DataIdx;
+
+  for (DataIdx = 0; DataIdx < len; DataIdx++)
+  {
+    __io_putchar(*ptr++);
+  }
+  return len;
+}
+
+int _close(int file)
+{
+  (void)file;
+  return -1;
+}
+
+
+int _fstat(int file, struct stat *st)
+{
+  (void)file;
+  st->st_mode = S_IFCHR;
+  return 0;
+}
+
+int _isatty(int file)
+{
+  (void)file;
+  return 1;
+}
+
+int _lseek(int file, int ptr, int dir)
+{
+  (void)file;
+  (void)ptr;
+  (void)dir;
+  return 0;
+}
+
+int _open(char *path, int flags, ...)
+{
+  (void)path;
+  (void)flags;
+  /* Pretend like we always fail */
+  return -1;
+}
+
+int _wait(int *status)
+{
+  (void)status;
+  errno = ECHILD;
+  return -1;
+}
+
+int _unlink(char *name)
+{
+  (void)name;
+  errno = ENOENT;
+  return -1;
+}
+
+int _times(struct tms *buf)
+{
+  (void)buf;
+  return -1;
+}
+
+int _stat(char *file, struct stat *st)
+{
+  (void)file;
+  st->st_mode = S_IFCHR;
+  return 0;
+}
+
+int _link(char *old, char *new)
+{
+  (void)old;
+  (void)new;
+  errno = EMLINK;
+  return -1;
+}
+
+int _fork(void)
+{
+  errno = EAGAIN;
+  return -1;
+}
+
+int _execve(char *name, char **argv, char **env)
+{
+  (void)name;
+  (void)argv;
+  (void)env;
+  errno = ENOMEM;
+  return -1;
+}
diff --git a/Production/ISKBoard_QCTester/Core/Src/sysmem.c b/Production/ISKBoard_QCTester/Core/Src/sysmem.c
new file mode 100644
index 0000000..54081ac
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Src/sysmem.c
@@ -0,0 +1,79 @@
+/**
+ ******************************************************************************
+ * @file      sysmem.c
+ * @author    Generated by STM32CubeIDE
+ * @brief     STM32CubeIDE System Memory calls file
+ *
+ *            For more information about which C functions
+ *            need which of these lowlevel functions
+ *            please consult the newlib libc manual
+ ******************************************************************************
+ * @attention
+ *
+ * Copyright (c) 2022 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software is licensed under terms that can be found in the LICENSE file
+ * in the root directory of this software component.
+ * If no LICENSE file comes with this software, it is provided AS-IS.
+ *
+ ******************************************************************************
+ */
+
+/* Includes */
+#include <errno.h>
+#include <stdint.h>
+
+/**
+ * Pointer to the current high watermark of the heap usage
+ */
+static uint8_t *__sbrk_heap_end = NULL;
+
+/**
+ * @brief _sbrk() allocates memory to the newlib heap and is used by malloc
+ *        and others from the C library
+ *
+ * @verbatim
+ * ############################################################################
+ * #  .data  #  .bss  #       newlib heap       #          MSP stack          #
+ * #         #        #                         # Reserved by _Min_Stack_Size #
+ * ############################################################################
+ * ^-- RAM start      ^-- _end                             _estack, RAM end --^
+ * @endverbatim
+ *
+ * This implementation starts allocating at the '_end' linker symbol
+ * The '_Min_Stack_Size' linker symbol reserves a memory for the MSP stack
+ * The implementation considers '_estack' linker symbol to be RAM end
+ * NOTE: If the MSP stack, at any point during execution, grows larger than the
+ * reserved size, please increase the '_Min_Stack_Size'.
+ *
+ * @param incr Memory size
+ * @return Pointer to allocated memory
+ */
+void *_sbrk(ptrdiff_t incr)
+{
+  extern uint8_t _end; /* Symbol defined in the linker script */
+  extern uint8_t _estack; /* Symbol defined in the linker script */
+  extern uint32_t _Min_Stack_Size; /* Symbol defined in the linker script */
+  const uint32_t stack_limit = (uint32_t)&_estack - (uint32_t)&_Min_Stack_Size;
+  const uint8_t *max_heap = (uint8_t *)stack_limit;
+  uint8_t *prev_heap_end;
+
+  /* Initialize heap end at first call */
+  if (NULL == __sbrk_heap_end)
+  {
+    __sbrk_heap_end = &_end;
+  }
+
+  /* Protect heap from growing into the reserved MSP stack */
+  if (__sbrk_heap_end + incr > max_heap)
+  {
+    errno = ENOMEM;
+    return (void *)-1;
+  }
+
+  prev_heap_end = __sbrk_heap_end;
+  __sbrk_heap_end += incr;
+
+  return (void *)prev_heap_end;
+}
diff --git a/Production/ISKBoard_QCTester/Core/Src/system_stm32l4xx.c b/Production/ISKBoard_QCTester/Core/Src/system_stm32l4xx.c
new file mode 100644
index 0000000..be9cfee
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Src/system_stm32l4xx.c
@@ -0,0 +1,332 @@
+/**
+  ******************************************************************************
+  * @file    system_stm32l4xx.c
+  * @author  MCD Application Team
+  * @brief   CMSIS Cortex-M4 Device Peripheral Access Layer System Source File
+  *
+  *   This file provides two functions and one global variable to be called from
+  *   user application:
+  *      - SystemInit(): This function is called at startup just after reset and
+  *                      before branch to main program. This call is made inside
+  *                      the "startup_stm32l4xx.s" file.
+  *
+  *      - SystemCoreClock variable: Contains the core clock (HCLK), it can be used
+  *                                  by the user application to setup the SysTick
+  *                                  timer or configure other parameters.
+  *
+  *      - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must
+  *                                 be called whenever the core clock is changed
+  *                                 during program execution.
+  *
+  *   After each device reset the MSI (4 MHz) is used as system clock source.
+  *   Then SystemInit() function is called, in "startup_stm32l4xx.s" file, to
+  *   configure the system clock before to branch to main program.
+  *
+  *   This file configures the system clock as follows:
+  *=============================================================================
+  *-----------------------------------------------------------------------------
+  *        System Clock source                    | MSI
+  *-----------------------------------------------------------------------------
+  *        SYSCLK(Hz)                             | 4000000
+  *-----------------------------------------------------------------------------
+  *        HCLK(Hz)                               | 4000000
+  *-----------------------------------------------------------------------------
+  *        AHB Prescaler                          | 1
+  *-----------------------------------------------------------------------------
+  *        APB1 Prescaler                         | 1
+  *-----------------------------------------------------------------------------
+  *        APB2 Prescaler                         | 1
+  *-----------------------------------------------------------------------------
+  *        PLL_M                                  | 1
+  *-----------------------------------------------------------------------------
+  *        PLL_N                                  | 8
+  *-----------------------------------------------------------------------------
+  *        PLL_P                                  | 7
+  *-----------------------------------------------------------------------------
+  *        PLL_Q                                  | 2
+  *-----------------------------------------------------------------------------
+  *        PLL_R                                  | 2
+  *-----------------------------------------------------------------------------
+  *        PLLSAI1_P                              | NA
+  *-----------------------------------------------------------------------------
+  *        PLLSAI1_Q                              | NA
+  *-----------------------------------------------------------------------------
+  *        PLLSAI1_R                              | NA
+  *-----------------------------------------------------------------------------
+  *        PLLSAI2_P                              | NA
+  *-----------------------------------------------------------------------------
+  *        PLLSAI2_Q                              | NA
+  *-----------------------------------------------------------------------------
+  *        PLLSAI2_R                              | NA
+  *-----------------------------------------------------------------------------
+  *        Require 48MHz for USB OTG FS,          | Disabled
+  *        SDIO and RNG clock                     |
+  *-----------------------------------------------------------------------------
+  *=============================================================================
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2017 STMicroelectronics.
+  * All rights reserved.
+  *
+  * This software is licensed under terms that can be found in the LICENSE file
+  * in the root directory of this software component.
+  * If no LICENSE file comes with this software, it is provided AS-IS.
+  *
+  ******************************************************************************
+  */
+
+/** @addtogroup CMSIS
+  * @{
+  */
+
+/** @addtogroup stm32l4xx_system
+  * @{
+  */
+
+/** @addtogroup STM32L4xx_System_Private_Includes
+  * @{
+  */
+
+#include "stm32l4xx.h"
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32L4xx_System_Private_TypesDefinitions
+  * @{
+  */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32L4xx_System_Private_Defines
+  * @{
+  */
+
+#if !defined  (HSE_VALUE)
+  #define HSE_VALUE    8000000U  /*!< Value of the External oscillator in Hz */
+#endif /* HSE_VALUE */
+
+#if !defined  (MSI_VALUE)
+  #define MSI_VALUE    4000000U  /*!< Value of the Internal oscillator in Hz*/
+#endif /* MSI_VALUE */
+
+#if !defined  (HSI_VALUE)
+  #define HSI_VALUE    16000000U /*!< Value of the Internal oscillator in Hz*/
+#endif /* HSI_VALUE */
+
+/* Note: Following vector table addresses must be defined in line with linker
+         configuration. */
+/*!< Uncomment the following line if you need to relocate the vector table
+     anywhere in Flash or Sram, else the vector table is kept at the automatic
+     remap of boot address selected */
+/* #define USER_VECT_TAB_ADDRESS */
+
+#if defined(USER_VECT_TAB_ADDRESS)
+/*!< Uncomment the following line if you need to relocate your vector Table
+     in Sram else user remap will be done in Flash. */
+/* #define VECT_TAB_SRAM */
+
+#if defined(VECT_TAB_SRAM)
+#define VECT_TAB_BASE_ADDRESS   SRAM1_BASE      /*!< Vector Table base address field.
+                                                     This value must be a multiple of 0x200. */
+#define VECT_TAB_OFFSET         0x00000000U     /*!< Vector Table base offset field.
+                                                     This value must be a multiple of 0x200. */
+#else
+#define VECT_TAB_BASE_ADDRESS   FLASH_BASE      /*!< Vector Table base address field.
+                                                     This value must be a multiple of 0x200. */
+#define VECT_TAB_OFFSET         0x00000000U     /*!< Vector Table base offset field.
+                                                     This value must be a multiple of 0x200. */
+#endif /* VECT_TAB_SRAM */
+#endif /* USER_VECT_TAB_ADDRESS */
+
+/******************************************************************************/
+/**
+  * @}
+  */
+
+/** @addtogroup STM32L4xx_System_Private_Macros
+  * @{
+  */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32L4xx_System_Private_Variables
+  * @{
+  */
+  /* The SystemCoreClock variable is updated in three ways:
+      1) by calling CMSIS function SystemCoreClockUpdate()
+      2) by calling HAL API function HAL_RCC_GetHCLKFreq()
+      3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency
+         Note: If you use this function to configure the system clock; then there
+               is no need to call the 2 first functions listed above, since SystemCoreClock
+               variable is updated automatically.
+  */
+  uint32_t SystemCoreClock = 4000000U;
+
+  const uint8_t  AHBPrescTable[16] = {0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 1U, 2U, 3U, 4U, 6U, 7U, 8U, 9U};
+  const uint8_t  APBPrescTable[8] =  {0U, 0U, 0U, 0U, 1U, 2U, 3U, 4U};
+  const uint32_t MSIRangeTable[12] = {100000U,   200000U,   400000U,   800000U,  1000000U,  2000000U, \
+                                      4000000U, 8000000U, 16000000U, 24000000U, 32000000U, 48000000U};
+/**
+  * @}
+  */
+
+/** @addtogroup STM32L4xx_System_Private_FunctionPrototypes
+  * @{
+  */
+
+/**
+  * @}
+  */
+
+/** @addtogroup STM32L4xx_System_Private_Functions
+  * @{
+  */
+
+/**
+  * @brief  Setup the microcontroller system.
+  * @retval None
+  */
+
+void SystemInit(void)
+{
+#if defined(USER_VECT_TAB_ADDRESS)
+  /* Configure the Vector Table location -------------------------------------*/
+  SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET;
+#endif
+
+  /* FPU settings ------------------------------------------------------------*/
+#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
+  SCB->CPACR |= ((3UL << 20U)|(3UL << 22U));  /* set CP10 and CP11 Full Access */
+#endif
+}
+
+/**
+  * @brief  Update SystemCoreClock variable according to Clock Register Values.
+  *         The SystemCoreClock variable contains the core clock (HCLK), it can
+  *         be used by the user application to setup the SysTick timer or configure
+  *         other parameters.
+  *
+  * @note   Each time the core clock (HCLK) changes, this function must be called
+  *         to update SystemCoreClock variable value. Otherwise, any configuration
+  *         based on this variable will be incorrect.
+  *
+  * @note   - The system frequency computed by this function is not the real
+  *           frequency in the chip. It is calculated based on the predefined
+  *           constant and the selected clock source:
+  *
+  *           - If SYSCLK source is MSI, SystemCoreClock will contain the MSI_VALUE(*)
+  *
+  *           - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(**)
+  *
+  *           - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(***)
+  *
+  *           - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(***)
+  *             or HSI_VALUE(*) or MSI_VALUE(*) multiplied/divided by the PLL factors.
+  *
+  *         (*) MSI_VALUE is a constant defined in stm32l4xx_hal.h file (default value
+  *             4 MHz) but the real value may vary depending on the variations
+  *             in voltage and temperature.
+  *
+  *         (**) HSI_VALUE is a constant defined in stm32l4xx_hal.h file (default value
+  *              16 MHz) but the real value may vary depending on the variations
+  *              in voltage and temperature.
+  *
+  *         (***) HSE_VALUE is a constant defined in stm32l4xx_hal.h file (default value
+  *              8 MHz), user has to ensure that HSE_VALUE is same as the real
+  *              frequency of the crystal used. Otherwise, this function may
+  *              have wrong result.
+  *
+  *         - The result of this function could be not correct when using fractional
+  *           value for HSE crystal.
+  *
+  * @retval None
+  */
+void SystemCoreClockUpdate(void)
+{
+  uint32_t tmp, msirange, pllvco, pllsource, pllm, pllr;
+
+  /* Get MSI Range frequency--------------------------------------------------*/
+  if ((RCC->CR & RCC_CR_MSIRGSEL) == 0U)
+  { /* MSISRANGE from RCC_CSR applies */
+    msirange = (RCC->CSR & RCC_CSR_MSISRANGE) >> 8U;
+  }
+  else
+  { /* MSIRANGE from RCC_CR applies */
+    msirange = (RCC->CR & RCC_CR_MSIRANGE) >> 4U;
+  }
+  /*MSI frequency range in HZ*/
+  msirange = MSIRangeTable[msirange];
+
+  /* Get SYSCLK source -------------------------------------------------------*/
+  switch (RCC->CFGR & RCC_CFGR_SWS)
+  {
+    case 0x00:  /* MSI used as system clock source */
+      SystemCoreClock = msirange;
+      break;
+
+    case 0x04:  /* HSI used as system clock source */
+      SystemCoreClock = HSI_VALUE;
+      break;
+
+    case 0x08:  /* HSE used as system clock source */
+      SystemCoreClock = HSE_VALUE;
+      break;
+
+    case 0x0C:  /* PLL used as system clock  source */
+      /* PLL_VCO = (HSE_VALUE or HSI_VALUE or MSI_VALUE/ PLLM) * PLLN
+         SYSCLK = PLL_VCO / PLLR
+         */
+      pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC);
+      pllm = ((RCC->PLLCFGR & RCC_PLLCFGR_PLLM) >> 4U) + 1U ;
+
+      switch (pllsource)
+      {
+        case 0x02:  /* HSI used as PLL clock source */
+          pllvco = (HSI_VALUE / pllm);
+          break;
+
+        case 0x03:  /* HSE used as PLL clock source */
+          pllvco = (HSE_VALUE / pllm);
+          break;
+
+        default:    /* MSI used as PLL clock source */
+          pllvco = (msirange / pllm);
+          break;
+      }
+      pllvco = pllvco * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 8U);
+      pllr = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLR) >> 25U) + 1U) * 2U;
+      SystemCoreClock = pllvco/pllr;
+      break;
+
+    default:
+      SystemCoreClock = msirange;
+      break;
+  }
+  /* Compute HCLK clock frequency --------------------------------------------*/
+  /* Get HCLK prescaler */
+  tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4U)];
+  /* HCLK clock frequency */
+  SystemCoreClock >>= tmp;
+}
+
+
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/Production/ISKBoard_QCTester/Core/Src/tim.c b/Production/ISKBoard_QCTester/Core/Src/tim.c
new file mode 100644
index 0000000..71caa4e
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Src/tim.c
@@ -0,0 +1,221 @@
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file    tim.c
+  * @brief   This file provides code for the configuration
+  *          of the TIM instances.
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2023 STMicroelectronics.
+  * All rights reserved.
+  *
+  * This software is licensed under terms that can be found in the LICENSE file
+  * in the root directory of this software component.
+  * If no LICENSE file comes with this software, it is provided AS-IS.
+  *
+  ******************************************************************************
+  */
+/* USER CODE END Header */
+/* Includes ------------------------------------------------------------------*/
+#include "tim.h"
+
+/* USER CODE BEGIN 0 */
+
+/* USER CODE END 0 */
+
+TIM_HandleTypeDef htim1;
+TIM_HandleTypeDef htim6;
+
+/* TIM1 init function */
+void MX_TIM1_Init(void)
+{
+
+  /* USER CODE BEGIN TIM1_Init 0 */
+
+  /* USER CODE END TIM1_Init 0 */
+
+  TIM_MasterConfigTypeDef sMasterConfig = {0};
+  TIM_OC_InitTypeDef sConfigOC = {0};
+  TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
+
+  /* USER CODE BEGIN TIM1_Init 1 */
+
+  /* USER CODE END TIM1_Init 1 */
+  htim1.Instance = TIM1;
+  htim1.Init.Prescaler = 80-1;
+  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
+  htim1.Init.Period = 370-1;
+  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
+  htim1.Init.RepetitionCounter = 0;
+  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
+  if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
+  {
+    Error_Handler();
+  }
+  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
+  sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
+  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
+  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
+  {
+    Error_Handler();
+  }
+  sConfigOC.OCMode = TIM_OCMODE_PWM1;
+  sConfigOC.Pulse = 185;
+  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
+  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
+  sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
+  sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
+  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
+  {
+    Error_Handler();
+  }
+  sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
+  sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
+  sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
+  sBreakDeadTimeConfig.DeadTime = 0;
+  sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
+  sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
+  sBreakDeadTimeConfig.BreakFilter = 0;
+  sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE;
+  sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH;
+  sBreakDeadTimeConfig.Break2Filter = 0;
+  sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
+  if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
+  {
+    Error_Handler();
+  }
+  /* USER CODE BEGIN TIM1_Init 2 */
+
+  /* USER CODE END TIM1_Init 2 */
+  HAL_TIM_MspPostInit(&htim1);
+
+}
+/* TIM6 init function */
+void MX_TIM6_Init(void)
+{
+
+  /* USER CODE BEGIN TIM6_Init 0 */
+
+  /* USER CODE END TIM6_Init 0 */
+
+  TIM_MasterConfigTypeDef sMasterConfig = {0};
+
+  /* USER CODE BEGIN TIM6_Init 1 */
+
+  /* USER CODE END TIM6_Init 1 */
+  htim6.Instance = TIM6;
+  htim6.Init.Prescaler = 80-1;
+  htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
+  htim6.Init.Period = 1;
+  htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
+  if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
+  {
+    Error_Handler();
+  }
+  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
+  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
+  if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK)
+  {
+    Error_Handler();
+  }
+  /* USER CODE BEGIN TIM6_Init 2 */
+
+  /* USER CODE END TIM6_Init 2 */
+
+}
+
+void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef* tim_pwmHandle)
+{
+
+  if(tim_pwmHandle->Instance==TIM1)
+  {
+  /* USER CODE BEGIN TIM1_MspInit 0 */
+
+  /* USER CODE END TIM1_MspInit 0 */
+    /* TIM1 clock enable */
+    __HAL_RCC_TIM1_CLK_ENABLE();
+  /* USER CODE BEGIN TIM1_MspInit 1 */
+
+  /* USER CODE END TIM1_MspInit 1 */
+  }
+}
+
+void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
+{
+
+  if(tim_baseHandle->Instance==TIM6)
+  {
+  /* USER CODE BEGIN TIM6_MspInit 0 */
+
+  /* USER CODE END TIM6_MspInit 0 */
+    /* TIM6 clock enable */
+    __HAL_RCC_TIM6_CLK_ENABLE();
+  /* USER CODE BEGIN TIM6_MspInit 1 */
+
+  /* USER CODE END TIM6_MspInit 1 */
+  }
+}
+void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
+{
+
+  GPIO_InitTypeDef GPIO_InitStruct = {0};
+  if(timHandle->Instance==TIM1)
+  {
+  /* USER CODE BEGIN TIM1_MspPostInit 0 */
+
+  /* USER CODE END TIM1_MspPostInit 0 */
+
+    __HAL_RCC_GPIOA_CLK_ENABLE();
+    /**TIM1 GPIO Configuration
+    PA11     ------> TIM1_CH4
+    */
+    GPIO_InitStruct.Pin = GPIO_PIN_11;
+    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+    GPIO_InitStruct.Pull = GPIO_NOPULL;
+    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
+    GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
+    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+
+  /* USER CODE BEGIN TIM1_MspPostInit 1 */
+
+  /* USER CODE END TIM1_MspPostInit 1 */
+  }
+
+}
+
+void HAL_TIM_PWM_MspDeInit(TIM_HandleTypeDef* tim_pwmHandle)
+{
+
+  if(tim_pwmHandle->Instance==TIM1)
+  {
+  /* USER CODE BEGIN TIM1_MspDeInit 0 */
+
+  /* USER CODE END TIM1_MspDeInit 0 */
+    /* Peripheral clock disable */
+    __HAL_RCC_TIM1_CLK_DISABLE();
+  /* USER CODE BEGIN TIM1_MspDeInit 1 */
+
+  /* USER CODE END TIM1_MspDeInit 1 */
+  }
+}
+
+void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
+{
+
+  if(tim_baseHandle->Instance==TIM6)
+  {
+  /* USER CODE BEGIN TIM6_MspDeInit 0 */
+
+  /* USER CODE END TIM6_MspDeInit 0 */
+    /* Peripheral clock disable */
+    __HAL_RCC_TIM6_CLK_DISABLE();
+  /* USER CODE BEGIN TIM6_MspDeInit 1 */
+
+  /* USER CODE END TIM6_MspDeInit 1 */
+  }
+}
+
+/* USER CODE BEGIN 1 */
+
+/* USER CODE END 1 */
diff --git a/Production/ISKBoard_QCTester/Core/Src/usart.c b/Production/ISKBoard_QCTester/Core/Src/usart.c
new file mode 100644
index 0000000..ed6a6ca
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Src/usart.c
@@ -0,0 +1,409 @@
+/* USER CODE BEGIN Header */
+/**
+  ******************************************************************************
+  * @file    usart.c
+  * @brief   This file provides code for the configuration
+  *          of the USART instances.
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2023 STMicroelectronics.
+  * All rights reserved.
+  *
+  * This software is licensed under terms that can be found in the LICENSE file
+  * in the root directory of this software component.
+  * If no LICENSE file comes with this software, it is provided AS-IS.
+  *
+  ******************************************************************************
+  */
+/* USER CODE END Header */
+/* Includes ------------------------------------------------------------------*/
+#include "usart.h"
+
+/* USER CODE BEGIN 0 */
+#include "comport.h"
+
+uint8_t     g_uart1_rxch;
+uint8_t     uart2_rxch;
+uint8_t     uart2_rxbuf[COMM_RXBUFSIZE];
+uint8_t     uart3_rxch;
+uint8_t     uart3_rxbuf[COMM_RXBUFSIZE];
+uint8_t     lpuart1_rxch;
+uint8_t     lpuart1_rxbuf[COMM_RXBUFSIZE];
+/* USER CODE END 0 */
+
+UART_HandleTypeDef hlpuart1;
+UART_HandleTypeDef huart1;
+UART_HandleTypeDef huart2;
+UART_HandleTypeDef huart3;
+
+/* LPUART1 init function */
+
+void MX_LPUART1_UART_Init(void)
+{
+
+  /* USER CODE BEGIN LPUART1_Init 0 */
+
+  /* USER CODE END LPUART1_Init 0 */
+
+  /* USER CODE BEGIN LPUART1_Init 1 */
+
+  /* USER CODE END LPUART1_Init 1 */
+  hlpuart1.Instance = LPUART1;
+  hlpuart1.Init.BaudRate = 115200;
+  hlpuart1.Init.WordLength = UART_WORDLENGTH_8B;
+  hlpuart1.Init.StopBits = UART_STOPBITS_1;
+  hlpuart1.Init.Parity = UART_PARITY_NONE;
+  hlpuart1.Init.Mode = UART_MODE_TX_RX;
+  hlpuart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
+  hlpuart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
+  hlpuart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
+  if (HAL_UART_Init(&hlpuart1) != HAL_OK)
+  {
+    Error_Handler();
+  }
+  /* USER CODE BEGIN LPUART1_Init 2 */
+  comport_init(&hlpuart1 , &lpuart1_rxch, lpuart1_rxbuf, sizeof(lpuart1_rxbuf));
+  HAL_UART_Receive_IT(&hlpuart1 , &lpuart1_rxch, 1);
+  /* USER CODE END LPUART1_Init 2 */
+
+}
+/* USART1 init function */
+
+void MX_USART1_UART_Init(void)
+{
+
+  /* USER CODE BEGIN USART1_Init 0 */
+
+  /* USER CODE END USART1_Init 0 */
+
+  /* USER CODE BEGIN USART1_Init 1 */
+
+  /* USER CODE END USART1_Init 1 */
+  huart1.Instance = USART1;
+  huart1.Init.BaudRate = 115200;
+  huart1.Init.WordLength = UART_WORDLENGTH_8B;
+  huart1.Init.StopBits = UART_STOPBITS_1;
+  huart1.Init.Parity = UART_PARITY_NONE;
+  huart1.Init.Mode = UART_MODE_TX_RX;
+  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
+  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
+  huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
+  huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
+  if (HAL_UART_Init(&huart1) != HAL_OK)
+  {
+    Error_Handler();
+  }
+  /* USER CODE BEGIN USART1_Init 2 */
+  HAL_UART_Receive_IT(&huart1 , &g_uart1_rxch, 1);
+  /* USER CODE END USART1_Init 2 */
+
+}
+/* USART2 init function */
+
+void MX_USART2_UART_Init(void)
+{
+
+  /* USER CODE BEGIN USART2_Init 0 */
+
+  /* USER CODE END USART2_Init 0 */
+
+  /* USER CODE BEGIN USART2_Init 1 */
+
+  /* USER CODE END USART2_Init 1 */
+  huart2.Instance = USART2;
+  huart2.Init.BaudRate = 115200;
+  huart2.Init.WordLength = UART_WORDLENGTH_8B;
+  huart2.Init.StopBits = UART_STOPBITS_1;
+  huart2.Init.Parity = UART_PARITY_NONE;
+  huart2.Init.Mode = UART_MODE_TX_RX;
+  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
+  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
+  huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
+  huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
+  if (HAL_UART_Init(&huart2) != HAL_OK)
+  {
+    Error_Handler();
+  }
+  /* USER CODE BEGIN USART2_Init 2 */
+  comport_init(&huart2 , &uart2_rxch, uart2_rxbuf, sizeof(uart2_rxbuf));
+  HAL_UART_Receive_IT(&huart2 , &uart2_rxch, 1);
+  /* USER CODE END USART2_Init 2 */
+
+}
+/* USART3 init function */
+
+void MX_USART3_UART_Init(void)
+{
+
+  /* USER CODE BEGIN USART3_Init 0 */
+
+  /* USER CODE END USART3_Init 0 */
+
+  /* USER CODE BEGIN USART3_Init 1 */
+
+  /* USER CODE END USART3_Init 1 */
+  huart3.Instance = USART3;
+  huart3.Init.BaudRate = 115200;
+  huart3.Init.WordLength = UART_WORDLENGTH_8B;
+  huart3.Init.StopBits = UART_STOPBITS_1;
+  huart3.Init.Parity = UART_PARITY_NONE;
+  huart3.Init.Mode = UART_MODE_TX_RX;
+  huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
+  huart3.Init.OverSampling = UART_OVERSAMPLING_16;
+  huart3.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
+  huart3.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
+  if (HAL_UART_Init(&huart3) != HAL_OK)
+  {
+    Error_Handler();
+  }
+  /* USER CODE BEGIN USART3_Init 2 */
+  comport_init(&huart3 , &uart3_rxch, uart3_rxbuf, sizeof(uart3_rxbuf));
+  HAL_UART_Receive_IT(&huart3 , &uart3_rxch, 1);
+  /* USER CODE END USART3_Init 2 */
+
+}
+
+void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
+{
+
+  GPIO_InitTypeDef GPIO_InitStruct = {0};
+  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
+  if(uartHandle->Instance==LPUART1)
+  {
+  /* USER CODE BEGIN LPUART1_MspInit 0 */
+
+  /* USER CODE END LPUART1_MspInit 0 */
+
+  /** Initializes the peripherals clock
+  */
+    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_LPUART1;
+    PeriphClkInit.Lpuart1ClockSelection = RCC_LPUART1CLKSOURCE_PCLK1;
+    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
+    {
+      Error_Handler();
+    }
+
+    /* LPUART1 clock enable */
+    __HAL_RCC_LPUART1_CLK_ENABLE();
+
+    __HAL_RCC_GPIOC_CLK_ENABLE();
+    /**LPUART1 GPIO Configuration
+    PC0     ------> LPUART1_RX
+    PC1     ------> LPUART1_TX
+    */
+    GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;
+    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+    GPIO_InitStruct.Pull = GPIO_NOPULL;
+    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+    GPIO_InitStruct.Alternate = GPIO_AF8_LPUART1;
+    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
+
+    /* LPUART1 interrupt Init */
+    HAL_NVIC_SetPriority(LPUART1_IRQn, 3, 0);
+    HAL_NVIC_EnableIRQ(LPUART1_IRQn);
+  /* USER CODE BEGIN LPUART1_MspInit 1 */
+
+  /* USER CODE END LPUART1_MspInit 1 */
+  }
+  else if(uartHandle->Instance==USART1)
+  {
+  /* USER CODE BEGIN USART1_MspInit 0 */
+
+  /* USER CODE END USART1_MspInit 0 */
+
+  /** Initializes the peripherals clock
+  */
+    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1;
+    PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
+    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
+    {
+      Error_Handler();
+    }
+
+    /* USART1 clock enable */
+    __HAL_RCC_USART1_CLK_ENABLE();
+
+    __HAL_RCC_GPIOA_CLK_ENABLE();
+    /**USART1 GPIO Configuration
+    PA9     ------> USART1_TX
+    PA10     ------> USART1_RX
+    */
+    GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
+    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+    GPIO_InitStruct.Pull = GPIO_NOPULL;
+    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+    GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
+    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+
+    /* USART1 interrupt Init */
+    HAL_NVIC_SetPriority(USART1_IRQn, 5, 0);
+    HAL_NVIC_EnableIRQ(USART1_IRQn);
+  /* USER CODE BEGIN USART1_MspInit 1 */
+
+  /* USER CODE END USART1_MspInit 1 */
+  }
+  else if(uartHandle->Instance==USART2)
+  {
+  /* USER CODE BEGIN USART2_MspInit 0 */
+
+  /* USER CODE END USART2_MspInit 0 */
+
+  /** Initializes the peripherals clock
+  */
+    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART2;
+    PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
+    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
+    {
+      Error_Handler();
+    }
+
+    /* USART2 clock enable */
+    __HAL_RCC_USART2_CLK_ENABLE();
+
+    __HAL_RCC_GPIOA_CLK_ENABLE();
+    /**USART2 GPIO Configuration
+    PA2     ------> USART2_TX
+    PA3     ------> USART2_RX
+    */
+    GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
+    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+    GPIO_InitStruct.Pull = GPIO_NOPULL;
+    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+    GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
+    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+
+    /* USART2 interrupt Init */
+    HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
+    HAL_NVIC_EnableIRQ(USART2_IRQn);
+  /* USER CODE BEGIN USART2_MspInit 1 */
+
+  /* USER CODE END USART2_MspInit 1 */
+  }
+  else if(uartHandle->Instance==USART3)
+  {
+  /* USER CODE BEGIN USART3_MspInit 0 */
+
+  /* USER CODE END USART3_MspInit 0 */
+
+  /** Initializes the peripherals clock
+  */
+    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART3;
+    PeriphClkInit.Usart3ClockSelection = RCC_USART3CLKSOURCE_PCLK1;
+    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
+    {
+      Error_Handler();
+    }
+
+    /* USART3 clock enable */
+    __HAL_RCC_USART3_CLK_ENABLE();
+
+    __HAL_RCC_GPIOC_CLK_ENABLE();
+    /**USART3 GPIO Configuration
+    PC4     ------> USART3_TX
+    PC5     ------> USART3_RX
+    */
+    GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5;
+    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+    GPIO_InitStruct.Pull = GPIO_NOPULL;
+    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+    GPIO_InitStruct.Alternate = GPIO_AF7_USART3;
+    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
+
+    /* USART3 interrupt Init */
+    HAL_NVIC_SetPriority(USART3_IRQn, 2, 0);
+    HAL_NVIC_EnableIRQ(USART3_IRQn);
+  /* USER CODE BEGIN USART3_MspInit 1 */
+
+  /* USER CODE END USART3_MspInit 1 */
+  }
+}
+
+void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
+{
+
+  if(uartHandle->Instance==LPUART1)
+  {
+  /* USER CODE BEGIN LPUART1_MspDeInit 0 */
+
+  /* USER CODE END LPUART1_MspDeInit 0 */
+    /* Peripheral clock disable */
+    __HAL_RCC_LPUART1_CLK_DISABLE();
+
+    /**LPUART1 GPIO Configuration
+    PC0     ------> LPUART1_RX
+    PC1     ------> LPUART1_TX
+    */
+    HAL_GPIO_DeInit(GPIOC, GPIO_PIN_0|GPIO_PIN_1);
+
+    /* LPUART1 interrupt Deinit */
+    HAL_NVIC_DisableIRQ(LPUART1_IRQn);
+  /* USER CODE BEGIN LPUART1_MspDeInit 1 */
+
+  /* USER CODE END LPUART1_MspDeInit 1 */
+  }
+  else if(uartHandle->Instance==USART1)
+  {
+  /* USER CODE BEGIN USART1_MspDeInit 0 */
+
+  /* USER CODE END USART1_MspDeInit 0 */
+    /* Peripheral clock disable */
+    __HAL_RCC_USART1_CLK_DISABLE();
+
+    /**USART1 GPIO Configuration
+    PA9     ------> USART1_TX
+    PA10     ------> USART1_RX
+    */
+    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);
+
+    /* USART1 interrupt Deinit */
+    HAL_NVIC_DisableIRQ(USART1_IRQn);
+  /* USER CODE BEGIN USART1_MspDeInit 1 */
+
+  /* USER CODE END USART1_MspDeInit 1 */
+  }
+  else if(uartHandle->Instance==USART2)
+  {
+  /* USER CODE BEGIN USART2_MspDeInit 0 */
+
+  /* USER CODE END USART2_MspDeInit 0 */
+    /* Peripheral clock disable */
+    __HAL_RCC_USART2_CLK_DISABLE();
+
+    /**USART2 GPIO Configuration
+    PA2     ------> USART2_TX
+    PA3     ------> USART2_RX
+    */
+    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_2|GPIO_PIN_3);
+
+    /* USART2 interrupt Deinit */
+    HAL_NVIC_DisableIRQ(USART2_IRQn);
+  /* USER CODE BEGIN USART2_MspDeInit 1 */
+
+  /* USER CODE END USART2_MspDeInit 1 */
+  }
+  else if(uartHandle->Instance==USART3)
+  {
+  /* USER CODE BEGIN USART3_MspDeInit 0 */
+
+  /* USER CODE END USART3_MspDeInit 0 */
+    /* Peripheral clock disable */
+    __HAL_RCC_USART3_CLK_DISABLE();
+
+    /**USART3 GPIO Configuration
+    PC4     ------> USART3_TX
+    PC5     ------> USART3_RX
+    */
+    HAL_GPIO_DeInit(GPIOC, GPIO_PIN_4|GPIO_PIN_5);
+
+    /* USART3 interrupt Deinit */
+    HAL_NVIC_DisableIRQ(USART3_IRQn);
+  /* USER CODE BEGIN USART3_MspDeInit 1 */
+
+  /* USER CODE END USART3_MspDeInit 1 */
+  }
+}
+
+/* USER CODE BEGIN 1 */
+
+/* USER CODE END 1 */
diff --git a/Production/ISKBoard_QCTester/Core/Startup/startup_stm32l431rctx.s b/Production/ISKBoard_QCTester/Core/Startup/startup_stm32l431rctx.s
new file mode 100644
index 0000000..f652136
--- /dev/null
+++ b/Production/ISKBoard_QCTester/Core/Startup/startup_stm32l431rctx.s
@@ -0,0 +1,461 @@
+/**
+  ******************************************************************************
+  * @file      startup_stm32l431xx.s
+  * @author    MCD Application Team
+  * @brief     STM32L431xx devices vector table for GCC toolchain.
+  *            This module performs:
+  *                - Set the initial SP
+  *                - Set the initial PC == Reset_Handler,
+  *                - Set the vector table entries with the exceptions ISR address,
+  *                - Configure the clock system  
+  *                - Branches to main in the C library (which eventually
+  *                  calls main()).
+  *            After Reset the Cortex-M4 processor is in Thread mode,
+  *            priority is Privileged, and the Stack is set to Main.
+  ******************************************************************************
+  * @attention
+  *
+  * Copyright (c) 2017 STMicroelectronics.
+  * All rights reserved.
+  *
+  * This software is licensed under terms that can be found in the LICENSE file
+  * in the root directory of this software component.
+  * If no LICENSE file comes with this software, it is provided AS-IS.
+  *
+  ******************************************************************************
+  */
+
+  .syntax unified
+	.cpu cortex-m4
+	.fpu softvfp
+	.thumb
+
+.global	g_pfnVectors
+.global	Default_Handler
+
+/* start address for the initialization values of the .data section.
+defined in linker script */
+.word	_sidata
+/* start address for the .data section. defined in linker script */
+.word	_sdata
+/* end address for the .data section. defined in linker script */
+.word	_edata
+/* start address for the .bss section. defined in linker script */
+.word	_sbss
+/* end address for the .bss section. defined in linker script */
+.word	_ebss
+
+.equ  BootRAM,        0xF1E0F85F
+/**
+ * @brief  This is the code that gets called when the processor first
+ *          starts execution following a reset event. Only the absolutely
+ *          necessary set is performed, after which the application
+ *          supplied main() routine is called.
+ * @param  None
+ * @retval : None
+*/
+
+    .section	.text.Reset_Handler
+	.weak	Reset_Handler
+	.type	Reset_Handler, %function
+Reset_Handler:
+  ldr   sp, =_estack    /* Set stack pointer */
+
+/* Call the clock system initialization function.*/
+    bl  SystemInit
+
+/* Copy the data segment initializers from flash to SRAM */
+  ldr r0, =_sdata
+  ldr r1, =_edata
+  ldr r2, =_sidata
+  movs r3, #0
+  b LoopCopyDataInit
+
+CopyDataInit:
+  ldr r4, [r2, r3]
+  str r4, [r0, r3]
+  adds r3, r3, #4
+
+LoopCopyDataInit:
+  adds r4, r0, r3
+  cmp r4, r1
+  bcc CopyDataInit
+  
+/* Zero fill the bss segment. */
+  ldr r2, =_sbss
+  ldr r4, =_ebss
+  movs r3, #0
+  b LoopFillZerobss
+
+FillZerobss:
+  str  r3, [r2]
+  adds r2, r2, #4
+
+LoopFillZerobss:
+  cmp r2, r4
+  bcc FillZerobss
+
+/* Call static constructors */
+    bl __libc_init_array
+/* Call the application's entry point.*/
+	bl	main
+
+LoopForever:
+    b LoopForever
+    
+.size	Reset_Handler, .-Reset_Handler
+
+/**
+ * @brief  This is the code that gets called when the processor receives an
+ *         unexpected interrupt.  This simply enters an infinite loop, preserving
+ *         the system state for examination by a debugger.
+ *
+ * @param  None
+ * @retval : None
+*/
+    .section	.text.Default_Handler,"ax",%progbits
+Default_Handler:
+Infinite_Loop:
+	b	Infinite_Loop
+	.size	Default_Handler, .-Default_Handler
+/******************************************************************************
+*
+* The minimal vector table for a Cortex-M4.  Note that the proper constructs
+* must be placed on this to ensure that it ends up at physical address
+* 0x0000.0000.
+*
+******************************************************************************/
+ 	.section	.isr_vector,"a",%progbits
+	.type	g_pfnVectors, %object
+	.size	g_pfnVectors, .-g_pfnVectors
+
+
+g_pfnVectors:
+	.word	_estack
+	.word	Reset_Handler
+	.word	NMI_Handler
+	.word	HardFault_Handler
+	.word	MemManage_Handler
+	.word	BusFault_Handler
+	.word	UsageFault_Handler
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	SVC_Handler
+	.word	DebugMon_Handler
+	.word	0
+	.word	PendSV_Handler
+	.word	SysTick_Handler
+	.word	WWDG_IRQHandler
+	.word	PVD_PVM_IRQHandler
+	.word	TAMP_STAMP_IRQHandler
+	.word	RTC_WKUP_IRQHandler
+	.word	FLASH_IRQHandler
+	.word	RCC_IRQHandler
+	.word	EXTI0_IRQHandler
+	.word	EXTI1_IRQHandler
+	.word	EXTI2_IRQHandler
+	.word	EXTI3_IRQHandler
+	.word	EXTI4_IRQHandler
+	.word	DMA1_Channel1_IRQHandler
+	.word	DMA1_Channel2_IRQHandler
+	.word	DMA1_Channel3_IRQHandler
+	.word	DMA1_Channel4_IRQHandler
+	.word	DMA1_Channel5_IRQHandler
+	.word	DMA1_Channel6_IRQHandler
+	.word	DMA1_Channel7_IRQHandler
+	.word	ADC1_IRQHandler
+	.word	CAN1_TX_IRQHandler
+	.word	CAN1_RX0_IRQHandler
+	.word	CAN1_RX1_IRQHandler
+	.word	CAN1_SCE_IRQHandler
+	.word	EXTI9_5_IRQHandler
+	.word	TIM1_BRK_TIM15_IRQHandler
+	.word	TIM1_UP_TIM16_IRQHandler
+	.word	TIM1_TRG_COM_IRQHandler
+	.word	TIM1_CC_IRQHandler
+	.word	TIM2_IRQHandler
+	.word	0
+	.word	0
+	.word	I2C1_EV_IRQHandler
+	.word	I2C1_ER_IRQHandler
+	.word	I2C2_EV_IRQHandler
+	.word	I2C2_ER_IRQHandler
+	.word	SPI1_IRQHandler
+	.word	SPI2_IRQHandler
+	.word	USART1_IRQHandler
+	.word	USART2_IRQHandler
+	.word	USART3_IRQHandler
+	.word	EXTI15_10_IRQHandler
+	.word	RTC_Alarm_IRQHandler
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	SDMMC1_IRQHandler
+	.word	0
+	.word	SPI3_IRQHandler
+	.word	0
+	.word	0
+	.word	TIM6_DAC_IRQHandler
+	.word	TIM7_IRQHandler
+	.word	DMA2_Channel1_IRQHandler
+	.word	DMA2_Channel2_IRQHandler
+	.word	DMA2_Channel3_IRQHandler
+	.word	DMA2_Channel4_IRQHandler
+	.word	DMA2_Channel5_IRQHandler
+	.word	0
+	.word	0
+	.word	0
+	.word	COMP_IRQHandler
+	.word	LPTIM1_IRQHandler
+	.word	LPTIM2_IRQHandler
+	.word	0
+	.word	DMA2_Channel6_IRQHandler
+	.word	DMA2_Channel7_IRQHandler
+	.word	LPUART1_IRQHandler
+	.word	QUADSPI_IRQHandler
+	.word	I2C3_EV_IRQHandler
+	.word	I2C3_ER_IRQHandler
+	.word	SAI1_IRQHandler
+	.word	0
+	.word	SWPMI1_IRQHandler
+	.word	TSC_IRQHandler
+	.word	0
+	.word	0
+	.word	RNG_IRQHandler
+	.word	FPU_IRQHandler
+	.word	CRS_IRQHandler
+
+
+/*******************************************************************************
+*
+* Provide weak aliases for each Exception handler to the Default_Handler.
+* As they are weak aliases, any function with the same name will override
+* this definition.
+*
+*******************************************************************************/
+
+  .weak	NMI_Handler
+	.thumb_set NMI_Handler,Default_Handler
+
+  .weak	HardFault_Handler
+	.thumb_set HardFault_Handler,Default_Handler
+
+  .weak	MemManage_Handler
+	.thumb_set MemManage_Handler,Default_Handler
+
+  .weak	BusFault_Handler
+	.thumb_set BusFault_Handler,Default_Handler
+
+	.weak	UsageFault_Handler
+	.thumb_set UsageFault_Handler,Default_Handler
+
+	.weak	SVC_Handler
+	.thumb_set SVC_Handler,Default_Handler
+
+	.weak	DebugMon_Handler
+	.thumb_set DebugMon_Handler,Default_Handler
+
+	.weak	PendSV_Handler
+	.thumb_set PendSV_Handler,Default_Handler
+
+	.weak	SysTick_Handler
+	.thumb_set SysTick_Handler,Default_Handler
+
+	.weak	WWDG_IRQHandler
+	.thumb_set WWDG_IRQHandler,Default_Handler
+
+	.weak	PVD_PVM_IRQHandler
+	.thumb_set PVD_PVM_IRQHandler,Default_Handler
+
+	.weak	TAMP_STAMP_IRQHandler
+	.thumb_set TAMP_STAMP_IRQHandler,Default_Handler
+
+	.weak	RTC_WKUP_IRQHandler
+	.thumb_set RTC_WKUP_IRQHandler,Default_Handler
+
+	.weak	FLASH_IRQHandler
+	.thumb_set FLASH_IRQHandler,Default_Handler
+
+	.weak	RCC_IRQHandler
+	.thumb_set RCC_IRQHandler,Default_Handler
+
+	.weak	EXTI0_IRQHandler
+	.thumb_set EXTI0_IRQHandler,Default_Handler
+
+	.weak	EXTI1_IRQHandler
+	.thumb_set EXTI1_IRQHandler,Default_Handler
+
+	.weak	EXTI2_IRQHandler
+	.thumb_set EXTI2_IRQHandler,Default_Handler
+
+	.weak	EXTI3_IRQHandler
+	.thumb_set EXTI3_IRQHandler,Default_Handler
+
+	.weak	EXTI4_IRQHandler
+	.thumb_set EXTI4_IRQHandler,Default_Handler
+
+	.weak	DMA1_Channel1_IRQHandler
+	.thumb_set DMA1_Channel1_IRQHandler,Default_Handler
+
+	.weak	DMA1_Channel2_IRQHandler
+	.thumb_set DMA1_Channel2_IRQHandler,Default_Handler
+
+	.weak	DMA1_Channel3_IRQHandler
+	.thumb_set DMA1_Channel3_IRQHandler,Default_Handler
+
+	.weak	DMA1_Channel4_IRQHandler
+	.thumb_set DMA1_Channel4_IRQHandler,Default_Handler
+
+	.weak	DMA1_Channel5_IRQHandler
+	.thumb_set DMA1_Channel5_IRQHandler,Default_Handler
+
+	.weak	DMA1_Channel6_IRQHandler
+	.thumb_set DMA1_Channel6_IRQHandler,Default_Handler
+
+	.weak	DMA1_Channel7_IRQHandler
+	.thumb_set DMA1_Channel7_IRQHandler,Default_Handler
+
+	.weak	ADC1_IRQHandler
+	.thumb_set ADC1_IRQHandler,Default_Handler
+
+	.weak	CAN1_TX_IRQHandler
+	.thumb_set CAN1_TX_IRQHandler,Default_Handler
+
+	.weak	CAN1_RX0_IRQHandler
+	.thumb_set CAN1_RX0_IRQHandler,Default_Handler
+
+	.weak	CAN1_RX1_IRQHandler
+	.thumb_set CAN1_RX1_IRQHandler,Default_Handler
+
+	.weak	CAN1_SCE_IRQHandler
+	.thumb_set CAN1_SCE_IRQHandler,Default_Handler
+
+	.weak	EXTI9_5_IRQHandler
+	.thumb_set EXTI9_5_IRQHandler,Default_Handler
+
+	.weak	TIM1_BRK_TIM15_IRQHandler
+	.thumb_set TIM1_BRK_TIM15_IRQHandler,Default_Handler
+
+	.weak	TIM1_UP_TIM16_IRQHandler
+	.thumb_set TIM1_UP_TIM16_IRQHandler,Default_Handler
+
+	.weak	TIM1_TRG_COM_IRQHandler
+	.thumb_set TIM1_TRG_COM_IRQHandler,Default_Handler
+
+	.weak	TIM1_CC_IRQHandler
+	.thumb_set TIM1_CC_IRQHandler,Default_Handler
+
+	.weak	TIM2_IRQHandler
+	.thumb_set TIM2_IRQHandler,Default_Handler
+
+	.weak	I2C1_EV_IRQHandler
+	.thumb_set I2C1_EV_IRQHandler,Default_Handler
+
+	.weak	I2C1_ER_IRQHandler
+	.thumb_set I2C1_ER_IRQHandler,Default_Handler
+
+	.weak	I2C2_EV_IRQHandler
+	.thumb_set I2C2_EV_IRQHandler,Default_Handler
+
+	.weak	I2C2_ER_IRQHandler
+	.thumb_set I2C2_ER_IRQHandler,Default_Handler
+
+	.weak	SPI1_IRQHandler
+	.thumb_set SPI1_IRQHandler,Default_Handler
+
+	.weak	SPI2_IRQHandler
+	.thumb_set SPI2_IRQHandler,Default_Handler
+
+	.weak	USART1_IRQHandler
+	.thumb_set USART1_IRQHandler,Default_Handler
+
+	.weak	USART2_IRQHandler
+	.thumb_set USART2_IRQHandler,Default_Handler
+
+	.weak	USART3_IRQHandler
+	.thumb_set USART3_IRQHandler,Default_Handler
+
+	.weak	EXTI15_10_IRQHandler
+	.thumb_set EXTI15_10_IRQHandler,Default_Handler
+
+	.weak	RTC_Alarm_IRQHandler
+	.thumb_set RTC_Alarm_IRQHandler,Default_Handler
+
+	.weak	SDMMC1_IRQHandler
+	.thumb_set SDMMC1_IRQHandler,Default_Handler
+
+	.weak	SPI3_IRQHandler
+	.thumb_set SPI3_IRQHandler,Default_Handler
+
+	.weak	TIM6_DAC_IRQHandler
+	.thumb_set TIM6_DAC_IRQHandler,Default_Handler
+
+	.weak	TIM7_IRQHandler
+	.thumb_set TIM7_IRQHandler,Default_Handler
+
+	.weak	DMA2_Channel1_IRQHandler
+	.thumb_set DMA2_Channel1_IRQHandler,Default_Handler
+
+	.weak	DMA2_Channel2_IRQHandler
+	.thumb_set DMA2_Channel2_IRQHandler,Default_Handler
+
+	.weak	DMA2_Channel3_IRQHandler
+	.thumb_set DMA2_Channel3_IRQHandler,Default_Handler
+
+	.weak	DMA2_Channel4_IRQHandler
+	.thumb_set DMA2_Channel4_IRQHandler,Default_Handler
+
+	.weak	DMA2_Channel5_IRQHandler
+	.thumb_set DMA2_Channel5_IRQHandler,Default_Handler
+
+	.weak	COMP_IRQHandler
+	.thumb_set COMP_IRQHandler,Default_Handler
+	
+	.weak	LPTIM1_IRQHandler
+	.thumb_set LPTIM1_IRQHandler,Default_Handler
+	
+	.weak	LPTIM2_IRQHandler
+	.thumb_set LPTIM2_IRQHandler,Default_Handler	
+	
+	.weak	DMA2_Channel6_IRQHandler
+	.thumb_set DMA2_Channel6_IRQHandler,Default_Handler	
+	
+	.weak	DMA2_Channel7_IRQHandler
+	.thumb_set DMA2_Channel7_IRQHandler,Default_Handler	
+	
+	.weak	LPUART1_IRQHandler
+	.thumb_set LPUART1_IRQHandler,Default_Handler	
+	
+	.weak	QUADSPI_IRQHandler
+	.thumb_set QUADSPI_IRQHandler,Default_Handler	
+	
+	.weak	I2C3_EV_IRQHandler
+	.thumb_set I2C3_EV_IRQHandler,Default_Handler	
+	
+	.weak	I2C3_ER_IRQHandler
+	.thumb_set I2C3_ER_IRQHandler,Default_Handler	
+	
+	.weak	SAI1_IRQHandler
+	.thumb_set SAI1_IRQHandler,Default_Handler
+	
+	.weak	SWPMI1_IRQHandler
+	.thumb_set SWPMI1_IRQHandler,Default_Handler
+	
+	.weak	TSC_IRQHandler
+	.thumb_set TSC_IRQHandler,Default_Handler
+	
+	.weak	RNG_IRQHandler
+	.thumb_set RNG_IRQHandler,Default_Handler
+	
+	.weak	FPU_IRQHandler
+	.thumb_set FPU_IRQHandler,Default_Handler
+	
+	.weak	CRS_IRQHandler
+	.thumb_set CRS_IRQHandler,Default_Handler
+
diff --git a/Production/ISKBoard_QCTester/ISKBoard_QCTester Debug.launch b/Production/ISKBoard_QCTester/ISKBoard_QCTester Debug.launch
new file mode 100644
index 0000000..7167a3f
--- /dev/null
+++ b/Production/ISKBoard_QCTester/ISKBoard_QCTester Debug.launch
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="com.st.stm32cube.ide.mcu.debug.launch.launchConfigurationType">
+    <stringAttribute key="com.st.stm32cube.ide.mcu.debug.launch.access_port_id" value="0"/>
+    <stringAttribute key="com.st.stm32cube.ide.mcu.debug.launch.cubeprog_external_loaders" value="[]"/>
+    <booleanAttribute key="com.st.stm32cube.ide.mcu.debug.launch.enable_live_expr" value="true"/>
+    <booleanAttribute key="com.st.stm32cube.ide.mcu.debug.launch.enable_swv" value="false"/>
+    <intAttribute key="com.st.stm32cube.ide.mcu.debug.launch.formatVersion" value="2"/>
+    <stringAttribute key="com.st.stm32cube.ide.mcu.debug.launch.ip_address_local" value="localhost"/>
+    <booleanAttribute key="com.st.stm32cube.ide.mcu.debug.launch.limit_swo_clock.enabled" value="false"/>
+    <stringAttribute key="com.st.stm32cube.ide.mcu.debug.launch.limit_swo_clock.value" value=""/>
+    <stringAttribute key="com.st.stm32cube.ide.mcu.debug.launch.loadList" value="{&quot;fItems&quot;:[{&quot;fIsFromMainTab&quot;:true,&quot;fPath&quot;:&quot;Debug/ISKBoard_QCTester.elf&quot;,&quot;fProjectName&quot;:&quot;ISKBoard_QCTester&quot;,&quot;fPerformBuild&quot;:true,&quot;fDownload&quot;:true,&quot;fLoadSymbols&quot;:true}]}"/>
+    <stringAttribute key="com.st.stm32cube.ide.mcu.debug.launch.override_start_address_mode" value="default"/>
+    <stringAttribute key="com.st.stm32cube.ide.mcu.debug.launch.remoteCommand" value="target remote"/>
+    <booleanAttribute key="com.st.stm32cube.ide.mcu.debug.launch.startServer" value="true"/>
+    <booleanAttribute key="com.st.stm32cube.ide.mcu.debug.launch.startuptab.exception.divby0" value="true"/>
+    <booleanAttribute key="com.st.stm32cube.ide.mcu.debug.launch.startuptab.exception.unaligned" value="false"/>
+    <booleanAttribute key="com.st.stm32cube.ide.mcu.debug.launch.startuptab.haltonexception" value="true"/>
+    <booleanAttribute key="com.st.stm32cube.ide.mcu.debug.launch.swd_mode" value="true"/>
+    <stringAttribute key="com.st.stm32cube.ide.mcu.debug.launch.swv_port" value="61235"/>
+    <stringAttribute key="com.st.stm32cube.ide.mcu.debug.launch.swv_trace_hclk" value="80000000"/>
+    <booleanAttribute key="com.st.stm32cube.ide.mcu.debug.launch.useRemoteTarget" value="true"/>
+    <stringAttribute key="com.st.stm32cube.ide.mcu.debug.launch.vector_table" value=""/>
+    <booleanAttribute key="com.st.stm32cube.ide.mcu.debug.launch.verify_flash_download" value="true"/>
+    <booleanAttribute key="com.st.stm32cube.ide.mcu.debug.stlink.cti_allow_halt" value="false"/>
+    <booleanAttribute key="com.st.stm32cube.ide.mcu.debug.stlink.cti_signal_halt" value="false"/>
+    <booleanAttribute key="com.st.stm32cube.ide.mcu.debug.stlink.enable_logging" value="false"/>
+    <booleanAttribute key="com.st.stm32cube.ide.mcu.debug.stlink.enable_max_halt_delay" value="false"/>
+    <booleanAttribute key="com.st.stm32cube.ide.mcu.debug.stlink.enable_shared_stlink" value="false"/>
+    <stringAttribute key="com.st.stm32cube.ide.mcu.debug.stlink.frequency" value="0"/>
+    <booleanAttribute key="com.st.stm32cube.ide.mcu.debug.stlink.halt_all_on_reset" value="false"/>
+    <stringAttribute key="com.st.stm32cube.ide.mcu.debug.stlink.log_file" value="E:\STM32CubeIDE\ISKBoard_QCTester\Debug\st-link_gdbserver_log.txt"/>
+    <stringAttribute key="com.st.stm32cube.ide.mcu.debug.stlink.low_power_debug" value="enable"/>
+    <stringAttribute key="com.st.stm32cube.ide.mcu.debug.stlink.max_halt_delay" value="2"/>
+    <stringAttribute key="com.st.stm32cube.ide.mcu.debug.stlink.reset_strategy" value="connect_under_reset"/>
+    <booleanAttribute key="com.st.stm32cube.ide.mcu.debug.stlink.stlink_check_serial_number" value="false"/>
+    <stringAttribute key="com.st.stm32cube.ide.mcu.debug.stlink.stlink_txt_serial_number" value=""/>
+    <stringAttribute key="com.st.stm32cube.ide.mcu.debug.stlink.watchdog_config" value="none"/>
+    <booleanAttribute key="com.st.stm32cube.ide.mcu.debug.stlinkenable_rtos" value="false"/>
+    <stringAttribute key="com.st.stm32cube.ide.mcu.debug.stlinkrestart_configurations" value="{&quot;fVersion&quot;:1,&quot;fItems&quot;:[{&quot;fDisplayName&quot;:&quot;Reset&quot;,&quot;fIsSuppressible&quot;:false,&quot;fResetAttribute&quot;:&quot;Software system reset&quot;,&quot;fResetStrategies&quot;:[{&quot;fDisplayName&quot;:&quot;Software system reset&quot;,&quot;fLaunchAttribute&quot;:&quot;system_reset&quot;,&quot;fGdbCommands&quot;:[&quot;monitor reset\r\n&quot;],&quot;fCmdOptions&quot;:[&quot;-g&quot;]},{&quot;fDisplayName&quot;:&quot;Hardware reset&quot;,&quot;fLaunchAttribute&quot;:&quot;hardware_reset&quot;,&quot;fGdbCommands&quot;:[&quot;monitor reset hardware\r\n&quot;],&quot;fCmdOptions&quot;:[&quot;-g&quot;]},{&quot;fDisplayName&quot;:&quot;Core reset&quot;,&quot;fLaunchAttribute&quot;:&quot;core_reset&quot;,&quot;fGdbCommands&quot;:[&quot;monitor reset core\r\n&quot;],&quot;fCmdOptions&quot;:[&quot;-g&quot;]},{&quot;fDisplayName&quot;:&quot;None&quot;,&quot;fLaunchAttribute&quot;:&quot;no_reset&quot;,&quot;fGdbCommands&quot;:[],&quot;fCmdOptions&quot;:[&quot;-g&quot;]}],&quot;fGdbCommandGroup&quot;:{&quot;name&quot;:&quot;Additional commands&quot;,&quot;commands&quot;:[]},&quot;fStartApplication&quot;:true}]}"/>
+    <booleanAttribute key="com.st.stm32cube.ide.mcu.rtosproxy.enableRtosProxy" value="false"/>
+    <stringAttribute key="com.st.stm32cube.ide.mcu.rtosproxy.rtosProxyCustomProperties" value=""/>
+    <stringAttribute key="com.st.stm32cube.ide.mcu.rtosproxy.rtosProxyDriver" value="threadx"/>
+    <booleanAttribute key="com.st.stm32cube.ide.mcu.rtosproxy.rtosProxyDriverAuto" value="false"/>
+    <stringAttribute key="com.st.stm32cube.ide.mcu.rtosproxy.rtosProxyDriverPort" value="cortex_m0"/>
+    <intAttribute key="com.st.stm32cube.ide.mcu.rtosproxy.rtosProxyPort" value="60000"/>
+    <booleanAttribute key="org.eclipse.cdt.debug.gdbjtag.core.doHalt" value="false"/>
+    <booleanAttribute key="org.eclipse.cdt.debug.gdbjtag.core.doReset" value="false"/>
+    <stringAttribute key="org.eclipse.cdt.debug.gdbjtag.core.initCommands" value=""/>
+    <stringAttribute key="org.eclipse.cdt.debug.gdbjtag.core.ipAddress" value="localhost"/>
+    <stringAttribute key="org.eclipse.cdt.debug.gdbjtag.core.jtagDeviceId" value="com.st.stm32cube.ide.mcu.debug.stlink"/>
+    <stringAttribute key="org.eclipse.cdt.debug.gdbjtag.core.pcRegister" value=""/>
+    <intAttribute key="org.eclipse.cdt.debug.gdbjtag.core.portNumber" value="61234"/>
+    <stringAttribute key="org.eclipse.cdt.debug.gdbjtag.core.runCommands" value=""/>
+    <booleanAttribute key="org.eclipse.cdt.debug.gdbjtag.core.setPcRegister" value="false"/>
+    <booleanAttribute key="org.eclipse.cdt.debug.gdbjtag.core.setResume" value="true"/>
+    <booleanAttribute key="org.eclipse.cdt.debug.gdbjtag.core.setStopAt" value="true"/>
+    <stringAttribute key="org.eclipse.cdt.debug.gdbjtag.core.stopAt" value="main"/>
+    <stringAttribute key="org.eclipse.cdt.dsf.gdb.DEBUG_NAME" value="arm-none-eabi-gdb"/>
+    <booleanAttribute key="org.eclipse.cdt.dsf.gdb.NON_STOP" value="false"/>
+    <booleanAttribute key="org.eclipse.cdt.dsf.gdb.UPDATE_THREADLIST_ON_SUSPEND" value="false"/>
+    <intAttribute key="org.eclipse.cdt.launch.ATTR_BUILD_BEFORE_LAUNCH_ATTR" value="2"/>
+    <stringAttribute key="org.eclipse.cdt.launch.COREFILE_PATH" value=""/>
+    <stringAttribute key="org.eclipse.cdt.launch.DEBUGGER_START_MODE" value="remote"/>
+    <booleanAttribute key="org.eclipse.cdt.launch.DEBUGGER_STOP_AT_MAIN" value="true"/>
+    <stringAttribute key="org.eclipse.cdt.launch.DEBUGGER_STOP_AT_MAIN_SYMBOL" value="main"/>
+    <stringAttribute key="org.eclipse.cdt.launch.PROGRAM_NAME" value="Debug/ISKBoard_QCTester.elf"/>
+    <stringAttribute key="org.eclipse.cdt.launch.PROJECT_ATTR" value="ISKBoard_QCTester"/>
+    <booleanAttribute key="org.eclipse.cdt.launch.PROJECT_BUILD_CONFIG_AUTO_ATTR" value="true"/>
+    <stringAttribute key="org.eclipse.cdt.launch.PROJECT_BUILD_CONFIG_ID_ATTR" value="com.st.stm32cube.ide.mcu.gnu.managedbuild.config.exe.debug.88492984"/>
+    <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+        <listEntry value="/ISKBoard_QCTester"/>
+    </listAttribute>
+    <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+        <listEntry value="4"/>
+    </listAttribute>
+    <stringAttribute key="org.eclipse.dsf.launch.MEMORY_BLOCKS" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&lt;memoryBlockExpressionList context=&quot;reserved-for-future-use&quot;&gt;&lt;gdbmemoryBlockExpression address=&quot;1342242392&quot; label=&quot;0x5000fe58&quot;/&gt;&lt;/memoryBlockExpressionList&gt;"/>
+    <stringAttribute key="process_factory_id" value="com.st.stm32cube.ide.mcu.debug.launch.HardwareDebugProcessFactory"/>
+    <stringAttribute key="saved_expressions&lt;seperator&gt;Unknown" value="0x5000fe58,0xfffffff1"/>
+</launchConfiguration>
diff --git a/Production/ISKBoard_QCTester/ISKBoard_QCTester.ioc b/Production/ISKBoard_QCTester/ISKBoard_QCTester.ioc
new file mode 100644
index 0000000..d41699e
--- /dev/null
+++ b/Production/ISKBoard_QCTester/ISKBoard_QCTester.ioc
@@ -0,0 +1,367 @@
+#MicroXplorer Configuration settings - do not modify
+ADC1.Channel-0\#ChannelRegularConversion=ADC_CHANNEL_15
+ADC1.Channel-1\#ChannelRegularConversion=ADC_CHANNEL_16
+ADC1.CommonPathInternal=null|null|null|null
+ADC1.ContinuousConvMode=ENABLE
+ADC1.IPParameters=Rank-0\#ChannelRegularConversion,Channel-0\#ChannelRegularConversion,SamplingTime-0\#ChannelRegularConversion,OffsetNumber-0\#ChannelRegularConversion,NbrOfConversionFlag,master,NbrOfConversion,Rank-1\#ChannelRegularConversion,Channel-1\#ChannelRegularConversion,SamplingTime-1\#ChannelRegularConversion,OffsetNumber-1\#ChannelRegularConversion,ContinuousConvMode,CommonPathInternal
+ADC1.NbrOfConversion=2
+ADC1.NbrOfConversionFlag=1
+ADC1.OffsetNumber-0\#ChannelRegularConversion=ADC_OFFSET_NONE
+ADC1.OffsetNumber-1\#ChannelRegularConversion=ADC_OFFSET_NONE
+ADC1.Rank-0\#ChannelRegularConversion=1
+ADC1.Rank-1\#ChannelRegularConversion=2
+ADC1.SamplingTime-0\#ChannelRegularConversion=ADC_SAMPLETIME_2CYCLES_5
+ADC1.SamplingTime-1\#ChannelRegularConversion=ADC_SAMPLETIME_2CYCLES_5
+ADC1.master=1
+CAD.formats=
+CAD.pinconfig=
+CAD.provider=
+CAN1.BS1=CAN_BS1_9TQ
+CAN1.BS2=CAN_BS2_6TQ
+CAN1.CalculateBaudRate=500000
+CAN1.CalculateTimeBit=2000
+CAN1.CalculateTimeQuantum=125.0
+CAN1.IPParameters=CalculateTimeQuantum,CalculateTimeBit,CalculateBaudRate,Prescaler,BS1,BS2
+CAN1.Prescaler=10
+File.Version=6
+GPIO.groupedBy=Group By Peripherals
+KeepUserPlacement=false
+LPUART1.BaudRate=115200
+LPUART1.IPParameters=BaudRate,WordLength
+LPUART1.WordLength=UART_WORDLENGTH_8B
+Mcu.CPN=STM32L431RCT6
+Mcu.Family=STM32L4
+Mcu.IP0=ADC1
+Mcu.IP1=CAN1
+Mcu.IP10=USART2
+Mcu.IP11=USART3
+Mcu.IP2=LPUART1
+Mcu.IP3=NVIC
+Mcu.IP4=RCC
+Mcu.IP5=SPI1
+Mcu.IP6=SYS
+Mcu.IP7=TIM1
+Mcu.IP8=TIM6
+Mcu.IP9=USART1
+Mcu.IPNb=12
+Mcu.Name=STM32L431R(B-C)Tx
+Mcu.Package=LQFP64
+Mcu.Pin0=PH0-OSC_IN (PH0)
+Mcu.Pin1=PH1-OSC_OUT (PH1)
+Mcu.Pin10=PA5
+Mcu.Pin11=PA6
+Mcu.Pin12=PA7
+Mcu.Pin13=PC4
+Mcu.Pin14=PC5
+Mcu.Pin15=PB0
+Mcu.Pin16=PB1
+Mcu.Pin17=PB2
+Mcu.Pin18=PB10
+Mcu.Pin19=PB11
+Mcu.Pin2=PC0
+Mcu.Pin20=PB12
+Mcu.Pin21=PB13
+Mcu.Pin22=PB14
+Mcu.Pin23=PC6
+Mcu.Pin24=PC7
+Mcu.Pin25=PC8
+Mcu.Pin26=PC9
+Mcu.Pin27=PA9
+Mcu.Pin28=PA10
+Mcu.Pin29=PA11
+Mcu.Pin3=PC1
+Mcu.Pin30=PA13 (JTMS-SWDIO)
+Mcu.Pin31=PA14 (JTCK-SWCLK)
+Mcu.Pin32=PA15 (JTDI)
+Mcu.Pin33=PC10
+Mcu.Pin34=PC11
+Mcu.Pin35=PC12
+Mcu.Pin36=PD2
+Mcu.Pin37=PB3 (JTDO-TRACESWO)
+Mcu.Pin38=PB4 (NJTRST)
+Mcu.Pin39=PB5
+Mcu.Pin4=PC2
+Mcu.Pin40=PB8
+Mcu.Pin41=PB9
+Mcu.Pin42=VP_TIM6_VS_ClockSourceINT
+Mcu.Pin5=PC3
+Mcu.Pin6=PA1
+Mcu.Pin7=PA2
+Mcu.Pin8=PA3
+Mcu.Pin9=PA4
+Mcu.PinsNb=43
+Mcu.ThirdPartyNb=0
+Mcu.UserConstants=
+Mcu.UserName=STM32L431RCTx
+MxCube.Version=6.8.0
+MxDb.Version=DB.6.0.80
+NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
+NVIC.CAN1_RX0_IRQn=true\:1\:0\:true\:false\:true\:true\:true\:true
+NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
+NVIC.EXTI15_10_IRQn=true\:0\:0\:false\:false\:true\:true\:true\:true
+NVIC.ForceEnableDMAVector=true
+NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
+NVIC.LPUART1_IRQn=true\:3\:0\:true\:false\:true\:true\:true\:true
+NVIC.MemoryManagement_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
+NVIC.NonMaskableInt_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
+NVIC.PendSV_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
+NVIC.PriorityGroup=NVIC_PRIORITYGROUP_4
+NVIC.SVCall_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
+NVIC.SysTick_IRQn=true\:15\:0\:false\:false\:true\:false\:true\:false
+NVIC.USART1_IRQn=true\:5\:0\:true\:false\:true\:true\:true\:true
+NVIC.USART2_IRQn=true\:0\:0\:false\:false\:true\:true\:true\:true
+NVIC.USART3_IRQn=true\:2\:0\:true\:false\:true\:true\:true\:true
+NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
+PA1.GPIOParameters=GPIO_Label
+PA1.GPIO_Label=mikrobus_pwm
+PA1.Locked=true
+PA1.Signal=GPIO_Input
+PA10.Mode=Asynchronous
+PA10.Signal=USART1_RX
+PA11.Signal=S_TIM1_CH4
+PA13\ (JTMS-SWDIO).Mode=Serial_Wire
+PA13\ (JTMS-SWDIO).Signal=SYS_JTMS-SWDIO
+PA14\ (JTCK-SWCLK).Mode=Serial_Wire
+PA14\ (JTCK-SWCLK).Signal=SYS_JTCK-SWCLK
+PA15\ (JTDI).GPIOParameters=GPIO_Label
+PA15\ (JTDI).GPIO_Label=mikrobus_cs
+PA15\ (JTDI).Locked=true
+PA15\ (JTDI).Signal=GPIO_Output
+PA2.Mode=Asynchronous
+PA2.Signal=USART2_TX
+PA3.Mode=Asynchronous
+PA3.Signal=USART2_RX
+PA4.GPIOParameters=GPIO_Speed,PinState,GPIO_Label
+PA4.GPIO_Label=SPI1_CS
+PA4.GPIO_Speed=GPIO_SPEED_FREQ_LOW
+PA4.Locked=true
+PA4.PinState=GPIO_PIN_SET
+PA4.Signal=GPIO_Output
+PA5.Locked=true
+PA5.Mode=Full_Duplex_Master
+PA5.Signal=SPI1_SCK
+PA6.Mode=Full_Duplex_Master
+PA6.Signal=SPI1_MISO
+PA7.Mode=Full_Duplex_Master
+PA7.Signal=SPI1_MOSI
+PA9.Mode=Asynchronous
+PA9.Signal=USART1_TX
+PB0.Signal=ADCx_IN15
+PB1.Signal=ADCx_IN16
+PB10.GPIOParameters=GPIO_Label
+PB10.GPIO_Label=mikrobus_scl
+PB10.Locked=true
+PB10.Signal=GPIO_Output
+PB11.GPIOParameters=GPIO_Label
+PB11.GPIO_Label=mikrobus_sda
+PB11.Locked=true
+PB11.Signal=GPIO_Output
+PB12.GPIOParameters=GPIO_Label,GPIO_ModeDefaultEXTI
+PB12.GPIO_Label=Key1
+PB12.GPIO_ModeDefaultEXTI=GPIO_MODE_IT_FALLING
+PB12.Locked=true
+PB12.Signal=GPXTI12
+PB13.GPIOParameters=GPIO_Label,GPIO_ModeDefaultEXTI
+PB13.GPIO_Label=Key2
+PB13.GPIO_ModeDefaultEXTI=GPIO_MODE_IT_FALLING
+PB13.Locked=true
+PB13.Signal=GPXTI13
+PB14.GPIOParameters=GPIO_Label,GPIO_ModeDefaultEXTI
+PB14.GPIO_Label=Key3
+PB14.GPIO_ModeDefaultEXTI=GPIO_MODE_IT_FALLING
+PB14.Locked=true
+PB14.Signal=GPXTI14
+PB2.GPIOParameters=PinState,GPIO_Label
+PB2.GPIO_Label=LedBlue
+PB2.Locked=true
+PB2.PinState=GPIO_PIN_SET
+PB2.Signal=GPIO_Output
+PB3\ (JTDO-TRACESWO).GPIOParameters=PinState,GPIO_Label
+PB3\ (JTDO-TRACESWO).GPIO_Label=WiFiRst
+PB3\ (JTDO-TRACESWO).Locked=true
+PB3\ (JTDO-TRACESWO).PinState=GPIO_PIN_SET
+PB3\ (JTDO-TRACESWO).Signal=GPIO_Output
+PB4\ (NJTRST).GPIOParameters=GPIO_Label
+PB4\ (NJTRST).GPIO_Label=mikrobus_int
+PB4\ (NJTRST).Locked=true
+PB4\ (NJTRST).Signal=GPIO_Output
+PB5.GPIOParameters=GPIO_Label
+PB5.GPIO_Label=mikrobus_rst
+PB5.Locked=true
+PB5.Signal=GPIO_Output
+PB8.Mode=CAN_Activate
+PB8.Signal=CAN1_RX
+PB9.Locked=true
+PB9.Mode=CAN_Activate
+PB9.Signal=CAN1_TX
+PC0.Mode=Asynchronous
+PC0.Signal=LPUART1_RX
+PC1.Mode=Asynchronous
+PC1.Signal=LPUART1_TX
+PC10.GPIOParameters=GPIO_Label
+PC10.GPIO_Label=mikrobus_sck
+PC10.Locked=true
+PC10.Signal=GPIO_Output
+PC11.GPIOParameters=GPIO_Label
+PC11.GPIO_Label=mikrobus_miso
+PC11.Locked=true
+PC11.Signal=GPIO_Output
+PC12.GPIOParameters=GPIO_Label
+PC12.GPIO_Label=mikrobus_mosi
+PC12.Locked=true
+PC12.Signal=GPIO_Output
+PC2.GPIOParameters=GPIO_Label
+PC2.GPIO_Label=StripLights
+PC2.Locked=true
+PC2.Signal=GPIO_Output
+PC3.GPIOParameters=GPIO_Label
+PC3.GPIO_Label=mikrobus_an
+PC3.Locked=true
+PC3.Signal=GPIO_Output
+PC4.Mode=Asynchronous
+PC4.Signal=USART3_TX
+PC5.Mode=Asynchronous
+PC5.Signal=USART3_RX
+PC6.GPIOParameters=PinState,GPIO_Label
+PC6.GPIO_Label=LedGreen
+PC6.Locked=true
+PC6.PinState=GPIO_PIN_SET
+PC6.Signal=GPIO_Output
+PC7.GPIOParameters=GPIO_Label
+PC7.GPIO_Label=RS485_Dir
+PC7.Locked=true
+PC7.Signal=GPIO_Output
+PC8.GPIOParameters=GPIO_Label
+PC8.GPIO_Label=CAN_STB
+PC8.Locked=true
+PC8.Signal=GPIO_Output
+PC9.GPIOParameters=PinState,GPIO_Label
+PC9.GPIO_Label=LedRed
+PC9.Locked=true
+PC9.PinState=GPIO_PIN_SET
+PC9.Signal=GPIO_Output
+PD2.GPIOParameters=GPIO_Label
+PD2.GPIO_Label=Relay
+PD2.Locked=true
+PD2.Signal=GPIO_Output
+PH0-OSC_IN\ (PH0).Mode=HSE-External-Oscillator
+PH0-OSC_IN\ (PH0).Signal=RCC_OSC_IN
+PH1-OSC_OUT\ (PH1).Mode=HSE-External-Oscillator
+PH1-OSC_OUT\ (PH1).Signal=RCC_OSC_OUT
+PinOutPanel.RotationAngle=0
+ProjectManager.AskForMigrate=true
+ProjectManager.BackupPrevious=false
+ProjectManager.CompilerOptimize=6
+ProjectManager.ComputerToolchain=false
+ProjectManager.CoupleFile=true
+ProjectManager.CustomerFirmwarePackage=
+ProjectManager.DefaultFWLocation=true
+ProjectManager.DeletePrevious=true
+ProjectManager.DeviceId=STM32L431RCTx
+ProjectManager.FirmwarePackage=STM32Cube FW_L4 V1.17.2
+ProjectManager.FreePins=false
+ProjectManager.HalAssertFull=false
+ProjectManager.HeapSize=0x200
+ProjectManager.KeepUserCode=true
+ProjectManager.LastFirmware=true
+ProjectManager.LibraryCopy=1
+ProjectManager.MainLocation=Core/Src
+ProjectManager.NoMain=false
+ProjectManager.PreviousToolchain=
+ProjectManager.ProjectBuild=false
+ProjectManager.ProjectFileName=ISKBoard_QCTester.ioc
+ProjectManager.ProjectName=ISKBoard_QCTester
+ProjectManager.ProjectStructure=
+ProjectManager.RegisterCallBack=
+ProjectManager.StackSize=0x400
+ProjectManager.TargetToolchain=STM32CubeIDE
+ProjectManager.ToolChainLocation=
+ProjectManager.UnderRoot=true
+ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_USART2_UART_Init-USART2-false-HAL-true,4-MX_USART3_UART_Init-USART3-false-HAL-true,5-MX_USART1_UART_Init-USART1-false-HAL-true,6-MX_LPUART1_UART_Init-LPUART1-false-HAL-true,7-MX_ADC1_Init-ADC1-false-HAL-true,8-MX_TIM6_Init-TIM6-false-HAL-true,9-MX_TIM1_Init-TIM1-false-HAL-true,10-MX_SPI1_Init-SPI1-false-HAL-true,11-MX_CAN1_Init-CAN1-false-HAL-true
+RCC.ADCFreq_Value=12000000
+RCC.AHBFreq_Value=80000000
+RCC.APB1Freq_Value=80000000
+RCC.APB1TimFreq_Value=80000000
+RCC.APB2Freq_Value=80000000
+RCC.APB2TimFreq_Value=80000000
+RCC.CortexFreq_Value=80000000
+RCC.FCLKCortexFreq_Value=80000000
+RCC.FamilyName=M
+RCC.HCLKFreq_Value=80000000
+RCC.HSE_VALUE=8000000
+RCC.HSI48_VALUE=48000000
+RCC.HSI_VALUE=16000000
+RCC.I2C1Freq_Value=80000000
+RCC.I2C2Freq_Value=80000000
+RCC.I2C3Freq_Value=80000000
+RCC.IPParameters=ADCFreq_Value,AHBFreq_Value,APB1Freq_Value,APB1TimFreq_Value,APB2Freq_Value,APB2TimFreq_Value,CortexFreq_Value,FCLKCortexFreq_Value,FamilyName,HCLKFreq_Value,HSE_VALUE,HSI48_VALUE,HSI_VALUE,I2C1Freq_Value,I2C2Freq_Value,I2C3Freq_Value,LPTIM1Freq_Value,LPTIM2Freq_Value,LPUART1Freq_Value,LSCOPinFreq_Value,LSE_VALUE,LSI_VALUE,MCO1PinFreq_Value,MSI_VALUE,PLLN,PLLPoutputFreq_Value,PLLQoutputFreq_Value,PLLRCLKFreq_Value,PLLSAI1N,PLLSAI1PoutputFreq_Value,PLLSAI1QoutputFreq_Value,PLLSAI1R,PLLSAI1RoutputFreq_Value,PLLSourceVirtual,PWRFreq_Value,RNGFreq_Value,SAI1Freq_Value,SDMMCFreq_Value,SWPMI1Freq_Value,SYSCLKFreq_VALUE,SYSCLKSource,USART1Freq_Value,USART2Freq_Value,USART3Freq_Value,VCOInputFreq_Value,VCOOutputFreq_Value,VCOSAI1OutputFreq_Value
+RCC.LPTIM1Freq_Value=80000000
+RCC.LPTIM2Freq_Value=80000000
+RCC.LPUART1Freq_Value=80000000
+RCC.LSCOPinFreq_Value=32000
+RCC.LSE_VALUE=32768
+RCC.LSI_VALUE=32000
+RCC.MCO1PinFreq_Value=80000000
+RCC.MSI_VALUE=4000000
+RCC.PLLN=20
+RCC.PLLPoutputFreq_Value=22857142.85714286
+RCC.PLLQoutputFreq_Value=80000000
+RCC.PLLRCLKFreq_Value=80000000
+RCC.PLLSAI1N=9
+RCC.PLLSAI1PoutputFreq_Value=10285714.285714285
+RCC.PLLSAI1QoutputFreq_Value=36000000
+RCC.PLLSAI1R=RCC_PLLR_DIV6
+RCC.PLLSAI1RoutputFreq_Value=12000000
+RCC.PLLSourceVirtual=RCC_PLLSOURCE_HSE
+RCC.PWRFreq_Value=80000000
+RCC.RNGFreq_Value=36000000
+RCC.SAI1Freq_Value=10285714.285714285
+RCC.SDMMCFreq_Value=36000000
+RCC.SWPMI1Freq_Value=80000000
+RCC.SYSCLKFreq_VALUE=80000000
+RCC.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK
+RCC.USART1Freq_Value=80000000
+RCC.USART2Freq_Value=80000000
+RCC.USART3Freq_Value=80000000
+RCC.VCOInputFreq_Value=8000000
+RCC.VCOOutputFreq_Value=160000000
+RCC.VCOSAI1OutputFreq_Value=72000000
+SH.ADCx_IN15.0=ADC1_IN15,IN15-Single-Ended
+SH.ADCx_IN15.ConfNb=1
+SH.ADCx_IN16.0=ADC1_IN16,IN16-Single-Ended
+SH.ADCx_IN16.ConfNb=1
+SH.GPXTI12.0=GPIO_EXTI12
+SH.GPXTI12.ConfNb=1
+SH.GPXTI13.0=GPIO_EXTI13
+SH.GPXTI13.ConfNb=1
+SH.GPXTI14.0=GPIO_EXTI14
+SH.GPXTI14.ConfNb=1
+SH.S_TIM1_CH4.0=TIM1_CH4,PWM Generation4 CH4
+SH.S_TIM1_CH4.ConfNb=1
+SPI1.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_4
+SPI1.CLKPhase=SPI_PHASE_2EDGE
+SPI1.CLKPolarity=SPI_POLARITY_HIGH
+SPI1.CalculateBaudRate=20.0 MBits/s
+SPI1.DataSize=SPI_DATASIZE_8BIT
+SPI1.Direction=SPI_DIRECTION_2LINES
+SPI1.IPParameters=VirtualType,Mode,Direction,CalculateBaudRate,DataSize,BaudRatePrescaler,CLKPolarity,CLKPhase
+SPI1.Mode=SPI_MODE_MASTER
+SPI1.VirtualType=VM_MASTER
+TIM1.Channel-PWM\ Generation4\ CH4=TIM_CHANNEL_4
+TIM1.IPParameters=Channel-PWM Generation4 CH4,Prescaler,Period,Pulse-PWM Generation4 CH4
+TIM1.Period=370-1
+TIM1.Prescaler=80-1
+TIM1.Pulse-PWM\ Generation4\ CH4=185
+TIM6.IPParameters=Prescaler,Period
+TIM6.Period=1
+TIM6.Prescaler=80-1
+USART1.IPParameters=VirtualMode-Asynchronous
+USART1.VirtualMode-Asynchronous=VM_ASYNC
+USART2.IPParameters=VirtualMode-Asynchronous
+USART2.VirtualMode-Asynchronous=VM_ASYNC
+USART3.IPParameters=VirtualMode-Asynchronous
+USART3.VirtualMode-Asynchronous=VM_ASYNC
+VP_TIM6_VS_ClockSourceINT.Mode=Enable_Timer
+VP_TIM6_VS_ClockSourceINT.Signal=TIM6_VS_ClockSourceINT
+board=custom
+isbadioc=false
diff --git a/Production/ISKBoard_QCTester/STM32L431RCTX_FLASH.ld b/Production/ISKBoard_QCTester/STM32L431RCTX_FLASH.ld
new file mode 100644
index 0000000..3786b04
--- /dev/null
+++ b/Production/ISKBoard_QCTester/STM32L431RCTX_FLASH.ld
@@ -0,0 +1,187 @@
+/*
+******************************************************************************
+**
+** @file        : LinkerScript.ld
+**
+** @author      : Auto-generated by STM32CubeIDE
+**
+** @brief       : Linker script for STM32L431RCTx Device from STM32L4 series
+**                      256Kbytes FLASH
+**                      64Kbytes RAM
+**                      16Kbytes RAM2
+**
+**                Set heap size, stack size and stack location according
+**                to application requirements.
+**
+**                Set memory bank area and size if external memory is used
+**
+**  Target      : STMicroelectronics STM32
+**
+**  Distribution: The file is distributed as is, without any warranty
+**                of any kind.
+**
+******************************************************************************
+** @attention
+**
+** Copyright (c) 2023 STMicroelectronics.
+** All rights reserved.
+**
+** This software is licensed under terms that can be found in the LICENSE file
+** in the root directory of this software component.
+** If no LICENSE file comes with this software, it is provided AS-IS.
+**
+******************************************************************************
+*/
+
+/* Entry Point */
+ENTRY(Reset_Handler)
+
+/* Highest address of the user mode stack */
+_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */
+
+_Min_Heap_Size = 0x200; /* required amount of heap */
+_Min_Stack_Size = 0x400; /* required amount of stack */
+
+/* Memories definition */
+MEMORY
+{
+  RAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 64K
+  RAM2    (xrw)    : ORIGIN = 0x10000000,   LENGTH = 16K
+  FLASH    (rx)    : ORIGIN = 0x8000000,   LENGTH = 256K
+}
+
+/* Sections */
+SECTIONS
+{
+  /* The startup code into "FLASH" Rom type memory */
+  .isr_vector :
+  {
+    . = ALIGN(4);
+    KEEP(*(.isr_vector)) /* Startup code */
+    . = ALIGN(4);
+  } >FLASH
+
+  /* The program code and other data into "FLASH" Rom type memory */
+  .text :
+  {
+    . = ALIGN(4);
+    *(.text)           /* .text sections (code) */
+    *(.text*)          /* .text* sections (code) */
+    *(.glue_7)         /* glue arm to thumb code */
+    *(.glue_7t)        /* glue thumb to arm code */
+    *(.eh_frame)
+
+    KEEP (*(.init))
+    KEEP (*(.fini))
+
+    . = ALIGN(4);
+    _etext = .;        /* define a global symbols at end of code */
+  } >FLASH
+
+  /* Constant data into "FLASH" Rom type memory */
+  .rodata :
+  {
+    . = ALIGN(4);
+    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
+    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
+    . = ALIGN(4);
+  } >FLASH
+
+  .ARM.extab   : {
+    . = ALIGN(4);
+    *(.ARM.extab* .gnu.linkonce.armextab.*)
+    . = ALIGN(4);
+  } >FLASH
+
+  .ARM : {
+    . = ALIGN(4);
+    __exidx_start = .;
+    *(.ARM.exidx*)
+    __exidx_end = .;
+    . = ALIGN(4);
+  } >FLASH
+
+  .preinit_array     :
+  {
+    . = ALIGN(4);
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+    . = ALIGN(4);
+  } >FLASH
+
+  .init_array :
+  {
+    . = ALIGN(4);
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT(.init_array.*)))
+    KEEP (*(.init_array*))
+    PROVIDE_HIDDEN (__init_array_end = .);
+    . = ALIGN(4);
+  } >FLASH
+
+  .fini_array :
+  {
+    . = ALIGN(4);
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(SORT(.fini_array.*)))
+    KEEP (*(.fini_array*))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+    . = ALIGN(4);
+  } >FLASH
+
+  /* Used by the startup to initialize data */
+  _sidata = LOADADDR(.data);
+
+  /* Initialized data sections into "RAM" Ram type memory */
+  .data :
+  {
+    . = ALIGN(4);
+    _sdata = .;        /* create a global symbol at data start */
+    *(.data)           /* .data sections */
+    *(.data*)          /* .data* sections */
+    *(.RamFunc)        /* .RamFunc sections */
+    *(.RamFunc*)       /* .RamFunc* sections */
+
+    . = ALIGN(4);
+    _edata = .;        /* define a global symbol at data end */
+
+  } >RAM AT> FLASH
+
+  /* Uninitialized data section into "RAM" Ram type memory */
+  . = ALIGN(4);
+  .bss :
+  {
+    /* This is used by the startup in order to initialize the .bss section */
+    _sbss = .;         /* define a global symbol at bss start */
+    __bss_start__ = _sbss;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+
+    . = ALIGN(4);
+    _ebss = .;         /* define a global symbol at bss end */
+    __bss_end__ = _ebss;
+  } >RAM
+
+  /* User_heap_stack section, used to check that there is enough "RAM" Ram  type memory left */
+  ._user_heap_stack :
+  {
+    . = ALIGN(8);
+    PROVIDE ( end = . );
+    PROVIDE ( _end = . );
+    . = . + _Min_Heap_Size;
+    . = . + _Min_Stack_Size;
+    . = ALIGN(8);
+  } >RAM
+
+  /* Remove information from the compiler libraries */
+  /DISCARD/ :
+  {
+    libc.a ( * )
+    libm.a ( * )
+    libgcc.a ( * )
+  }
+
+  .ARM.attributes 0 : { *(.ARM.attributes) }
+}

--
Gitblit v1.9.1