Control RP2040 on Windows
Introducts relationship between Intel N100,RP2040 and 40-PIN GPIO
The Radxa X2L integrates an Intel J4125 CPU and a RP2040 MCU, which communicate with each other via USB and UART. The RP2040 exists as a USB memory device in the system, and the program can be burned by dragging and dropping the files to the USB device. In addition, they share the same UART communication interface, which enables Intel J4125 to control the RP2040 through UART, for example, to realize the control of RP2040 GPIO through the UART interface. The relationship between the two is shown in the following figure:
40 PIN Usage
In order to operate the IO resources on RP2040, we need a complete software environment, such as MicroPython or C/C++ SDK, here we mainly introduce a set of C/C++ SDKs, namely pico-sdk and pico-examples. pico-sdk mainly provides some APIs to operate the RP2040, while pico-examples provides a compilation framework for us to add our own programs according to the compilation framework provided by pico-examples. pico-sdk provides some APIs to operate the RP2040, while pico-examples provides a compilation framework for us to add our own programs according to the compilation framework provided by pico-examples.
Install necessary Tools
-
This tool facilitates the use of git commands and provides you with some Linux commands that you can use to build the
-
Download and unzip it.
Set Environment Variables
-
Search for ‘Edit the system environment’ in the search bar.
-
Click "Environment Variables"
-
Add system or user variables
- Add variables PICO_INSTALL_PATH and PICO-SDK_PATH
-
Ads PATH
Build
Open git bash, go to the pico-examples/build directory, and execute the following command to build the system
cmake -G "Ninja" ..
ninja
Flash
Press the BOOTSEL key and a USB device will pop up. Copy the compiled .uf2 file to the USB device of RP2040 and wait until the USB device disappears, then the programme will start to execute.
Examples
- GPIO
- I2C
- PWM
- UART
GPIO
1. Preparation
- One Radxa X2L
- One LED
2. Connection
Radxa X2L | <--> | LED |
---|---|---|
PIN_5 | <--> | LED |
PIN_1 | <--> | VCC |
PIN_9 | <--> | GND |
Here PIN_5 corresponds to GPIO29 in the following code, please refer to GPIO Definition for details.
3. Test
-
Replace pico-examples/blink/blink.c with the following code
blink.c
#include "pico/stdlib.h"
#define BLINK_PIN 29 // GPIO29
int main() {
gpio_init(BLINK_PIN);
gpio_set_dir(BLINK_PIN, GPIO_OUT);
while (true) {
gpio_put(BLINK_PIN, 1);
sleep_ms(250);
gpio_put(BLINK_PIN, 0);
sleep_ms(250);
}
} -
Compile
cd pico-examples/build/
rm -rf *
cmake -G "Ninja" ..
ninjaAfter successful compilation, a file named blink.uf2 will be created in the pico-examples/build/blink/ directory.
-
Flash
- Reboot RP2040
-
Drag the blink.uf2 file into the RP2040, and when the RP2040 disappears, the LED will start blinking.
I2C
1. Preparation
- One Radxa X2L
- One I2C OLED 1306
2. Connection
Radxa X2L | <--> | OLED |
---|---|---|
PIN_5 | <--> | SCL |
PIN_3 | <--> | SDA |
PIN_1 | <--> | VCC |
PIN_9 | <--> | GND |
Here PIN_5 corresponds to I2C0 SDA for GPIO29 in the following code, and PIN_3 corresponds to I2C0 SCL for GPIO28 in the following code, for details, please refer to GPIO Definition.
3. Test
- Replace the following code with pico-examples/i2c/lcd_1602_i2c/lcd_1602_i2c.c
lcd_1602_i2c.c
#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "pico/binary_info.h"
#define I2C_ID i2c0
#define I2C_SDA_PIN 28
#define I2C_SCL_PIN 29
// commands
const int LCD_CLEARDISPLAY = 0x01;
const int LCD_RETURNHOME = 0x02;
const int LCD_ENTRYMODESET = 0x04;
const int LCD_DISPLAYCONTROL = 0x08;
const int LCD_CURSORSHIFT = 0x10;
const int LCD_FUNCTIONSET = 0x20;
const int LCD_SETCGRAMADDR = 0x40;
const int LCD_SETDDRAMADDR = 0x80;
// flags for display entry mode
const int LCD_ENTRYSHIFTINCREMENT = 0x01;
const int LCD_ENTRYLEFT = 0x02;
// flags for display and cursor control
const int LCD_BLINKON = 0x01;
const int LCD_CURSORON = 0x02;
const int LCD_DISPLAYON = 0x04;
// flags for display and cursor shift
const int LCD_MOVERIGHT = 0x04;
const int LCD_DISPLAYMOVE = 0x08;
// flags for function set
const int LCD_5x10DOTS = 0x04;
const int LCD_2LINE = 0x08;
const int LCD_8BITMODE = 0x10;
// flag for backlight control
const int LCD_BACKLIGHT = 0x08;
const int LCD_ENABLE_BIT = 0x04;
// By default these LCD display drivers are on bus address 0x27
static int addr = 0x27;
// Modes for lcd_send_byte
#define LCD_CHARACTER 1
#define LCD_COMMAND 0
#define MAX_LINES 2
#define MAX_CHARS 16
/* Quick helper function for single byte transfers */
void i2c_write_byte(uint8_t val) {
i2c_write_blocking(I2C_ID, addr, &val, 1, false);
}
void lcd_toggle_enable(uint8_t val) {
// Toggle enable pin on LCD display
// We cannot do this too quickly or things don't work
#define DELAY_US 600
sleep_us(DELAY_US);
i2c_write_byte(val | LCD_ENABLE_BIT);
sleep_us(DELAY_US);
i2c_write_byte(val & ~LCD_ENABLE_BIT);
sleep_us(DELAY_US);
}
// The display is sent a byte as two separate nibble transfers
void lcd_send_byte(uint8_t val, int mode) {
uint8_t high = mode | (val & 0xF0) | LCD_BACKLIGHT;
uint8_t low = mode | ((val << 4) & 0xF0) | LCD_BACKLIGHT;
i2c_write_byte(high);
lcd_toggle_enable(high);
i2c_write_byte(low);
lcd_toggle_enable(low);
}
void lcd_clear(void) {
lcd_send_byte(LCD_CLEARDISPLAY, LCD_COMMAND);
}
// go to location on LCD
void lcd_set_cursor(int line, int position) {
int val = (line == 0) ? 0x80 + position : 0xC0 + position;
lcd_send_byte(val, LCD_COMMAND);
}
static void inline lcd_char(char val) {
lcd_send_byte(val, LCD_CHARACTER);
}
void lcd_string(const char *s) {
while (*s) {
lcd_char(*s++);
}
}
void lcd_init() {
lcd_send_byte(0x03, LCD_COMMAND);
lcd_send_byte(0x03, LCD_COMMAND);
lcd_send_byte(0x03, LCD_COMMAND);
lcd_send_byte(0x02, LCD_COMMAND);
lcd_send_byte(LCD_ENTRYMODESET | LCD_ENTRYLEFT, LCD_COMMAND);
lcd_send_byte(LCD_FUNCTIONSET | LCD_2LINE, LCD_COMMAND);
lcd_send_byte(LCD_DISPLAYCONTROL | LCD_DISPLAYON, LCD_COMMAND);
lcd_clear();
}
int main() {
i2c_init(I2C_ID, 100 * 1000);
gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C);
gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C);
gpio_pull_up(I2C_SDA_PIN);
gpio_pull_up(I2C_SCL_PIN);
// Make the I2C pins available to picotool
bi_decl(bi_2pins_with_func(I2C_SDA_PIN, I2C_SCL_PIN, GPIO_FUNC_I2C));
lcd_init();
static char *message[] =
{
"RP2040 by", "Raspberry Pi",
"A brand new", "microcontroller",
"Twin core M0", "Full C SDK",
"More power in", "your product",
"More beans", "than Heinz!"
};
while (1) {
for (int m = 0; m < sizeof(message) / sizeof(message[0]); m += MAX_LINES) {
for (int line = 0; line < MAX_LINES; line++) {
lcd_set_cursor(line, (MAX_CHARS / 2) - strlen(message[m + line]) / 2);
lcd_string(message[m + line]);
}
sleep_ms(2000);
lcd_clear();
}
}
}
-
Compile
cd pico-examples/build/
rm -rf *
cmake -G "Ninja" ..
ninjaAfter successful compilation, a file named lcd_1602_i2c.uf2 will be created in the pico-examples/build/i2c/lcd_1602_i2c/ directory
-
Flash
- Reboot RP2040
-
Drag the lcd_1602_i2c.uf2 file into the RP2040, and after the RP2040 disappears, the OLED will have a paragraph of text
PWM
1. Preparation
- One Radxa X2L
- One LED
2. Connection
Radxa X2L | <--> | LED |
---|---|---|
PIN_5 | <--> | LED |
PIN_1 | <--> | VCC |
PIN_9 | <--> | GND |
Here PIN_5 corresponds to GPIO29 in the following code, please refer to GPIO Definition for details.
3. Test
- Replace the following code with pico-examples/pwm/led_fade/pwm_led_fade.c
pwm_led_fade.c
#include "pico/stdlib.h"
#include <stdio.h>
#include "pico/time.h"
#include "hardware/irq.h"
#include "hardware/pwm.h"
#define PWD_FADE_LED_PIN 29
void on_pwm_wrap() {
static int fade = 0;
static bool going_up = true;
pwm_clear_irq(pwm_gpio_to_slice_num(PWD_FADE_LED_PIN));
if (going_up) {
++fade;
if (fade > 255) {
fade = 255;
going_up = false;
}
} else {
--fade;
if (fade < 0) {
fade = 0;
going_up = true;
}
}
pwm_set_gpio_level(PWD_FADE_LED_PIN, fade * fade);
}
int main() {
gpio_set_function(PWD_FADE_LED_PIN, GPIO_FUNC_PWM);
uint slice_num = pwm_gpio_to_slice_num(PWD_FADE_LED_PIN);
pwm_clear_irq(slice_num);
pwm_set_irq_enabled(slice_num, true);
irq_set_exclusive_handler(PWM_IRQ_WRAP, on_pwm_wrap);
irq_set_enabled(PWM_IRQ_WRAP, true);
pwm_config config = pwm_get_default_config();
pwm_config_set_clkdiv(&config, 4.f);
pwm_init(slice_num, &config, true);
while (1)
tight_loop_contents();
}
-
Compile
cd pico-examples/build/
rm -rf *
cmake -G "Ninja" ..
ninjaAfter successful compilation, a file named pwm_led_fade.uf2 will be created in the pico-examples/build/pwm/led_fade/ directory.
-
Flash
- Reboot RP2040
-
Drag the pwm_led_fade.uf2 file into the RP2040, after the RP2040 disappears, the LED will fade from dark to light/bright to dark.
UART
- Replace pico-examples/uart/hello_uart/hello_uart.c with the following code
hello_uart.c
/**
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/uart.h"
/// \tag::hello_uart[]
#define UART_ID uart0
#define BAUD_RATE 115200
// We are using pins 0 and 1, but see the GPIO function select table in the
// datasheet for information on which other pins can be used.
#define UART_TX_PIN 0
#define UART_RX_PIN 1
int main() {
// Set up our UART with the required speed.
uart_init(UART_ID, BAUD_RATE);
// Set the TX and RX pins by using the function select on the GPIO
// Set datasheet for more information on function select
gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART);
gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART);
// Use some the various UART functions to send out data
// In a default system, printf will also output via the default UART
// Send out a character without any conversions
uart_putc_raw(UART_ID, 'A');
// Send out a character but do CR/LF conversions
uart_putc(UART_ID, 'B');
// Send out a string, with CR/LF conversions
while(1) {
uart_puts(UART_ID, "Hello, UART!\r\n");
sleep_ms(1000);
}
}
/// \end::hello_uart[]
- Replace pico-examples/uart/hello_uart/CMakeLists.txt with the following code
CMakeLists.txt
add_executable(hello_uart
hello_uart.c
)
# pull in common dependencies
target_link_libraries(hello_uart pico_stdlib)
# create map/bin/hex file etc.
pico_add_extra_outputs(hello_uart)
# add url via pico_set_program_url
example_auto_set_url(hello_uart)
pico_enable_stdio_usb(hello_uart 1)
pico_enable_stdio_uart(hello_uart 1)
-
Compile
cd pico-examples/build/
rm -rf *
cmake -G "Ninja" ..
ninja
After successful compilation, a file named hello_uart.uf2 will be created in the pico-examples/build/uart/hello_uart/ directory.
-
Flash
- Reboot RP2040
-
Drag the hello_uart.uf2 file into the RP2040, after the RP2040 disappears, the program starts to execute.
-
Enter the following command in the terminal to view the serial port output
sudo minicom -D /dev/ttyS0 -b 115200
- Verification
In minicom, you will see the terminal output the line "Hello, UART!" every second.