📜 ⬆️ ⬇️

STM32 and FreeRTOS. 5. Benefit and welcome!

Just in case, and then suddenly apply sanctions (Smail). The case described has no relation to reality and is entirely the invention of the author.

It used to be about threads , semaphores , queues and HAL

Once they asked me to look at one very expensive device. There was one problem: among those using this device, a strong belief arose that 99.99% of its price comes from the fact that the manufacturer of this device is a monopolist in its sphere and there is no place for users of this device.
')


Armed with an oscilloscope, I climbed inside.

After some time, the search led to two wiring, which were in a bundle connecting the units of the device. The oscillogram showed that the wiring is almost normal USART. Almost - because "there" the data ran at a speed of 9600, and back to 115200.

He took out two usb-usart adapters, hooked their inputs to the RX / TX and began to analyze the protocol. The task was complicated by different speeds, asynchrony and binary protocol. In general, after a while I discovered that my eyes were tritely trying to track me in the terminals, what was going on and what was responding to.

To preserve your vision and reason in a similar situation, let's make usart hardware sniffer. And we will make it so that he would not care for the speed and sequence of transmissions. Well, having understood usart, you can then add analysis of the legs and any SPI at the same time.

The work of the sniffer will be simple: the STM32 has a bunch of built-in USART. Let 2 of them listen to their RX feet and then send them out via USB. And we will attach these legs to the RX / TX device under study.

To begin with, we take (I hope) the familiar STM32F3DISCOVERY board and with the help of the STM32Cube we create a blank. In this preset should be the following

5 threads.

defaultTask - the “device is working and not hanging” LED will flash
Usart1Rx - will be engaged in receiving data on port 1
Usart2Rx - the same, then at 2m
UsbSend - data will be processed and sent here outside
Usart3Tx - and this will be a test stream that will send quotes from famous robots. Use to test the performance of the first two ports "at the entrance." Well, or for pampering.

And one queue - in which the parcels from the “receivers” of the data will be added. In order not to bother, I made such a structure

typedef struct { uint8_t usart; uint8_t byte; } myMes; 


To begin with, we will write the most important function - the sending of test data.

 for(;;) { // Wall-e: Eeeee... va? uint8_t walle[]="Eeeee... va?\r\n"; // Short Circuit: Johny Five is Alive! uint8_t johny[]="Johny Five is Alive! \r\n"; // StarWars uint8_t c3po[]="Sir, the possibility of successfully navigating an asteroid field is approximately 3,720 to 1 \r\n"; HAL_UART_Transmit(&huart3,(uint8_t *)&walle,15,16); //PB10 HAL_UART_Transmit(&huart2,(uint8_t *)&johny,23,100); //PA2 HAL_UART_Transmit(&huart1,(uint8_t *)&c3po,97,100); //PC4 osDelay(1000); HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_15); } 


And check it out, just alternately hooking on the external usart-usb adapter for the suitable legs. This will at least give an understanding of what to expect at the “reception” if we simply connect the legs of the RX-TX right on the board.



We now turn to a function that accepts and executes the received.

 xQueueReceive( RecQHandle, &w, portMAX_DELAY ); if(oldusart!=w.usart || char_count>16) { oldusart=w.usart; newline=1; char_count=0; } buf[char_count++]=w.byte; 

From previous articles and knowledge of the C language, it becomes clear that we are just waiting for the next “package” and depending on the conditions we set flags.

Well, then we wait until the USB interface is ready and just form (admittedly, you can be neater) a string and output it to the user. I did not paint it here, because there work with the buffer and strings head-on, without any optimizations.

Now you need to write what we will accept data.

 void Usart1Rx(void const * argument) { /* USER CODE BEGIN Usart1Rx */ /* Infinite loop */ for(;;) { if(HAL_UART_Receive_IT(&huart1, &b1,1)==HAL_OK) { HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_9); } osDelay(1); } /* USER CODE END Usart1Rx */ } 

Usart2Rx is absolutely the same. If that, then in the real code I left comments for inquisitive users.

Finally, the USART interrupt handler

 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle) { myMes m; HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_14); switch((uint32_t)UartHandle->Instance) { case (uint32_t)USART1: HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_10); m.usart=1; m.byte=b1; xQueueSend( RecQHandle, &m, portMAX_DELAY ); // Do this need? //HAL_UART_Receive_IT(&huart1, &b1,1); break; case (uint32_t)USART2: HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_12); m.usart=2; m.byte=b2; xQueueSend( RecQHandle, &m, portMAX_DELAY ); HAL_UART_Receive_IT(&huart2, &b2,1); break; } } 


Note the comment in the middle and why it is not commented out for the second port.

At the request of one of the attentive readers: Also note that I used xQueueSend in the interrupt handler instead of xQueueSendFromISR . This was done on purpose, so that the controller during the tests “as it is able to” (or in simple words, when raising the speed on the serial port somewhere up to 57600-115200) began to hang regularly. Simply turning on the debugger would show that it hung in anticipation of trying to write to a crowded queue. Inside the real code there are a couple of such “traps”.

We compile, build and look into the terminal



It seems, that is necessary. We cling to the 1st port of another "data source" and just copy-paste a string into it.



By the way, a very good question for the proceedings “how interrupts work” - why the lines from both ports do not interrupt each other?

But to confirm that everything is not so bad, I bring another screenshot, where I just tapped the keyboard



As you can see, if there is a sho, then one port can “kill” another.

And finally, the most interesting feature of the resulting analyzer. If the following lines are added to the initial USART port initialization code

 huart1.Init.OneBitSampling = UART_ONEBIT_SAMPLING_DISABLED ; huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_AUTOBAUDRATE_INIT; huart1.AdvancedInit.AutoBaudRateEnable = UART_ADVFEATURE_AUTOBAUDRATE_ENABLE; huart1.AdvancedInit.AutoBaudRateMode = UART_ADVFEATURE_AUTOBAUDRATE_ONSTARTBIT; 


Then the microcontroller will adjust itself to the speed of the start bit on the move. It is very useful for cases when at first the devices “sniff” for identification at a low speed, and then switch to a higher one.

In general, that's all. Returning to the seed: a completely similar method was obtained by dumping the exchange between modules, rather quickly using the “spear” method, was disassembled in parts and after some time the expensive device was replaced with a stack box, exceeding it in a key (for the customer) parameter. True, the manufacturer did not stop paying money anyway (there was a handful of other reasons there), but they got rid of the fear that “it would break, but the process should be controlled”.

As usual, a full set of sources can be found here.

PS On the photo in the title is a model of a laser surgery device for which we wrote firmware at studiovsemoe.com. It has nothing to do with the device itself . Just a beautiful picture turned out.

Source: https://habr.com/ru/post/249975/


All Articles