Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OPAMP library for Uno R4 (WiFi, Minima) #97

Merged
merged 23 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
0379ba3
Add files via upload
maxgerhardt Aug 12, 2023
73d77f2
Declare architecture properly
maxgerhardt Aug 12, 2023
1c60323
Add OPAMP examples to CI
maxgerhardt Aug 12, 2023
e8a6cab
Fix CI
maxgerhardt Aug 12, 2023
8893eb9
Add files via upload
maxgerhardt Aug 12, 2023
c7ecddc
Add Fritzing circuit
maxgerhardt Aug 12, 2023
e37f4b0
Fix schematic
maxgerhardt Aug 12, 2023
12b7b85
Apply suggestions from code review
maxgerhardt Aug 12, 2023
c84a823
Minify PNGs
maxgerhardt Aug 13, 2023
8bc14eb
Merge branch 'main' into main
maxgerhardt Sep 1, 2023
c6f564e
Dont compile OTA examples for stable version
maxgerhardt Sep 1, 2023
18a59a5
Merge branch 'arduino:main' into main
maxgerhardt Sep 5, 2023
57c7f97
Revert removal of OTAUpdate CI run
maxgerhardt Sep 5, 2023
ef3472a
Rework begin(), end() to use channel masks, create isRunning() instead
maxgerhardt Sep 5, 2023
2720ef5
Add newline at end of file
maxgerhardt Sep 5, 2023
35d2d6e
Unify usage of space before brace for if, for
maxgerhardt Sep 5, 2023
7434247
Explain OPAMP channel pins in sketch code
maxgerhardt Sep 5, 2023
f340349
Center images and adjust size (can be further readjusted).
aentinger Sep 6, 2023
1098501
Replace default arguments with overloaded functions
maxgerhardt Sep 13, 2023
6dd13b1
Use const on all uint8_t params
maxgerhardt Sep 13, 2023
1ad825b
Make macro name more explicit, use official product name
maxgerhardt Sep 13, 2023
5a43138
Merge branch 'main' into main
aentinger Sep 14, 2023
9f82290
Apply suggestions from code review
maxgerhardt Sep 14, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/compile-examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ jobs:
additional-sketch-paths: |
- libraries/WiFiS3
- libraries/OTAUpdate
- libraries/OPAMP
- board:
fqbn: "arduino-git:renesas:minima"
additional-sketch-paths: |
- libraries/OPAMP

steps:
- name: Checkout repository
Expand Down
64 changes: 64 additions & 0 deletions libraries/OPAMP/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# OPAMP Libray for Uno R4 Minima & WiFi

## Description

The Arduino UNO R4 Minima and WiFi boards, both a featuring a Renesas R7FA4M1AB3CFM#AA0 microcontroller, do have a built-in OPAMP peripheral.

Operational amplifiers (or "op amp") are very versatile. They can:
* mirror an input voltage to its output ("voltage follower")
* amplify a small analog voltage to its output pin, output voltage range from 0 to ~4.7 V ("non-inverting amplifier")
* compare two input voltages and give a binary "higher" or "lower" output ("comparator")
* integrate and differentiate signals ("integrator", "differentiator")
* many more

Electrical characteristics:
* Input from 0.2 V (low speed) / 0. 3V (highspeed) to AVCC0 - 0.5 V (lowspeed) to AVCC0 - 0.6 V (high-speed)
* Output from 0.1 V to AVCC0 - 0.1 V
* Open gain: 120 dB typical
* Input offset voltage: -10 to 10 mV
* Gain-bandwidth product: 0.04 MHz (low-speed) / 1.7 MHz (high-speed)
* Load current: -100 to 100 µA max.

## Usage

To startup the opamp, simply include the library and call `OPAMP.begin(speed)`. As the optional `speed` argument to this function, can chose either `OPAMP_SPEED_LOWSPEED` as the low-speed (=lower power) mode or `OPAMP_SPEED_HIGHSPEED` as the high-speed, high-power mode.

```cpp
#include <OPAMP.h>

void setup () {
OPAMP.begin(OPAMP_SPEED_HIGHSPEED);
}

void loop() {}
```

## Pinout

Both the Uno R4 Minima and WiFi feature their OPAMP channel 0 output on the same pins:
* Analog A1: Plus
* Analog A2: Minus
* Analog A3: Output

<p align="center">
<img src="amp_symbol.png"></a>
</p>

(Vs+ is fixed to about 5V, Vs- is fixed to GND.)
## Testing

To test the OPAMP in the simplest possible "voltage follower" configuration, connect A2 to A3.
Then, any voltage applied at A1 should be mirrored onto A3. For example, if you connect A1 to GND, the OPAMP output should be GND.
Connect A1 to 3.3V, the output should be 3.3V.

For an amplifier circuit, see https://www.electronics-tutorials.ws/opamp/opamp_3.html. A simple 2x amplifier can be built by using e.g. two 10K resistors: Connect one resistor between "Minus" and GND. Then use the second resistor to connect the output and "Minus" together. Any signal input at the "Plus" will now appear with double the amplitude at the output pin. Of course, the input signal and the Arduino Uno R4 should share the same GND and the amplified output signal should not go above ~4.7V, otherwise clipping will appear.

