Scan to FollowLearn Embedded Together, learn and grow together

The first step is to look at embedded issues from the perspective of PC programming;
The second step is to learn the embedded programming mindset;
The third step is to combine PC and embedded thinking and apply it to real projects.
Many friends transition from PC programming to embedded programming.
In China, few embedded programmers graduate from computer science; most come from automatic control or electronics-related fields.
These individuals have strong practical experience but lack theoretical knowledge; many computer science graduates go into online games or web applications that are independent of operating systems.
They are also reluctant to enter the embedded industry, as this path is challenging. They have strong theoretical knowledge but lack knowledge of circuits, making it difficult to learn embedded systems without additional study.
Although I haven’t conducted an industry survey, from what I’ve seen and the people I’ve hired, engineers in the embedded field either lack theoretical knowledge or practical experience.
Very few have both. The root cause lies in the issues with university education in China. I won’t delve into this to avoid disputes. I want to list a few examples from my practice to draw attention to some issues in embedded projects.
A colleague developed a serial port driver under uC/OS-II, and no issues were found during testing of the driver and interface. An application was developed that included a communication program, and the serial driver provided a function to query the driver buffer character: GetRxBuffCharNum().
The application layer needs to receive a certain number of characters before parsing the packet. The code written by a colleague can be represented in pseudocode as follows:
This code checks if the current buffer has more than 30 characters, and if so, reads all characters from the buffer until successfully read.
The logic is clear, and the thought process is straightforward. However, this code does not work properly. If it were on a PC, there would be no issues, but in embedded systems, it’s uncertain. My colleague was confused and didn’t know why.
He came to me for a solution, and when I saw the code, I asked him how GetRxBuffCharNum() was implemented. Upon inspection:
It was clear that there was a global critical section between interrupt_disable() and interrupt_enable() to ensure the integrity of gRxBufCharNum.
However, due to the frequent enabling and disabling of interrupts in the outer do { } while() loop, this time is very short.
In reality, the CPU may not respond to the UART interrupt correctly. This is related to the baud rate, the size of the hardware buffer, and the speed of the CPU. The baud rate we used was very high, approximately 3Mbps.
The start and stop signals of the UART occupy one bit. One byte requires 10 cycles. At a baud rate of 3Mbps, it takes about 3.3us to transmit one byte.
By analyzing the waveform, we can thoroughly understand UART communication.
How many CPU instructions can be executed in 3.3us?
At 100MHz ARM, about 150 instructions can be executed. How long does it take to disable interrupts? Generally, disabling interrupts on ARM requires more than 4 instructions, and enabling again requires more than 4 instructions.
The code for receiving UART interrupts actually exceeds 20 instructions. Therefore, it is possible to lose communication data, manifesting as system-level communication instability.
Modifying this code is actually quite simple; the easiest way is to change it from the higher level:
This allows the CPU time to execute the interrupt code, thus avoiding the delays caused by frequently disabling interrupts, which leads to information loss.
In embedded systems, most RTOS applications do not include serial port drivers. When designing code, one must fully consider the integration of the code with the kernel to avoid deep-seated issues.
The reason RTOS is called RTOS is due to its rapid response to events; the quick response to events relies on the CPU’s speed in responding to interrupts.
Drivers in systems like Linux are highly integrated with the kernel, running in kernel mode. Although RTOS cannot replicate the Linux structure, there are lessons to be learned.
From the above example, it is clear that embedded developers need to understand every aspect of their code.
A colleague was driving a 14094 serial-to-parallel chip. The serial signal was simulated using IO since there was no dedicated hardware. The colleague wrote a driver casually, but after 3-4 days of debugging, there were still issues.
I couldn’t watch any longer, so I went to check; the control of the parallel signals was sometimes normal and sometimes not. I looked at the code, which can be represented in pseudocode as:
Data’s 8 bits are sent out sequentially from bit0 to bit7 on each high level. It should be fine. I couldn’t see where the problem was.
After thinking carefully, I checked the 14094 datasheet and understood.
It turns out that the 14094 requires the clock’s high level to last for 10ns and the low level to last for 10ns. This code only delays the high level time without considering the low level delay. If an interrupt occurs during the low level, this code works.
However, if the CPU does not execute an interrupt during the low level, it will not work correctly. That’s why it sometimes works and sometimes doesn’t.
Modification is also quite simple:
This works perfectly. However, this is not easily portable code, because if the compiler optimizes it, it may lose these two delay loops.
If lost, it cannot guarantee that the high and low levels last for 10ns, and thus will not work correctly.
Therefore, truly portable code should implement this loop as a nanosecond-level DelayNs(10);
Like Linux, at power-up, first measure how long the nop instruction takes to execute, and how many nop instructions are needed to execute for 10ns.
Executing a certain number of nop instructions should suffice. Using compiler-specific directives or keywords to prevent the compiler from optimizing away the delay loops, such as in GCC:
__volatile__ __asm__(“nop;\n”);
This example clearly illustrates that writing good code requires substantial knowledge support. What do you think?
Original Article:https://blog.csdn.net/coolbacon/article/details/6842921
Source: The article is from the internet, and the copyright belongs to the original author. If there is any infringement, please contact for removal.

Personal WeChat is open, scan to add, join a high-quality embedded exchange group

Follow me 【Learn Embedded Together】, learn and grow together.
If you find this article useful, click “Share”, “Like”, “View”!