Termbank
  1. A
    1. Abstraction
    2. Alias
    3. Argument
    4. Array
  2. B
    1. Binary code file
    2. Binary number
    3. Bit
    4. Bitwise negation
    5. Bitwise operation
    6. Byte
  3. C
    1. C library
    2. C-function
    3. C-variable
    4. Character
    5. Code block
    6. Comment
    7. Compiler
    8. Complement
    9. Conditional statement
    10. Conditional structure
    11. Control structure
  4. D
    1. Data structure
    2. Duck typing
  5. E
    1. Error message
    2. Exception
  6. F
    1. Flag
    2. Float
  7. H
    1. Header file
    2. Headers
    3. Hexadecimal
  8. I
    1. Immutable
    2. Initialization
    3. Instruction
    4. Integer
    5. Interpreter
    6. Introduction
    7. Iteroitava
  9. K
    1. Keyword
  10. L
    1. Library
    2. Logical operation
  11. M
    1. Machine language
    2. Macro
    3. Main function
    4. Memory
    5. Method
  12. O
    1. Object
    2. Optimization
  13. P
    1. Parameter
    2. Placeholder
    3. Pointer
    4. Precompiler
    5. Precompiler directive
    6. Prototype
    7. Python console
    8. Python format
    9. Python function
    10. Python import
    11. Python list
    12. Python main program
    13. Python variable
    14. Python-for
    15. Pääfunktio
    16. printf
  14. R
    1. Resource
    2. Return value
  15. S
    1. Statement
    2. Static typing
    3. String
    4. Syntax
  16. T
    1. Terminal
    2. Type
    3. Typecast
  17. U
    1. Unsigned
  18. V
    1. Value
  19. W
    1. Warning
    2. while
Completed: / exercises
Learning Objectives: General principles of embedded programming. Implementation of an embedded program on the SensorTag device.

Embedded Real-Time Operating System

Learning Objectives: General principles of embedded programming.
We will start exploring embedded device programming by first going through the structure of a embedded programming program and then adding functionalities by learning how to use various peripheral libraries. In the course we will use the SensorTag device although we will provide information also about the Raspberri Pi Pico as extra material.

Real-Time Operating System

The manufacturer of the SensorTag (Texas Instruments) offers a real-time operating system (RTOS) to facilitate programming the device. We will use its libraries and services for practice and eventually for the completion of the course project. This material presents programming examples for the device, but the best way to learn is, of course, by experimenting and doing it yourself. But first, let's go through some related concepts.
An operating system is software that runs computer applications and has two main tasks. Firstly, it provides an execution environment for applications and secondly it handles the computer’s hardware resources, peripherals, and permissions. The operating system offers services through interfaces and libraries to applications so they can utilize those resources. Resources include memory management, file systems, peripheral device usage. The operating system also controls the execution of applications by distributing the processor(s) execution time and memory among them. These topics are covered in more detail in a later Operating Systems course, so we will skip a comprehensive explanation here.
A real-time operating system provides an execution environment for embedded system applications. In this context, real-time means that the operating system guarantees a certain response time for functionalities. For example, a response to an input/event is guaranteed to happen within one millisecond, and the response time may take a maximum of 10 milliseconds. The RTOS itself can be very lightweight, for example, it may simply distribute processor time between applications. A lightweight implementation also supports applications with strict real-time requirements, as very little time is spent on the OS's own routines. The implementation of RTOS services significantly impacts the design, implementation, and maintenance of embedded software.
As mentioned earlier, it is possible to program embedded systems without an operating system and/or firmware, but in that case, the programmer is responsible for handling the functionality of the device, such as programmatically connecting peripheral components to the microcontroller and implementing the interfaces/services required by the application.

Implementing an Embedded Program

