Episode 8: How to Use Two GPIOs to Debug Firmware and Log Data
July 28, 2014 |
(Go to the complete list of videos)
It's hard to get visibility into many embedded systems. In this video Jack will show you how to use a protocol analyzer (found in most digital scopes) with a little code (get it here) to extract lots of data from your system.
Video Transcription
Hi, I'm Jack Ganssle. Welcome to the Embedded Muse Video Blog, which is a companion to my free online Embedded News Newsletter. Today we're going to look at different way of extracting debugging information from an embedded system. Here's a typical embedded system. This is a little Freescale board; it's running a Cortex M0+ Processor, a 32 bit processor; a pretty decent chip. And it has a program burned into the flash in here. How do you debug? How do you know what's going on inside of this chip?
Obviously we have lots of tools. We can get these BDM debuggers and JTAG debuggers and things of this nature, and these are great. But sometimes they're not available. And sometimes we don't even want to use them. For example, you may have a system running for a long period of time just logging data or running some sort of tests, and you may want to have some sort of ability to send information out of this board that gets captured once in a while. Sure it's easy to do if you've got an RS232 interface for example on the board or TCPIP, CAN, or any of those standard buses. But all of those require a certain amount of hardware to support them. And in case of things like USB or TCP, an awful lot of software is involved as well.
Today we're going to talk about an approach called Bit Banging, and what we're going to do is use bit banging in order to artificially generate something called I2C. Some people call it I2C. It's the inter integrated circuit communications bus. The idea here is that all you need are two unused GPIO bits, a little tiny bit of code, and most importantly some sort of protocol analyzer, which almost everybody has today to automatically decode the messages; because if you are trying to decode this stuff looking at the serial stream on a scope you'll go crazy quickly. It's called bit banging because instead of using hardware to drive those two bits a little bit of code generates two signals, one of which is a clock and the other one is a serial data stream.
The first is signals used in I2C is SDA for serial data, which starts with a low indicating that a transmission is starting. And it ends with a low to indicate the end of the transmission. In between we can see we have various data bits; they are transmitted one byte at a time.
There is also the Serial clock, SCL, and every rising edge of SCL; the data on SDA is clocked into the receiver. It's important to note that after each byte is received the device receiving that byte acknowledges the reception by driving SDA low during the next clock period.
So I have those two signals, the serial clock, the serial data going to two separate channels on my Agilent scope, and sure enough here's the data, and it means practically nothing to me. I can blow it up and here's the clock, here's the data. I'm sending a couple of different strings out, like test and another test in ASCII. But I'd have to work very hard to figure that out.
But this is where this approach really makes a lot of sense. This scope, like so many digital scopes today, has a built-in protocol analyzer, and if I turn it on, I'm on serial channel one, I have selected I2C mode and in this signals thing I've told it channel two is SCL, channel one is SDA. Now what happens is you can see along the screen here it's decoding those patterns for me. So let me get that channel one out of the way. You can see the hex codes equivalent to the strings I'm sending out. So for example this is 65 here, 73, T-E-S-T with a space between that and the next word. It's all very clear.
This scope has a nice mode with a lister where you can actually see the strings in hex on the screen. If I slow down the sweep rate what happens is that there is more data displayed and there's more data than shown on the screen. And the ASCI table comes really trivial to convert those hex streams into a readable string.
Now it's kind of nice that the scope gives us more information, for example the address. Every I2C packet does have an address associated with it. And I've gone ahead and put just totally random addresses in here, which can convey more information if you wish; and then of course the data string. This approach is especially nice when you're sending out data only occasionally.
Suppose you want to send out an error message to the developers when something goes wrong. So here's a case where only once in a while do we get the message coming out; it should come up in the next couple of seconds. And it's a compressed data stream there, but as we blow it up you can see that the message is decoded for us right down there.
If you don't have a fancy bench scope, those very inexpensive USB scopes can be just as effective. This is one called the Logic Port from Intronix. And here I've captured exactly the same data and that error message has popped up and as you can see is being decoded.
Let's look at how the code works. Here I define that the signal SDA is connected to the GPIO pin named PTA1 on my board. The signal named SCL is connected to GPIO pin PTA2. And then here's a very simple routine that actually sets that bit to either a higher or low for both the data and for the clock.
If we scroll through the code a little bit, we'll come down here. There's a very simple set of routines that generate both the start and the stop pulse. Going a little bit further here's a routine that writes an entire byte out by toggling the SDA appropriately as well as driving the clock line if it's necessary to clock the signal into the receiver.
At the end of this though, here we are actually simulating an acknowledge. What's happening is that this code is broadcasting the serial stream sort into the air or into your protocol analyzer, and there's no slave connected to it to actually indicate that the signal has been received. So at the end of every byte we fake this acknowledge so that your protocol analyzer doesn't get confused, because some will.
And so here is an example of using this routine, the i2c_send_string routine. And this is just a loop that goes for about three million times, and once through that loop if I happen to be 29,000, it actually sends out this message, I equals 29,000 to the I2C.
The code is available earlier on this page.
How much time does it take? Well of course it depends a lot on the processor and the clock rate. On this little freedom board it's taking about 20 microseconds per pair; that is one address byte and one data byte to go out. That's with the Cortex M0 Plus at 48 megahertz.
On a Cortex M3 at 96 megahertz that dropped to about 11 microseconds. As I mentioned, this can be useful for a lot of things; error handling for example, data lining, for example, or just sending messages out to you the developer when you really don't have very many good ways of peering inside the system as it works in real time.
So there you have it. It's a very simple way of providing lots of information to you the developer. All you need are two GPIO bits and a little bit of code, which is available on my website. Thanks for watching and don't forget to go to Ganssle.com for plenty more videos and over a thousand articles about better ways of building embedded systems.
Obviously we have lots of tools. We can get these BDM debuggers and JTAG debuggers and things of this nature, and these are great. But sometimes they're not available. And sometimes we don't even want to use them. For example, you may have a system running for a long period of time just logging data or running some sort of tests, and you may want to have some sort of ability to send information out of this board that gets captured once in a while. Sure it's easy to do if you've got an RS232 interface for example on the board or TCPIP, CAN, or any of those standard buses. But all of those require a certain amount of hardware to support them. And in case of things like USB or TCP, an awful lot of software is involved as well.
Today we're going to talk about an approach called Bit Banging, and what we're going to do is use bit banging in order to artificially generate something called I2C. Some people call it I2C. It's the inter integrated circuit communications bus. The idea here is that all you need are two unused GPIO bits, a little tiny bit of code, and most importantly some sort of protocol analyzer, which almost everybody has today to automatically decode the messages; because if you are trying to decode this stuff looking at the serial stream on a scope you'll go crazy quickly. It's called bit banging because instead of using hardware to drive those two bits a little bit of code generates two signals, one of which is a clock and the other one is a serial data stream.
The first is signals used in I2C is SDA for serial data, which starts with a low indicating that a transmission is starting. And it ends with a low to indicate the end of the transmission. In between we can see we have various data bits; they are transmitted one byte at a time.
There is also the Serial clock, SCL, and every rising edge of SCL; the data on SDA is clocked into the receiver. It's important to note that after each byte is received the device receiving that byte acknowledges the reception by driving SDA low during the next clock period.
So I have those two signals, the serial clock, the serial data going to two separate channels on my Agilent scope, and sure enough here's the data, and it means practically nothing to me. I can blow it up and here's the clock, here's the data. I'm sending a couple of different strings out, like test and another test in ASCII. But I'd have to work very hard to figure that out.
But this is where this approach really makes a lot of sense. This scope, like so many digital scopes today, has a built-in protocol analyzer, and if I turn it on, I'm on serial channel one, I have selected I2C mode and in this signals thing I've told it channel two is SCL, channel one is SDA. Now what happens is you can see along the screen here it's decoding those patterns for me. So let me get that channel one out of the way. You can see the hex codes equivalent to the strings I'm sending out. So for example this is 65 here, 73, T-E-S-T with a space between that and the next word. It's all very clear.
This scope has a nice mode with a lister where you can actually see the strings in hex on the screen. If I slow down the sweep rate what happens is that there is more data displayed and there's more data than shown on the screen. And the ASCI table comes really trivial to convert those hex streams into a readable string.
Now it's kind of nice that the scope gives us more information, for example the address. Every I2C packet does have an address associated with it. And I've gone ahead and put just totally random addresses in here, which can convey more information if you wish; and then of course the data string. This approach is especially nice when you're sending out data only occasionally.
Suppose you want to send out an error message to the developers when something goes wrong. So here's a case where only once in a while do we get the message coming out; it should come up in the next couple of seconds. And it's a compressed data stream there, but as we blow it up you can see that the message is decoded for us right down there.
If you don't have a fancy bench scope, those very inexpensive USB scopes can be just as effective. This is one called the Logic Port from Intronix. And here I've captured exactly the same data and that error message has popped up and as you can see is being decoded.
Let's look at how the code works. Here I define that the signal SDA is connected to the GPIO pin named PTA1 on my board. The signal named SCL is connected to GPIO pin PTA2. And then here's a very simple routine that actually sets that bit to either a higher or low for both the data and for the clock.
If we scroll through the code a little bit, we'll come down here. There's a very simple set of routines that generate both the start and the stop pulse. Going a little bit further here's a routine that writes an entire byte out by toggling the SDA appropriately as well as driving the clock line if it's necessary to clock the signal into the receiver.
At the end of this though, here we are actually simulating an acknowledge. What's happening is that this code is broadcasting the serial stream sort into the air or into your protocol analyzer, and there's no slave connected to it to actually indicate that the signal has been received. So at the end of every byte we fake this acknowledge so that your protocol analyzer doesn't get confused, because some will.
And so here is an example of using this routine, the i2c_send_string routine. And this is just a loop that goes for about three million times, and once through that loop if I happen to be 29,000, it actually sends out this message, I equals 29,000 to the I2C.
The code is available earlier on this page.
How much time does it take? Well of course it depends a lot on the processor and the clock rate. On this little freedom board it's taking about 20 microseconds per pair; that is one address byte and one data byte to go out. That's with the Cortex M0 Plus at 48 megahertz.
On a Cortex M3 at 96 megahertz that dropped to about 11 microseconds. As I mentioned, this can be useful for a lot of things; error handling for example, data lining, for example, or just sending messages out to you the developer when you really don't have very many good ways of peering inside the system as it works in real time.
So there you have it. It's a very simple way of providing lots of information to you the developer. All you need are two GPIO bits and a little bit of code, which is available on my website. Thanks for watching and don't forget to go to Ganssle.com for plenty more videos and over a thousand articles about better ways of building embedded systems.