Simple PWM-based square wave works

This commit is contained in:
rbaron 2021-02-11 17:59:35 +01:00
parent a4adc0087d
commit ea4bd872bc
4 changed files with 61 additions and 15 deletions

View file

@ -2,12 +2,19 @@
A low power soil moisture sensor based on the nRF52840.
# TODO
* Hook square wave generator to the protoboard sensor circuit
* Make the protoboard sensor work
* Implement ADC; check out air/water range (using protoboard)
* Figure out how to calibrate the ADC when running from different voltages, as the battery discharges
* Implement deep sleep
* Measure power consumption in deep sleep
* Measure power consumption in transmission
* Implement BLE advertising with moisture, battery level
* Measure current in deep sleep
* Measure current in operation (ADC + BLE adversiting)
* Test with a range of 2-5V input. Disconnect from USB and monitor using a serial post. Use the bench power supply to power Vcc with variable voltage. *DO NOT CONNECT VCC FROM THE USB-TO-SERIAL BOARD!**
* Test with a coin cell
* Figure out a way for people to configure the device with a custom name. Idea: BLE service (this is what my [hacked xiaomi temp sensor](https://github.com/atc1441/ATC_MiThermometer) does)
* Figure out how OTA works (if at all) over BLE
* Design new board using the nrf52 instead of esp32
# Done
* Simple PWM square wave generator
* Simple PWM square wave generator
* Hook square wave generator to the protoboard sensor circuit
* Make the protoboard sensor work

View file

@ -22,11 +22,11 @@ void SetupSquareWave(double frequency, int pin_number) {
const int max_count = 16e6 / frequency;
// Since we want a duty cycle of 0.5, we flip the PVM output when the counter
// reaches kPWMMaxCounter / 2/;
// reaches kPWMMaxCounter / 2;
const int flip_at_count = max_count / 2;
HwPWM0.addPin(pin_number);
HwPWM0.setClockDiv(PWM_PRESCALER_PRESCALER_DIV_1);
HwPWM0.setClockDiv(kPWMFrequencyPrescale);
HwPWM0.setMaxValue(max_count);
HwPWM0.writePin(pin_number, flip_at_count);
HwPWM0.begin();

View file

@ -5,21 +5,23 @@
constexpr int kLED1Pin = 17;
constexpr int kLED2Pin = 18;
constexpr int kPWMPin = 19;
constexpr int kSensAnalogPin = 4; // AIN2
constexpr int kDischargeEnablePin = 16;
constexpr double kPWMFrequency = 500000;
void setup() {
Serial.begin(9600);
pinMode(kLED1Pin, OUTPUT);
pinMode(kLED2Pin, OUTPUT);
pinMode(kDischargeEnablePin, OUTPUT);
parasite::SetupSquareWave(kPWMFrequency, kPWMPin);
digitalWrite(kDischargeEnablePin, HIGH);
analogReference(AR_VDD4);
}
void loop() {
Serial.print("Hello, world\n");
digitalWrite(kLED1Pin, LOW);
digitalWrite(kLED2Pin, LOW);
delay(500);
digitalWrite(kLED1Pin, HIGH);
digitalWrite(kLED2Pin, HIGH);
int sens_val = analogRead(kSensAnalogPin);
Serial.printf("Val: %d\n", sens_val);
digitalToggle(kLED1Pin);
delay(500);
}

View file

@ -103,4 +103,41 @@ The [SoftDevice S132 spec](https://infocenter.nordicsemi.com/index.jsp?topic=%2F
# PWM
* [HardwarePWM.cpp](https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/a86fd9f36c88db96f676cead1e836377a37c7b05/cores/nRF5/HardwarePWM.cpp#L112) by Adafruit is a good reference. [Servo.c](https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/4d703b6f38262775863a16a603c12aa43d249f04/libraries/Servo/src/nrf52/Servo.cpp#L154) uses it.
* [nrf52832 pwm spec](https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52832.ps.v1.1%2Fpwm.html)
* [nrf5 SDK PWM reference](https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v11.0.0%2Fgroup__nrf__pwm__hal.html) HAL => Hardware Abstraction Layer?
* [nrf5 SDK PWM reference](https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v11.0.0%2Fgroup__nrf__pwm__hal.html) HAL => Hardware Abstraction Layer?
# Analog to digital converter (ADC)
* In nrf52-land, the ADC is called [SSADC](https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v15.0.0%2Fgroup__nrf__adc.html)
* Example in the sdk: in `examples/peripheral/saadc/main.c`
* [Docs from Adafruit](https://learn.adafruit.com/bluefruit-nrf52-feather-learning-guide/nrf52-adc). It uses Arduino's `analogRead`.
## Resoultion
The default resolution is 10 bits (1024 values),
## Reference value
The default reference is an internally supplied 3.6V. It seems like we woulld only be able to read values up to 3.6V. This might be enough if we're using a CR2032 coin cell, but might not if we use a LiPo (4.2V fully charged).
**Question**: what happens if Vcc < 3.6?
There is a possibility of using Vcc as a reference. Then I imagine 0 -> 0V; 1024 -> Vcc. This might be exactly what we want:
In a PWM positive pulse, our RC_parasitic will rise to 0.63Vcc in R*C_para time. If we use Vcc as a reference for the ADC, I think we "cancel out" the Vcc factor.
* How adafruit uses the Vcc reference: [link](https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/master/cores/nRF5/wiring_analog_nRF52.c#L76)
* How arduino-nRF5 uses the Vcc refenrece: [link](https://github.com/sandeepmistry/arduino-nRF5/blob/master/cores/nRF5/wiring_analog_nRF52.c#L98)
So in Arduino land, using AR_VDD4 does all we need: use nrf's SAADC_CH_CONFIG_REFSEL_VDD1_4 (Vcc/4 ref) and a gain of 4 (SAADC_CH_CONFIG_GAIN_Gain1_4).
While connected via USB, with:
```
analogReference(AR_VDD4);
int sens_val = analogRead(kSensAnalogPin);
```
I'm getting ~680 when in the air; ~65 while holding the sensor. The default resolution is 10 bits, so 1024 -> 3.3V. On the scope, I'm reading the sensor output value to be 2.16V. This matches the value I'm reading: 1024/3.3 * 2.15 = 667. Nice.
# Battery
* [CR2032 datasheet](https://data.energizer.com/pdfs/cr2032.pdf)
* 235 mAh (from 3.0V to 2.0V)
* Typical discharge current: 0.19 mA -> 1200 hours
# Battery monitoring
* Good post on how to measure lipo batteries: [link](https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/measuring-lithium-battery-voltage-with-nrf52#:~:text=To%20reduce%20the%20leakage%20current,of%2040%20us%2C%20see%20here.)
# OTA