As mentioned in the earlier C language material, it makes a lot of sense to implement computer programs in a modular way. In the embedded world, it is also reasonable to divide the entire program modularly into different tasks, where the functionality can be logically encapsulated, i.e., separated from each other. Now, each task in the program is assigned its own inputs (events to which it responds) and its own outputs (action or effects that it produces).
For example, in the course, tasks in the SensorTag device could include reading sensor data, updating the display, or communicate with other device. Well, these are not just examples; they are exactly what we will implement in the course project.
Now, it is, of course, reasonable to let the RTOS handle the concurrent execution of tasks to ensure that responses are as efficient as possible. The programmer's responsibility is then to implement the task and define its execution parameters, considering the efficiency requirements. These execution parameters include things like timing, priority, memory allocation, various initial settings, etc. In this regard, learning embedded programming is essentially about learning to use the built-in features of the RTOS through libraries, interfaces, and software structures.
An alternative would be to program the RTOS or firmware yourself, similar to Arduino's superloop structure (a large loop inside the main function that handles all program functionality). As mentioned, the superloop is an acceptable solution for implementing small embedded systems that do not have significant resource or performance requirements. However, in this approach, much of the benefits of modularity are lost, and the programmer may even complicate the program's execution. From a maintenance perspective, such a solution can be a nightmare, as even a small code modification can disrupt carefully tuned timings. Hardly anyone would attempt to implement, for example, task execution based on different priorities within a superloop—it would inevitably be clumsy. Well, in the industry, embedded programming is a separate art compared to general workstation programming, especially when dealing with tasks with strict requirements, peripheral drivers, or even custom operating systems.
In addition to implementing tasks and defining execution parameters, synchronization between tasks requires solutions. For instance, a single input, such as pressing a button on a device, might trigger a response from one or multiple tasks or lead to a series of sequential operations across different tasks. How can such implementation be achieved?
Actually, we are now getting into the territory of the Operating Systems course, so we will leave it at that. However, there is a relatively simple solution from digital technology, which we will use in the course: state machines. The course does not intend to dive deeper into the theory of state machines (a very large topic), but we will use them to implement synchronization between tasks in our program.
In short, a state machine operates by transitioning the program logically from one state to another based on inputs or events. Each state is responsible for performing a specific task and may produce an output. The program tracks the current state using at least one state variable, which updates as events or actions occur. Functions or tasks in the program monitor the state variable and execute their operations based on the current state. The state may change after a task is completed, or it may change in response to an external input, triggering the next appropriate state, where the cycle repeats.
For example, in an embedded system, this could look like the following sequence of state transitions:
IDLE state -> button press -> read sensor data -> display result/transmit message -> IDLE state
In this example, the program could have three or four different tasks: an interrupt handler reacting to a button press, a task for communicating with the sensor, a task for updating the display, and a simultaneous task for wireless communication. Perhaps also a task for data preprocessing, but this depends on the design and, for instance, how much data there is. Now, of course, this logical functionality could be implemented based on a superloop, but in that case, all subtasks would have to account for the state of other functionalities in the program, such as whether communication with peripherals is ongoing. For example, in the above state machine, we would not be able to receive messages while reading sensor data.
Example: A state machine-based implementation of an embedded program for Arduino. Here also there is an interesting example It’s worth checking out these pages just for an overview, and because we will use a similar solution in the course.
Let's present a simple example with Arduino:
enum State {
  WAITING,
  READ_SENSOR,
  PROCESS_DATA,
  DISPLAY_RESULT,
  TRANSMIT_DATA
};
State currentState = WAITING;

void loop() {
  switch (currentState) {
    case WAITING:
      if (buttonPressed()) {
        currentState = READ_SENSOR;
      }
      break;
      
    case READ_SENSOR:
      readSensor();
      currentState = PROCESS_DATA;
      break;
      
    case PROCESS_DATA:
      processData();
      currentState = DISPLAY_RESULT;
      break;
      
    case DISPLAY_RESULT:
      displayResult();
      currentState = TRANSMIT_DATA;
      break;
      
    case TRANSMIT_DATA:
      transmitData();
      currentState = WAITING;
      break;
  }
}
Well, more on this shortly.

Introduction to TI-RTOS

The diagram below shows a block diagram of the ready-made functionalities that TI-RTOS, firmware, and device drivers provide for the SensorTag device. The RTOS offers programmers interfaces (the red area in the diagram) that hide the details of the lower-level implementation, such as device and component drivers, real-time features, and communication details. In this course, we focus on the application level, where we use various libraries through interfaces (Application Programming Interface, API). We will stay "within the red area" of the device's operation and will not dive deeper into the internal workings.
"TI RTOS components"
We will mention some of these libraries now, but their usage in programs will be explored in detail in later lecture materials.

Services and Libraries

