How can we debug something we cannot observe?
The motivation for writing this post came from the two weeks I spent debugging a piece of code that samples infrared signals. Ideally, the modulated IR signal received by a receiver would perfectly match the protocol it uses (the NEC infrared transmission protocol in my case), but real IR signals are very different. Before this, the only form of software debugging I performed was using GDB, and you can probably understand how difficult it would be to debug software you cannot step through. As frustrating as it was, the entire debugging process was rather insightful, and I would like to share the methods I used to make sure that there was proper sampling of the signal.
Parts list
- TM4C123GXL Evaluation Board
- NEC Infrared remote
- GP1UX511QS 38kHz IR detector/demodulator
Software
Code Composer Studio
What the code needs to do
The idea is simple. Take the signal that is received by the IR detector and sample it (note down what the bits are at specific time intervals. While sampling, make sure that it matches the desired NEC format.
The debugging-process
I will not get into the details of how the signal is being sampled. It is as easy as reading a register at specific time intervals and checking the sampled data against a table of values. The part of the process I am going to share however is how I made sure that I was receiving and sampling the signal properly.
The very beginning of the NEC infrared protocol consists of a burst of infrared light that lasts for 9ms. The output of the IR detector is high or a logical 1 when no signal is received. When it sees the IR light, the output of the detector is supposed to go low, 9ms in our case, at the start of the IR signal. In other words, we are looking for a falling edge of the signal on a GPIO (General Purpose Input/Output) pin. When we see a falling edge, we are going to call an ISR (Interrupt service routine) to process the signal. The process of configuring the interrupt is beyond the scope of this post, but you need to configure a few registers that correspond to the GPIO pin that the IR detector output is connected to. Even before moving forward with writing the rest of our software, we need to make sure that the ISR is being called properly on the falling edge of the signal. This is the point where you cannot just step through the code as the IR signal can come in at any point in time. So, how can we do this?
1. Light an LED
The easiest way of figuring out if everything is working properly at this point is to toggle an LED inside of the ISR. Since the ISR is called on the falling edge of the IR output signal, we expect the LED to blink whenever we press a button in our remote. Since the signal consists of many falling edges, the LED should blink rapidly.
Though this was not a full-proof way of making sure if everything worked correctly, it helped me visually inspect if my IR signal was being received and being processed by my software.
2. Using a logic analyzer
(My course Lecturer’s method — Dr. Jason Losh, Director, Computer Engineering Undergraduate Program, The University of Texas at Arlington)
The next and best approach is to use a logic analyzer. Instead of toggling an LED inside of the ISR, we can go ahead and toggle a GPIO pin. Configure one of the GPIO pins to be an output and connect that pin to an input of the logic analyzer. Connect another one of the logic analyzer pins to the output of the IR detector directly to serve as a reference trace. Now, we can look and see if our GPIO (output) pin follows the IR signal. This would confirm if our ISR is being called properly.
1 & 2 summarize the procedure I followed to check if the edge-detect interrupt was working or not. What if we were trying to sample the data itself? How would we verify if the data bits were correct?
The way I sampled the data was by using a timer that was enabled when the edge-detect ISR was called. The timer was set up to fire an interrupt periodically at the times when I wanted to sample the data. The logic analyzer method is still the best, but oftentimes a visual 0 or a 1 is better in my opinion.
3. Using a UART (My favorite)
We can use a serial communication device like a UART to put characters onto a terminal interface such as Putty. The shortest time interval between which the timer ISR was being called was 562.5µs and so a baud rate of 115200 allowed me to print out the sampled bits properly. The best part about using the UART is that you do not need to guess or zoom into the logic analyzer output trace to figure out what the bit is. Each time the timer calls the ISR to sample the data, we read the register of the GPIO pin that the IR sensor is connected to and output the bit value onto our terminal using the UART.
Most people, certainly students, probably will not have a logic analyzer lying around. I was lucky enough to receive an Analog Discovery 2 kit from my University and I used that for debugging. The best way I think is just to blink an LED or use a UART for debugging (Though I am not sure, I suspect that most microcontrollers would come with UARTs built-in). At least, this way, you can visually inspect the unseeable!
Note: This project was a part of my Embedded Systems lab. Since I am still going through the course, all the things I have said should be taken with a grain of salt.