diff --git a/README.md b/README.md index 34d8f0d..9a8e65c 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,24 @@ -# MakeCode extension: Arcade Shield for BBC micro:bit (V2) +# Display shield MakeCode extension for BBC micro:bit -This MakeCode extension allows you to use any of the MakeCode Arcade shields with the MakeCode for BBC micro:bit editor. The extension provides access -to the screen and buttons on the shield, and has +This MakeCode extension allows you to use any of the MakeCode Arcade shields with the MakeCode for BBC micro:bit editor. +The extension provides access to the color display and buttons on the shield, and has a Bitmap abstraction with numerous drawing primitives (draw line, circle, square, etc). Bitmaps also can be created using the built-in image editor in MakeCode. -> **NOTE: This extension will only work in https://makecode.microbit.org/beta**. The extension is still under development and is subject to changes. Please file issues at https://github.com/microbit-apps/arcadeshield/issues +> **NOTE: This extension will only work in https://makecode.microbit.org/beta**. The extension is still under development and is subject to changes. Please file issues at https://github.com/microbit-apps/display-shield/issues -## Arcade Shields for the micro:bit V2 +## Arcade shields for the micro:bit V2 -Various Arcade shields for the micro:bit V2 are available on the market today, including: +Various Arcade (display) shields for the micro:bit V2 are available on the market today, including: * [Kittenbot's newbit Arcade shield](https://www.kittenbot.cc/products/newbit-arcade-shield): No assembly required Small screen and nice enclosure with LiPo battery inside. One [Jacdac](https://aka.ms/jacdac) port. * [ELECFREAK's micro:bit Arcade shield](https://www.kittenbot.cc/products/newbit-arcade-shield): Assembly required. Small screen. AAA Battery pack on back. One [Jacdac](https://aka.ms/jacdac) port. * [ICShopping's Game:bit Arcade shield](https://www.icshop.com.tw/products/368112100137?locale=en): No assembly required. Large screen and 3d-printed enclosure with LiPo battery inside. Two [Jacdac](https://aka.ms/jacdac) ports. * [Kitronik's Arcade for BBC micro:bit](https://kitronik.co.uk/products/56116-kitronik-arcade-for-bbc-micro-bit-makecode-arcade): No assembly required. Small screen. Battery holder on back. No Jacdac port. -![MakeCode Arcade Shields](https://github.com/microbit-apps/arcadeshield/blob/master/assets/shields.png?raw=true) +![MakeCode Arcade shields](https://github.com/microbit-apps/display-shield/blob/master/assets/shields.png?raw=true) ## Simulator support @@ -30,10 +30,9 @@ D-pad, see arrow buttons). Blocks for the shield are under the toolbox categories `Controller` and `Drawing` and are described further below. -![MakeCode with Arcade Shield Simulator](https://github.com/microbit-apps/arcadeshield/blob/master/assets/shieldSim.png?raw=true) +![MakeCode with Display Shield Simulator](https://github.com/microbit-apps/display-shield/blob/master/assets/shieldSim.png?raw=true) - -> [Open in MakeCode](https://makecode.microbit.org/beta/#pub:S07267-08481-73083-11887) +> [Open in MakeCode](https://makecode.microbit.org/beta/#pub:S80683-78265-58968-34229) ## Using this extension @@ -41,14 +40,14 @@ for the shield are under the toolbox categories `Controller` and - Open https://makecode.microbit.org/beta - Create a new project - Add an extension via the "Extensions" item in the gear wheel (upper right) -- Type "arcade" into the search box -- Select the **arcadeshield** extension, as shown below +- Type "display shield" into the search box +- Select the **display-shield** extension, as shown below -![MakeCode extension dialog](https://github.com/microbit-apps/arcadeshield/blob/master/assets/extensions.png?raw=true) +![MakeCode extension dialog](https://github.com/microbit-apps/display-shield/blob/master/assets/extensions.png?raw=true) ## Mashup!!! -With this extension, you have access to **all** the MakeCode APIs for the micro:bit, and the new ability to plot data or create your own user interface. For example, one very cool thing about the micro:bit is it's (X,Y,Z) accelerometer, which senses motion in three dimensions. Below is a program that maps the three accelerometer values to a scrolling line graph: +With this extension, you have access to **all** the MakeCode APIs for the micro:bit, and the new ability to plot data or create your own user interface. For example, one very cool thing about the micro:bit is its (X,Y,Z) accelerometer, which senses motion in three dimensions. Below is a program that maps the three accelerometer values to a scrolling line graph: ```block let x = 0, old_x = 0 let y = 0, old_y = 0 @@ -76,13 +75,13 @@ basic.forever(function () { ## Tutorials -- [Getting started](https://makecode.microbit.org/beta#tutorial:github:microbit-apps/arcadeshield/tutorials/getting-started) +- [Getting started](https://makecode.microbit.org/beta#tutorial:github:microbit-apps/display-shield/tutorials/getting-started) ## Overview of API The examples below are illustrative. All blocks have their own detailed help pages, available from the MakeCode editor. -[This page with block rendering](https://makecode.microbit.org/pkg/microbit-apps/arcadeshield). More APIs are available via TypeScript. +[This page with block rendering](https://makecode.microbit.org/pkg/microbit-apps/display-shield). More APIs are available via TypeScript. ### Controller @@ -99,7 +98,7 @@ controller.left.onEvent(ControllerButtonEvent.Pressed, function () { }) ``` -> [Open in MakeCode](https://makecode.microbit.org/beta/#pub:S24163-00898-21210-28197) +> [Open in MakeCode](https://makecode.microbit.org/beta/#pub:S64613-82831-68506-89974) ### Drawing into the screen bitmap @@ -115,7 +114,7 @@ screen().fill(8) screen().drawLine(0, 0, 159, 119, 2) screen().drawLine(159, 0, 0, 119, 5) ``` -> [Open in MakeCode](https://makecode.microbit.org/beta/#pub:S31225-91260-81293-38509) +> [Open in MakeCode](https://makecode.microbit.org/beta/#pub:S96327-83750-33845-21213) The first two parameters to the function are the (x,y) coordinate where the line should start, while the next @@ -221,13 +220,12 @@ screen().drawCircle(10, 10, 8, 5) ### Bitmap -Let's dig into bitmaps, which you can create yourself (the screen is represented by a bitmap, as we have seen already). A bitmap is some number of rows and columns of color pixels that make up rectangular picture. A _pixel_ is a single point of color inside the bitmap. - -Bitmaps are have a set height (number of rows) and width (number of columns). When a bitmap is declared, or created, the height and width are specified either by the _layout_ of the bitmap or as parameters to it's `create` method. +Let's dig into bitmaps, which you can create yourself (the screen is represented by a bitmap, as we have seen already). A bitmap is some number of rows and columns of color pixels that make up rectangular picture. A _pixel_ is a single point of color inside the bitmap. A bitmap has a fixed height (number of rows) and width (number of columns). When a bitmap is declared, or created, the height and width are specified either by the _layout_ of the bitmap or as parameters to its `create` method. #### Bitmap editor -The easiest way to create a bitmap is with the bitmap editor, which is accessible both from blocks and text view. Here is the block view of an +The easiest way to create a bitmap is with the bitmap editor, +which is accessible both from blocks and text view. Here is the block view of an 16x16 bitmap of an apple: ```block @@ -256,10 +254,10 @@ screen().drawTransparentBitmap(apple, 70, 50) Here is the bitmap editor, which appears when you click on the icon in bitmap block -![MakeCode Arcade Shields](https://github.com/microbit-apps/arcadeshield/blob/master/assets/appleBitmapEditor.png?raw=true) +![MakeCode Arcade Shields](https://github.com/microbit-apps/display-shield/blob/master/assets/appleBitmapEditor.png?raw=true) -The bitmap also is represented as a text literal, as shown below +A bitmap is represented in code as a text literal, as shown below ``` screen().fill(6) @@ -286,7 +284,7 @@ screen().drawTransparentBitmap(apple, 70, 50) Click on the painter's palette icon next to the bitmap literal in the text view to bring up the bitmap editor -![MakeCode Arcade Shields](https://github.com/microbit-apps/arcadeshield/blob/master/assets/bitmapEditorFromText.png?raw=true) +![MakeCode Arcade Shields](https://github.com/microbit-apps/display-shield/blob/master/assets/bitmapEditorFromText.png?raw=true) #### Bitmap layout @@ -333,7 +331,7 @@ let sixByTwo = bmp` ##### Transparent pixels -A pixel value of `.` means an empty pixel. This pixel has no color and that pixel _location_ in the bitmap is _transparent_. Being transparent means that if this bitmap is on top of another bitmap (overlapping) that has some pixel color, then the color of the pixel in the bitmap underneath shows through to the bitmap above it. +A pixel value of `.` denotes a transparent pixel. This pixel has no color and that pixel _location_ in the bitmap is _transparent_. Being transparent means that if this bitmap is on top of another bitmap (overlapping) that has some pixel color, then the color of the pixel in the bitmap underneath shows through to the bitmap above it. ##### Pixel colors @@ -365,7 +363,7 @@ let oneRed = bmp`2` As a block it looks like this: -``` +```block let oneRed = bmp`2` ``` @@ -458,7 +456,7 @@ let yellowSquare = bmp` screen().drawBitmap(yellowSquare, 0, 0) screen().drawTransparentBitmap(greenBall, 0, 0) ``` -> [Open in MakeCode](https://makecode.microbit.org/beta/#pub:S29479-80151-27505-97683) +> [Open in MakeCode](https://makecode.microbit.org/beta/#pub:S62195-40202-75476-02422) #### Setting pixels at locations @@ -493,7 +491,7 @@ screen().fill(8) screen().drawTransparentBitmap(orangeBox, 0, 0) screen().drawTransparentBitmap(orangeBox, 32, 32) ``` -> [Open in MakeCode](https://makecode.microbit.org/beta/#pub:S29084-47427-55388-74348) +> [Open in MakeCode](https://makecode.microbit.org/beta/#pub:S62672-77446-94897-23460) # Supported targets diff --git a/assets/extensions.png b/assets/extensions.png index b573d12..ed4813f 100644 Binary files a/assets/extensions.png and b/assets/extensions.png differ diff --git a/assets/newbit-shield.png b/assets/newbit-shield.png index 6e74366..9a168d0 100644 Binary files a/assets/newbit-shield.png and b/assets/newbit-shield.png differ diff --git a/assets/shields.png b/assets/shields.png index ba225ea..1f92839 100644 Binary files a/assets/shields.png and b/assets/shields.png differ diff --git a/bitmap.d.ts b/bitmap.d.ts index c98c9e4..1cdc50e 100644 --- a/bitmap.d.ts +++ b/bitmap.d.ts @@ -5,7 +5,7 @@ interface Bitmap { //% shim=BitmapMethods::fill blockNamespace="drawing" group="Drawing" //% blockId=bitmapFill //% block="fill $this with $c=colorindexpicker" - //% help=github:arcadeshield/docs/fill + //% help=github:display-shield/docs/fill //% this.shadow="theScreen" //% weight=100 //% c.defl=8 @@ -17,7 +17,7 @@ interface Bitmap { //% shim=BitmapMethods::setPixel blockNamespace="drawing" group="Drawing" //% block="set $this color at x $x y $y to $c=colorindexpicker" //% blockId=bitmapSetPixel - //% help=github:arcadeshield/docs/set-pixel + //% help=github:display-shield/docs/set-pixel //% this.shadow="theScreen" //% weight=96 //% x.defl=80 @@ -31,7 +31,7 @@ interface Bitmap { //% shim=BitmapMethods::getPixel blockNamespace="drawing" group="Drawing" //% block="$this color at x $x y $y" //% blockId=bitmapGetPixel - //% help=github:arcadeshield/docs/get-pixel + //% help=github:display-shield/docs/get-pixel //% this.shadow="theScreen" //% weight=92 //% x.defl=80 @@ -44,7 +44,7 @@ interface Bitmap { //% helper=imageDrawLine blockNamespace="drawing" inlineInputMode="inline" group="Drawing" //% block="draw line in $this from x $x0 y $y0 to x $x1 y $y1 $c=colorindexpicker" //% blockId=bitmapDrawLine - //% help=github:arcadeshield/docs/draw-line + //% help=github:display-shield/docs/draw-line //% this.shadow="theScreen" //% weight=88 //% x0.defl=0 @@ -60,7 +60,7 @@ interface Bitmap { //% helper=imageDrawRect blockNamespace="drawing" inlineInputMode="inline" group="Drawing" //% block="draw rectangle in $this at x $x y $y width $w height $h $c=colorindexpicker" //% blockId=bitmapDrawRect - //% help=github:arcadeshield/docs/draw-rect + //% help=github:display-shield/docs/draw-rect //% this.shadow="theScreen" //% weight=84 //% x.defl=0 @@ -76,7 +76,7 @@ interface Bitmap { //% helper=imageFillRect blockNamespace="drawing" inlineInputMode="inline" group="Drawing" //% block="fill rectangle in $this at x $x y $y width $w height $h $c=colorindexpicker" //% blockId=bitmapFillRect - //% help=github:arcadeshield/docs/fill-rect + //% help=github:display-shield/docs/fill-rect //% this.shadow="theScreen" //% weight=80 //% x.defl=0 @@ -142,7 +142,7 @@ interface Bitmap { //% group="Drawing" //% block="draw $from in $this at x $x y $y" //% blockId=bitmapDrawBitmap - //% help=github:arcadeshield/docs/draw-bitmap + //% help=github:display-shield/docs/draw-bitmap //% this.shadow="theScreen" //% this.defl=bitmap //% from.shadow=variables_get @@ -158,7 +158,7 @@ interface Bitmap { //% blockId=bitmapDrawTransparentBitmap //% group="Drawing" //% block="draw $from transparent in $this at x $x y $y" - //% help=github:arcadeshield/docs/draw-transparent-bitmap + //% help=github:display-shield/docs/draw-transparent-bitmap //% this.shadow="theScreen" //% this.defl=bitmap //% from.shadow=variables_get @@ -172,7 +172,7 @@ interface Bitmap { //% shim=BitmapMethods::flipX blockNamespace="drawing" group="Transformations" //% block="flip $this horizontally" //% blockId=bitmapFlipX - //% help=github:arcadeshield/docs/flip-x + //% help=github:display-shield/docs/flip-x //% this.shadow="theScreen" //% weight=72 flipX(): void; @@ -183,7 +183,7 @@ interface Bitmap { //% shim=BitmapMethods::flipY blockNamespace="drawing" group="Transformations" //% block="flip $this vertically" //% blockId=bitmapFlipY - //% help=github:arcadeshield/docs/flip-y + //% help=github:display-shield/docs/flip-y //% this.shadow="theScreen" //% weight=68 flipY(): void; @@ -192,7 +192,7 @@ interface Bitmap { * Every pixel in bitmap is moved by (dx,dy) */ //% shim=BitmapMethods::scroll blockNamespace="drawing" group="Transformations" - //% help=github:arcadeshield/docs/scroll + //% help=github:display-shield/docs/scroll //% this.shadow="theScreen" //% block="scroll $this by x $dx y $dy" //% blockId=bitmapScroll @@ -207,7 +207,7 @@ interface Bitmap { //% block="change color in $this from $from=colorindexpicker to $to=colorindexpicker" //% blockId=bitmapReplace //% help=bitmaps/bitmap/replace - //% help=github:arcadeshield/docs/replace + //% help=github:display-shield/docs/replace //% this.shadow="theScreen" //% weight=60 replace(from: int32, to: int32): void; @@ -224,7 +224,7 @@ interface Bitmap { //% other.shadow=variables_get //% this.defl="bitmap" //% other.defl="bitmap2" - //% help=github:arcadeshield/docs/equals + //% help=github:display-shield/docs/equals equals(other: Bitmap): boolean; //% shim=BitmapMethods::isStatic @@ -246,7 +246,7 @@ interface Bitmap { //% weight=40 //% block="clone $this" //% blockId=bitmapClone - //% help=github:arcadeshield/docs/clone + //% help=github:display-shield/docs/clone //% this.shadow=variables_get //% this.defl="bitmap" clone(): Bitmap; @@ -258,7 +258,7 @@ declare namespace bitmaps { //% block="create bitmap width $width height $height" group="Create" //% weight=80 //% blockSetVariable=bitmap - //% help=github:arcadeshield/docs/create + //% help=github:display-shield/docs/create //% width.defl=16 //% height.defl=16 function create(width: number, height: number): Bitmap; diff --git a/bitmap.ts b/bitmap.ts index 9e34b6f..5fc09df 100644 --- a/bitmap.ts +++ b/bitmap.ts @@ -3,7 +3,7 @@ */ //% blockNamespace="drawing" group="Create" //% blockId=theScreen block="screen" -//% help=github:arcadeshield/docs/screen-bitmap +//% help=github:display-shield/docs/screen-bitmap function screen(): Bitmap { return theScreen; } diff --git a/config.ts b/config.ts index 1bd5c19..dc9f10f 100644 --- a/config.ts +++ b/config.ts @@ -2,6 +2,13 @@ // button configuration for Arcade Shield here namespace config { + // the following are the default values used in C++ + // add to your code, uncomment and change to get different value + // export const DISPLAY_TYPE = 4242 // smart shield + // export const DISPLAY_CFG0 = 0x02000080 // allow execution without shield plugged in + // export const DISPLAY_CFG1 = 0x00000603 + // export const DISPLAY_CFG2 = 8 // maximum SPI frequency for smart shield + // pybadge-like layout export const PIN_BTN_LEFT = 1050 export const PIN_BTN_UP = 1051 diff --git a/controllerbutton.ts b/controllerbutton.ts index cc7567b..0c2bd10 100644 --- a/controllerbutton.ts +++ b/controllerbutton.ts @@ -55,7 +55,7 @@ namespace controller { * Run some code when shield is absent/present */ //% weight=30 - //% help=github:arcadeshield/docs/on-shield-event + //% help=github:display-shield/docs/on-shield-event //% blockId=shieldEvent block="on shield $event" export function onShieldEvent(event: ControllerShieldEvent, handler: () => void) { context.onEvent(event, 0, handler); @@ -146,7 +146,7 @@ namespace controller { * Run some code when a button is pressed, released, or held */ //% weight=99 blockGap=8 - //% help=github:arcadeshield/docs/on-button-event + //% help=github:display-shield/docs/on-button-event //% blockId=keyonevent block="on $this **button** $event" onEvent(event: ControllerButtonEvent, handler: () => void) { const eventHandler = this.getOrCreateHandlerForEvent(event); @@ -206,7 +206,7 @@ namespace controller { * Indicates if the button is currently pressed */ //% weight=96 blockGap=8 - //% help=github:arcadeshield/docs/is-pressed + //% help=github:display-shield/docs/is-pressed //% blockId=keyispressed block="is $this **button** pressed" isPressed() { return this._pressed; diff --git a/cpp/config_nrf.h b/cpp/config_nrf.h index 8c8a531..ae2ed77 100644 --- a/cpp/config_nrf.h +++ b/cpp/config_nrf.h @@ -7,23 +7,32 @@ #define CODAL_PIN NRF52Pin #define CODAL_SPI NRF52SPI -#define MY_DISPLAY_TYPE 4242 // smart shield -#define MY_DISPLAY_CFG0 0x02000080 // allow execution without shield plugged in -#define MY_DISPLAY_CFG1 0x00000603 -#define MY_DISPLAY_CFG2 8 // maximum SPI frequency for smart shield - -#define MY_PIN_BTNMX_LATCH &uBit.io.P9 // DAL.P0_9 -#define MY_PIN_BTNMX_CLOCK &uBit.io.P20 // DAL.P1_0 -#define MY_PIN_BTNMX_DATA &uBit.io.P14 // DAL.P0_1 - -#define MY_PIN_DISPLAY_SCK &uBit.io.P13 // DAL.P0_17 -#define MY_PIN_DISPLAY_MOSI &uBit.io.P15 // DAL.P0_13 -#define MY_PIN_DISPLAY_MISO &uBit.io.P14 // DAL.P0_1 -#define MY_PIN_DISPLAY_BL &uBit.io.P19 // DAL.P0_26 -#define MY_PIN_DISPLAY_DC &uBit.io.P8 // DAL.P0_10 -#define MY_PIN_DISPLAY_RST &uBit.io.P16 // DAL.P1_2 -#define MY_PIN_DISPLAY_CS ((CODAL_PIN*)NULL) // not connected -#define MY_PIN_LED ((CODAL_PIN*)NULL) // not connected + +#define IN_WDS_MODE true + +#define MY_PIN_BTNMX_LATCH ((CODAL_PIN *)NULL) +#define MY_PIN_BTNMX_CLOCK ((CODAL_PIN *)NULL) +#define MY_PIN_BTNMX_DATA ((CODAL_PIN *)NULL) + + +// Buttons: +#define MY_PIN_WDS_TOP_BTN &uBit.io.P2 // Pin 31 DAL.P0.04 +#define MY_PIN_WDS_BOT_BTN &uBit.io.P1 // Pin 4 DAL.P0.00 +#define MY_PIN_WDS_LEFT_BTN &uBit.io.P22 // Pin 2 DAL.P0.20 (prev. used by speaker) +#define MY_PIN_WDS_RIGHT_BTN &uBit.io.P21 // Pin 17 DAL.P0.00 (prev. used by runmic) + + +// Screen: +#define MY_PIN_DISPLAY_SCK &uBit.io.P0 // DAL.P0_02 +#define MY_PIN_DISPLAY_MOSI &uBit.io.P4 // DAL.P0.28 +#define MY_PIN_DISPLAY_RST &uBit.io.P10 // DAL.P1_00 +#define MY_PIN_DISPLAY_DC &uBit.io.P23 // DAL.P0_29 +#define MY_PIN_DISPLAY_BL &uBit.io.P3 // DAL.P0_31 + +#define MY_PIN_DISPLAY_MISO ((CODAL_PIN *)NULL) // not connected +#define MY_PIN_DISPLAY_CS ((CODAL_PIN *)NULL) // not connected +#define MY_PIN_LED ((CODAL_PIN *)NULL) // not connected + #undef DEV_NUM_PINS #define DEV_NUM_PINS 48 @@ -60,10 +69,9 @@ typedef DevicePin *PwmOnlyPin; #define BUTTON_ACTIVE_LOW_PULL_UP 32 namespace pxt { - uint32_t readButtonMultiplexer(int bits); - void disableButtonMultiplexer(); - DevicePin *myLookupPin(int pinName); - CodalComponent *lookupComponent(int id); - int pressureLevelByButtonId(int btnId, int codalId); -} - +uint32_t readButtonMultiplexer(int bits); +void disableButtonMultiplexer(); +DevicePin *myLookupPin(int pinName); +CodalComponent *lookupComponent(int id); +int pressureLevelByButtonId(int btnId, int codalId); +} // namespace pxt diff --git a/cpp/controllerbuttons.cpp b/cpp/controllerbuttons.cpp index bea8b4e..72e866a 100644 --- a/cpp/controllerbuttons.cpp +++ b/cpp/controllerbuttons.cpp @@ -9,7 +9,6 @@ namespace pxt { - class PressureButton : public codal::Button { public: PressureButton(Pin &pin, uint16_t id, @@ -120,48 +119,76 @@ static void sendBtnUp(Event ev) { Event(PXT_INTERNAL_KEY_UP, ev.source - DEVICE_ID_FIRST_BUTTON); } + //% expose void setupButton(int buttonId, int key) { - int pin = getConfig(key); - if (pin == -1) - return; - - unsigned highflags = (unsigned)pin >> 16; - int flags = BUTTON_ACTIVE_LOW_PULL_UP; - if (highflags & 0xff) - flags = highflags & 0xff; + if (IN_WDS_MODE) { + codal::Pin* pin; + switch (buttonId) { + case 1: + pin = MY_PIN_WDS_LEFT_BTN; + break; + case 2: + pin = MY_PIN_WDS_TOP_BTN; + break; + case 3: + pin = MY_PIN_WDS_RIGHT_BTN; + break; + case 4: + pin = MY_PIN_WDS_BOT_BTN; + break; + default: + return; + } - pin &= 0xffff; + auto pull = PullMode::Down; + auto cpid = DEVICE_ID_FIRST_BUTTON + buttonId; - auto cpid = DEVICE_ID_FIRST_BUTTON + buttonId; - auto btn = (PressureButton *)lookupComponent(cpid); - if (btn == NULL) { - if (registerMultiplexedButton(pin, buttonId)) + auto btn = new PressureButton(*pin, cpid, DEVICE_BUTTON_ALL_EVENTS, ButtonPolarity::ACTIVE_HIGH, pull); + EventModel::defaultEventBus->listen(btn->id, DEVICE_BUTTON_EVT_DOWN, sendBtnDown); + EventModel::defaultEventBus->listen(btn->id, DEVICE_BUTTON_EVT_UP, sendBtnUp); + } else { + int pin = getConfig(key); + if (pin == -1) return; - if (1100 <= pin && pin < 1300) { - pin -= 1100; - int thr = getConfig(CFG_ANALOG_BUTTON_THRESHOLD, 300); - if (pin >= 100) { - thr = -thr; - pin -= 100; + unsigned highflags = (unsigned)pin >> 16; + int flags = BUTTON_ACTIVE_LOW_PULL_UP; + if (highflags & 0xff) + flags = highflags & 0xff; + + pin &= 0xffff; + + auto cpid = DEVICE_ID_FIRST_BUTTON + buttonId; + auto btn = (PressureButton *)lookupComponent(cpid); + if (btn == NULL) { + if (registerMultiplexedButton(pin, buttonId)) + return; + + if (1100 <= pin && pin < 1300) { + pin -= 1100; + int thr = getConfig(CFG_ANALOG_BUTTON_THRESHOLD, 300); + if (pin >= 100) { + thr = -thr; + pin -= 100; + } + btn = new AnalogButton(lookupAnalogCache(myLookupPin(pin)), cpid, thr); + } else { + auto pull = PullMode::None; + if ((flags & 0xf0) == 0x10) + pull = PullMode::Down; + else if ((flags & 0xf0) == 0x20) + pull = PullMode::Up; + else if ((flags & 0xf0) == 0x30) + pull = PullMode::None; + else + oops(3); + btn = new PressureButton(*myLookupPin(pin), cpid, DEVICE_BUTTON_ALL_EVENTS, + (ButtonPolarity)(flags & 0xf), pull); } - btn = new AnalogButton(lookupAnalogCache(myLookupPin(pin)), cpid, thr); - } else { - auto pull = PullMode::None; - if ((flags & 0xf0) == 0x10) - pull = PullMode::Down; - else if ((flags & 0xf0) == 0x20) - pull = PullMode::Up; - else if ((flags & 0xf0) == 0x30) - pull = PullMode::None; - else - oops(3); - btn = new PressureButton(*myLookupPin(pin), cpid, DEVICE_BUTTON_ALL_EVENTS, - (ButtonPolarity)(flags & 0xf), pull); + EventModel::defaultEventBus->listen(btn->id, DEVICE_BUTTON_EVT_DOWN, sendBtnDown); + EventModel::defaultEventBus->listen(btn->id, DEVICE_BUTTON_EVT_UP, sendBtnUp); } - EventModel::defaultEventBus->listen(btn->id, DEVICE_BUTTON_EVT_DOWN, sendBtnDown); - EventModel::defaultEventBus->listen(btn->id, DEVICE_BUTTON_EVT_UP, sendBtnUp); } } diff --git a/cpp/screen.cpp b/cpp/screen.cpp index 7307847..7835578 100644 --- a/cpp/screen.cpp +++ b/cpp/screen.cpp @@ -3,11 +3,11 @@ #include "Pin.h" #define PinCompat codal::Pin -#include "ST7735.h" #include "ILI9341.h" +#include "ST7735.h" -// this is a hack because someone (don't know where) #defined SPI to be NRF52SPI, -// which messes with the include file below the #undef +// this is a hack because someone (don't know where) #defined SPI to be +// NRF52SPI, which messes with the include file below the #undef #undef SPI #include "SPIScreenIO.h" @@ -20,329 +20,348 @@ typedef RefImage *Bitmap_; namespace pxt { class WDisplay { - public: - ScreenIO *io; - ST7735 *lcd; - JDDisplay *smart; - - uint32_t currPalette[16]; - bool present; - bool newPalette; - bool inUpdate; - uint8_t *screenBuf; - - uint16_t width, height; - uint16_t displayHeight; - uint8_t offX, offY; - bool doubleSize; - uint32_t palXOR; - - WDisplay() { - uint32_t cfg2 = MY_DISPLAY_CFG2; - - uint32_t cfg0 = MY_DISPLAY_CFG0; - uint32_t frmctr1 = MY_DISPLAY_CFG1; - - int dispTp = MY_DISPLAY_TYPE; - - doubleSize = false; - smart = NULL; - - auto miso = LOOKUP_PIN(DISPLAY_MISO); - dispTp = smartConfigure(&cfg0, &frmctr1, &cfg2); - - if (dispTp != DISPLAY_TYPE_SMART) - miso = NULL; // only JDDisplay needs MISO, otherwise leave free - - SPI *spi = new CODAL_SPI(*LOOKUP_PIN(DISPLAY_MOSI), *miso, *LOOKUP_PIN(DISPLAY_SCK)); - io = new SPIScreenIO(*spi); - - if (dispTp == DISPLAY_TYPE_ST7735) { - width = 160; - height = 128; - lcd = new ST7735(*io, *LOOKUP_PIN(DISPLAY_CS), *LOOKUP_PIN(DISPLAY_DC)); - } else if (dispTp == DISPLAY_TYPE_SMART) { - lcd = NULL; - width = 160; - height = 120; - smart = new JDDisplay(spi, LOOKUP_PIN(DISPLAY_CS), LOOKUP_PIN(DISPLAY_DC)); - } else - target_panic(128); // PANIC_SCREEN_ERROR - - palXOR = (cfg0 & 0x1000000) ? 0xffffff : 0x000000; - auto madctl = cfg0 & 0xff; - offX = (cfg0 >> 8) & 0xff; - offY = (cfg0 >> 16) & 0xff; - - DMESG("configure screen: FRMCTR1=%p MADCTL=%p type=%d", frmctr1, madctl, dispTp); - - if (spi) { - auto freq = (cfg2 & 0xff); - if (!freq) - freq = 15; - spi->setFrequency(freq * 1000000); - spi->setMode(0); - // make sure the SPI peripheral is initialized before toggling reset - spi->write(0); - } - - auto rst = LOOKUP_PIN(DISPLAY_RST); - if (rst) { - rst->setDigitalValue(0); - fiber_sleep(20); - rst->setDigitalValue(1); - fiber_sleep(20); - } - - if (lcd) { - auto bl = LOOKUP_PIN(DISPLAY_BL); - if (bl) { - bl->setDigitalValue(1); - } - - lcd->init(); - lcd->configure(madctl, frmctr1); - } - - displayHeight = height; - setAddrMain(); - DMESG("screen: %d x %d, off=%d,%d", width, height, offX, offY); - int sz = doubleSize ? (width >> 1) * (height >> 1) : width * height; - screenBuf = (uint8_t *)app_alloc(sz / 2 + 20); - inUpdate = false; +public: + ScreenIO *io; + ST7735 *lcd; + JDDisplay *smart; + + uint32_t currPalette[16]; + bool present; + bool newPalette; + bool inUpdate; + uint8_t *screenBuf; + + uint16_t width, height; + uint16_t displayHeight; + uint8_t offX, offY; + bool doubleSize; + uint32_t palXOR; + + WDisplay() { + uint32_t cfg2 = getConfig(CFG_DISPLAY_CFG2, 8); + + uint32_t cfg0 = getConfig(CFG_DISPLAY_CFG0, 0x02000080); + uint32_t frmctr1 = getConfig(CFG_DISPLAY_CFG1, 0x00000603); + + int dispTp = getConfig(CFG_DISPLAY_TYPE,4242); + + doubleSize = false; + smart = NULL; + + auto miso = LOOKUP_PIN(DISPLAY_MISO); + dispTp = smartConfigure(&cfg0, &frmctr1, &cfg2); + + if (dispTp != DISPLAY_TYPE_SMART) + miso = NULL; // only JDDisplay needs MISO, otherwise leave free + + SPI *spi = new CODAL_SPI(*LOOKUP_PIN(DISPLAY_MOSI), *miso, + *LOOKUP_PIN(DISPLAY_SCK)); + io = new SPIScreenIO(*spi); + + if (dispTp == DISPLAY_TYPE_ST7735) { + width = 160; + height = 128; + + // auto dc = LOOKUP_PIN(DISPLAY_DC); + // if (dc) { + // dc->setHighDrive(true); + // dc->setDigitalValue(1); + // } + + lcd = new ST7735(*io, *LOOKUP_PIN(DISPLAY_CS), *LOOKUP_PIN(DISPLAY_DC)); + } else if (dispTp == DISPLAY_TYPE_SMART) { + lcd = NULL; + width = 160; + height = 120; + smart = + new JDDisplay(spi, LOOKUP_PIN(DISPLAY_CS), LOOKUP_PIN(DISPLAY_DC)); + } else + target_panic(128); // PANIC_SCREEN_ERROR + + palXOR = (cfg0 & 0x1000000) ? 0xffffff : 0x000000; + auto madctl = cfg0 & 0xff; + offX = (cfg0 >> 8) & 0xff; + offY = (cfg0 >> 16) & 0xff; + + DMESG("configure screen: FRMCTR1=%p MADCTL=%p type=%d", frmctr1, madctl, + dispTp); + + if (spi) { + auto freq = (cfg2 & 0xff); + if (!freq) + freq = 15; + spi->setFrequency(freq * 1000000); + spi->setMode(0); + // make sure the SPI peripheral is initialized before toggling reset + spi->write(0); } - uint32_t smartConfigure(uint32_t *cfg0, uint32_t *cfg1, uint32_t *cfg2) { - uint32_t hc; - present = false; - - DMESG("74HC: waiting..."); - - // wait while nothing is connected - for (;;) { - auto rst = LOOKUP_PIN(DISPLAY_RST); - if (rst) { - rst->setDigitalValue(0); - target_wait_us(10); - rst->setDigitalValue(1); - fiber_sleep(3); // in reality we need around 1.2ms - } - - hc = readButtonMultiplexer(17); - if (hc != 0) - break; - - fiber_sleep(100); - - // the device will run without shield when the following is specified in user program: - // namespace userconfig { export const DISPLAY_CFG0 = 0x02000080 } - if (*cfg0 & 0x2000000) { - DMESG("74HC: no wait requested"); - return DISPLAY_TYPE_ST7735; - } - } - present = true; - - DMESG("74HC: %x", hc); - - // is the line forced up? if so, assume JDDisplay - if (hc == 0x1FFFF) { - disableButtonMultiplexer(); - return DISPLAY_TYPE_SMART; - } - - hc = hc >> 1; - - // SER pin (or first bit of second HC) is orientation - if (hc & 0x0010) - *cfg0 = 0x80; - else - *cfg0 = 0x40; - - uint32_t configId = (hc & 0xe0) >> 5; - - - switch (configId) { - case 1: - *cfg1 = 0x0603; // ST7735 - break; - case 2: - *cfg1 = 0xe14ff; // ILI9163C - *cfg0 |= 0x08; // BGR colors - break; - case 3: - *cfg1 = 0x0603; // ST7735 - *cfg0 |= 0x1000000; // inverted colors - break; - default: - target_panic(129); // PANIC_SCREEN_ERROR - break; - } - - DMESG("config type: %d; cfg0=%x cfg1=%x", configId, *cfg0, *cfg1); - - // for some reason, setting SPI frequency to 32 doesn't - // work with ST77735 in pxt-microbit - *cfg2 = 16; // Damn the torpedoes! 32MHz + auto rst = LOOKUP_PIN(DISPLAY_RST); + if (rst) { + rst->setDigitalValue(0); + fiber_sleep(20); + rst->setDigitalValue(1); + fiber_sleep(20); + } - return DISPLAY_TYPE_ST7735; + if (lcd) { + auto bl = LOOKUP_PIN(DISPLAY_BL); + if (bl) { + bl->setHighDrive(true); + bl->setDigitalValue(1); + } + + lcd->init(); + lcd->configure(madctl, frmctr1); } - void setAddrMain() { - if (lcd) - lcd->setAddrWindow(offX, offY, width, displayHeight); - else - smart->setAddrWindow(offX, offY, width, displayHeight); + + displayHeight = height; + setAddrMain(); + DMESG("screen: %d x %d, off=%d,%d", width, height, offX, offY); + int sz = doubleSize ? (width >> 1) * (height >> 1) : width * height; + screenBuf = (uint8_t *)app_alloc(sz / 2 + 20); + inUpdate = false; + } + + uint32_t smartConfigure(uint32_t *cfg0, uint32_t *cfg1, uint32_t *cfg2) { + uint32_t hc; + present = false; + + DMESG("74HC: waiting..."); + + // wait while nothing is connected + for (;;) { + auto rst = LOOKUP_PIN(DISPLAY_RST); + if (rst) { + rst->setDigitalValue(0); + target_wait_us(10); + rst->setDigitalValue(1); + fiber_sleep(3); // in reality we need around 1.2ms + } + + hc = readButtonMultiplexer(17); + if (hc != 0) + break; + + fiber_sleep(100); + + // the device will run without shield when the following is specified in + // user program: namespace config { export const DISPLAY_CFG0 = 0x02000080 } + if (*cfg0 & 0x02000000) { + DMESG("74HC: no wait requested"); + return DISPLAY_TYPE_ST7735; + } } - void waitForSendDone() { - if (lcd) - lcd->waitForSendDone(); - else - smart->waitForSendDone(); + present = true; + + DMESG("74HC: %x", hc); + + // is the line forced up? if so, assume JDDisplay + if (hc == 0x1FFFF) { + disableButtonMultiplexer(); + return DISPLAY_TYPE_SMART; } - int sendIndexedImage(const uint8_t *src, unsigned width, unsigned height, uint32_t *palette) { - if (lcd) - return lcd->sendIndexedImage(src, width, height, palette); - else - return smart->sendIndexedImage(src, width, height, palette); + + hc = hc >> 1; + + // SER pin (or first bit of second HC) is orientation + if (hc & 0x0010) + *cfg0 = ((*cfg0) & 0xffffff3f) | 0x80; + else + *cfg0 = ((*cfg0) & 0xffffff3f) | 0x40; + + uint32_t configId = (hc & 0xe0) >> 5; + + switch (configId) { + case 1: + *cfg1 = 0x0603; // ST7735 + break; + case 2: + *cfg1 = 0xe14ff; // ILI9163C + *cfg0 |= 0x08; // BGR colors + break; + case 3: + *cfg1 = 0x0603; // ST7735 + *cfg0 |= 0x1000000; // inverted colors + break; + default: + target_panic(129); // PANIC_SCREEN_ERROR + break; } + + DMESG("config type: %d; cfg0=%x cfg1=%x", configId, *cfg0, *cfg1); + + // for some reason, setting SPI frequency to 32 doesn't + // work with ST77735 in pxt-microbit + *cfg2 = 16; // Damn the torpedoes! 32MHz + + return DISPLAY_TYPE_ST7735; + } + + void setAddrMain() { + if (lcd) + lcd->setAddrWindow(offX, offY, width, displayHeight); + else + smart->setAddrWindow(offX, offY, width, displayHeight); + } + void waitForSendDone() { + if (lcd) + lcd->waitForSendDone(); + else + smart->waitForSendDone(); + } + int sendIndexedImage(const uint8_t *src, unsigned width, unsigned height, + uint32_t *palette) { + if (lcd) + return lcd->sendIndexedImage(src, width, height, palette); + else + return smart->sendIndexedImage(src, width, height, palette); + } }; SINGLETON_IF_PIN(WDisplay, DISPLAY_MOSI); //% int setScreenBrightnessSupported() { - auto display = getWDisplay(); - if (display && display->smart) - return 1; + auto display = getWDisplay(); + if (display && display->smart) + return 1; - auto bl = LOOKUP_PIN(DISPLAY_BL); - if (!bl) - return 0; + auto bl = LOOKUP_PIN(DISPLAY_BL); + if (!bl) + return 0; #ifdef SAMD51 - if (bl->name == PA06) - return 0; + if (bl->name == PA06) + return 0; #endif #ifdef NRF52_SERIES - // PWM not implemented yet - return 0; + // PWM not implemented yet + return 0; #else - return 1; + return 1; #endif } //% void setScreenBrightness(int level) { - if (level < 0) - level = 0; - if (level > 100) - level = 100; - - auto display = getWDisplay(); - if (display && display->smart) { - display->smart->brightness = level; - return; - } - - auto bl = LOOKUP_PIN(DISPLAY_BL); - if (!bl) - return; - - if (level == 0) - bl->setDigitalValue(0); - else if (level == 100) - bl->setDigitalValue(1); - else { - if (setScreenBrightnessSupported()) { - bl->setAnalogPeriodUs(1000); - bl->setAnalogValue(level * level * 1023 / 10000); - } + if (level < 0) + level = 0; + if (level > 100) + level = 100; + + auto display = getWDisplay(); + if (display && display->smart) { + display->smart->brightness = level; + return; + } + + auto bl = LOOKUP_PIN(DISPLAY_BL); + if (!bl) + return; + + if (level == 0) + bl->setDigitalValue(0); + else if (level == 100) + bl->setDigitalValue(1); + else { + if (setScreenBrightnessSupported()) { + bl->setAnalogPeriodUs(1000); + bl->setAnalogValue(level * level * 1023 / 10000); } + } } //% void setPalette(Buffer buf) { - auto display = getWDisplay(); - if (!display) - return; - - if (48 != buf->length) - target_panic(130); // PANIC_SCREEN_ERROR - for (int i = 0; i < 16; ++i) { - display->currPalette[i] = - (buf->data[i * 3] << 16) | (buf->data[i * 3 + 1] << 8) | (buf->data[i * 3 + 2] << 0); - display->currPalette[i] ^= display->palXOR; - } - display->newPalette = true; + auto display = getWDisplay(); + if (!display) + return; + + if (48 != buf->length) + target_panic(130); // PANIC_SCREEN_ERROR + for (int i = 0; i < 16; ++i) { + display->currPalette[i] = (buf->data[i * 3] << 16) | + (buf->data[i * 3 + 1] << 8) | + (buf->data[i * 3 + 2] << 0); + display->currPalette[i] ^= display->palXOR; + } + display->newPalette = true; } //% bool displayPresent() { - auto display = getWDisplay(); - if (!display) - return false; - return display->present; + auto display = getWDisplay(); + if (!display) + return false; + return display->present; } //% int displayHeight() { - auto display = getWDisplay(); - if (!display) - return -1; - return display->displayHeight; + auto display = getWDisplay(); + if (!display) + return -1; + return display->displayHeight; } //% int displayWidth() { - auto display = getWDisplay(); - if (!display) - return -1; - return display->width; + auto display = getWDisplay(); + if (!display) + return -1; + return display->width; } //% void updateScreen(Bitmap_ img) { - auto display = getWDisplay(); - if (!display) - return; + auto display = getWDisplay(); + if (!display) + return; - if (display->inUpdate) - return; + if (display->inUpdate) + return; - display->inUpdate = true; + display->inUpdate = true; - auto mult = display->doubleSize ? 2 : 1; + auto mult = display->doubleSize ? 2 : 1; - if (img) { - if (img->bpp() != 4 || img->width() * mult != display->width || - img->height() * mult != display->displayHeight) - target_panic(131); // PANIC_SCREEN_ERROR + if (img) { + if (img->bpp() != 4 || img->width() * mult != display->width || + img->height() * mult != display->displayHeight) + target_panic(131); // PANIC_SCREEN_ERROR - // DMESG("wait for done"); - display->waitForSendDone(); + // DMESG("wait for done"); + display->waitForSendDone(); - auto palette = display->currPalette; + auto palette = display->currPalette; - if (display->newPalette) { - display->newPalette = false; - } else { - // smart mode always sends palette - if (!display->smart) - palette = NULL; - } + if (display->newPalette) { + display->newPalette = false; + } else { + // smart mode always sends palette + if (!display->smart) + palette = NULL; + } - memcpy(display->screenBuf, img->pix(), img->pixLength()); + memcpy(display->screenBuf, img->pix(), img->pixLength()); - // DMESG("send"); - display->sendIndexedImage(display->screenBuf, img->width(), img->height(), palette); - } + // DMESG("send"); + display->sendIndexedImage(display->screenBuf, img->width(), img->height(), + palette); + + // volatile bool a = true; + // while (a) { + // a = true; + // } + } - display->inUpdate = false; + display->inUpdate = false; } //% void updateStats(String msg) { - // ignore... + // ignore... } -} // namespace pxt \ No newline at end of file +} // namespace pxt diff --git a/cpp/test.ts b/cpp/test.ts index de610e9..88c0266 100644 --- a/cpp/test.ts +++ b/cpp/test.ts @@ -1,29 +1,41 @@ // tests go here; this will not be compiled when this package is used as an extension. -const present = __screenhelpers.displayPresent(); -basic.showNumber(present ? 1 : 0) - -// set palette before creating screen, which initializes the display -__screenhelpers.setPalette(hex`000000ffffffff2121ff93c4ff8135fff609249ca378dc52003fad87f2ff8e2ec4a4839f5c406ce5cdc491463d000000`) - -const screen = bitmaps.create( - __screenhelpers.displayWidth(), - __screenhelpers.displayHeight() -) - -input.onButtonPressed(Button.A, () => { - screen.fill(2) - screen.drawLine(0,0,100,100,0) - __screenhelpers.updateScreen(screen) -}) - -input.onButtonPressed(Button.B, () => { - screen.fill(4) - screen.drawLine(100,0,0,100,0) - __screenhelpers.updateScreen(screen) -}) - -input.onButtonPressed(Button.AB, () => { - screen.fill(0) - __screenhelpers.updateScreen(screen) -}) +// const present = __screenhelpers.displayPresent(); +// basic.showNumber(present ? 1 : 0) + +// // set palette before creating screen, which initializes the display +// __screenhelpers.setPalette(hex`000000ffffffff2121ff93c4ff8135fff609249ca378dc52003fad87f2ff8e2ec4a4839f5c406ce5cdc491463d000000`) + +// const screen = bitmaps.create( +// __screenhelpers.displayWidth(), +// __screenhelpers.displayHeight() +// ) + +// input.onButtonPressed(Button.A, () => { +// screen.fill(2) +// screen.drawLine(0,0,100,100,0) +// __screenhelpers.updateScreen(screen) +// }) + +// input.onButtonPressed(Button.B, () => { +// screen.fill(4) +// screen.drawLine(100,0,0,100,0) +// __screenhelpers.updateScreen(screen) +// }) + +// input.onButtonPressed(Button.AB, () => { +// screen.fill(0) +// __screenhelpers.updateScreen(screen) +// }) + + +let i = 0; +while (true) { + // basic.showNumber(i) + screen().fill(i % 16) + basic.pause(500) + + i += 1 +} + +// display.enable(false) diff --git a/docs/bitmap.md b/docs/bitmap.md index 0614bc8..e55b73c 100644 --- a/docs/bitmap.md +++ b/docs/bitmap.md @@ -39,5 +39,5 @@ screen().drawTransparentBitmap(cake, 0, 0) ``` ```package -arcadeshield=github:microbit-apps/arcadeshield +display-shield=github:microbit-apps/display-shield ``` diff --git a/docs/clone.md b/docs/clone.md index 9eccadc..cfd8633 100644 --- a/docs/clone.md +++ b/docs/clone.md @@ -37,5 +37,5 @@ screen().drawBitmap(stickPerson2, 0, 32) ``` ```package -arcadeshield=github:microbit-apps/arcadeshield +display-shield=github:microbit-apps/display-shield ``` diff --git a/docs/create.md b/docs/create.md index 9714577..c54886b 100644 --- a/docs/create.md +++ b/docs/create.md @@ -36,5 +36,5 @@ screen().drawTransparentBitmap(orangeBox, 0, 0) ``` ```package -arcadeshield=github:microbit-apps/arcadeshield +display-shield=github:microbit-apps/display-shield ``` diff --git a/docs/draw-bitmap.md b/docs/draw-bitmap.md index a8b7be0..524fa5b 100644 --- a/docs/draw-bitmap.md +++ b/docs/draw-bitmap.md @@ -28,5 +28,5 @@ screen().drawBitmap(doubleRect,10,10) ``` ```package -arcadeshield=github:microbit-apps/arcadeshield +display-shield=github:microbit-apps/display-shield ``` diff --git a/docs/draw-circle.md b/docs/draw-circle.md index 184f7f2..951461c 100644 --- a/docs/draw-circle.md +++ b/docs/draw-circle.md @@ -43,5 +43,5 @@ screen().drawBitmap(blueRect, 0, 0) ```package -arcadeshield=github:microbit-apps/arcadeshield +display-shield=github:microbit-apps/display-shield ``` diff --git a/docs/draw-line.md b/docs/draw-line.md index 4880712..f14817b 100644 --- a/docs/draw-line.md +++ b/docs/draw-line.md @@ -29,5 +29,5 @@ screen().drawBitmap(drawBigX,64,44) ``` ```package -arcadeshield=github:microbit-apps/arcadeshield +display-shield=github:microbit-apps/display-shield ``` diff --git a/docs/draw-rect.md b/docs/draw-rect.md index 2189516..76a58a1 100644 --- a/docs/draw-rect.md +++ b/docs/draw-rect.md @@ -29,5 +29,5 @@ screen().drawBitmap(blueRect, 0, 0) ```package -arcadeshield=github:microbit-apps/arcadeshield +display-shield=github:microbit-apps/display-shield ``` diff --git a/docs/draw-transparent-bitmap.md b/docs/draw-transparent-bitmap.md index a24fa58..038e33a 100644 --- a/docs/draw-transparent-bitmap.md +++ b/docs/draw-transparent-bitmap.md @@ -50,5 +50,5 @@ screen().drawTransparentBitmap(greenBall, 0, 0) To see the difference, change the second call from `drawTransparentBitmap` to `drawBitmap`. ```package -arcadeshield=github:microbit-apps/arcadeshield +display-shield=github:microbit-apps/display-shield ``` diff --git a/docs/equals.md b/docs/equals.md index 912780f..d49c3c2 100644 --- a/docs/equals.md +++ b/docs/equals.md @@ -66,5 +66,5 @@ if (secondBitmap.equals(firstBitmap)) { ```package -arcadeshield=github:microbit-apps/arcadeshield +display-shield=github:microbit-apps/display-shield ``` diff --git a/docs/fill-rect.md b/docs/fill-rect.md index 0f3a9f9..658751e 100644 --- a/docs/fill-rect.md +++ b/docs/fill-rect.md @@ -50,5 +50,5 @@ screen().drawBitmap(chessBoard, 0, 0) ``` ```package -arcadeshield=github:microbit-apps/arcadeshield +display-shield=github:microbit-apps/display-shield ``` diff --git a/docs/fill.md b/docs/fill.md index 88d7310..247f282 100644 --- a/docs/fill.md +++ b/docs/fill.md @@ -25,5 +25,5 @@ screen().drawBitmap(yellowRect, 0, 0) ```package -arcadeshield=github:microbit-apps/arcadeshield +display-shield=github:microbit-apps/display-shield ``` diff --git a/docs/flip-x.md b/docs/flip-x.md index ad12a4a..3317d3f 100644 --- a/docs/flip-x.md +++ b/docs/flip-x.md @@ -33,5 +33,5 @@ for (let i = 0; i < 10; i++) { ```package -arcadeshield=github:microbit-apps/arcadeshield +display-shield=github:microbit-apps/display-shield ``` diff --git a/docs/flip-y.md b/docs/flip-y.md index 3ae8cdc..26c6c7e 100644 --- a/docs/flip-y.md +++ b/docs/flip-y.md @@ -37,5 +37,5 @@ for (let i = 0; i < 10; i++) { ``` ```package -arcadeshield=github:microbit-apps/arcadeshield +display-shield=github:microbit-apps/display-shield ``` diff --git a/docs/get-pixel.md b/docs/get-pixel.md index aee9b60..8addaf9 100644 --- a/docs/get-pixel.md +++ b/docs/get-pixel.md @@ -34,5 +34,5 @@ screen().drawBitmap(randoColors, 0, 0) ```package -arcadeshield=github:microbit-apps/arcadeshield +display-shield=github:microbit-apps/display-shield ``` diff --git a/docs/is-pressed.md b/docs/is-pressed.md index 0ca2f00..91bfe78 100644 --- a/docs/is-pressed.md +++ b/docs/is-pressed.md @@ -41,5 +41,5 @@ basic.forever(() => { ```package -arcadeshield=github:microbit-apps/arcadeshield +display-shield=github:microbit-apps/display-shield ``` diff --git a/docs/on-button-event.md b/docs/on-button-event.md index 116910a..492ba5e 100644 --- a/docs/on-button-event.md +++ b/docs/on-button-event.md @@ -42,5 +42,5 @@ controller.A.onEvent(ControllerButtonEvent.Pressed, function () { ``` ```package -arcadeshield=github:microbit-apps/arcadeshield +display-shield=github:microbit-apps/display-shield ``` diff --git a/docs/on-shield-event.md b/docs/on-shield-event.md index ff0a9b0..fc51b71 100644 --- a/docs/on-shield-event.md +++ b/docs/on-shield-event.md @@ -21,5 +21,5 @@ controller.onShieldEvent(ControllerShieldEvent.Present, function () { ``` ```package -arcadeshield=github:microbit-apps/arcadeshield +display-shield=github:microbit-apps/display-shield ``` diff --git a/docs/replace.md b/docs/replace.md index a0c4992..43901ef 100644 --- a/docs/replace.md +++ b/docs/replace.md @@ -31,5 +31,5 @@ basic.forever(() => { ```package -arcadeshield=github:microbit-apps/arcadeshield +display-shield=github:microbit-apps/display-shield ``` diff --git a/docs/screen-bitmap.md b/docs/screen-bitmap.md index 7089c90..c7f328d 100644 --- a/docs/screen-bitmap.md +++ b/docs/screen-bitmap.md @@ -59,5 +59,5 @@ screen().drawBitmap(cake,0,0) ```package -arcadeshield=github:microbit-apps/arcadeshield +display-shield=github:microbit-apps/display-shield ``` diff --git a/docs/scroll.md b/docs/scroll.md index a529a57..23dd093 100644 --- a/docs/scroll.md +++ b/docs/scroll.md @@ -68,5 +68,5 @@ basic.forever(() => { ``` ```package -arcadeshield=github:microbit-apps/arcadeshield +display-shield=github:microbit-apps/display-shield ``` diff --git a/docs/set-pixel.md b/docs/set-pixel.md index 0f2004f..3c6427e 100644 --- a/docs/set-pixel.md +++ b/docs/set-pixel.md @@ -34,5 +34,5 @@ screen().drawBitmap(hatch, 0, 0) ``` ```package -arcadeshield=github:microbit-apps/arcadeshield +display-shield=github:microbit-apps/display-shield ``` diff --git a/fieldeditors.ts b/fieldeditors.ts index c544d26..f4ab730 100644 --- a/fieldeditors.ts +++ b/fieldeditors.ts @@ -9,7 +9,7 @@ namespace bitmaps { //% weight=78 //% group="Create" //% blockAliasFor="bitmaps.create" - //% help=github:arcadeshield/docs/create + //% help=github:display-shield/docs/create //% width.defl=16 //% height.defl=16 export function _create(width: number, height: number): Bitmap { @@ -44,7 +44,7 @@ namespace bitmaps { //% bitmap.fieldOptions.decompileArgumentAsString="true" //% weight=85 //% group="Create" - //% help=github:arcadeshield/docs/bitmap + //% help=github:display-shield/docs/bitmap //% blockAliasFor="bitmaps.create" //% blockNamespace="drawing" export function _bitmap(bitmap: Bitmap): Bitmap { diff --git a/frame.ts b/frame.ts index 0464012..af772a7 100644 --- a/frame.ts +++ b/frame.ts @@ -18,7 +18,7 @@ namespace control.__screen { control.runInParallel(() => { while (true) { __updated = false - pause(200) + pause(20) if (!__updated) { __screen.update(); __updated = true diff --git a/icon.png b/icon.png index deb39c0..1775064 100644 Binary files a/icon.png and b/icon.png differ diff --git a/icon_backup.png b/icon_backup.png index 1d6d466..3e705e2 100644 Binary files a/icon_backup.png and b/icon_backup.png differ diff --git a/init.ts b/init.ts index ceaa517..81036b4 100644 --- a/init.ts +++ b/init.ts @@ -5,11 +5,14 @@ //% groups=["0.","1#","2T","3t","4N","5n","6G","7g","8","9","aAR","bBP","cCp","dDO","eEY","fFW"] function bmp(lits: any, ...args: any[]): Bitmap { return null; } +control.waitMicros(300000) + // set palette before creating screen, which initializes the display shieldhelpers.setPalette(hex`000000ffffffff2121ff93c4ff8135fff609249ca378dc52003fad87f2ff8e2ec4a4839f5c406ce5cdc491463d000000`) const theScreen: Bitmap = __screen_internal.createScreen(); theScreen.fill(15) +// __screen_internal.loop() namespace __screen_internal { @@ -26,4 +29,16 @@ namespace __screen_internal { return img as Bitmap; } + + export function loop() { + + let i = 0; + while (true) { + // basic.showNumber(i) + screen().fill(i % 16) + i += 1 + + basic.pause(500) + } + } } diff --git a/pxt.json b/pxt.json index f0f112b..59a35e1 100644 --- a/pxt.json +++ b/pxt.json @@ -1,7 +1,7 @@ { "name": "display-shield", - "version": "1.0.2", - "description": "Arcade Shield extension for MakeCode micro:bit", + "version": "1.0.6", + "description": "Display shield extension for MakeCode micro:bit", "dependencies": { "core": "file:../core", "radio": "file:../radio", diff --git a/shieldhelpers.ts b/shieldhelpers.ts index ac7953d..3c9f1b8 100644 --- a/shieldhelpers.ts +++ b/shieldhelpers.ts @@ -55,7 +55,7 @@ namespace shieldhelpers { } private sendMessage(msg: string) { - control.simmessages.send("microbit-apps/arcadeshield", Buffer.fromUTF8(msg), false) + control.simmessages.send("microbit-apps/display-shield", Buffer.fromUTF8(msg) , false) } initSim() { @@ -101,7 +101,7 @@ namespace shieldhelpers { //% shim=TD_NOOP function startSim() { - control.simmessages.onReceived("microbit-apps/arcadeshield", handleShieldMessage) + control.simmessages.onReceived("microbit-apps/display-shield", handleShieldMessage) _screenState.initSim() while (!_screenState.gotSimMessage) { basic.pause(0) @@ -199,7 +199,7 @@ namespace shieldhelpers { //% blockId=shieldPresent block="shield present?" //% blockNamespace="Controller" //% weight=0 - //% help=github:arcadeshield/docs/shield-resent + //% help=github:display-shield/docs/shield-resent export function shieldPresent(): boolean { __present = undefined while (__present === undefined) { @@ -244,6 +244,7 @@ namespace shieldhelpers { if (msg.type === "button-down" || msg.type === "button-up") { const button = getButton((msg).buttonId) if (button) { +// button.setPressed(msg.type === "button-down") if (msg.type === "button-down") button.raiseButtonDown(wds) else diff --git a/simx/README.md b/simx/README.md index 1db842a..d465d1c 100644 --- a/simx/README.md +++ b/simx/README.md @@ -1,6 +1,6 @@ -# Simulator for the arcadeshield extension +# Simulator for the display-shield extension -Simulates an Arcade Shield accessory attached to a BBC micro:bit. When the arcadeshield extension is added to a [MakeCode for BBC micro:bit](https://makecode.microbit.org) project, this simulator will instantiate below MakeCode's main simulator. +Simulates an Arcade Shield accessory attached to a BBC micro:bit. When the display-shield extension is added to a [MakeCode for BBC micro:bit](https://makecode.microbit.org) project, this simulator will instantiate below MakeCode's main simulator. ## Local testing and development @@ -22,7 +22,12 @@ This starts the simx's local development server. 1. Open the MakeCode editor for BBC micro:bit (https://makecode.microbit.org/beta) 2. Add the URL parameter `simxdev=1` 3. Create a project -4. Add the arcadeshield extension to your project +4. Add the display-shield extension to your project The `simxdev=1` parameter will cause sim messages to be routed to your locally running development server. +## More info + +See https://github.com/microsoft/pxt-simx-sample/blob/master/simx/README.md + + diff --git a/simx/index.html b/simx/index.html index b63255a..e9e29ee 100644 --- a/simx/index.html +++ b/simx/index.html @@ -3,7 +3,7 @@ - MakeCode Arcade Shield Simulator + MakeCode Display Shield Simulator
diff --git a/simx/package-lock.json b/simx/package-lock.json index 47c7386..b657503 100644 --- a/simx/package-lock.json +++ b/simx/package-lock.json @@ -1,14 +1,14 @@ { - "name": "@arcadeshield/sim", + "name": "@display-shield/simx", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "@arcadeshield/sim", + "name": "@display-shield/simx", "version": "1.0.0", "dependencies": { - "arcadeshield": "file:../", + "display-shield": "file:../", "react": "^18.3.1", "react-dom": "^18.3.1", "react-svg": "^16.1.34" @@ -34,6 +34,7 @@ }, "..": { "dependencies": { + "makecode": "^1.3.0", "pxt-microbit": "^7.1.9" } }, @@ -1851,6 +1852,10 @@ "node": ">=8" } }, + "node_modules/display-shield": { + "resolved": "..", + "link": true + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -2987,10 +2992,6 @@ "node": ">=6" } }, - "node_modules/arcadeshield": { - "resolved": "..", - "link": true - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", diff --git a/simx/package.json b/simx/package.json index 67712ed..0eae67e 100644 --- a/simx/package.json +++ b/simx/package.json @@ -1,8 +1,8 @@ { - "name": "@arcadeshield/simx", + "name": "@display-shield/simx", "private": true, "version": "1.0.0", - "homepage": "https://github.com/microbit-apps/arcadeshield", + "homepage": "https://github.com/microbit-apps/display-shield", "type": "module", "scripts": { "dev": "vite", @@ -14,7 +14,7 @@ "prettier": "prettier --write ./src" }, "dependencies": { - "arcadeshield": "file:../", + "display-shield": "file:../", "react": "^18.3.1", "react-dom": "^18.3.1", "react-svg": "^16.1.34" diff --git a/simx/src/components/Shield.tsx b/simx/src/components/Shield.tsx index 70917c8..a60aae4 100644 --- a/simx/src/components/Shield.tsx +++ b/simx/src/components/Shield.tsx @@ -58,7 +58,7 @@ function postMessagePacket(msg: any) { window.parent.postMessage( { type: "messagepacket", - channel: "microbit-apps/arcadeshield", + channel: "microbit-apps/display-shield", data: payload, }, "*" diff --git a/simx/src/hooks/useShieldService.ts b/simx/src/hooks/useShieldService.ts index abc9eca..49c80a9 100644 --- a/simx/src/hooks/useShieldService.ts +++ b/simx/src/hooks/useShieldService.ts @@ -6,7 +6,7 @@ function postMessage(msg: protocol.ArcadeShieldMessage) { window.parent.postMessage( { type: "messagepacket", - channel: "microbit-apps/arcadeshield", + channel: "microbit-apps/display-shield", data: payload, }, "*" @@ -42,7 +42,7 @@ export function useShieldService( function handleMessagePacket(msg: any) { const srcFrameIndex = (msg.srcFrameIndex as number) ?? -1 switch (msg.channel) { - case "microbit-apps/arcadeshield": + case "microbit-apps/display-shield": return handleShieldMessage(msg.data, srcFrameIndex) case "jacdac": return @@ -77,7 +77,7 @@ export function useShieldService( case "set-palette": return handleSetPaletteMessage(msg as protocol.SetPaletteMessage) default: - console.log(`unknown arcadeshield message: ${JSON.stringify(msg)}`) + console.log(`unknown display-shield message: ${JSON.stringify(msg)}`) } } function handleShowImageMessage(msg: protocol.ShowImageMessage) { diff --git a/test.ts b/test.ts index cf73a2b..e8ee445 100644 --- a/test.ts +++ b/test.ts @@ -1,3 +1,38 @@ +// namespace config { +// export const DISPLAY_CFG0 = 0x02030180 // allow execution without shield plugged in +// } + +// tests go here; this will not be compiled when this package is used as an extension. + +// const present = shieldhelpers.shieldPresent(); + +// basic.showNumber(present ? 1 : 0) + +// while (true) { +// let x = 0 +// let my = theScreen.height -1 +// theScreen.fill(0) +// theScreen.print((my+1).toString(), 60, 60) +// while (x < 160) { +// theScreen.setPixel(x, 0, 9) +// theScreen.setPixel(x, 2, 10) +// theScreen.setPixel(x, 4, 11) +// theScreen.setPixel(x, my, 9) +// theScreen.setPixel(x, my - 2, 10) +// theScreen.setPixel(x, my - 4, 11) +// x++ +// basic.pause(100) +// // pause +// } +// } + +// screen().fill(3) +// radio.onReceivedNumber(function (receivedNumber) { +// screen().fill(receivedNumber); +// }) + + + // // tests go here; this will not be compiled when this package is used as an extension. // const present = shieldhelpers.shieldPresent(); diff --git a/tutorials/getting-started.md b/tutorials/getting-started.md index fadcee8..25bae67 100644 --- a/tutorials/getting-started.md +++ b/tutorials/getting-started.md @@ -1,93 +1,190 @@ -# Micro:bit Arcade Shield Getting Started +# Micro:bit Display Shield Getting Started ## Getting started @showdialog -In this tutorial, you will learn to draw on the screen of a micro:bit Arcade shield. -Let's go! +In this tutorial, you will learn to draw on the screen of a micro:bit display shield with +a variety of drawing blocks. Let's go! ## Fill the screen with a color @showhint -Drag the `||drawing:fill||` at the start of `||basic:on start||`. After restarting, you should also see the arcade shield simulator. +Drag the `||drawing:fill||` block to the start of `||basic:on start||`. You should see the display shield simulator and the display should turn blue: ```blocks // @highlight -screen().fill(4) +screen().fill(8) ``` -## Draw some lines @showhint +## Set a pixel to white @showhint -Drag the `||drawing:draw line||` twice to create two diagonal lines. +Drag the `||drawing:set pixel||` block to plot a single point: ```block -screen().fill(4) +screen().fill(8) // @highlight -screen().drawLine(0, 0, 159, 119, 2) -// @highlight -screen().drawLine(159, 0, 0, 119, 5) +screen().setPixel(80, 60, 1) ``` -## Draw a filled circle @showhint +## Draw a diagonal line @showhint -Drag the `||drawing:fill circle||` to create a filled yellow circle at the center of the screeb. +Drag the `||drawing: draw line||` to draw a line from the upper-left of the display to the lower-right: ```block -screen().fill(4) -screen().drawLine(0, 0, 159, 119, 2) -screen().drawLine(159, 0, 0, 119, 5) +screen().fill(8) +screen().setPixel(80, 60, 1) // @highlight -screen().fillCircle(80, 60, 20, 5) +screen().drawLine(0, 0, 159, 119, 1) ``` +## Fill a rectangle @showhint + +Drag the `||drawing: fill rectangle||` to fill the rectangle in the upper-left quadrant of the display: + +```block +screen().fill(8) +screen().setPixel(80, 60, 1) +screen().drawLine(0, 0, 159, 119, 1) +// @highlight +screen().fillRect(0, 0, 79, 59, 6) +``` ## Draw a rectangle @showhint +Drag the `||drawing: draw rectangle||` to draw a rectangle outline in the upper-left quadrant of the display: + ```block -screen().fill(4) -screen().drawLine(0, 0, 159, 119, 2) -screen().drawLine(159, 0, 0, 119, 5) -screen().fillCircle(80, 60, 20, 5) -screen().drawRect(70, 50, 20, 20, 10) +screen().fill(8) +screen().setPixel(80, 60, 1) +screen().drawLine(0, 0, 159, 119, 1) +screen().fillRect(0, 0, 79, 59, 6) +// @highlight +screen().drawRect(0, 0, 79, 59, 5) ``` -## Create a bitmap +## Fill a circle @showhint +Drag the `||drawing: fill dircle||` to fill a circle in the center of the display: ```block -screen().fill(4) -screen().drawLine(0, 0, 159, 119, 2) -screen().drawLine(159, 0, 0, 119, 5) -screen().fillCircle(80, 60, 20, 5) -screen().drawRect(70, 50, 20, 20, 10) +screen().fill(8) +screen().setPixel(80, 60, 1) +screen().drawLine(0, 0, 159, 119, 1) +screen().fillRect(0, 0, 79, 59, 6) +screen().drawRect(0, 0, 79, 59, 5) // @highlight -let bitmap = bitmaps.create(32, 32) +screen().fillCircle(80, 60, 10, 10) ``` -## Draw into the bitmap +## Draw a circle @showhint + +Drag the `||drawing: draw dircle||` to draw a circular outline at the center of the display: ```block -screen().fill(4) -screen().drawLine(0, 0, 159, 119, 2) -screen().drawLine(159, 0, 0, 119, 5) -screen().fillCircle(80, 60, 20, 5) -screen().drawRect(70, 50, 20, 20, 10) -let bitmap = bitmaps.create(32, 32) +screen().fill(8) +screen().setPixel(80, 60, 1) +screen().drawLine(0, 0, 159, 119, 1) +screen().fillRect(0, 0, 79, 59, 6) +screen().drawRect(0, 0, 79, 59, 5) +screen().fillCircle(80, 60, 10, 10) // @highlight -bitmap.fillCircle(16, 16, 15, 5) +screen().drawCircle(80, 60, 10, 9) +``` + +# Create a bitmap with the bitmap editor @showhint + +Drag the `||drawing: set bitmap||` block to create a new variable holding a bitmap. Click on the bitmp block to open the bitmap editor and paint an image: + +```block +screen().fill(8) +screen().setPixel(80, 60, 1) +screen().drawLine(0, 0, 159, 119, 1) +screen().fillRect(0, 0, 79, 59, 6) +screen().drawRect(0, 0, 79, 59, 5) +screen().fillCircle(80, 60, 10, 10) +screen().drawCircle(80, 60, 10, 9) // @highlight -bitmap.drawRect(6, 6, 21, 21, 10) -screen().drawBitmap(bitmap, 0, 0) +let bitmap = bmp` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . 7 7 7 7 . . . . . . . + . . . 7 7 7 2 2 7 7 7 . . . . . + . . . 7 2 2 2 2 2 2 7 . . . . . + . . 7 7 2 2 2 2 2 2 7 7 . . . . + . . 7 7 2 2 2 2 2 2 2 7 . . . . + . . 7 7 2 2 2 2 2 2 2 7 7 . . . + . . 7 7 2 2 2 2 2 2 2 2 7 . . . + . . . 7 2 2 2 2 2 2 2 2 7 . . . + . . . 7 2 2 2 2 2 2 2 2 7 . . . + . . . 7 7 2 2 2 2 2 2 7 7 . . . + . . . . . 7 7 2 2 7 7 7 . . . . + . . . . . . 7 7 7 7 . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` ``` -# Draw the bitmap to the screen +# Draw the bitmap (no transparency) + +Drag the `||drawing: draw bitmap||` to draw the bitmap you created without transparency (transparent pixels appear black): ```block -screen().fill(4) -screen().drawLine(0, 0, 159, 119, 2) -screen().drawLine(159, 0, 0, 119, 5) -screen().fillCircle(80, 60, 20, 5) -screen().drawRect(70, 50, 20, 20, 10) -let bitmap = bitmaps.create(32, 32) -bitmap.fillCircle(16, 16, 15, 5) -bitmap.drawRect(6, 6, 21, 21, 10) +screen().fill(8) +screen().setPixel(80, 60, 1) +screen().drawLine(0, 0, 159, 119, 1) +screen().fillRect(0, 0, 79, 59, 6) +screen().drawRect(0, 0, 79, 59, 5) +screen().fillCircle(80, 60, 10, 10) +screen().drawCircle(80, 60, 10, 9) +let bitmap = bmp` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . 7 7 7 7 . . . . . . . + . . . 7 7 7 2 2 7 7 7 . . . . . + . . . 7 2 2 2 2 2 2 7 . . . . . + . . 7 7 2 2 2 2 2 2 7 7 . . . . + . . 7 7 2 2 2 2 2 2 2 7 . . . . + . . 7 7 2 2 2 2 2 2 2 7 7 . . . + . . 7 7 2 2 2 2 2 2 2 2 7 . . . + . . . 7 2 2 2 2 2 2 2 2 7 . . . + . . . 7 2 2 2 2 2 2 2 2 7 . . . + . . . 7 7 2 2 2 2 2 2 7 7 . . . + . . . . . 7 7 2 2 7 7 7 . . . . + . . . . . . 7 7 7 7 . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` // @highlight -screen().drawBitmap(bitmap, 0, 0) +screen().drawBitmap(bitmap, 72, 52) ``` +# Draw the bitmap (with transparency) + +Drag the `||drawing: draw bitmap transparent||` to draw the bitmap you created with transparency (background color shows through transparent pixels): + +```block +screen().fill(8) +screen().setPixel(80, 60, 1) +screen().drawLine(0, 0, 159, 119, 1) +screen().fillRect(0, 0, 79, 59, 6) +screen().drawRect(0, 0, 79, 59, 5) +screen().fillCircle(80, 60, 10, 10) +screen().drawCircle(80, 60, 10, 9) +let bitmap = bmp` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . 7 7 7 7 . . . . . . . + . . . 7 7 7 2 2 7 7 7 . . . . . + . . . 7 2 2 2 2 2 2 7 . . . . . + . . 7 7 2 2 2 2 2 2 7 7 . . . . + . . 7 7 2 2 2 2 2 2 2 7 . . . . + . . 7 7 2 2 2 2 2 2 2 7 7 . . . + . . 7 7 2 2 2 2 2 2 2 2 7 . . . + . . . 7 2 2 2 2 2 2 2 2 7 . . . + . . . 7 2 2 2 2 2 2 2 2 7 . . . + . . . 7 7 2 2 2 2 2 2 7 7 . . . + . . . . . 7 7 2 2 7 7 7 . . . . + . . . . . . 7 7 7 7 . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` +screen().drawBitmap(bitmap, 72, 52) +// @highlight +screen().drawTransparentBitmap(bitmap, 32, 22) +``` \ No newline at end of file