Communicate via UART Advanced

Motivation

There are certain scenarios, when the simulator needs to communicate with the device-under-test via a UART interface, for example to exchange control commands.
The following tutorial shows how to implement a UART sender on the simulator side. For the sake of simplicity the simulator will also act as UART receiver.

Pre-requisites

For the miniHIL to be able to talk to itself a physical connection between the RX and TX pins need to be established. In our case both are I/Os of the H7 processor. For this HowTo the pin PD5 acts as TX and PD6 as RX.
Connect them using a wire jumper as shown in the picture below:

UART: TX/RX Wiring

To prepare the SW create a new project as described in the "Creating a new project" How-To.

Finally, to be able to see some output, make sure you can log as described in here. Note that in this case you only need the USB connection. Log messages will be generated by the UART tester below.

Tip The contents of this How-To is also part of the miniHIL Examples project. Look there for a fully functional sample in model-user/examples/uart.room.

Instantiate the UART in SW

To run the UART and add the ability to test it we need two actors: one that represents the UART and one that represents the testing code. Both of them need to be instantiated and connected to each other.

As a container we will use the Application contained in MiniHILApplication.room. So open the file in your newly created project and go to the Structure Section of the Application actor.
There, create an instance of the AUart2Adapter as shown below.

RoomModel MiniHilProject {
	//...
	import minihil.api.adapters.uart2.AUart2Adapter 1

	ActorClass Application {
		Structure {
			//	  ...
			ActorRef uartAdapter: AUart2Adapter 2
		}
	}
1 Add the appropriate import for the adapter
2 Instantiate the adapter

The AUart2Adapter is an abstraction for the miniHIL hardware, freeing you from the specifics of pin assignment and such.
In this case the adapter is configured to use the PD5/PD6 pins for TX/RX and uses a DMA for transfer. If you are interested in details set the cursor on AUart2Adapter and press F3.

Tip In the miniHIL Examples project this setup is contained in model-user/examples/uart.room. Here, instead of using the application, a separate container actor called AUartExampleContainer is used. The general approach is the same, though.

Create a test actor

Now it is time to create the test actor in the MiniHilProject ROOM model. First we create the hull for it and connect it to the UART adapter as show below.

RoomModel MiniHilProject {
	//...
	import etrice.api.timer.PTimer
	import etrice.api.logger.PLogger
	import minihil.api.adapters.uart.*
	import minihil.api.adapters.uart2.AUart2Adapter

	ActorClass AUartTester {

		Interface {
			conjugated Port config: PUartControl 1
			conjugated Port comm: PFixedSizePacketCommunication 2
		}
		Structure {
			external Port config
			external Port comm
			SAP timer: PTimer 3
			SAP logger: PLogger 4
		}
		Behavior {
			}
		}
	}

	ActorClass Application {
		//	  ...
		ActorRef uartAdapter: AUart2Adapter
		ActorRef uartTester: AUartTester 5
		Binding uartTester.comm and uartAdapter.comm 6
		Binding uartTester.config and uartAdapter.config 7
	}
}
1 Port for controlling the UART adapter
2 Port for communication
3 To be able to do periodic actions we need the timer service
4 We will need to do some logging to see what is going on
5 UART tester instance
6 Connect the communication port
7 Connect the configuration port

With this you should see the following inside the structure diagram of the Application actor (use ALT-S to open it):

UARTExampleStructure
UART Example Structure

Making the tester behave

To have the tester do something useful we now create a state machine as behavior. The idea is that the test will wait for a second, then send a "Hello" message, try to receive the "Hello" message and then start over again.
This can be done using the state machine shown below:

UART Tester Behavior
UART Tester FSM

The code for initializing the UART is contained in the init transition. Sending is done in the sendTransition. When receiving the data we simply print it in the receiveTransition.
The full code should look as shown below:

ActorClass AUartTester {
	// ...
	Behavior {
		StateMachine {
			State WaitingForReceivePacket {
			}
			State WaitingForASecond {
				entry '''
					// starting timeout to send a uart packet after a second
					timer.startTimeout(1000); // 1000 milliseconds'''
			}
			Transition init: initial -> WaitingForASecond {
				action '''
					// creating instance of uart configuration data class
					DUartConfiguration conf;
					// setting the parameters
					conf.baudrate = 115200;
					conf.databits = 8; // only 8 is currently supported
					conf.parity = EParity_NONE;
					conf.stopbits = EStopBits_STOPBITS_1;
					conf.packet_size = 6;
					// sending configuration to uart actor via config port
					config.configureUart(&conf);
					config.enableReceiveData();

				'''
			}
			Transition sendTransition: WaitingForASecond -> WaitingForReceivePacket {
				triggers {
					<timeout: timer>
				}
				action '''
					// creating a packet to send
					DFixedSizePacket32Byte packet;
					packet.len = 5; // length
					// fill buffer with data
					packet.buffer[0] = 'H';
					packet.buffer[1] = 'E';
					packet.buffer[2] = 'L';
					packet.buffer[3] = 'L';
					packet.buffer[4] = 'O';
					// sending packet via comm port
					comm.sendPacket(&packet);'''
			}
			Transition receiveTransition: WaitingForReceivePacket -> WaitingForASecond {
				triggers {
					<receivedPacket: comm>
				}
				action '''
					// transitionDate->len contains the configured package size. The payload size is currently not available
					if (transitionData->len == 6) {
						transitionData->buffer[6] = (char)0; // terminating the received buffer to be used as string for logging
						logger.logF("received packet[0] = %d", transitionData->buffer[0]);
						logger.logF("received packet[1] = %d", transitionData->buffer[1]);
						logger.logF("received packet[2] = %d", transitionData->buffer[2]);
						logger.logF("received packet[3] = %d", transitionData->buffer[3]);
						logger.logF("received packet[4] = %d", transitionData->buffer[4]);
						logger.logF("received packet[5] = %d", transitionData->buffer[5]);
						logger.logF("received packet[6] = %d", transitionData->buffer[6]);
						logger.logF("complete packet	= %s", &transitionData->buffer[0]);
						logger.log("------------------------");
					}
				'''
			}
		}
	}
}

Running the example

Now it is time to flash the example and see what it is doing. Once you have flashed and started the target you should see output like this in your terminal.

UART Example Output
received packet[0] = 72
received packet[1] = 69
received packet[2] = 76
received packet[3] = 76
received packet[4] = 79
received packet[5] = 0
received packet[6] = 0
complete packet    = HELLO
------------------------
received packet[0] = 72
received packet[1] = 69
received packet[2] = 76
received packet[3] = 76
received packet[4] = 79
received packet[5] = 0
received packet[6] = 0
complete packet    = HELLO
------------------------
received packet[0] = 72
received packet[1] = 69
received packet[2] = 76
received packet[3] = 76
received packet[4] = 79
received packet[5] = 0
received packet[6] = 0
complete packet    = HELLO
------------------------
Tip HTerm requires you to disconnect and reconnect each timer you flash the target. Otherwise you might not see output.

Summary

  • Instantiate a UART adapter from the miniHIL library

  • Create your own UART application

  • Connect it to the UART adapter

  • Have fun!

See Also