Below is a capture of an oscilloscope in which a circa 2V square-wave (green, channel 2) is amplified to 4V square-wave (yellow, channel 1) with the aforementioned circuit. The input signal was generated by a function generator (and shared GND was connected).

<p align="center">
<img src="amp_screenshot.png" width="75%"></a>
</p>

<p align="center">
<img src="amp_circuit.png" width="40%"></a>
</p>
Binary file added libraries/OPAMP/amp_circuit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added libraries/OPAMP/amp_screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added libraries/OPAMP/amp_symbol.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions libraries/OPAMP/examples/start_opamp/start_opamp.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include <OPAMP.h>

void setup () {
Serial.begin(9600);
delay(2000); // serial monitor delay
// activate OPAMP, default channel 0
// Plus: Analog A1
// Minus: Analog A2
// Output: Analog A3
if (!OPAMP.begin(OPAMP_SPEED_HIGHSPEED)) {
Serial.println("Failed to start OPAMP!");
}
bool const isRunning = OPAMP.isRunning(0);
if (isRunning) {
Serial.println("OPAMP running on channel 0!");
} else {
Serial.println("OPAMP channel 0 is not running!");
}
}

void loop() {
delay(1000); // do nothing
}
10 changes: 10 additions & 0 deletions libraries/OPAMP/library.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name=OPAMP
version=1.0.0
author=Maximilian Gerhardt
maintainer=Maximilian Gerhardt <maximilian.gerhardt@rub.de>
sentence=Library for using the OPAMP on Arduino UNO R4 boards.
paragraph=Supports OPAMP on Arduino UNO R4 (Minima, WiFi).
category=Signal Input/Output
url=https://github.com/arduino/ArduinoCore-renesas/tree/main/libraries/OPAMP
architectures=renesas,renesas_uno
includes=OPAMP.h
113 changes: 113 additions & 0 deletions libraries/OPAMP/src/OPAMP.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#include "OPAMP.h"
#include <Arduino.h>

/* Make sure this library fails to compile for unsupported boards. */
#if !defined(ARDUINO_UNOWIFIR4) && !defined(ARDUINO_MINIMA)
aentinger marked this conversation as resolved.
Show resolved Hide resolved
#error "Unsupported board for OPAMP library."
aentinger marked this conversation as resolved.
Show resolved Hide resolved
#endif

/* pin mode needed to activate OPAMP functionality */
#define OPAMP_IN_PINCFG (IOPORT_CFG_PORT_DIRECTION_INPUT | IOPORT_CFG_PERIPHERAL_PIN | IOPORT_CFG_ANALOG_ENABLE)
#define OPAMP_OUT_PINCFG (IOPORT_CFG_PORT_DIRECTION_OUTPUT | IOPORT_CFG_PERIPHERAL_PIN | IOPORT_CFG_ANALOG_ENABLE)
#define FSP_CHECK(err) do { if( (err) != FSP_SUCCESS) return false; } while(0)

// Compact structure for OPAMP channel pins
struct opamp_channel_pins_t {
bsp_io_port_pin_t plus;
bsp_io_port_pin_t minus;
bsp_io_port_pin_t output;
};

// See Renesas RA4M1 Group Datasheet
// Note: Channel 0 is the only accessible one one the Arduino Minima boards.
static const opamp_channel_pins_t opamp_channels[] = {
{BSP_IO_PORT_00_PIN_00, BSP_IO_PORT_00_PIN_01, BSP_IO_PORT_00_PIN_02}, /* CH0 */
{BSP_IO_PORT_00_PIN_13, BSP_IO_PORT_00_PIN_12, BSP_IO_PORT_00_PIN_03}, /* CH1 */
{BSP_IO_PORT_00_PIN_11, BSP_IO_PORT_00_PIN_10, BSP_IO_PORT_00_PIN_04}, /* CH2 */
{BSP_IO_PORT_00_PIN_05, BSP_IO_PORT_00_PIN_06, BSP_IO_PORT_00_PIN_07}, /* CH3 */
};

bool OpampClass::initPins(uint8_t const channel_mask) {
fsp_err_t err;
ioport_instance_ctrl_t ioport_ctrl {};
// Make sure to return false if nothing was given to initialize
// or a too high channel bit is in there
if (channel_mask == 0 || channel_mask > 0b1111) {
return false;
}
// Check the 4 possible channels
for (uint8_t i = 0; i < 4; i++) {
// was this channel selected?
if (!(channel_mask & (1u << i))) {
continue;
}
opamp_channel_pins_t pins = opamp_channels[i];
err = R_IOPORT_PinCfg(&ioport_ctrl, pins.plus, OPAMP_IN_PINCFG);
FSP_CHECK(err);
err = R_IOPORT_PinCfg(&ioport_ctrl, pins.minus, OPAMP_IN_PINCFG);
FSP_CHECK(err);
err = R_IOPORT_PinCfg(&ioport_ctrl, pins.output, OPAMP_OUT_PINCFG);
FSP_CHECK(err);
}
// if we got here, none of the checks triggered an early return.
return true;
}

