M cmake/modules/ProjectConfig.cmake => cmake/modules/ProjectConfig.cmake +3 -0
@@ 24,6 24,9 @@ else()
set (LOG_REDIRECT "RTT_JLINK" CACHE INTERNAL "")
endif()
+# add CurrentMeasurement enable option
+option(CURRENT_MEASUREMENT "CURRENT_MEASUREMENT" OFF)
+
# add USB-CDC echo test enable option
option(USBCDC_ECHO "USBCDC_ECHO" OFF)
if (${USBCDC_ECHO} STREQUAL "ON")
M module-bsp/board/rt1051/puretx/CMakeLists.txt => module-bsp/board/rt1051/puretx/CMakeLists.txt +12 -0
@@ 11,6 11,7 @@ target_sources(
PRIVATE
hal/battery_charger/BatteryCharger.cpp
+ hal/battery_charger/CurrentMeasurementScope.cpp
hal/key_input/KeyInput.cpp
bsp/battery_charger/battery_charger.cpp
bsp/eink/eink_pin_config.cpp
@@ 30,3 31,14 @@ target_sources(
board/irq_gpio.hpp
board/BoardDefinitions.hpp
)
+
+if((${PROJECT_TARGET} STREQUAL "TARGET_RT1051") AND (${CURRENT_MEASUREMENT} STREQUAL "ON"))
+ set (ENABLE_CURRENT_MEASUREMENT_SCOPE 1 CACHE INTERNAL "")
+else()
+ set (ENABLE_CURRENT_MEASUREMENT_SCOPE 0 CACHE INTERNAL "")
+endif()
+
+target_compile_definitions(module-bsp
+ PRIVATE
+ ENABLE_CURRENT_MEASUREMENT_SCOPE=${ENABLE_CURRENT_MEASUREMENT_SCOPE}
+)
M module-bsp/board/rt1051/puretx/bsp/battery_charger/battery_charger.cpp => module-bsp/board/rt1051/puretx/bsp/battery_charger/battery_charger.cpp +9 -41
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "battery_charger.hpp"
@@ 55,7 55,7 @@ namespace bsp::battery_charger
/// Initial parameters for fuel gauge battery model
constexpr std::uint16_t LearnCFG = 0x2602;
- constexpr std::uint16_t FilterCFG = 0xCEA4;
+ constexpr std::uint16_t FilterCFG = 0xCEA0; // Current filter Tc = 0.7s
constexpr std::uint16_t MiscCFG = 0x01D0;
constexpr std::uint16_t RelaxCFG = 0x2039;
constexpr std::uint16_t RCOMP0 = 0x0070;
@@ 476,33 476,7 @@ namespace bsp::battery_charger
std::uint8_t temperatureInt = value.second >> 8;
return utils::twosComplimentToInt(temperatureInt);
}
- /*
- int getCurrentMeasurement()
- {
- auto value = fuelGaugeRead(Registers::Current_REG);
- int current;
- // 2's compliment into decimal
- if (value.second & 0x8000) {
- // negative numbers
- std::bitset<16> currentBitset = std::bitset<16>(value.second - 1);
- currentBitset.flip();
- current =
- static_cast<int>((static_cast<std::uint16_t>(currentBitset.to_ulong()) * -1) * currentSenseGain);
- }
- else {
- // positive numbers
- current = static_cast<int>(value.second * currentSenseGain);
- }
- return current;
- }
- int getCellVoltage()
- {
- auto value = fuelGaugeRead(Registers::VCELL_REG);
- int voltage = value.second * voltageSenseGain;
- return voltage;
- }
- */
void chargingFinishedAction()
{
LOG_DEBUG("Charging finished.");
@@ 750,19 724,13 @@ namespace bsp::battery_charger
int getAvgCurrent()
{
const auto [_, value] = fuelGaugeRead(Registers::AvgCurrent_REG);
- int current;
- // 2's compliment into decimal
- if (value & 0x8000) {
- // negative numbers
- std::bitset<16> currentBitset = std::bitset<16>(value - 1);
- currentBitset.flip();
- current = static_cast<int>((static_cast<std::uint16_t>(currentBitset.to_ulong()) * -1) * currentSenseGain);
- }
- else {
- // positive numbers
- current = static_cast<int>(value * currentSenseGain);
- }
- return current;
+ return static_cast<int>(utils::twosComplimentToInt(value) * currentSenseGain);
+ }
+
+ int getCurrentMeasurement()
+ {
+ auto [_, value] = fuelGaugeRead(Registers::Current_REG);
+ return static_cast<int>(utils::twosComplimentToInt(value) * currentSenseGain);
}
MaxMinVolt getMaxMinVolt()
M module-bsp/board/rt1051/puretx/bsp/battery_charger/battery_charger.hpp => module-bsp/board/rt1051/puretx/bsp/battery_charger/battery_charger.hpp +3 -1
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#pragma once
@@ 94,6 94,8 @@ namespace bsp::battery_charger
int getAvgCurrent();
+ int getCurrentMeasurement();
+
MaxMinVolt getMaxMinVolt();
void printFuelGaugeInfo();
M module-bsp/board/rt1051/puretx/hal/battery_charger/BatteryCharger.cpp => module-bsp/board/rt1051/puretx/hal/battery_charger/BatteryCharger.cpp +9 -1
@@ 3,6 3,10 @@
#include "BatteryCharger.hpp"
+#if ENABLE_CURRENT_MEASUREMENT_SCOPE
+#include "CurrentMeasurementScope.hpp"
+#endif
+
#include <bsp/battery_charger/battery_charger.hpp>
#include <hal/GenericFactory.hpp>
@@ 25,7 29,11 @@ namespace hal::battery
: eventsHandler(eventsHandler),
timerHandle{xTimerCreate(
"clearIrqStatusTimer", pdMS_TO_TICKS(clearStatusTickTime_MS), false, nullptr, clearIrqStatusHandler)}
- {}
+ {
+#if ENABLE_CURRENT_MEASUREMENT_SCOPE
+ CurrentMeasurementScope::start();
+#endif
+ }
void BatteryCharger::init(xQueueHandle queueBatteryHandle, xQueueHandle queueChargerDetect)
{
A module-bsp/board/rt1051/puretx/hal/battery_charger/CurrentMeasurementScope.cpp => module-bsp/board/rt1051/puretx/hal/battery_charger/CurrentMeasurementScope.cpp +42 -0
@@ 0,0 1,42 @@
+// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "CurrentMeasurementScope.hpp"
+
+extern "C"
+{
+#include "FreeRTOS.h"
+}
+#include <timers.h>
+#include <bsp/battery_charger/battery_charger.hpp>
+#include <ticks.hpp>
+
+#include <chrono>
+
+namespace hal::battery::CurrentMeasurementScope
+{
+ namespace
+ {
+ using namespace std::chrono_literals;
+ constexpr auto samplingTime = 100ms;
+ TimerHandle_t samplingTimerHandle;
+
+ static void getSample(TimerHandle_t xTimer)
+ {
+ LOG_DEBUG("[scope]: { \"timestamp\" : %ld, \"current\" : %d, \"current_filtered\" : %d }",
+ cpp_freertos::Ticks::TicksToMs(cpp_freertos::Ticks::GetTicks()),
+ bsp::battery_charger::getCurrentMeasurement(),
+ bsp::battery_charger::getAvgCurrent());
+ }
+ } // namespace
+
+ void start()
+ {
+ if (samplingTimerHandle == nullptr) {
+ samplingTimerHandle =
+ xTimerCreate("samplingTimer", pdMS_TO_TICKS(samplingTime.count()), true, nullptr, getSample);
+ }
+ xTimerStart(samplingTimerHandle, 0);
+ }
+
+} // namespace hal::battery::CurrentMeasurementScope
A module-bsp/board/rt1051/puretx/hal/battery_charger/CurrentMeasurementScope.hpp => module-bsp/board/rt1051/puretx/hal/battery_charger/CurrentMeasurementScope.hpp +9 -0
@@ 0,0 1,9 @@
+// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+namespace hal::battery::CurrentMeasurementScope
+{
+ void start();
+} // namespace hal::battery::CurrentMeasurementScope
A tools/plot_current_measurement.py => tools/plot_current_measurement.py +130 -0
@@ 0,0 1,130 @@
+#!/usr/bin/python3
+# Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+'''
+Tool for plotting realtime data provided through RTT via JLink.
+Data must be wrapped in string containing json with timestamp, current and current_filtered keywords.
+Specific line is detected by searching for SCOPE_KEYWORD. All other logs are printed in console.
+
+Usage:
+ 1. Before running the script JLinkServer must be running
+ 2. Max number of displayed samples is specified by MAX_SAMPLES_TO_DISPLAY
+ 3. Plot refresh rate is specified by REFRESH_INTERVAL_MS
+ 4. To log data into CSV file please provide --save_to_csv FILENAME
+'''
+
+import pylink
+import sys
+import time
+import json
+import matplotlib.pyplot as plt
+import numpy as np
+from matplotlib.animation import FuncAnimation
+import collections
+import argparse
+import csv
+
+try:
+ import thread
+except ImportError:
+ import _thread as thread
+
+# Plot constants
+MAX_SAMPLES_TO_DISPLAY = 200
+REFRESH_INTERVAL_MS = 100
+
+# Parser constants
+SCOPE_KEYWORD = '[scope]: '
+
+ts = collections.deque(np.zeros(MAX_SAMPLES_TO_DISPLAY))
+meas = collections.deque(np.zeros(MAX_SAMPLES_TO_DISPLAY))
+meas_flt = collections.deque(np.zeros(MAX_SAMPLES_TO_DISPLAY))
+fig = plt.figure(figsize=(12,6))
+ax = plt.subplot()
+csv_filename = str()
+
+def save_to_csv(to_save):
+ # save to csv if filename provided
+ if csv_filename:
+ with open(csv_filename, 'a') as file:
+ writer = csv.writer(file)
+ writer.writerow(to_save)
+
+def parse_lines(charbuf):
+ for line in charbuf.splitlines():
+ if SCOPE_KEYWORD in line:
+ try:
+ to_parse = line.split(SCOPE_KEYWORD)[1]
+ json_meas = json.loads(to_parse)
+
+ ts.popleft()
+ ts.append(int(json_meas['timestamp']))
+ meas.popleft()
+ meas.append(int(json_meas['current']))
+ meas_flt.popleft()
+ meas_flt.append(int(json_meas['current_filtered']))
+
+ save_to_csv([int(json_meas['timestamp']), int(json_meas['current']), int(json_meas['current_filtered'])])
+ except:
+ print("exception in json parse, corrupted data")
+ else:
+ print(line) # print other logs in console
+
+def read_rtt(jlink):
+ try:
+ while jlink.connected():
+ terminal_bytes = jlink.rtt_read(0, 1024)
+ if terminal_bytes:
+ parse_lines("".join(map(chr, terminal_bytes)))
+ time.sleep(0.01)
+ except Exception:
+ print("IO read thread exception, exiting...")
+ thread.interrupt_main()
+ raise
+
+def animation(i):
+ try:
+ ax.cla()
+ num_of_elems = int(np.nonzero(ts)[0][0]) # find first nonzero element in ts queue
+ ax.plot(ts, meas, 'b')
+ ax.plot(ts, meas_flt, 'g')
+
+ # plot styling
+ plt.title("Current measurement - b: inst, g: filtered", loc = 'left')
+ plt.xlabel("Timestamp [ms]")
+ plt.ylabel("Current [mA]")
+ plt.xlim([ts[num_of_elems], ts[-1]])
+ plt.grid(True)
+ except:
+ print("exception in plotter - no data provided")
+
+def main():
+ jlink = pylink.JLink()
+ print("connecting to JLink...")
+ jlink.open()
+ print("starting RTT...")
+ jlink.rtt_start()
+
+ ani = FuncAnimation(fig, animation, interval=REFRESH_INTERVAL_MS)
+
+ try:
+ thread.start_new_thread(read_rtt, (jlink,))
+ plt.show()
+ while jlink.connected():
+ time.sleep(1)
+ print("JLink disconnected, exiting...")
+ except KeyboardInterrupt:
+ print("ctrl-c detected, exiting...")
+ pass
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description="RTT realtime plotter")
+ parser.add_argument("--save_to_csv",
+ help="Save data to csv file. Provide filename as an argument.",
+ default='')
+
+ args = parser.parse_args()
+ csv_filename = args.save_to_csv
+
+ sys.exit(main())
M tools/requirements.txt => tools/requirements.txt +2 -0
@@ 8,3 8,5 @@ smmap==4.0.0
tqdm==4.62.2
urllib3==1.26.6
ghapi
+pylink
+matplotlib