Wires and protocols
This part will talk about general protocols used to communicate over wires.
UART
UART is an acronym for Universal Asynchronous Receiver-Transmitter. This communication method is used for bidirectional communication between two devices. There are typically two wires; one transmitter Tx and one receiver Rx. The Tx terminal of one side is connected to the Rx of the other side. The bits are transmitted one-by-one and contain their own clock.
Also our D1 Mini contains a Tx and Rx pin. To use UART0 (TX = GPIO1, RX = GPIO3)
, you can use the Serial object, just like on an Arduino: Serial.begin(baud)
. To use the alternative pins (TX = GPIO15, RX = GPIO13)
, use Serial.swap()
after Serial.begin
. To use UART1 (TX = GPIO2)
, use the Serial1
object.
The signals can be processed by the main processor of a computer system, but often there is special hardware executing the protocol that buffers the messages to send and the received messages. There is special hardware that converts these signals to USB communication. In our D1 Mini this is handled by the CH340G chip.
Example script for UART between 2 D1 Mini’s
1-wire
A 1-wire connection is used typically to connect a microprocessor to two or more peripherals.The DHT temperature sensor is a well known example. In this protocol the processor typically starts the combination by sending some sequence of bits , the addressed peripheral continues the communication over the shared communication wire. In contrast to the UART there is only communication in one direction at any moment. Apart from this one data wire there are additional wires for the power supply and the ground which connects the microprocessor and the sensor.
IC
As outlined above the IC is a bus that allows communication of the processor with many peripherals. In fact it is synchronous, multi-master, multi-slave, packet switched, single-ended, serial computer bus invented in 1982 by Philips Semiconductor. All of the devices have a fixed bus address. The bus is synchronous which implies that there is a separate clock line used to time the individual bits. This implies that the bits can have in single level. The clock line is typically called SCL and the data line is called SDA.
USB
USB is abbreviation of Universal Serial Bus, meanwhile we have version 3.2 of this bus. This bus does not only define the protocol, but also a variety of connectors. A standard connection contains a 5V and a ground wire as well as two communication wires. The transaction protocol allows dynamic address assignment.
MCU’s
There are many different types of microcontrollers. If we already only look at the one we are using for assignment 1, there are already over 10 different types which might work differently across versions. In this section we will look at why this happens.
Software stack of a MCU
The software on a MCU consists out of a bootloader and a program. The bootloader is a small program that is executed when the MCU is powered on. It is responsible for loading the program into the memory of the MCU. The program is the main software that you wrote in the IDE. Most MCU just have a single thread of execution, which means that the program is executed from top to bottom only being interrupted by interrupts. The interrupts are small pieces of code that are executed when a certain event happens. For example, when a button is pressed, the interrupt for the button is executed. The interrupt can be used to change the flow of the program.
Memory management in an MCU
Due to it’s simplicity, memory management is not really present in MCU’s (It does not have a garbage collector). The next block is a small example of a program which will work for a while, but then stop reading the sensor:
It looks like a normal program, but due to the memory management differences with c++, every time string x is read, it allocates memory for this on the stack. Which causes the stack to overflow, but it does not throw an exception! It just acts like its okay. The MCU:
To fix our previous program, it would be as easy as moving the declaration to a global one! Like this:
This program ‘should’ not stop working after a while. A nifty way of checking if your program is leaking is by using the function:”
This function will print the amount of free space in the heap. Using this at the end or start of the loop you can see if memory is leaking from the program. Using this function before and after a huge chuck of memory is allocated would be good to check if a specific part of your code introduces memory leaks. Another way to solve the memory leaks ‘partially’ is by using malloc() and free() commands from the standard C library. However, do note that these functions will not work in the same way as in C. An example:
From this code we will be able to see
- the memory available in general.
- the memory after malloc is called.
- the memory after free is called. If 3 is equal to 1 then there is no leak and bar does not have to be declared globally, if it does leak somehow, then its easier to declare everything globally than to try to find the leak.