Tutorial 5 : Digital Input Output driver - sudeshmoreyos/Morey_os-demo-1.0 GitHub Wiki
Introduction to Digital Input/Output
The most fundamental peripheral of any microcontroller is its digital input and output capability. As the name suggests, a microcontroller uses its pins either to send a digital output (0 or 1) to an external device or to read a digital input from one. Although this concept is simple, it is extremely powerful and forms the basis of many important applications. Examples of digital output applications include LED blinking, seven-segment displays, LCD control, buzzer on/off operation, etc. Examples of digital input applications include switch state detection, reading the output of digital sensors, etc.
In the next section, we will explore how to use the Digital Driver in Morey_OS to implement digital input/output functionality.
Introduction to Digital Driver
In Morey_os, Digital.h is the first driver we will learn about. To use this driver, you must include the following header file:
#include "Digital.h"
Please note D is capital, while all other letters are in small letters.
Digital driver supports following functions :
1. Digital.pinmode(pin,direction);
2. Digital.write(pin,value);
3. Digital.read(pin);
1. Digital.pinmode(pin,direction);
This function is used to set the direction of a digital pin on a controller or development board. It takes two inputs: the pin number and the desired pin direction. The valid pin directions are OUTPUT and INPUT. Some controllers or boards may support additional options—such as INPUT_PULLUP on AVR microcontrollers. Note that OUTPUT, INPUT, and INPUT_PULLUP must always be written in capital letters.
When a pin is configured as OUTPUT, it behaves as a digital output pin. When configured as INPUT, the pin becomes a high-impedance (tri-state) input. When configured as INPUT_PULLUP, the pin is treated as an input with an internal pull-up resistor enabled, causing a floating input to read as HIGH.
This function does not return any value.
Pin Numbering Methods in the Digital Driver
The Digital Driver supports three methods of specifying pin numbers:
a) Universal Morey_OS Pin Numbers These are portable across all controllers, platforms, and boards—subject only to the capabilities of the selected controller. They are denoted as OS_pin0, OS_pin1, OS_pin2, OS_pin3, and so on. For example, on the Arduino Uno, OS_pin0 corresponds to pin0, OS_pin1 to pin1, and so forth.
b) Architecture-Specific Pin Numbers These are portable across all controllers belonging to the same architecture or family. For example, AVR Mega-series controllers use pin names such as A0–A7, B0–B7, C0–C7, etc.
On the Arduino Uno Board:
D0–D7 correspond to pin0–pin7,
B0–B5 correspond to pins pin8–pin13,
C0–C5 correspond to analog pins pinA0–pinA5 (or digital pins 14–19).
Use of Macro DEFINE_CUSTOM_OS_PINS
We can also define OS pins to custom pins than stardard defined by OS. To do so we have to declare:
#define DEFINE_CUSTOM_OS_PINS
#define OS_pin5 D0
By defining the DEFINE_CUSTOM_OS_PINS macro, we instruct the OS not to use its default pin definitions. After that, we manually map OS_pin5 to pin D0, whereas originally the OS had mapped OS_pin5 to D5.
Important Note
a) A board supports all three pin-numbering methods.
b) A microcontroller (without a board abstraction) supports only methods (a) and (b).
2. Digital.write(pin,value);
This function is used to set value of an output pin. Before using this function, selected pin must be set as OUTPUT using Digital.pinmode function. It takes two inputs : pin number and its value. pin numbering we have already discussed before. A digital output pin can be given three possible values a) LOW or 0 b) HIGH or 1 c) TOGGLE or 2. LOW sets output low, HIGH sets output high, while TOGGLE toggles the output. Please note LOW , HIGH and TOGGLE are all in capital letters. This function has no output. This function should not be used for pins which are set as INPUT or INPUT_PULLUP by Digital.pinmode function.
3. Digital.read(pin);
This function is used to read the input status of an input pin. Before using this function, selected pin must be set as INPUT or INPUT_PULLUP using Digital.pinmode function. It takes one input as pin number and gives 1 or 0 as output. This function can also be used for pins which are set in OUTPUT direction. This function reads the current output state of the selected pin.
4. Example Code
Let us now check an example code named digital-input-output.c that can be found in example folder (examples/board-examples/arduino-uno/digital-input-output). It implements both digital input and output. The code is as follows :
#include "morey_os.h"
#include "Digital.h"
void setup(void)
{
Digital.pinmode(pin13,OUTPUT);
Digital.pinmode(pin12,OUTPUT);
Digital.pinmode(pin2,INPUT);
}
TASK_CREATE(led_blink,"led blink");
TASK_CREATE(input_test,"input test");
TASK_AUTOSTART(&led_blink, &input_test);
TASK_RUN(led_blink)
{
// Process starts here
BEGIN();
while(1)
{
Digital.write(pin13,HIGH);
DELAY_SEC(0.5);
Digital.write(pin13,LOW);
DELAY_SEC(0.5);
}
// process ends here
END();
}
TASK_RUN(input_test)
{
// Process starts here
BEGIN();
while(1)
{
if(Digital.read(pin2)==1)
Digital.write(pin12,HIGH);
else
Digital.write(pin12,LOW);
DELAY_SEC(0.1);
}
// process ends here
END();
}
Code Explanation :
- morey_os.h header file is compulsory to include in all codes. Since we are using digital driver functionality, we have added Digital.h header too.
#include "morey_os.h"
#include "Digital.h"
- In setup function we have declared pin13 and pin12 of Arduino Uno as output while pin2 is set as input.
void setup(void)
{
Digital.pinmode(pin13,OUTPUT);
Digital.pinmode(pin12,OUTPUT);
Digital.pinmode(pin2,INPUT);
}
- We have created two tasks named led_blink and input_test. Please note TASK_CREATE Macro has two inputs. First input is name of the task to be created, other input is string name of the task which is used during code debugging process. We will discuss more about code debugging in subsequent tutorials.
TASK_CREATE(led_blink,"led blink");
TASK_CREATE(input_test,"input test");
- Next we autostart both the tasks at startup or powerup of the board. We can optionally start these tasks manually inside some other tasks. Starting or ending tasks inside a task will be discuss in subsequent tutorials. For now we will autostart all tasks at controller/board boot time.
TASK_AUTOSTART(&led_blink, &input_test);
- Next we declare TASK_RUN for both the tasks. led_blink task blinks LED connected at pin13 after every 0.5 seconds. To give delay in morey_os we can use DELAY_SEC() or DELAY_SEC_PRECISE() macros. Detailed discussion of these two macros is to be done in subsequent tutorials. Also it is very important to note that to blink LED continuously we have used while(1) in our code which is an infinite loop. Morey_os is based on cooperative threads, thats why this infinite loop must have at least one DELAY_SEC() or DELAY_SEC_PRECISE() macro. To give delay between LED blinks we have used DELAY_SEC(0.5); hence this condition is satisfied.
TASK_RUN(led_blink)
{
BEGIN();
while(1)
{
Digital.write(pin13,HIGH);
DELAY_SEC(0.5);
Digital.write(pin13,LOW);
DELAY_SEC(0.5);
}
END();
}
- input_check task continuously reads input from pin2 and accordingly gives high low output to pin12. Again here we are using an infinite loop using while(1). In a typical code we will not use any delay in this infinite loop. But since it is a requirement for morey_os to add at least one DELAY_SEC() or DELAY_SEC_PRECISE() macro inside an infinite loop, we have added DELAY_SEC(0.1); Ideally this delay should be as low as possible for fast response. DELAY_SEC() or DELAY_SEC_PRECISE() macros supports minimum of 1 milli-second or 0.001 second delay. However, it is recommended not to give delay less than 50 milli-second or 0.05 Seconds.
TASK_RUN(input_test)
{
BEGIN();
while(1)
{
if(Digital.read(pin2)==1)
Digital.write(pin12,HIGH);
else
Digital.write(pin12,LOW);
DELAY_SEC(0.1);
}
END();
}
-
What happens if we forget to use at least one DELAY_SEC() or DELAY_SEC_PRECISE() macro inside an infinite loop? Detailed discussions of cooperative thread implementation is done is subsequent tutorials. Let me share basic information here. In Morey_os, tasks must return their control to Morey_os kernel. Being cooperative thread implementation, OS cannot take back control on its own. Control to OS is returned back to OS by DELAY_SEC() or DELAY_SEC_PRECISE() macros. If we forgot to add it, respective task will stuck in infinite loop without returning control to OS. In that case after 2 seconds, watchdog timer is triggered and OS will restart itself. So in any code implementation, if you notice that code is automatically restarting again and again, then you might have missed adding DELAY_SEC() macros inside an infinite loop.
-
Content of Makefile is as follows :
PROJECT = digital-input-output
MOREY_OS_PATH = ../../../..
BOARD = ARDUINO_UNO
include $(MOREY_OS_PATH)/Makefile.include
PROJECT Macro will hold name of the project file name. MOREY_OS_PATH holds the location of Morey_os root directory. BOARD Macro should hold ARDUINO_UNO since we are programming for Arduino_uno board. And last line should remain the same as mentioned above.
- This code is simple and requires no special configurations. Hence config.h file content will remain same as below :
#ifndef CONFIG_H
#define CONFIG_H
#define COMPILER AVR_GCC
#endif
Next we will learn about SevenSegment library in Tutorial-6.