Creating an Arcade: Animations and Games - Showcases of the CLB Using the PIC16F13145 Microcontroller with MCC Melody
The repository provides a comprehensive collection of MPLAB® X projects specifically designed to leverage the PIC16F13145's peripheral functionality, particularly the Configurable Logic Block (CLB), to control a WS2812 LED matrix. These projects showcase various interactive applications that demonstrate the capabilities of the microcontroller in managing complex LED patterns and animations. Among the featured applications, there are several dynamic and visually engaging examples:
- Scrolling Images: This application enables animated images to move across the LED matrix, creating smooth transitions and a visually appealing display.
- Snake Game: A classic arcade game where the player controls a growing line, themed as a snake, collecting items while keeping the snake away from colliding with obstacles and itself.
- Langton's Ant Demo: A simulation of Langton's Ant, a cellular automaton that produces intricate, unpredictable patterns as it moves across the LED matrix.
- Pong Game: A simple version of the iconic Pong game, where players can bounce a pixelated "ball" back and forth on the LED matrix.
- Static Images: Displays static images and patterns on the LED matrix for a wide variety of visual effects.
These projects highlight the versatility of the PIC16F13145 in creative, interactive displays, making it a valuable resource for anyone interested in embedded systems and LED control.
More details and code examples on the PIC16F13145 can be found at the following links:
- The PIC16F13145 Curiosity Nano Development board is used as a test platform:
- The Curiosity Nano Explorer board is used as a development platform:
- MikroE SRAM Click
- MikroE Flash 6 Click
- 6 x WS2812 LED Matrix 8x32:
- DC Power Supply 5V 40A
To program the Curiosity Nano board with this MPLAB X project, follow the steps provided in the How to Program the Curiosity Nano Board chapter.
To complete this hardware setup, the next steps must be done:
-
Plug the PIC16F13145 Curiosity Nano board and MikroE Click boards (Flash 6 and SRAM) onto the Curiosity Nano Explorer board, but do not connect the assembled LED panel yet.
IMPORTANT! The Curiosity Nano Explorer board has only one mikroBUS slot, so the Flash chip is connected in parallel with the SRAM chip on the SRAM click, wiring the CS pin of the Flash to the AN slot of the mikroBUS to be used as the Chip Select pin of the SPI protocol.
-
On the Curiosity Nano Explorer platform, connect PWM4 (RC1) to MBUS_CS using a jumper wire.
-
Build and compile the application and program the Curiosity Nano Board with the application.
-
Update the serial communication port in the UploadAll.bat file, found in the Scripts folder. Replace the COM port number with the one assigned to the PIC16F13145 Curiosity Nano on local machine.
-
Run the Flash and EEPROM loading script by running UploadAll.bat from the Scripts folder.
-
Connect the LED Matrix to PWM-C_OUT pin, ensuring the LED panel's ground (GND) is also connected to the ground on the Curiosity Nano Explorer.
-
Power on the LED matrix, followed by the Curiosity Nano Explorer board. The LED Matrix is now displaying the Microchip logo.
The PIC16F13145 Curiosity Nano pin allocation is configured specifically for use on the Curiosity Nano Explorer platform, as in the below picture:
The following image illustrates the interconnection of WS2812 LED matrices and shows how the image will appear on the display.
6 x WS2812 LED Matrix 8x32 are used, connected in chain, meaning the output of a panel is the input of another panel. The figure above shows how all of the matrix are chaining. Data signal in connected to the first panel on Panel_1 DIN
, the last panel remaining not connected, Panel_6 DIN
. To display the image, all six matrices are treated as a single 48x32 matrix. The orientation used for this matrix is serpentine orientation, in which the data flow follows a zig-zag pattern.
The first pixel from the resulting matrix is the pixel set in TOP_LEFT
corner corresponding to panel number 6. For each panel is it known as the last entry pixel. The last pixel for odd panels is in BOTTOM_RIGHT
and for even panels is in TOP_LEFT
, these panels being inverted. To display the desired image, it is necessary to find the memory offset relative to the start address where the image is stored in memory. The offset is calculated on the basis of the current position in resulted matrix and in actual panel. If the pannel possition type is TOP_LEFT
, the offset is decremented and the pixels are set from bottom to top; otherwise, the offset is incremented and the pixels are set from top to bottom.
The PIC16F13145 utilizes the CLB peripheral to read data from external SPI memory sources (SRAM, Flash, and/or the Curiosity Nano Explorer on-board EEPROM) and transmit it to the WS2812 LED Matrix. The CLB handles the conversion of SPI data to the digital LED protocol in real-time, without requiring CPU intervention. The next circuit is implemented using the CLB peripheral.
For user interaction, the on-board switches and the joystick are employed, which are read through the I2C interface.
The use of different types of external memory is determined by access time requirements and the need for volatile or nonvolatile data:
- The SRAM memory offers the fastest access time and is used to store images displayed on the matrix during the execution of
Snake
,Pong
, andLangton's Ant
applications. In these scenarios, the image is updated with every frame, making read/write operation speed crucial for the application's refresh rate. - The Flash memory is utilized to store animation images due to its larger capacity compared to EEPROM, allowing it to hold the extensive number of images required for animations. Data can be accessed after a reset once it has been written using a loader script.
- The Curiosity Nano Explorer on-board EEPROM is designated for storing static images, such as the initial image of each application displayed in the menu. A loader script is used to write the images to memory once and read them as needed.
The interconnection between the microcontroller and the external components is illustrated in the image below, along with the necessary communication protocols and the output signal to the LED matrix.
The CLB peripheral implements an SPI to WS2812B converter, reading data stored in external SPI memory (such as SRAM, EEPROM, or FLASH) and converting it in real-time to a WS2812 stream. This data transfer from the external SPI memory to the WS2812 LED matrix occurs automatically, with minimal CPU load, similar to DMA mode on a PIC18. The application CPU is responsible for setting the parameters. The current demo implements the following applications:
- Displaying static images and text (Microchip Logo or the introduction text)
- Playing animations and scrolling text,
Scrolling Images
- Running games such as
Snake
andPong
- Demonstrating cellular automation with
Langton's Ant
Note: All five applications are available for programming only with the XC8 compiler in the PRO version. In the header file named mainApp.h
, the user can select, through four define directives, which applications to have available upon code loading. A define with a value of 1
includes the application in the compilation, whereas a value of 0
excludes it. Additionally, for the Scrolling Images example, the user must set the FRAMES_NUMBER define to match the number of bmp
files in the bmp_48x32
folder, located in the Scripts folder.
Below is an introductory overview of the user interface for the Curiosity Nano Explorer. This section provides key details about the layout, functionality, and available controls within the interface, helping users understand how to navigate and operate the platform effectively.
- SW1 and SW3 are used to navigate through existing applications in the main menu
- SW2 is used to select and start the current application. While the application is running, SW2 can also be used to pause or continue the current application
- The joystick serves as the controller for games such as
Snake
andPong
- To return to the main menu, SW1 and SW3 can be used when the current application is paused or has ended
The Snake Game is a classic arcade game where the player controls a snake that navigates a plane, collecting food items while avoiding collisions with its tail or the walls. Each time the snake consumes food, it grows longer, increasing the game's difficulty. The objective is to survive as long as possible as the snake continues to grow and the game speed potentially increases.
- Note: This gif is played at 8x speed.
This application is divided into two zones on the LED matrix. The first zone, defined by the walls, snake, and food, is described by the dimensions SNAKE_PANEL_XSIZE
and SNAKE_PANEL_YSIZE
, which can be modified by the user. The second zone displays the PTS
(short for POINTS) message and the current length of the snake, which also is used to represent the score, by subtracting 3 that represents the initial length of the snake. The maximum length of the snake can be adjusted by setting the SNAKE_MAX_LENGTH
value. A difficulty system is included to enhance the game's challenge using the SNAKE_SpeedSet
function, which adjusts the game speed based on the snake's length. The score is displayed using the SNAKE_ScorePrint
function, which clears the matrix area and shows the game points via the WS2812_Printf
function.
The SNAKE_Initialize
function sets up the snake game, fixing the initial positions of the snake and food based on the panel size (SNAKE_PANEL_XSIZE
and SNAKE_PANEL_YSIZE
). The snake's first move is downward, determined by the values in the direction
vector, which indicates the movement direction on the map. The SNAKE_Advance
function moves the snake to the next position, checking if the move is valid by ensuring the snake's head does not collide with the walls or its own tail. If the move is valid and the snake reaches the food, the snake's length increases, a new random food position is generated, and the score is updated. If the move is not to a food position, the snake's length remains the same, and the head moves to the next position while the tail is removed.
The SNAKE_Main
function is the primary function, called periodically from the main loop. It checks the last pressed key, with six possible actions, including game initialization, returning to the menu, or changing direction. The game speed is updated by calling the SNAKE_SpeedSet
function, and the snake advances to the next position. Additionally, this function includes two conditions to check if the game has ended: a loss if the snake collides with the walls or its own tail, or a win if the snake reaches the maximum length set by SNAKE_MAX_LENGTH
.
Note: The user can modify the previously mentioned parameters by updating the values in the define section of the source file.
Langton's Ant operates on a simple set of rules that generate complex patterns through the movement of ants. The rules dictate that an ant can move one step in any of the four cardinal directions: up, down, left, or right. After each movement, the ant rotates 90 degrees to the right if the new square is black, or 90 degrees to the left if it is colored. The color of the square is updated after each step; if the current square is colored, the ant changes it to black. The entire matrix is used to display the pattern generated by the ants' movements.
- Note: This gif is played at 8x speed.
The number of ants is defined by the N
parameter, which is based on the matrix dimensions. Upon starting the application, the LANT_Initialize
function randomly sets the initial positions of the ants. The LANT_Main
function is periodically invoked from the main loop to apply the game rules and move each ant accordingly. The LANT_PixelColorCheck
function determines whether the current pixel is colored and changes it to black or vice versa. The LANT_DirectionSet
function moves an ant to a new position based on its current direction, while the LANT_PositionCheck
function ensures the ant remains within the matrix boundaries. Finally, the LANT_DirectionCheck
function verifies whether the ant should rotate left or right for its next move, based on the aforementioned rules.
Note: The user can modify the previously mentioned parameters by updating the values in the define section of the source file.
Pong is a straightforward game featuring two paddles and a ball, designed to simulate table tennis. The objective is to strike the ball with the paddle and score points when the opponent fails to return it. This version is a single-player game where the user can control their paddle using a joystick for upward and downward movements, while the opponent's paddle operates automatically.
- Note: This gif is played at 4x speed.
The PONG_Main
function serves as the primary function, invoked periodically from the main loop. The PONG_CollisionCheck_BallMove
function checks if the ball has collided with the top or bottom walls or a player's paddle and adjusts its direction accordingly. To create the movement effect, the ball is erased and redrawn in its new position using the WS2812_PixelSet
function. Similar operations are performed to move the two paddles up and down using the PONG_PlayerPaddleMove
and PONG_EnemyPaddleMove
functions. When a player loses, the PONG_RoundNew
function updates the score and reinitializes the game with the ball in a random position. The score is managed using the WS2812_ValuePrint
operation, which deletes the old value and updates it with the new one. A new game round begins when an up or down command is executed. The PADDLE_COUNTER
and BALL_COUNTER
definitions are used to set the speed of the opponent's paddle and the ball.
The initial top-left position of the court can be configured using the COURT_X
and COURT_Y
definitions, which represent the physical (x, y) coordinates in the matrix. The court dimensions along each axis can be specified using the COURT_LENGTH_X
and COURT_LENGTH_Y
macros. These settings must be adjusted according to the court's starting position and the physical dimensions of the matrix. The paddle dimensions are automatically determined based on the court's height.
Note: The user can modify the previously mentioned parameters by updating the values in the define section of the source file.
The demonstration enables the playback of short animations or videos at a refresh rate of up to 25 frames per second. The animation is stored frame by frame in an external SPI Flash memory. To change the images, the next chapters must be followed, Flash/EEPROM Loader Script and Image/Animation Creation. If a GIF needs to be displayed on the panel, the user must update the FRAMES_NUMBER
macro located in the mainApp.h
header file to reflect the number of *.bmp
files stored in the designated script folder.
- Note: This gif is played at 3x speed.
The Flash/EEPROM loader script is detailed in the ws2812_panel.py
file located in the Scripts folder. This script generates two binary files containing bytearrays (flash.bin
and eeprom_new.bin
for each memory), which can be imported and utilized to write to the Flash and EEPROM memories. Make sure that all images used are sized at 48 x 32 pixels.
-
Flash Loader: This script is designed to load animation images stored in the
bmp_48x32
folder. For each image, the script generates a bytearray buffer to store the image data in G, R, B format, which is compatible with WS2812 LEDs. It extracts the RGB components of each pixel and stores them in the buffer at the address corresponding to the pixel's (x, y) position in the matrix. Each buffer is subsequently appended to theflash.bin
file. -
EEPROM Loader - This script is designed to load static images stored in the
images_48x32
folder. Each application has two associated images:- One for display in the menu (with a name ending in
_main
, e.g.,snake_main
) - One for display when the application is selected but not yet started (with a name ending in
_start
, e.g.,snake_start
).
The
addAppEntry
function adds these two images for each application into the specified file. The function parameters include the application ID, which represents the application's image index in memory, and the application name, which identifies the application images in the folder. Similar to the Flash loader, the function creates a bytearray buffer to store the first image data, extracts the RGB components of each pixel, and stores them in the buffer at the address corresponding to the (x,y) position in the matrix. This buffer is then appended to theeeprom_new.bin
file. The same process is repeated for the second image. - One for display in the menu (with a name ending in
The Jinx! – LED Matrix Control software facilitates the creation of images and animations, offering features to generate or import images and apply effects to achieve the desired outcome. The matrix dimensions (48 x 32) can be configured via the Setup->Matrix Options menu.
To create a scrolling text animation, select "Scrolling Text" from the Channel 1 / Effect 1
dropdown menu. The user can access the menu for this option by clicking the Edit button, which allows the user to modify the displayed text, its size, position, and other additional settings. Additionally, the user can add another effect by selecting "Raindrops" from the Channel 1 / Effect 2
dropdown menu.
To display the animation on the matrix, a series of images that constitute the animation must be exported in bitmap format and stored in the Flash memory. This export option can be accessed via the Setup->Output Devices menu. By selecting the Add option, the user can set the Device type to Bitmap Export and choose the folder for storing the images using the Output Redirection option.
To initiate the storage of images for the animation, select the Setup->Start Output option. Once this option is activated, images will be continuously saved in the previously designated folder for the Output Devices until the Start Output option is deactivated. Upon completion of the animation, the Flash loader script can be utilized to upload the images.
For the creation of static images, select the Channel 1 / Effect 1
option and set it to "Image Viewer". The user can upload the image using the Edit button. To save the image in bitmap format, follow the same procedures as for a scrolling image, but choose a single image from the generated options.
Note: All five applications are available for programming only with the XC8 compiler in the PRO version. In the header file named mainApp.h
, the user can select, through four define directives, which applications to have available upon code loading. A define with a value of 1
includes the application in the compilation, whereas a value of 0
excludes it. To program the entire project, the user must use the existing hex file of the pro version in the project.
As shown in the demos below, various images and interactive games are displayed on the LED matrix for each application. Each demo highlights a unique display feature, showcasing the versatility of the LED matrix in rendering different visuals and gameplay experiences.
This example showcases the capabilities of the CLB, a Core Independent Peripheral (CIP) that performs complex tasks with minimal CPU involvement. These projects emphasize the PIC16F13145's versatility in creating dynamic, interactive displays, making it an excellent resource for those interested in embedded systems and advanced LED control applications.
This chapter demonstrates how to use the MPLAB X IDE to program a PIC® device with an Example_Project.X. This is applicable to other projects.
-
Connect the board to the PC.
-
Open the
Example_Project.X
project in MPLAB X IDE. -
Set the
Example_Project.X
project as main project.
Right click the project in the Projects tab and click Set as Main Project. -
Clean and build the
Example_Project.X
project.
Right click theExample_Project.X
project and select Clean and Build. -
Select PICxxxxx Curiosity Nano in the Connected Hardware Tool section of the project settings:
Right click the project and click Properties.
Click the arrow under the Connected Hardware Tool.
Select PICxxxxx Curiosity Nano (click the SN), click Apply and then click OK: -
Program the project to the board.
Right click the project and click Make and Program Device.