中華 NanoVNA にバッテリー表示をつける
回路図の D2 はバッテリーから MCU の VBAT に接続する経路ですが、自分の入手した固体だと未実装でした。せっかくなので、手元にあった適当なダイオードをひとまず半田付けして、ファームウェア側の実装を試してみました。使ったのは汎用小信号用ダイオードの 1N4148 の SMD 版 (SOD-323) ですが、本来はショットキーのほうが良いはずです。
パッチの全体
- 現在の master (ec8c53bde3f05d2541d6ffec69f030711162468c) に対するパッチです
- https://github.com/cho45/NanoVNA/commit/vbat
D2 の実装が必要なことと、中華版のバッテリーが載っているモデルにしか適用できないので (一応 VBAT を見た結果、バッテリが載っていないあるいは D2 がない場合には無視するようにはしてありますが)、オリジナルの master には入れることができないパッチです。自分でパッチを管理する必要があります。
master に入れてもらったので、自分でパッチ管理する必要はなくなりました。ありがたや。D2 を実装したうえで最新ファームを書くだけで有効になります。
adc_vbat_read()
int16_t adc_vbat_read(ADC_TypeDef *adc)
{
#define ADC_FULL_SCALE 3300
#define VBAT_DIODE_VF 500
#define VREFINT_CAL (*((uint16_t*)0x1FFFF7BA))
float vbat = 0;
float vrefint = 0;
ADC->CCR |= ADC_CCR_VREFEN | ADC_CCR_VBATEN;
// VREFINT == ADC_IN17
vrefint = adc_single_read(adc, ADC_CHSELR_CHSEL17);
// VBAT == ADC_IN18
// VBATEN enables resiter devider circuit. It consume vbat power.
vbat = adc_single_read(adc, ADC_CHSELR_CHSEL18);
ADC->CCR &= ~(ADC_CCR_VREFEN | ADC_CCR_VBATEN);
uint16_t vbat_raw = (ADC_FULL_SCALE * VREFINT_CAL * vbat * 2 / (vrefint * ((1<<12)-1)));
if (vbat_raw < 100) {
// maybe D2 is not installed
return -1;
}
return vbat_raw + VBAT_DIODE_VF;
} 一応 VREFINT_CAL を使って補正をかけています。面倒なので計算に float 使ってますが、返り値は mV 単位の int16_t です。
ADCサイクル数
既存の adc_single_read() では ADC のサンプリングサイクルが最小になっているのですが、これだと VBAT から内部キャパシタを十分に充電できないようで、ADC で取得できる値がやたら低くく、うまくいきませんでした。ということで ADC_SMPR_SMP_239P5 までサイクル数を増やしました。
どこでバッテリ残量をとるか
そんなに頻繁に残量をとってもしかたないのですが、面倒なので sweep 直後に毎回取得するようにました。
if (vbat != -1) {
adc_stop(ADC1);
vbat = adc_vbat_read(ADC1);
touch_start_watchdog();
draw_battery_status();
} 表示
5x7 のフォントとして残量アイコンを実装しようと最初は考えていましたが、小さすぎるので、自力でモリモリ描くことにしました。
void
draw_battery_status(void)
{
int w = 10, h = 14;
int x = 0, y = 0;
int i, c;
uint16_t *buf = spi_buffer;
uint8_t vbati = vbat2bati(vbat);
uint16_t col = vbati == 0 ? RGB565(0, 255, 0) : RGB565(0, 0, 240);
memset(spi_buffer, 0, w * h * 2);
// battery head
x = 3;
buf[y * w + x++] = col;
buf[y * w + x++] = col;
buf[y * w + x++] = col;
buf[y * w + x++] = col;
y++;
x = 3;
buf[y * w + x++] = col;
x++; x++;
buf[y * w + x++] = col;
y++;
x = 1;
for (i = 0; i < 8; i++)
buf[y * w + x++] = col;
for (c = 0; c < 3; c++) {
y++;
x = 1;
buf[y * w + x++] = col;
x++; x++; x++; x++; x++; x++;
buf[y * w + x++] = col;
y++;
x = 1;
buf[y * w + x++] = col;
x++;
for (i = 0; i < 4; i++)
buf[y * w + x++] = ( ((c+1) * 25) >= (100 - vbati)) ? col : 0;
x++;
buf[y * w + x++] = col;
y++;
x = 1;
buf[y * w + x++] = col;
x++;
for (i = 0; i < 4; i++)
buf[y * w + x++] = ( ((c+1) * 25) >= (100 - vbati)) ? col : 0;
x++;
buf[y * w + x++] = col;
}
// battery foot
y++;
x = 1;
buf[y * w + x++] = col;
x++; x++; x++; x++; x++; x++;
buf[y * w + x++] = col;
y++;
x = 1;
for (i = 0; i < 8; i++)
buf[y * w + x++] = col;
ili9341_bulk(0, 1, w, h);
} vbat2bati は以下のような inline 関数です。閾値がいまいちわからないので、結構雑に設定してます。
// convert vbat [mV] to battery indicator
static inline uint8_t vbat2bati(int16_t vbat)
{
if (vbat < 3200) return 0;
if (vbat < 3450) return 25;
if (vbat < 3700) return 50;
if (vbat < 3950) return 75;
return 100;
} 関連エントリー
- STM32F103 で内部温度センサを読み出す 内部に温度センサを内蔵しており、追加の部品なしにある程度の温度は知ることができる。あまり正確とはいえないが相対的に温度の動きを見るには十分 ...
- mbed USBSerial を WebUSB から扱うには mbed USBDevice ライブラリの中に USB CDC で動く USBSerial クラスが実装されている。これを Web USB ...
- ST7735S 使用の 128x160 TFT SPI 液晶 だいぶ前に買ったのを放置してたので動かした。なぜかSDカードコネクタとかついてる。最初は 5V インターフェイスでレギュレータを使うようにな...
- LM1972 デジタルボリュームを mbed で LM1972 デジタルボリューム | tech - 氾濫原 の続きで、Arduino ではなくmbed環境での実装。 LPC11U35 で試...
- Node.js の fs モジュールだけで GPIO の割込みを扱うには? 以下のようにすれば動くことが確認できた。 select や poll を明示的に呼び出すことができないが、edge を設定しさえすれば fs...

.png)






 (1).png)
.png)


