10 KiB
Add table of contents
Blinky
The classic, helloWorld of embedded programming. This program turns the onboard LED on and off periodically.
Initial steps:
- Create new project with project generator.
- Open the project folder in VSCode.
- Build the project by pressing the
buildbutton at the bottom and select the option witharm-none-eabi. - Make sure the build exits with code 0.
Copy the below code into the project, build and upload to the pico.
Basic Code:
int main()
{
stdio_init_all();
const uint OnBoardLED = PICO_DEFAULT_LED_PIN;
gpio_init(OnBoardLED);
gpio_set_dir(OnBoardLED, GPIO_OUT);
while (true){
gpio_put(OnBoardLED, true);
sleep_ms(500);
gpio_put(OnBoardLED, false);
sleep_ms(500);
}
}
Explanation:
stdio_init_all(): initialize stuff
OnBoardLED = PICO_DEFAULT_LED_PIN: Get the GPIO number of onboard LED. Remember: this is GPIO Number, NOT PIN NUMBER.
gpio_init(OnBoardLED): Initialize that GPIO pin.
gpio_set_dir(OnBoardLED, GPIO_OUT): Set GPIO direction (GPIO_OUT for writing and GPIO_IN for reading).
while (true){}: Super loop of the program
gpio_put(OnBoardLED, true): Set OnBoardLED pin to on (true to turn on/set to high/voltage to VDD, false to turn off/set to low/voltage to GND)
Pico logic HIGH voltage is 3.3v and LOW voltage is 0v.
sleep_ms(500): sleep for 500ms.
Different Blinky
This makes the pico blink like a beacon, 2 short blinks followed by long delay. A for loop is used for repeating the short blink twice.
for (int i = 0; i < 2; i++)
{
gpio_put(OnBoardLED, true);
sleep_ms(50);
gpio_put(OnBoardLED, false);
sleep_ms(100);
}
sleep_ms(500);
External Blinky
This program blinks an LED connected to one of the GPIO(General Purpose Input/Output) ports.
For this example, I used Pin 20(GP15). I connected a resistor to this pin and connected an LED in series with the resistor to the ground, as shown in the image below. This resistor limits the amount of current drawn by the LED. There are other ways to limit the current, which are discussed in this document later.
const uint LEDPin = 15;
gpio_init(LEDPin);
gpio_set_dir(LEDPin, GPIO_OUT);
while (true)
{
for (int i = 0; i < 2; i++)
{
gpio_put(LEDPin, true);
sleep_ms(50);
gpio_put(LEDPin, false);
sleep_ms(100);
}
sleep_ms(500);
}
The only change here is in line 1, PICO_DEFAULT_LED_PIN is changed to 15, to represent GP15. Remember: in the program, a GPIO pin in the program is represented by the GP number and NOT by the pin number, as shown in the Pico's Pinout.
Multiple Blinky
This program controls multiple LED connected to various GPIO ports. Here, the program is written such that it resembles a loading bar. For this setup, internal current limits are used. They are toggled programatically as shown in the code below.
int LED1 = 11;
int LED2 = 12;
int LED3 = 13;
int LED4 = 14;
int LED5 = 15;
int LEDS[5] = {LED1, LED2, LED3, LED4, LED5};
for (int i = 0; i < 5; i++)
{
gpio_init(LEDS[i]);
gpio_set_dir(LEDS[i], GPIO_OUT);
gpio_set_drive_strength(LEDS[i], GPIO_DRIVE_STRENGTH_2MA);
}
while (true)
{
for (int i = 0; i < 5; i++)
{
gpio_put(LEDS[i], true);
sleep_ms(100);
}
for (int i = 0; i < 5; i++)
{
gpio_put(LEDS[i], false);
sleep_ms(100);
}
}
sleep_ms(500);
Explanation:
int LEDS[5] = {...};declares a list of integers of length 5. GPIO numbers of the 5 connected LEDS are stored here.gpio_set_drive_strength(LED[i], GPIO_DRIVE_STRENGTH_2MA)sets the Drive Strength/Current Limit of GPIO port. The RP2040 C SDK provides 4 drive strengths, 2mA, 4mA, 8mA and 12mA.
The rest of the code is similar to the prior examples, which toggles the LEDS one by one, in series and turns them off, creating a loading effect. Visual output can be seen below.
add gif of above
ToggleBlinky
This example uses gpio_get_out_level to to get the output level for given given gpio port. The above example can be simplified using this function call, as below. This function will be covered in DigitalIn.md as well.
int LED1 = 11;
int LED2 = 12;
int LED3 = 13;
int LED4 = 14;
int LED5 = 15;
int LEDS[5] = {LED1, LED2, LED3, LED4, LED5};
for (int i = 0; i < 5; i++)
{
gpio_init(LEDS[i]);
gpio_set_dir(LEDS[i], GPIO_OUT);
gpio_set_drive_strength(LEDS[i], GPIO_DRIVE_STRENGTH_2MA);
}
while (true)
{
for (int i = 0; i < 5; i++)
{
gpio_put(LEDS[i], !gpio_get_out_level(LEDS[i]));
sleep_ms(100);
}
}
sleep_ms(500);
ParallelBlinky
In this example, the init and set dir operations are performed simultaneously using gpio_init_mask and gpio_set_dir_masked calls. These take a integer value called mask, where each bit of that integer represents one GPIO pin. The above example can be simplified and replicated with the code below.
int LED1 = 11;
int LED2 = 12;
int LED3 = 13;
int LED4 = 14;
int LED5 = 15;
int LEDS[5] = {LED1, LED2, LED3, LED4, LED5};
int pinMask = 0b1111100000000000;
int dirMask = 0b1111100000000000;
gpio_init_mask(pinMask);
gpio_set_dir_masked(pinMask, dirMask);
while (true)
{
for (int i = 0; i < 5; i++)
{
gpio_put(LEDS[i], !gpio_get_out_level(LEDS[i]));
sleep_ms(100);
}
}
sleep_ms(500);
Here, the pinMask variable represents the gpio pins. The position number from the right represents GP# with that number. A 1 in a position means the pin represented by that position is highlighted. So the 0th bit maps to GP0, 1st bit maps to GP1 and so on. We need GP11 to GP15, so the bits in 11th to 15th positions are set to 1, making them highlighted.
gpio_init_mask will initialize only the pins highlighted in the function's input.
gpio_set_dir_masked will set the IO direction of only the highlighted pins in its first argument to the direction specified in its second argument dirMask. In dirMask, 1 denoted output and 0 denotes input.
For this example, the init mask functions will initialize GP11 to GP15, because they are highlighted by pinMask. The set dir mask function will focus on GP11 to GP15, because they are highlighted by pinMask, and set the direction if they are 1 or 0 based on dirMask.
Line Control
This experiment showcases how digital output pin can control a DC appliance, a fan in this case, using a particular IC. The code stays the same as Blinky, only the hardware changes. This example showcases real world use case for digital output.
For this example, a IRLZ44N MOSFET is used for controlling a 12v DC fan. This MOSFET acts as a switch, which can be digitally controlled using the digital out pin on the pico.
A 12v voltage module is used as power source for the fan. Positive of the module is connected to the fan's positive pin. The Gate pin of the mosfet is connected to a GPIO pin, Source is connected to ground and Drain is connected to -ve pin of the fan.
It is important to note that ALL THE GROUND MUST BE CONNECTED, forming whats knows as Common Ground. Explanaion provided in Arduino Forum. The ground of the 12v module is connected to ground of the pico. This should result in connections like below.
Code to turn the fan on and off with a 2 second delay:
{
stdio_init_all();
const uint FANPIN = 22;
gpio_init(FANPIN);
gpio_set_dir(FANPIN, GPIO_OUT);
while (true){
gpio_put(FANPIN, true);
sleep_ms(2000);
gpio_put(FANPIN, false);
sleep_ms(2000);
}
}
Binary LED Counter
This example again uses 5 LEDS to visualize a 5 bit binary counter. Mask commands are used to set the LEDS simultaneously.
int pinMask = 0b1111100000000000;
int dirMask = 0b1111100000000000;
int counter = 0;
gpio_init_mask(pinMask);
gpio_set_dir_masked(pinMask, dirMask);
while (true)
{
gpio_put_masked(pinMask, counter<<11);
counter++;
sleep_ms(500);
}
gpio_put_masked has similar characteristics as other masked functions. This will set the output pins according to the second argument simultaneously, counter<<11 in this case. Counter is left shifted by 11 bits to align with the mask.
if counter is 11001, counter<<11 will be 1100100000000000.
SmoothBlinky
This example demonstrates how brightness of an LED can be controlled by modulating the output using the time delay
const uint LEDPin = 14;
gpio_init(LEDPin);
gpio_set_dir(LEDPin, GPIO_OUT);
gpio_set_drive_strength(LEDPin, GPIO_DRIVE_STRENGTH_2MA);
int onDelay=0;
int totalDelay=10;
while (true)
{
for (int i = 0; i < 50; i++)
{
gpio_put(LEDPin, true);
sleep_ms(onDelay);
gpio_put(LEDPin, false);
sleep_ms(totalDelay - onDelay);
}
onDelay = (onDelay+1) % totalDelay;
}
onDelay is maintained such that total time for each iteration remains constant, equal to totalDelay. This above code will make the LED look like the brightness changes.
Summary
Below are the API calls used in this document:
Initializing Calls:
| API call | Arguments | Definition |
|---|---|---|
gpio_init |
GP# | Initializes GP# pin |
gpio_set_dir |
GP#, <GPIO_IN/GPIO_OUT> | Sets the direction of GP# pin to either read or write |
gpio_set_drive_strength |
GP#, GPIO_DRIVE_STRENGTH_#MA | Sets the drive strength of GP# pin to given value |
gpio_init_mask |
GP_MASK | Initializes the GP pins highlighted by GP_MASK |
gpio_set_dir_masked |
GP_MASK, DIR_MASK | Sets the direction of GP pins highlighted by GP_MASK according to DIR_MASK |
Controlling Calls:
| API call | Arguments | Definition |
|---|---|---|
gpio_put |
GP#, <true/false> | Sets the GP# pin to HIGH or LOW |
gpio_put_masked |
GP_MASK, DIR_MASK | Sets the GP pins highlighted by GP_MASK according to DIR_MASK |
MISC Calls:
| API call | Arguments | Definition |
|---|---|---|
sleep_ms |
TIME | Pauses the program for given number of milli-seconds when called |
stdio_init_all |
-- | Initializes stuff |
add fading led example by changing the amount of times the led blinks in a given cycle.