|
| 1 | +--- |
| 2 | +title: 'Portenta C33' |
| 3 | +description: 'Learn how to use specific features on the Portenta C33 using MicroPython' |
| 4 | +author: 'Karl Söderby' |
| 5 | +--- |
| 6 | + |
| 7 | +In this guide, you will information related only to the [Arduino® Portenta C33](https://store.arduino.cc/products/portenta-c33) and MicroPython. |
| 8 | + |
| 9 | +## Pinout Mapping |
| 10 | + |
| 11 | +The Portenta C33 has two ways its pins are physically available: through its MKR-styled connectors and its High-Density connectors. Most pins are referred to via their port name or function. In the image below, the Portenta C33 MKR-styled connectors pinout is shown. |
| 12 | + |
| 13 | + |
| 14 | + |
| 15 | +The MKR-styled connectors pinout is mapped in MicroPython as follows: |
| 16 | + |
| 17 | +| **Arduino Pin Mapping** | **MicroPython Pin Mapping** | |
| 18 | +|:-----------------------:|:---------------------------:| |
| 19 | +| `P006`/`A0` | `P006` | |
| 20 | +| `P005`/`A1` | `P005` | |
| 21 | +| `P004`/`A2` | `P004` | |
| 22 | +| `P002`/`A3` | `P002` | |
| 23 | +| `P001`/`A4` | `P001` | |
| 24 | +| `P015`/`A5` | `P015` | |
| 25 | +| `P014`/`A6` | `P014` | |
| 26 | +| `P105`/`D0` | `P105` | |
| 27 | +| `P106`/`D1` | `P106` | |
| 28 | +| `P111`/`D2` | `P111` | |
| 29 | +| `P303`/`D3` | `P303` | |
| 30 | +| `P401`/`D4` | `P401` | |
| 31 | +| `P210`/`D5` | `P210` | |
| 32 | +| `P602` | `P602` | |
| 33 | +| `P110` | `P110` | |
| 34 | +| `P408` | `P408` | |
| 35 | +| `P407` | `P407` | |
| 36 | +| `P315` | `P315` | |
| 37 | +| `P204` | `P204` | |
| 38 | +| `P900` | `P900` | |
| 39 | +| `P402` | `P402` | |
| 40 | +| `P601` | `P601` | |
| 41 | + |
| 42 | +The complete MicroPython pinout is available [here](https://github.com/micropython/micropython/blob/master/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/pins.csv). |
| 43 | + |
| 44 | +## Input/Output Pins |
| 45 | + |
| 46 | +The `Pin` class in the `machine` module is essential for controlling Input/Output (I/O) pins of the Portenta C33 board. These pins are crucial for a wide range of applications, including reading sensor data, controlling actuators, and interfacing with other hardware components. |
| 47 | + |
| 48 | +### Pin Initialization |
| 49 | + |
| 50 | +To begin using an I/O pin of the Portenta C33 board with MicroPython, you need to initialize it using the `Pin` class from the `machine` module. This involves specifying the pin identifier and its mode (input, output, etc.). |
| 51 | + |
| 52 | +```python |
| 53 | +from machine import Pin |
| 54 | + |
| 55 | +# Initializing pin P107 as an output |
| 56 | +p107 = Pin('P107', Pin.OUT) |
| 57 | +``` |
| 58 | + |
| 59 | +### Configuring Pin Modes |
| 60 | + |
| 61 | +You can configure a pin as an input or output. For input pins, it's often useful to activate an internal pull-up or pull-down resistor. This helps to stabilize the input signal, especially in cases where the pin is reading a mechanical switch or a button. |
| 62 | + |
| 63 | +```python |
| 64 | +# Configuring pin P105 as an input with its pull-up resistor enabled |
| 65 | +p105 = Pin('P105', Pin.IN, Pin.PULL_UP) |
| 66 | +``` |
| 67 | + |
| 68 | +### Reading from and Writing to Pins |
| 69 | + |
| 70 | +To read a digital value from a pin, use the `.value()` method without any arguments. This is particularly useful for input pins. Conversely, to write a digital value, use the `.value()` method with an argument. Passing `1` sets the pin to `HIGH`, while `0` sets it to `LOW`. This is applicable to output pins. |
| 71 | + |
| 72 | +```python |
| 73 | +# Reading from P105 |
| 74 | +pin_value = p105.value() |
| 75 | + |
| 76 | +# Writing to P107 |
| 77 | +p107.value(1) # Set p2 to high |
| 78 | +``` |
| 79 | + |
| 80 | +### Advanced Pin Configuration |
| 81 | + |
| 82 | +The Pin class allows dynamic reconfiguration of pins and setting up interrupt callbacks. This feature is essential for creating responsive and interactive applications. |
| 83 | + |
| 84 | +```python |
| 85 | +# Reconfiguring P105 as an input with a pull-down resistor |
| 86 | +p105.init(Pin.IN, Pin.PULL_DOWN) |
| 87 | + |
| 88 | +# Setting up an interrupt on P105 |
| 89 | +p105.irq(lambda p: print("- IRQ triggered!", p)) |
| 90 | +``` |
| 91 | + |
| 92 | +### Practical Example |
| 93 | + |
| 94 | +In this example, we will configure one pin as an input to read the state of a button and another pin as an output to control an LED. The LED will turn on when the button is pressed and off when it's released. |
| 95 | + |
| 96 | +```python |
| 97 | +from machine import Pin |
| 98 | +import time |
| 99 | + |
| 100 | +# Configure pin P107 as an output (for the LED) |
| 101 | +led = Pin('P107', Pin.OUT_PP) |
| 102 | + |
| 103 | +# Configure pin P105 as input with pull-up resistor enabled (for the button) |
| 104 | +button = Pin('P105', Pin.IN, Pin.PULL_UP) |
| 105 | + |
| 106 | +while True: |
| 107 | + # Read the state of the button |
| 108 | + button_state = button.value() |
| 109 | + if button_state == 0: |
| 110 | + # Turn on LED if button is pressed (button_state is LOW) |
| 111 | + led.value(1) |
| 112 | + else: |
| 113 | + # Turn off LED if button is not pressed (button_state is HIGH) |
| 114 | + led.value(0) |
| 115 | + |
| 116 | + # Short delay to debounce the button |
| 117 | + time.sleep(0.1) |
| 118 | +``` |
| 119 | + |
| 120 | +This practical example demonstrates controlling an LED based on a button's state. The LED, connected to pin `P107` (configured as an output), is turned on or off depending on the button's input read from pin `P105` (set as an input with a pull-up resistor). The main loop continually checks the button's state; pressing the button fixes the LED on while releasing it turns the LED off. A brief delay is included for debouncing, ensuring stable operation without false triggers from the button. |
| 121 | + |
| 122 | +## Analog to Digital Converter |
| 123 | + |
| 124 | +The `ADC` class in MicroPython provides an interface for the Analog-to-Digital (ADC) converter of the Portenta C33 board, enabling the measurement of continuous voltages and their conversion into discrete digital values. This functionality is crucial for applications that, for example, require reading from analog sensors. The `ADC` class represents a single endpoint for sampling voltage from an analog pin and converting it to a digital value. |
| 125 | + |
| 126 | +The available ADC pins of the Portenta C33 board in MicroPython are the following: |
| 127 | + |
| 128 | +| **Available ADC Pins** | |
| 129 | +|:----------------------:| |
| 130 | +| `P006` | |
| 131 | +| `P005` | |
| 132 | +| `P004` | |
| 133 | +| `P002` | |
| 134 | +| `P001` | |
| 135 | +| `P015` | |
| 136 | +| `P014` | |
| 137 | +| `P000` | |
| 138 | + |
| 139 | +### Initializing the ADC |
| 140 | + |
| 141 | +First, to use an ADC of the Portenta C33 board, create an ADC object associated with a specific pin. This pin will be used to read analog values. |
| 142 | + |
| 143 | +```python |
| 144 | +from machine import ADC |
| 145 | + |
| 146 | +# Create an ADC object on a specific pin |
| 147 | +adc = ADC(pin) |
| 148 | +``` |
| 149 | + |
| 150 | +### Reading Analog Values |
| 151 | + |
| 152 | +You can read analog values as raw values using the `read_u16()` method. This method returns a raw integer from 0-65535, representing the analog reading. |
| 153 | + |
| 154 | +```python |
| 155 | +# Reading a raw analog value |
| 156 | +val = adc.read_u16() |
| 157 | +``` |
| 158 | + |
| 159 | +### Practical Example |
| 160 | + |
| 161 | +This example demonstrates the use of the `ADC` class to read values from a potentiometer on the Portenta C33 board. First, connect your potentiometer to the Portenta C33 board. One outer pin goes to `GND`, the other to `3V3`, and the middle pin to an analog-capable I/O pin, such as `P006`. This setup creates a variable voltage divider, with the voltage at the center pin changing as you adjust the potentiometer. |
| 162 | + |
| 163 | +```python |
| 164 | +from machine import ADC, Pin |
| 165 | +import time |
| 166 | + |
| 167 | +# Initialize the ADC on the potentiometer-connected pin |
| 168 | +pot_pin = Pin('P006') |
| 169 | +pot_adc = ADC(pot_pin) |
| 170 | + |
| 171 | +while True: |
| 172 | + # Read the raw analog value |
| 173 | + raw_value = pot_adc.read_u16() |
| 174 | + print("- Potentiometer raw value:", raw_value) |
| 175 | + |
| 176 | + # Delay for readability |
| 177 | + time.sleep(0.1) |
| 178 | +``` |
| 179 | +The example starts by importing the necessary modules and setting up the ADC on a pin connected to a potentiometer (`P006`). The ADC object (`pot_adc`) is used to interface with the potentiometer. Inside the loop, the analog value from the potentiometer is continuously read using the `read_u16()` method that provides a raw integer value scaled between `0` and `65535`, reflecting the potentiometer's position. The analog value value is printed to the console, and a short delay is included in the loop to ensure the output is readable. |
| 180 | + |
| 181 | +## Pulse Width Modulation |
| 182 | + |
| 183 | +Pulse Width Modulation (PWM) is a method to emulate an analog output using a digital pin. It does this by rapidly toggling the pin between low and high states. Two primary aspects define PWM behavior: |
| 184 | + |
| 185 | +- **Frequency**: This is the speed at which the pin toggles between low and high states. A higher frequency means the pin toggles faster. |
| 186 | +- **Duty cycle**: This refers to the ratio of the high state duration to the total cycle duration. A 100% duty cycle means the pin remains high all the time, while a 0% duty cycle means it stays low. |
| 187 | + |
| 188 | +The available PWM pins of the Portenta C33 board in MicroPython are the following: |
| 189 | + |
| 190 | +| **Available PWM Pins** | |
| 191 | +|:----------------------:| |
| 192 | +| `P105` | |
| 193 | +| `P106` | |
| 194 | +| `P111` | |
| 195 | +| `P303` | |
| 196 | +| `P401` | |
| 197 | +| `P601` | |
| 198 | + |
| 199 | +### Setting Up PWM |
| 200 | + |
| 201 | +To use PWM, start by initializing a pin and then creating a PWM object associated with that pin. |
| 202 | + |
| 203 | +```python |
| 204 | +import machine |
| 205 | + |
| 206 | +# Initialize a pin for PWM (e.g., pin P105) |
| 207 | +p105 = machine.Pin('P105') |
| 208 | +pwm1 = machine.PWM(p105) |
| 209 | +``` |
| 210 | + |
| 211 | +### Configuring PWM Parameters |
| 212 | + |
| 213 | +The frequency and duty cycle of the PWM signal are set based on the specific needs of your application: |
| 214 | + |
| 215 | +```python |
| 216 | +# Set the frequency to 500 Hz |
| 217 | +pwm1.freq(500) |
| 218 | + |
| 219 | +# Adjusting the duty cycle to 50 for 50% duty |
| 220 | +pwm1.duty(50) |
| 221 | +``` |
| 222 | + |
| 223 | +### Checking PWM Configuration |
| 224 | + |
| 225 | +You can check the current configuration of the PWM object by printing it: |
| 226 | + |
| 227 | +```python |
| 228 | +# Will show the current frequency and duty cycle |
| 229 | +print(pwm1) |
| 230 | +``` |
| 231 | + |
| 232 | +Retrieve the frequency and duty cycle values: |
| 233 | + |
| 234 | +```python |
| 235 | +current_freq = pwm1.freq() |
| 236 | +current_duty = pwm1.duty() |
| 237 | +``` |
| 238 | + |
| 239 | +### Deinitializing PWM |
| 240 | + |
| 241 | +When PWM is no longer needed, the pin can be deinitialized: |
| 242 | + |
| 243 | +```python |
| 244 | +pwm1.deinit() |
| 245 | +``` |
| 246 | + |
| 247 | +### Practical Example |
| 248 | + |
| 249 | +In this example, we will use PWM to control the brightness of an LED connected to pin `P105` of the Portenta C33 board. |
| 250 | + |
| 251 | +```python |
| 252 | +import machine |
| 253 | +import time |
| 254 | + |
| 255 | +# Configure the LED pin and PWM |
| 256 | +led_pin = machine.Pin('P105') |
| 257 | +led_pwm = machine.PWM(led_pin) |
| 258 | +led_pwm.freq(500) |
| 259 | + |
| 260 | +# Loop to vary brightness |
| 261 | +while True: |
| 262 | + # Increase brightness |
| 263 | + for duty in range(100): |
| 264 | + led_pwm.duty(duty) |
| 265 | + time.sleep(0.001) |
| 266 | + |
| 267 | + # Decrease brightness |
| 268 | + for duty in range(100, -1, -1): |
| 269 | + led_pwm.duty(duty) |
| 270 | + time.sleep(0.001) |
| 271 | +``` |
| 272 | + |
| 273 | +## Real-Time Clock |
| 274 | + |
| 275 | +The `RTC` class in MicroPython provides a way to manage and utilize the Real-Time Clock (RTC) of the Portenta C33 board. This feature is essential for applications that require accurate timekeeping, even when the main processor is not active. The RTC maintains accurate time and date, functioning independently from the main system. It continues to keep track of the time even when the board is powered off, as long as it's connected to a power source like a battery. |
| 276 | + |
| 277 | +### Initializing the RTC |
| 278 | + |
| 279 | +To use the RTC, first create an RTC object. This object is then used to set or read the current date and time. |
| 280 | + |
| 281 | + |
| 282 | +```python |
| 283 | +import machine |
| 284 | + |
| 285 | +# Create an RTC object |
| 286 | +rtc = machine.RTC() |
| 287 | +``` |
| 288 | + |
| 289 | +### Setting and Getting Date and Time |
| 290 | + |
| 291 | +The RTC allows you to set and retrieve the current date and time. The date and time are represented as an 8-tuple format. |
| 292 | + |
| 293 | +```python |
| 294 | +# Setting the RTC date and time |
| 295 | +rtc.datetime((2024, 1, 4, 4, 20, 0, 0, 0)) |
| 296 | + |
| 297 | +# Getting the current date and time |
| 298 | +current_datetime = rtc.datetime() |
| 299 | +print("- Current date and time:", current_datetime) |
| 300 | +``` |
| 301 | + |
| 302 | +The 8-tuple for the date and time follows the format `(year, month, day, weekday, hours, minutes, seconds, subseconds)`. |
| 303 | + |
| 304 | +### Practical Example |
| 305 | + |
| 306 | +A practical use case for the RTC is to add timestamps to sensor data readings. By setting the current time on the RTC, you can then append an accurate timestamp each time a sensor value is logged. |
| 307 | + |
| 308 | +```python |
| 309 | +import machine |
| 310 | + |
| 311 | +# Initialize the RTC and set the current datetime |
| 312 | +rtc.datetime((2024, 1, 4, 4, 20, 0, 0, 0)) |
| 313 | + |
| 314 | +# Function to read a sensor value (placeholder) |
| 315 | +def read_sensor(): |
| 316 | + # Replace with actual sensor reading logic |
| 317 | + return 42 |
| 318 | + |
| 319 | +# Read sensor value and get the current time |
| 320 | +sensor_value = read_sensor() |
| 321 | +timestamp = rtc.datetime() |
| 322 | + |
| 323 | +# Output the sensor value with its timestamp |
| 324 | +print("- Sensor value at ", timestamp, ":", sensor_value) |
| 325 | +``` |
| 326 | + |
| 327 | +In this example, every sensor reading is accompanied by a timestamp, which can be crucial for data analysis or logging purposes. The RTC's ability to maintain time independently of the main system's power status makes it reliable for time-sensitive applications. |
0 commit comments