The following libraries will be used for program implementation. The terminology will be covered in more detail in the upcoming materials.
(For those interested: More detailed information on the library can be found in the TI-RTOS Kernel User Guide, TI RTOS User's Guide.) and the Technical reference manual of our device family.

Program Operation

Let’s take a look at the main function of a SensorTag C program as an example. At first, the C language used by the SensorTag’s RTOS and libraries may seem cryptic because it uses a lot of predefined components. However, by carefully studying the code and thinking about what is happening, it becomes more understandable.
The SensorTag program also has a single main function, from which the program execution begins. Ok, the RTOS does its own thing behind the scenes before the main function runs, but we don’t worry about that in this course.
Below is an example of the main function in a SensorTag program:
// Include SensorTag libraries
#include <xdc/runtime/System.h>
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/drivers/I2C.h>
#include <ti/drivers/PIN.h>

int main(void) {

   // Note: The initialization of the following variables is omitted here

   Board_initGeneral(); // Initialize the device
   Board_initI2C();     // Initialize the I2C bus

   // I/O definition to enable the button
   buttonHandle = PIN_open(&buttonState, buttonConfig);
   if(!buttonHandle) {
      System_abort("Error initializing button\n");
   }

   // Set up an interrupt handler for the button (function buttonFxn)
   if (PIN_registerIntCb(buttonHandle, &buttonFxn) != 0) {
      System_abort("Error registering button callback function");
   }
   
   // Initialize a task for the application, implemented in taskFn function
   Task_Params_init(&taskParams);
   taskParams.stackSize = STACKSIZE;
   taskParams.stack = &taskStack;
   taskParams.priority=2;
   task = Task_create((Task_FuncPtr)taskFxn, &taskParams, NULL);
   if (task == NULL) {
      System_abort("Task create failed!");
   }

   // Notify via the debugger that initialization was successful
   System_printf("Initialization OK!\n");
   System_flush();

   // Start the RTOS (BIOS firmware), at which point the task execution begins
   BIOS_start();

   return 0;
}
As in all C programs, at the beginning of the program, the precompiler directives are used to include the libraries needed for the program. With SensorTag's RTOS, there are quite a few, and this is jus a reduced number of them.
After that, at the beginning of the main function, the device itself is initialized. Essentially, what is being done here is informing the RTOS that it is running on the SensorTag device. Remember that TI's RTOS is a general-purpose operating system for all TI embedded platforms, so this is needed to take control of the device resources, peripherals, I/O definitions, device-specific connections, etc.
In addition, in this example, we want to use the I2C library to communicate with the sensors, so it is initialized with the function call Board_initI2C.
Once the device is initialized, the selected peripherals are brought into the program by initializing them and setting their I/O configurations. In this example, one of the device's buttons is brought into use, and the handler for the event generated by pressing the button (interrupt) is defined, which in this case is the function buttonFxn. Each task in the SensorTag is a C function, and their writing and usage (with some caveats) follow the same rules as C functions generally.
Then, in the main function, there would be room to do any other initializations necessary to control the operation of the program. For example, if we are using timers to control the execution of tasks, they can be initialized here.
After that, it's a good idea to send greetings via the debugger to the development environment's console window so we can see that the device initialization was successful. The console can be written to with the System_printf function, which works (with minor exceptions) in the same way as the standard C library's printf function. Remember our earlier discussion about how embedded implementations of standard libraries vary in terms of implemented features.
Lastly, the "actual" program is started, i.e., the execution of tasks, with the function call BIOS_start. After this, the program only responds to the events caused by the peripherals defined in this main function.
Note that the code uses a lot of checks after the initializations to ensure that the initializations were successful. These are needed because, when programming embedded systems, we have limited visibility into the internal operation of the device.

SensorTag Program Implementation

Rest of the material explains how to create an embedded program for the SensorTag in a task-based manner, so that multiple tasks can run concurrently using the device’s RTOS (Real-Time Operating System).
As hinted in the earlier material, the key idea behind TI-RTOS is that the functionality of the program is implemented as separate tasks that run concurrently based on the device’s processing power. The programmer has full control over how the program is divided into tasks and what each task does internally. A simple program can, of course, be implemented with a single task, but a more complex program that, for example, uses multiple peripherals, is not easily achieved with just one task. Properly implemented multitasking significantly speeds up program execution because tasks don’t need to wait for each other to finish.
This is where the programmer’s design skills come in. It’s not practical to create a separate task for every minor thing, as this would consume resources allocated to the RTOS. Internal functions within a task should also be implemented as functions. For example, calculating the area of a circle doesn’t require a task; a simple C function is enough, which can be called from within a task as needed.
For tasks to run concurrently on a single microcontroller, its real execution time must be shared between the tasks (and the RTOS). Time-sharing can be based on time-based allocation, priority-based allocation, or reactive handling of inputs or various signals when they arrive at the device. When dividing execution time, care must be taken to ensure that no task monopolizes the execution time, so the device's other functions aren’t stalled for too long. For example, this could happen if a high-priority task starves lower-priority tasks by taking all their execution time. Another possible situation could arise if a task communicates with a (relatively) slow peripheral and selfishly holds on to the CPU during the wait time.

How does the TI-RTOS work?

Let’s take a look at an example image to see how different tasks (Task) can run concurrently on the SensorTag. In the example, the program implements:
In the image, each task has a specific execution time and priority. Additionally, it’s noted that once certain tasks finish their execution, they are put to sleep with the sleep function call. It is also observed that the program tasks run at a higher priority (2) than the library task (1) and that the interrupt priority is the highest ("infinite").
"Task execution"
Next, let’s see what happens in the program:
Wait a second, what happens during an interrupt? We have to go a little ahead of ourselves here: an interrupt is a hardware-based signal received by the microcontroller, whereas task scheduling is managed by the software (RTOS). In this case, "infinite priority" means that the RTOS has no control over the execution of interrupts — they are handled by the microcontroller. (This is not a complete explanation of how interrupts work, but we’ll cover that in later material).
Ok, one more thing... why isn’t there a sleep call after the communication task? The explanation here is that the operation of that specific SensorTag’s wireless radio library requires the task to stay alive and not enter a sleep state. For this reason, the communication task has priority 1, while the higher-priority tasks are at priority 2 to ensure they get execution time.
Additionally, there’s one more thing to know about task execution. From this explanation, you might get the impression that each task runs once from start to finish, being triggered each time. However, this is not the case in the course. We actually use an internal superloop for each tasks, i.e., a while loop, within which the sleep function is called! This way, the tasks created in the program remain running and keep the program execution ongoing at all times. This means that the sleep function is called at the end of every iteration of the superloop. In a task, you start a timer that determines how long the task will sleep, allowing other tasks to execute during this time. The appropriate sleep time depends on the application and is defined by the programmer.
Ok, ok, but what would happen if there was no superloop in the tasks? Since a task is a C function, it would only run once, and thus the entire embedded program’s execution would end once all tasks had been executed once. The superloop in the tasks is, therefore, a crucial solution to keep the program running on the device.

RTOS Task Library

TI-RTOS allows the implementation of programs and multitasking environments for the SensorTag device through the Task library. With this library, tasks can be created as functions, and their operation can be controlled via the RTOS.
Let’s take a look at an example of the Task library in action:
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>

// Each task needs its own internal stack memory
#define STACKSIZE 512
Char myTaskStack[STACKSIZE];

// Task implementation function
Void myTaskFxn(UArg arg0, UArg arg1) {

   // The task’s eternal life
   while (1) {
      System_printf("My arguments are %ld and %ld\n", arg0, arg1);
      System_flush();   

      // Politely go to sleep for a moment
      Task_sleep(1000000L / Clock_tickPeriod);
   }
}

int main(void) {

   // Data structures related to tasks
   Task_Params myTaskParams;
   Task_Handle myTaskHandle;
	
   // Device initialization
   Board_initGeneral();

   // Initialize task execution parameters
   Task_Params_init(&myTaskParams);
   // Assign stack memory to the task
   myTaskParams.stackSize = STACKSIZE;
   myTaskParams.stack = &myTaskStack; 
   // Set the task’s priority
   myTaskParams.priority = 2;
   // Arguments for the task (for demonstration purposes)
   myParams.arg0 = 127; // Argument 1
   myParams.arg1 = 0xFFFF; // Argument 2

   // Create the task
   myTaskHandle = Task_create((Task_FuncPtr)myTaskFxn, &myTaskParams, NULL);
   if (myTaskHandle == NULL) {	
      System_abort("Task creation failed");
   }

   // Greetings to the console
   System_printf("Hello world!\n");
   System_flush();

   // Start the program
   BIOS_start();

   return (0);
}
Let’s break down this example.
In the task implementation function myTaskFxn, we have the aforementioned infinite loop and, at the end of each iteration, the polite Task_sleep function, which handles the sleep operation and returns control to the RTOS after every iteration.
Then, in the main function, the execution parameters for the task are set in the data structure myTaskParams, which is of type Task_Params. In this example, three things are set for the task, which are always required for every task:
Additionally, we set a handle for the task, which can be used to refer to the task in our program. A handle is simply an identifier that uniquely identifies the task. In this example, the handle (Task_Handle) is only used to check that the task was successfully created.
Arguments can be passed to the task in two unsigned int values (typedef unsigned int UArg) through the structure’s members arg0 and arg1. In this case, the RTOS uses typedef to define its own data types. We will use these as RTOS requires, although passing arguments to tasks might not be necessary in this course, as we may use global variables instead.
A task is created using the Task_create function, which takes the task’s implementation function (of type Task_FuncPtr) (here myTaskFxn) and the parameters myTask_Params as arguments.
We also see the System_abort function, which can be used to programmatically abort the execution of the program in case of an error and print an error message to the console window, in this case, if the task fails to start (for example, if the device runs out of memory).

The Clock is Ticking

The RTOS provides the Clock.h library, which includes the Clock_tickPeriod variable. This tells us how many microseconds there are in one system clock cycle (also known as a tick). By default, the value is 10, meaning one tick equals 10 microseconds. Now, the number of clock cycles can be calculated with the formula n / Clock_tickPeriod, where *n* is the desired time in microseconds.
Given Clock_tickPeriod=10, how many ticks are needed for 500 millisecond (ms) sleep period with function Task_sleep?

Warning: You have not logged in. You cannot answer.
Actually, for the hardware enthusiasts out there, the RTOS clock cycle is a software constant and doesn’t correspond 1:1 with the device’s hardware clock component.

Conclusion

We have become familiar with the basics of program execution in an RTOS. We will delve deeper into using the RTOS libraries in the course lab exercises. In the course lab exercise, students will be given a program template, which includes necessary task implementations and parameter definitions, so you won’t need to look them up outside of the material (although optional additional reading is available).
Free tip 1: If the device gets stuck in the program, the most common cause is a task that runs too long or a missing Task_sleep call in the implementation function. Such a task should be redesigned and divided into smaller parts.
Free tip 2: It’s not a good idea to place multiple consecutive sleep calls inside the implementation function, as this approach often causes problems when functionality is added to the program later and the previously adjusted timings go wrong. There should be only one sleep call in the implementation function, with functionality controlled by conditions and a state machine (see upcoming material).
Free tip 3: Sometimes, you’ll see solutions where multiple priority levels are set up. This often leads to device malfunction, especially if additional functionality is later added to the program. It’s better to stick with priority level 2 and reconsider the task’s functionality. Feel free to ask the assistants if needed.
Free tip 4: You definitely shouldn’t implement the SensorTag program Arduino-style with a single task and a superloop, as this will likely cause logic and timing issues between the functionalities. You’ll be doing yourself a disservice.
?
Abstraction is a process through which raw machine language instructions are "hidden" underneath the statements of a higher level programming language. Abstraction level determines how extensive the hiding is - the higher the abstraction level, the more difficult it is to exactly say how a complex statement will be turned into machine language instructions. For instance, the abstraction level of Python is much higher than that of C (in fact, Python has been made with C).
Alias is a directive for the precompiler that substitus a string with another string whenever encountered. In it's basic form it's comparable to the replace operation in a text editor. Aliases are define with the #define directeve, e.g. #define PI 3.1416
Argument is the name for values that are given to functions when they are called. Arguments are stored into parameters when inside the function, although in C both sides are often called just arguments. For example in printf("%c", character); there are two arguments: "%c" format template and the contents of the character variable.
Array is a common structure in programming languages that contains multiple values of (usually) the same type. Arrays in C are static - their size must be defined when they are introduced and it cannot change. C arrays can only contain values of one type (also defined when introduced).
Binary code file is a file that contains machine language instructions in binary format. They are meant to be read only by machines. Typically if you attempt to open a binary file in a text editor, you'll see just a mess of random characters as the editor is attempting to decode the bits into characters. Most editors will also warn that the file is binary.
Binary number is a number made of bits, i.e. digits 0 and 1. This makes it a base 2 number system.
A bit is the smallest unit of information. It can have exactly two values: 0 and 1. Inside the computer everything happens with bits. Typically the memory contains bitstrings that are made of multiple bits.
Bitwise negation is an operation where each bit of a binary number is negated so that zeros become ones and vice versa. The operator is ~.
Bitwise operations are a class of operations with the common feature that they manipulate individual bits. For example bitwise negation reverses each bit. Some operations take place between two binary values so that bits in the same position affect each other. These operations include and (&), or (|) and xor (^). There's also shift operations (<< and >>) where the bits of one binary number are shifted to the left or right N steps.
Byte is the size of one memory slot - typically 8 bits. It is the smallest unit of information that can be addressed from the computer's memory. The sizes of variable types are defined as bytes.
External code in C is placed in libraries from which they can be taken to use with the #include directive. C has its own standard libraries, and other libraries can also be included. However any non-standard libraries must be declared to the compiler. Typically a library is made of its source code file (.c) and header file (.h) which includes function prototypes etc.
Functions in C are more static than their Python counterparts. A function in C can only have ne return value and its type must be predefined. Likewise the types of all parameers must be defined. When a function is called, the values of arguments are copied into memory reserved for the function parameters. Therefore functions always handle values that are separate from the values handled by the coe that called them.
C variables are statically typed, which means their type is defined as the variable is introduced. In addition, C variables are tied to their memory area. The type of a variable cannot be changed.
Character is a single character, referred in C as char. It can be interpreted as an ASCII character but can also be used as an integer as it is the smallest integer that can be stored in memory. It's exactly 1 byte. A character is marked with single quotes, e.g. 'c'.
Code block is a group of code lines that are in the same context. For instance, in a conditional structure each condtion contains its own code block. Likewise the contents of a function are in their own code block. Code blocks can contain other code blocks. Python uses indentation to separate code blocks from each other. C uses curly braces to mark the beginning and end of a code block.
Comments are text in code files that are not part of the program. Each language has its own way of marking comments. Python uses the # character, C the more standard //. In C it's also possible to mark multiple lines as comments by placing them between /* and */.
A compiler is a program that transforms C source code into a binary file containing machine language instructions that can be executed by the computer's processor. The compiler also examines the source code and informs the user about any errors or potential issues in the code (warnings). The compiler's behavior can be altered with numerous flags.
Complement is a way to represent negative numbers, used typically in computers. The sign of a number is changed by flipping all its bits. In two's complement which is used in this course, 1 is added to the result after flipping.
Conditional statement is (usually) a line of code that defined a single condition, followed by a code block delimited by curly braces that is entered if the condition evaluates as true. Conditional statements are if statements that can also be present with the else keyword as else if. A set of conditional statements linked together by else keywords are called conditional structures.
Conditional structure is a control structure consisting of one or more conditional statements. Most contrl structures contain at least two branches: if and else. Between these two there can also be any number of else if statements. It is however also possible to have just a single if statement. Each branch in a conditional structure cotains executable code enclosed within a block. Only one branch of the structure is ever entered - with overlapping conditions the first one that matches is selected.
Control structures are code structures that somehow alter the program's control flow. Conditional structures and loops belong to this category. Exception handling can also be considered as a form of control structure.
Data structure is a comman name for collection that contain multiple values. In Python these include lists, tuples and dictionaries. In C the most common data structures are arrays and structs.
Python's way of treating variable values is called dynamic typing aka duck typing. The latter comes from the saying "if it swims like a duck, walks like a duck and quacks like a duck, it is a duck". In other words, the validity of a value is determined by its properties in a case-by-case fashion rather than its type.
An error message is given by the computer when something goes wrong while running or compiling a program. Typically it contains information about the problem that was encountered and its location in the source code.
An exception is what happens when a program encounters an error. Exceptions have type (e.g. TypeError) that can be used in exception handling within the program, and also as information when debugging. Typically exceptions also include textual description of the problem.
Flags are used when executing programs from the command line interface. Flags are options that define how the program behaves. Usually a flag is a single character prefixed with a single dash (e.g. -o) or a word (or multiple words connected with dashes) prefixed with two dashes (e.g. --system. Some flags are Boolean flags which means they are either on (if present) or off (if not present). Other flags take a parameter which is typically put after the flag separated either by a space or = character (e.g. -o hemulen.exe.
Floating point numbers are an approximation of decimal numbers that are used by computers. Due to their archicture computers aren't able to process real decimal numbers, so they use floats instead. Sometimes the imprecision of floats can cause rounding errors - this is good to keep in mind. In C there are two kinds of floating point numbers: float and double, where the latter has twice the number of bits.
Header files use the .h extension, and they contain the headers (function prototypes, type definitions etc.) for a .c file with the same name.
Headers in C are used to indicate what is in the code file. This includes things like function prototypes. Other typical content for headers are definition of types (structs etc.) and constants. Headers can be at the beginning of the code file, but more often - especially for libraries - they are in placed in a separate header (.h) file.
Hexadecimal numbers are base 16 numbers that are used particularly to represent memory addresses and the binary contents of memory. A hexadecimal number is typically prefixed with 0x. They use the letters A-F to represent digits 10 to 15. Hexadecimals are used because each digit represents exactly 4 bits which makes transformation to binary and back easy.
In Python objects were categorized into mutable and immutable values. An immutable value cannot have its contents changed - any operations that seemingly alter the object actually create an altered copy in a new memory location. For instance strings are immutable in Python. In C this categorization is not needed because the relationship of variables and memory is tighter - the same variable addresses the same area of memory for the duration of its existence.
When a variable is given its initial value in code, the process is called initialization. A typical example is the initialization of a number to zero. Initialization can be done alongside with introduction: int counter = 0; or separately. If a variable has not been initialized, its content is whatever was left there by the previous owner of the memory area.
Instruction set defines what instructions the processor is capable of. These instructions form the machine language of the processor architecture.
Integers themselves are probably familiar at this point. However in C there's many kinds of integers. Integer types are distinguished by their size in bits and whether they are signed or not. As a given number of bits can represent up to (2 ^ n) different integers, the maximum value for a signed integer is (2 * (n - 1))
Python interpreter is a program that transforms Python code into machine language instructions at runtime.
The moment a variable's existence is announed for the first is called introduction. When introduced, a variable's type and name must be defined, e.g. int number;. When a variable is introduced, memory is reserved for it even though nothing is written there yet - whatever was in the memory previously is still there. For this reason it's often a good idea to initialize variables when introducing them.
Iteroitava objekti on sellainen, jonka voi antaa silmukalle läpikäytäväksi (Pythonissa for-silmukalle). Tähän joukkoon kuuluvat yleisimpinä listat, merkkijonot ja generaattorit. C:ssä ei ole silmukkaa, joka vastaisi Pythonin for-silmukan toimintaa, joten taulukoiden yms. läpikäynti tehdään indeksiä kasvattavilla silmukoilla.
Keywords are words in programming languages that have been reserved. Good text editors generally use a different formatting for keywords (e.g. bold). Usually keywords are protected and their names cannot be used for variables. Typical keywords include if and else that are used in control structures. In a way keywords are part of the programming language's grammar.
A library is typically a toolbox of functions around a single purpose. Libraries are taken to use with the include directive. If a library is not part of the C standard library, its use must also be told to the compiler.
Logical operation refers to Boole's algebra, dealing with truth values. Typical logical operations are not, and, or which are often used in conditional statements. C also uses bitwise logical operations that work in the same way but affect each bit separately.
Machine language is made of instructions understood by the processor. Machine language is often called Assembly and it is the lowest level where it's reasonable for humans to give instructions to computers. Machine language is used at the latter part of this course - students taking the introduction part do not need to learn it.
Macro is an alias that defines a certain keyword to be replaced by a piece of code. When used well, macros can create more readable code. However, often the opposite is true. Using macros is not recommended in this course, you should just be able to recognize one when you see it.
In C the main function is the starting point when the program is run. The command line arguments of the program are passed on to the main function (although they do not have to be received), and its return value type is int. At its shortest a main function can defined as int main().
When programs are run, all their data is stored in the computer's memory. The memory consists of memory slots with an address and contents. All slots are of equal size - if an instance of data is larger, a continuous area of multiple memory slots is reserved.
Method is a function that belongs to an object, often used by the object to manipulate itself. When calling a method, the object is put before the method: values.sort().
Object is common terminology in Python. Everything in Python is treated as objects - this means that everything can be referenced by a variable (e.g. you can use a variable to refer to a function). Objects are typically used in object-oriented languages. C is not one.
Optimization means improving the performance of code, typically by reducing the time it takes to run the code or its memory usage. The most important thing to understand about opimization is that it should not be done unless it's needed. Optimization should only be considered once the code is running too slowly or doesn't fit into memory. Optimization should also not be done blindly. It's important to profile the code and only optimize the parts that are most wasteful.
A parameter is a variable defined alongside with a function. Parameters receive the values of the function's arguments when it's called. This differentation between parameters and arguments is not always used, sometimes both ends of the value transfer are called arguments.
Placeholders are used in string formatting to mark a place where a value from e.g. a variable will be placed. In Python we used curly braces to mark formatting placeholders. In C the % character is used which is followed by definitions, where the type of the value is mandatory. For instance "%c" can only receive a char type variable.
Pointers in C are special variables. A pointer contains a memory address of the memory location where the actual data value is located. In a sense they work like Python variables. A variable can be defined as a pointer by postfixing its type with * when it's being introduced, e.g. int* value_ptr; creates a pointer to an integer. The contents of the memory address can be fetched by prefixing the variable name with * (e.g. *value_ptr. On the other hand, the address of a memory adress can be fetched by prefixing a variable name with &, (e.g. &value.
The C precompiler is an apparatus that goes through all the precompiler directives in the code before the program is actually compiled. These directives include statements which add the source code of the included libraries into the program, and define directives that can define constant values (aliases) and macros.
Directives are instructions that are addressed at the precompiler. They are executed and removed from the code before the actual compilation. Directives start with the # character. The most common one is include which takes a library into use. Another common one is define, which is used e.g. to create constant values.
Prototype defines a function's signature - the type of its return value, its name and all the arguments. A prototype is separate from the actual function definition. It's just a promise that the function that matches the prototype will be found in the code file. Prototypes are introduced at the beginning of the file or in a separate header file. In common cases the prototype definition is the same as the line that actually starts the function introduction.
Interactive interpreter or Python console is a program where users can write Python code lines. It's called interactive because each code line is executed after its been fully written, and the interpreter shows the return value (if any).
The format method of string in Python is a powerful way to include variable values into printable text. The string can use placeholders to indicate where the format method's arguments are placed.
Python functions can have optional parameters that have a given default value. In Python the values of arguments in a function call are transferred to function parameters through reference, which means that the values are the same even though they may have different names. Python functions can have multiple return values.
In Python the import statement is used for bringing in modules/libraries - either built-in ones, thrid party modules or other parts of the same application. In Python the names from the imported module's namespace are accessible through the module name (e.g. math.sin). In C libraries are taken to use with include, and unlike Python import it brings the library's namespace into the program's global namespace.
Python lists were discovered to be extremely effective tools in Elementary Programming. A Python list is an ordered collection of values. Its size is dynamic (i.e. can be changed during execution) and it can include any values - even mixed types. Lists can also include other lists etc.
In Python main program is the part of code that is executed when the program is started. Usually the main program is at the end of the code file and most of the time under if __name__ == "__main__": if statement. In C there is no main program as such, code execution starts with the main function instead.
In Python a variable is a reference to a value, a connection between the variable's name in code and the actual data in memory. In Python variables have no type but their values do. The validity of a value is tested case by case when code is executed. In these ways they are different from C variables, and in truth Python variables are closer to C pointers.
Pythonin for-silmukka vastaa toiminnaltaan useimmissa kielissä olevaa foreach-silmukkaa. Se käy läpi sekvenssin -esim. listan - jäsen kerrallaan, ottaen kulloinkin käsittelyssä olevan jäsenen talteen silmukkamuuttujaan. Silmukka loppuu, kun iteroitava sekvenssi päättyy.
Pääfunktio on C:ssä ohjelman aloituspiste ja se korvaa Pythonista tutun pääohjelman. Oletuksena pääfunktion nimi on main ja se määritellään yksinkertaisimmillaan int main().
Resource referes to the processing power, memory, peripheral devices etc. that are availlable in the device. It includes all the limitations within which programs can be executed and therefore defines what is possible with program code. On a desktop PC resources are - for a programmer student - almost limitless, but on embedded devices resources are much more scarce.
Return value is what a function returns when its execution ends. In C functions can only have one return value, while in Python there can be multiple. When reading code, return value can be understood as something that replaces the function call after the function has been executed.
A statement is a generic name for a single executable set of instructions - usually one line of code.
C uses static typing This means that the type of variables is defined as they are created, and values of different types cannot be assigned to them. The validity of a value is determined by its type (usually done by the compiler). Python on the other hand uses dynamic typing aka.duck typing.
In Python all text is handled as strings and it has no type for single characters. However in C there are no strings at all - there's only character arrays. A character array can be defined like a string however, e.g. char animal[7] = "donkey"; where the number is the size of the array + 1. The +1 is neede because the string must have space for the null terminator '\0' which is automatically added to the end of the "string".
Syntax is the grammar of a programming language. If a text file does not follow the syntax of code, it cannot be executed as code, or in the case of C, it cannot be compiled.
Terminal, command line interface, command line prompt etc. are different names to the text-based interface of the operating system. In Windows you can start the command line prompt by typing md to the Run... window (Win+R). Command line is used to give text-based commands to the operating system.
The data in a computer's memory is just bits, but variables have type. Type defines how the bits in memory should be interpreted. It also defines how many bits are required to store a value of the type. Types are for instance int, float and char.
Typecast is an operation where a variable is transformed to another type. In the elementary course this was primarily done with int and float functions. In C typecast is marked a bit differently: floating = (float) integer}. It's also noteworthy that the result must be stored in a variable that is the proper type. it is not possible to change the type of an existing variable.
Unsigned integer is a an integer type where all values are interpreted as positive. Since sign bit is not needed, unsigned integers can represent twice as large numbers as signed integers of the same size. An integer can be introduced as unsigned by using the unsigend keyword, e.g. unsigned int counter;.
In the elementary programming course we used the term value to refer to all kinds of values handled by programs be it variables, statement results or anything. In short, a value is data in the computer's memory that can be referenced by variables. In C the relationship between a variable and its value is tighter as variables are strictly tied to the memory area where its value is stored.
A warning is a notification that while executing or - in this course particularly - compiling it, something suspicious was encountered. The program may still work, but parts of it may exhibit incorrect behavior. In general all warnings should be fixed to make the program stable.
One way to print stuff in C is the printf function, which closely resembles Python's print function. It is given a printable string along with values that will be formatted into the string if placeholders are used. Unlike Python, C's printf doesn't automatically add a newline at the end. Therefore adding \n at the end is usually needed.
Out of loops, while is based on repetition through checking a condition - the code block inside the loop is repeated until the loop's condition is false. The condition is defined similarly to conditional statements, e.g. while (sum < 21).