void OpampClass::initOpamp(OpampSpeedMode speed, uint8_t const channel_mask) {
uint8_t ampmc_val = 0U;
/* setup amplifier speed mode within amplifier mode control */
/* for all boards, this is at bit position 7 with either 0 (lowspeed) or 1 (highspeed) */
ampmc_val = (uint8_t) ((uint8_t) speed << R_OPAMP_AMPMC_AMPSP_Pos) & R_OPAMP_AMPMC_AMPSP_Msk;
aentinger marked this conversation as resolved.
Show resolved Hide resolved
/* reset opamp */
R_OPAMP->AMPC = 0U;
/* write prepared mode control value */
R_OPAMP->AMPMC = ampmc_val;
/* setup activation trigger select register */
/* we only support "Software start & stop" for now, value 0. */
R_OPAMP->AMPTRS = 0;
R_OPAMP->AMPTRM = 0;
/* set the bits for the activated channels */
R_OPAMP->AMPC |= channel_mask;
/* note: we don't have to activate the charge pump (AMPCPC) because here AVCC0 > 2.7V */
/* delay for the wanted init time in microseconds */
if (speed == OPAMP_SPEED_LOWSPEED) {
delayMicroseconds(BSP_FEATURE_OPAMP_MIN_WAIT_TIME_LP_US);
} else if (speed == OPAMP_SPEED_HIGHSPEED) {
delayMicroseconds(BSP_FEATURE_OPAMP_MIN_WAIT_TIME_HS_US);
}
}

bool OpampClass::begin() {
return this->begin(OPAMP_SPEED_HIGHSPEED);
}

bool OpampClass::begin(OpampSpeedMode const speed) {

return this->begin(1u << ARDUINO_UNO_R4_DEFAULT_OPAMP_CHANNEL, speed);
}

bool OpampClass::begin(uint8_t const channel_mask, OpampSpeedMode const speed) {

if (!initPins(channel_mask)) {
return false;
}
initOpamp(speed, channel_mask);
return true;
}

bool OpampClass::isRunning(uint8_t const channel) {
return (R_OPAMP->AMPMON & (1u << channel)) != 0;
}

void OpampClass::end() {
// deactivate all channels.
R_OPAMP->AMPC = 0;
}

void OpampClass::end(uint8_t const channel_mask) {
// deactivate given channels
R_OPAMP->AMPC &= ~channel_mask;
}

/* global instance */
OpampClass OPAMP;
57 changes: 57 additions & 0 deletions libraries/OPAMP/src/OPAMP.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#ifndef _OPAMP_H_
#define _OPAMP_H_

#include <stdint.h>

/* Available speed modes */
/* Note: No middle speed mode on the Uno R4 boards */
enum OpampSpeedMode {
OPAMP_SPEED_LOWSPEED = 0,
OPAMP_SPEED_HIGHSPEED = 1,
};

/* The supported boards have 4 OPAMP channels, however, only channel 0 is accessible. */
/* All other channels are connected to the LED matrix or not exposed. */
#define ARDUINO_UNO_R4_DEFAULT_OPAMP_CHANNEL 0

/**
* Pin Mapping for OPAMP
* Arduino UNO R4 (Minima, WiFi):
* ~Channel 0~
* Plus: Analog A1 (Renesas P0.00)
* Minus: Analog A2 (Renesas P0.01)
* Output: Analog A3 (Renesas P0.02)
* ~Channel 1~ (Inaccessible)
* +: P0.13, -: Renesas P0.12, Out: Renesas P0.03
* ~Channel 2~ (Inaccessible)
* +: P0.11, -: Renesas P0.10, Out: Renesas P0.04
* ~Channel 3~ (Inaccessible)
* +: P0.05, -: Renesas P0.06, Out: Renesas P0.07
*/
class OpampClass {
public:
/* startup the OPAMP on channel 0 in high-speed mode */
bool begin();
/* startup the OPAMP on channel 0 with specific mode */
bool begin(OpampSpeedMode const speed);

/* startup the OPAMP with arbitrary channel mask */
bool begin(uint8_t const channel_mask, OpampSpeedMode const speed);

/* stop all OPAMP channels */
void end();
/* stop specific OPAMP channel(s) */
void end(uint8_t const channel_mask);
/* returns true if the specified OPAMP channel number is running */
bool isRunning(uint8_t const channel);
private:
/* initializes OPAMP pins for given channel(s) */
bool initPins(uint8_t const channel_mask);
/* activates OPAMP for given speed and channel(s) */
void initOpamp(OpampSpeedMode const speed, uint8_t const channel_mask);

};

extern OpampClass OPAMP;

#endif /* _OPAMP_H_ */