PimoroniボードをPicoで動かす
2021/2/17
初版
rpi Pico Pimoroni board
rpi Pico Pimoroni board
概要
以下のPimoroniボードをPicoで動かす
デモとしてC++のデモとmicropythonのデモを動かしてみる。
(ホストPCとしてはubuntuを想定している)
・Pico Display Pack
・Pico Unicorn Pack
C++デモのビルド
以下の手順でビルドする:
# Pico-SDKがインストールされている前提
export PICO_SDK_PATH=~/pico/pico-sdk
cd ~/pico
git clone https://github.com/pimoroni/pimoroni-pico.git
cd pimoroni-pico
mkdir build
cd build
cmake ..
make
# 以上でデモプログラムのuf2fが出来る
C++デモの書き込み
# pico_unicorn_plasma デモを書き込む(unicornボードの場合)
cd ~/pico/pimoroni-pico/build/examples/pico_unicorn_plasma
cp unicornplasma.uf2 /media/<USER>/RPI-RP2/
# pico_display デモを書き込む(displayボードの場合)
cd ~/pico/pimoroni-pico/build/examples/pico_display
cp display.uf2 /media/<USER>/RPI-RP2/
<USER>は、環境依存なので、自分の環境に合わせる。
C++デモ・ソース
以下、デモのソース:(特に修正していない)
pico_display/demo.cpp
#include <string.h>
#include <math.h>
#include <vector>
#include <cstdlib>
#include "pico_display.hpp"
using namespace pimoroni;
extern unsigned char image_tif[];
extern unsigned int image_tif_len;
uint16_t buffer[PicoDisplay::WIDTH * PicoDisplay::HEIGHT];
PicoDisplay pico_display(buffer);
/*
void pixel(int x, int y, uint16_t c) {
x *= 2;
y *= 2;
pico_display.frame_buffer[x + y * 240] = c;
pico_display.frame_buffer[x + 1 + y * 240] = c;
pico_display.frame_buffer[x + 1 + (y + 1) * 240] = c;
pico_display.frame_buffer[x + (y + 1) * 240] = c;
}
void rect(int x, int y, int w, int h, uint16_t c) {
for(int rx = x; rx < x + w; rx++) {
for(int ry = y; ry < y + h; ry++) {
pixel(rx, ry, c);
}
}
}*/
uint8_t arrow[] = {
0b00010000,
0b00110000,
0b01110000,
0b11111111,
0b11111111,
0b01110000,
0b00110000,
0b00010000
};
uint8_t tick[] = {
0b00000000,
0b00000010,
0b00000111,
0b01001110,
0b11111100,
0b00111000,
0b00010000,
0b00000000,
};
/*
void sprite(uint8_t *p, int x, int y, bool flip, uint16_t c) {
for(int ay = 0; ay < 8; ay++) {
uint8_t sl = p[ay];
for(int ax = 0; ax < 8; ax++) {
if(flip) {
if((0b10000000 >> ax) & sl) {
pixel(ax + x, ay + y, c);
}
}else{
if((0b1 << ax) & sl) {
pixel(ax + x, ay + y, c);
}
}
}
}
}*/
int main() {
pico_display.init();
pico_display.set_backlight(100);
// uint16_t white = pico_display.create_pen(255, 255, 255);
// uint16_t black = pico_display.create_pen(0, 0, 0);
// uint16_t red = pico_display.create_pen(255, 0, 0);
// uint16_t green = pico_display.create_pen(0, 255, 0);
// uint16_t dark_grey = pico_display.create_pen(20, 40, 60);
// uint16_t dark_green = pico_display.create_pen(10, 100, 10);
// uint16_t blue = pico_display.create_pen(0, 0, 255);
bool a_pressed = false;
bool b_pressed = false;
bool x_pressed = false;
bool y_pressed = false;
struct pt {
float x;
float y;
uint8_t r;
float dx;
float dy;
uint16_t pen;
};
std::vector<pt> shapes;
for(int i = 0; i < 1000; i++) {
pt shape;
shape.x = rand() % 240;
shape.y = rand() % 135;
shape.r = (rand() % 10) + 3;
shape.dx = float(rand() % 255) / 128.0f;
shape.dy = float(rand() % 255) / 128.0f;
shape.pen = pico_display.create_pen(rand() % 255, rand() % 255, rand() % 255);
shapes.push_back(shape);
}
uint32_t i = 0;
while(true) {
pico_display.set_pen(120, 40, 60);
pico_display.clear();
for(auto &shape : shapes) {
shape.x += shape.dx;
shape.y += shape.dy;
if(shape.x < 0) shape.dx *= -1;
if(shape.x >= pico_display.bounds.w) shape.dx *= -1;
if(shape.y < 0) shape.dy *= -1;
if(shape.y >= pico_display.bounds.h) shape.dy *= -1;
pico_display.set_pen(shape.pen);
pico_display.circle(Point(shape.x, shape.y), shape.r);
}
float led_step = fmod(i / 20.0f, M_PI * 2.0f);
int r = (sin(led_step) * 25.0f) + 25.0f;
pico_display.set_led(r, r / 1.2f, r);
std::vector<Point> poly;
poly.push_back(Point(30, 30));
poly.push_back(Point(50, 35));
poly.push_back(Point(70, 25));
poly.push_back(Point(80, 65));
poly.push_back(Point(50, 85));
poly.push_back(Point(30, 45));
pico_display.set_pen(255, 255, 0);
pico_display.polygon(poly);
pico_display.set_pen(0, 255, 255);
pico_display.triangle(Point(50, 50), Point(130, 80), Point(80, 110));
pico_display.set_pen(255, 255, 255);
pico_display.line(Point(50, 50), Point(120, 80));
pico_display.line(Point(20, 20), Point(120, 20));
pico_display.line(Point(20, 20), Point(20, 120));
for(int r = 0; r < 30; r++) {
for(int j = 0; j < 10; j++) {
float rads = ((M_PI * 2) / 30.0f) * float(r);
rads += (float(i) / 100.0f);
rads += (float(j) / 100.0f);
float cx = sin(rads) * 300.0f;
float cy = cos(rads) * 300.0f;
pico_display.line(Point(120, 67), Point(cx + 120, cy + 67));
}
}
/*
if(pico_display.is_pressed(pico_display.A)) {
pico_display.rectangle(0, 0, 18, 18);
//sprite(tick, 5, 5, true, green);
}else{
//sprite(arrow, 10 + bounce, 10, true, white);
}
if(pico_display.is_pressed(pico_display.B)) {
pico_display.rectangle(0, 49, 18, 18);
//sprite(tick, 5, 54, true, green);
}else{
//sprite(arrow, 10 - bounce, 50, true, white);
}
if(pico_display.is_pressed(pico_display.X)) {
pico_display.rectangle(102, 0, 18, 18);
//sprite(tick, 107, 5, true, green);
}else{
//sprite(arrow, 102 - bounce, 10, false, white);
}
if(pico_display.is_pressed(pico_display.Y)) {
pico_display.rectangle(102, 49, 18, 18);
//sprite(tick, 107, 54, true, green);
}else{
//sprite(arrow, 102 + bounce, 50, false, white);
}
*/
// update screen
pico_display.update();
i++;
}
return 0;
}
pico_display/image_data.cpp
unsigned char image_tif[] = {
0x49, 0x49, 0x2a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x11, 0x00, 0xfe, 0x00,
0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x01,
0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x02, 0x01,
0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, 0x03, 0x01,
0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x01,
0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x11, 0x01,
0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0x12, 0x01,
<>
0x08, 0x0a, 0x04, 0x08, 0x09, 0x04, 0x07, 0x08, 0x03, 0x07, 0x08, 0x03,
0x07, 0x08, 0x03, 0x07, 0x07, 0x02, 0x06, 0x07, 0x03, 0x06, 0x07, 0x02,
0x06, 0x07, 0x03, 0x06, 0x07, 0x02, 0x06, 0x07, 0x03, 0x06, 0x06, 0x02,
0x06, 0x07, 0x03, 0x06
};
unsigned int image_tif_len = 97492;
pico_unicorn_plasma/demo.cpp
/*
Pico Unicorn Plasma Example
Written by Tim Kerby https://github.com/tkerby
Hardware: Raspberry Pi Pico with Pimoroni Unicorn Pico Display
Printf over USB 9600 8N1 (see makefile)
Based on Adafruit Arduino Example for 32x32 panel
Originally written by Limor Fried/Ladyada & Phil Burgess/PaintYourDragon for Adafruit Industries.
https://github.com/adafruit/RGB-matrix-Panel/blob/master/examples/plasma_32x32/plasma_32x32.ino
BSD License
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "pico/stdlib.h"
#include <time.h>
#include "pico_unicorn.hpp"
using namespace pimoroni;
PicoUnicorn pico_unicorn;
// Sine table to speed up execution
static const int8_t sinetab[256] = {
0, 2, 5, 8, 11, 15, 18, 21,
24, 27, 30, 33, 36, 39, 42, 45,
48, 51, 54, 56, 59, 62, 65, 67,
70, 72, 75, 77, 80, 82, 85, 87,
89, 91, 93, 96, 98, 100, 101, 103,
105, 107, 108, 110, 111, 113, 114, 116,
117, 118, 119, 120, 121, 122, 123, 123,
124, 125, 125, 126, 126, 126, 126, 126,
127, 126, 126, 126, 126, 126, 125, 125,
124, 123, 123, 122, 121, 120, 119, 118,
117, 116, 114, 113, 111, 110, 108, 107,
105, 103, 101, 100, 98, 96, 93, 91,
89, 87, 85, 82, 80, 77, 75, 72,
70, 67, 65, 62, 59, 56, 54, 51,
48, 45, 42, 39, 36, 33, 30, 27,
24, 21, 18, 15, 11, 8, 5, 2,
0, -3, -6, -9, -12, -16, -19, -22,
-25, -28, -31, -34, -37, -40, -43, -46,
-49, -52, -55, -57, -60, -63, -66, -68,
-71, -73, -76, -78, -81, -83, -86, -88,
-90, -92, -94, -97, -99,-101,-102,-104,
-106,-108,-109,-111,-112,-114,-115,-117,
-118,-119,-120,-121,-122,-123,-124,-124,
-125,-126,-126,-127,-127,-127,-127,-127,
-128,-127,-127,-127,-127,-127,-126,-126,
-125,-124,-124,-123,-122,-121,-120,-119,
-118,-117,-115,-114,-112,-111,-109,-108,
-106,-104,-102,-101, -99, -97, -94, -92,
-90, -88, -86, -83, -81, -78, -76, -73,
-71, -68, -66, -63, -60, -57, -55, -52,
-49, -46, -43, -40, -37, -34, -31, -28,
-25, -22, -19, -16, -12, -9, -6, -3
};
// We have four objects with radius and centre points as configured for the 16x7 display
const float radius1 =8.2, radius2 =11.5, radius3 =20.4, radius4 =22.1,
centerx1=8.0, centerx2=5.8, centerx3=12.7, centerx4= 2.0,
centery1= 2.1, centery2= 1.5, centery3=3.2, centery4=-0.5;
float angle1 = 0.0, angle2 = 0.0, angle3 = 0.0, angle4 = 0.0;
long hueShift= 0;
#define FPS 30 // Maximum frames-per-second
// HSV Conversion expects float inputs in the range of 0.00-1.00 for each channel
// Outputs are rgb in the range 0-255 for each channel
void from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) {
float i = floor(h * 6.0f);
float f = h * 6.0f - i;
v *= 255.0f;
uint8_t p = v * (1.0f - s);
uint8_t q = v * (1.0f - f * s);
uint8_t t = v * (1.0f - (1.0f - f) * s);
switch (int(i) % 6) {
case 0: r = v; g = t; b = p; break;
case 1: r = q; g = v; b = p; break;
case 2: r = p; g = v; b = t; break;
case 3: r = p; g = q; b = v; break;
case 4: r = t; g = p; b = v; break;
case 5: r = v; g = p; b = q; break;
}
}
int main() {
stdio_init_all();
pico_unicorn.init();
pico_unicorn.clear();
while(true) {
int x1, x2, x3, x4, y1, y2, y3, y4, sx1, sx2, sx3, sx4;
unsigned char x, y;
int8_t value;
uint8_t r, g, b,j,k,l,m;
// Setup a delay to slow the framerate.
// Would be better to read from a timer as some math operations take variable time
sleep_ms(1000 / FPS);
sx1 = (int)(cos(angle1) * radius1 + centerx1);
sx2 = (int)(cos(angle2) * radius2 + centerx2);
sx3 = (int)(cos(angle3) * radius3 + centerx3);
sx4 = (int)(cos(angle4) * radius4 + centerx4);
y1 = (int)(sin(angle1) * radius1 + centery1);
y2 = (int)(sin(angle2) * radius2 + centery2);
y3 = (int)(sin(angle3) * radius3 + centery3);
y4 = (int)(sin(angle4) * radius4 + centery4);
for(y=0; y<7; y++) {
x1 = sx1; x2 = sx2; x3 = sx3; x4 = sx4;
for(x=0; x<16; x++) {
j = (x1 * x1 + y1 * y1) >> 2;
k = (x2 * x2 + y2 * y2) >> 2;
l = (x3 * x3 + y3 * y3) >> 3;
m = (x4 * x4 + y4 * y4) >> 3;
//printf("X: %i, Y: %i, \t%i, %i, %i, %i\n",x,y,j,k,l,m);
value = (int8_t)(hueShift
+ (int8_t)*(sinetab + j)
+ (int8_t)*(sinetab + k)
+ (int8_t)*(sinetab + l)
+ (int8_t)*(sinetab + m));
//printf("X: %i, Y: %i, H: %i\n",x,y,value);
from_hsv(((value+128.0f)/256.0f), 1, 1, r, g, b);
pico_unicorn.set_pixel(x, y, r, g, b);
x1--; x2--; x3--; x4--;
}
y1--; y2--; y3--; y4--;
}
angle1 += 0.03;
angle2 -= 0.07;
angle3 += 0.13;
angle4 -= 0.15;
hueShift += 2;
}
return 0;
}
micorpythonの準備
pinoroni用モジュールを利用する都合上、
純正のmicropythonのファームウェアではなく
以下のpimoroni用のmicropythonのファームウェアを使用する:
cd ~/Downloads
wget https://github.com/pimoroni/pimoroni-pico/releases/download/v0.1.1/pimoroni-pico-micropython.uf2
cp pimoroni-pico-micropython.uf2 /media/<USER>/RPI-RP2/
micropythonデモ・ソース
以下、デモのソース:(特に修正なし)
displayボード用:
display_demo.py
import time
import random
import picodisplay as display
width = display.get_width()
height = display.get_height()
display_buffer = bytearray(width * height * 2) # 2-bytes per pixel (RGB565)
display.init(display_buffer)
display.set_backlight(1.0)
class Ball:
def __init__(self, x, y, r, dx, dy, pen):
self.x = x
self.y = y
self.r = r
self.dx = dx
self.dy = dy
self.pen = pen
# initialise shapes
balls = []
for i in range(0, 100):
r = random.randint(0, 10) + 3
balls.append(
Ball(
random.randint(r, r + (width - 2 * r)),
random.randint(r, r + (height - 2 * r)),
r,
(14 - r) / 2,
(14 - r) / 2,
display.create_pen(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)),
)
)
while True:
display.set_pen(40, 40, 40)
display.clear()
for ball in balls:
ball.x += ball.dx
ball.y += ball.dy
xmax = width - ball.r
xmin = ball.r
ymax = height - ball.r
ymin = ball.r
if ball.x < xmin or ball.x > xmax:
ball.dx *= -1
if ball.y < ymin or ball.y > ymax:
ball.dy *= -1
display.set_pen(ball.pen)
display.circle(int(ball.x), int(ball.y), int(ball.r))
display.update()
time.sleep(0.01)
unicornボード用:
unicorn_demo.py
import picounicorn
picounicorn.init()
# From CPython Lib/colorsys.py
def hsv_to_rgb(h, s, v):
if s == 0.0:
return v, v, v
i = int(h * 6.0)
f = (h * 6.0) - i
p = v * (1.0 - s)
q = v * (1.0 - s * f)
t = v * (1.0 - s * (1.0 - f))
i = i % 6
if i == 0:
return v, t, p
if i == 1:
return q, v, p
if i == 2:
return p, v, t
if i == 3:
return p, q, v
if i == 4:
return t, p, v
if i == 5:
return v, p, q
w = picounicorn.get_width()
h = picounicorn.get_height()
# Display a rainbow across Pico Unicorn
for x in range(w):
for y in range(h):
r, g, b = [int(c * 255) for c in hsv_to_rgb(x / w, y / h, 1.0)]
picounicorn.set_pixel(x, y, r, g, b)
print("Press Button A")
while not picounicorn.is_pressed(picounicorn.BUTTON_A): # Wait for Button A to be pressed
pass
# Clear the display
for x in range(w):
for y in range(h):
picounicorn.set_pixel(x, y, 0, 0, 0)
print("Button A pressed!")
参照情報
・PicoボードにPico_SDKでC言語をビルドする
・Pimoroni Pico Libraries and Examples
・Pinout for Pico Display Pack
・Pinout for Pico Unicorn Pack
・Pinout for Pico Audio Pack
・How to add a reset button to your Raspberry Pi Pico
All you need to do is to wire the GND and RUN pins together and add an extra momentary contact button to your breadboard. Pushing the button will reset the board.
・Programmable I/O with Raspberry Pi Pico
・Raspberry Pi Picoスタートガイド C/C++
・Pico C/C++ SDK
・Pico SDK docs
・Raspberry Pi Pico Datasheet
・RP2040 Datasheet
・Ubuntu 18.04 に Cmake の Latest Release をインストールする
・Ubuntu 20.04のPCでRaspberry Pi Picoを2台使ってOpenOCDによるデバッグ
以上