CC2538 SoCs are Cortex-M4 based SoCs by Texas Instruments for 802.15.4 PAN networking, especially for Zigbee communication using their Zigbee Stack solution. It also is a solid foundation for hobbyist-grade solutions involving Zigbee communication, especially for running a self-implemented Zigbee coordinator (often called "Zigbee gateway" in commercial distribution of similar tech). Software-wise, this is usually done by running "ZNP" (Zigbee Network Processor) firmware on the SoC and using a separate controller/computer to act as the "ZAP" (Zigbee Application Processor). In a hobbyist setting, the former is usually a cheap module from china containing the chip, capacitors and an antenna or antenna connector, while the latter is e.g. a Raspberry Pi or an Intel NUC or whatever floats your boat. The API between both components is specified and so the software side on the ZAP does not need to bother with Zigbee communication states but can act on a higher level. Examples for such software are zigbee2mqtt by koenkk or zigbee-lua by yours truly. This software usually was built on the CC2530/31 chipsets - however, the CC2538 is compatible in the ZNP/ZAP API and generally much more crafty and stable as it has a faster CPU core and more available memory. RF wise, as per the specs, they are mostly on par.
While the CC2530/31 uses an 8051 based CPU core and has its own proprietary two-wire debugging protocol that is also used for flashing the firmware, the CC2538 uses cJTAG (and in a second stage optionally JTAG) for this purpose. The easy way to flash firmware onto the CC2538 is therefore the Segger J-Link. In its "Edu" variant, which probably fits hobbyists best license-wise, it is somewhat affordable for hobbyists - but still not a bargain.
The reason of this short gist is therefore a short description on how to use a generic approach to flash CC2538 devices using open source OpenOCD software.
The CC2538 allows debug access using cJTAG (this is active by default upon boot) and - after switching over - using JTAG. We will use this facility to write to CC2538 memory. Currently, there does not seem to be available code that allows for easy flashing a full firmware blob. So we're resorting to a trick and do this in two steps:
- first we access the internal flash controller control registers. We will write a few values that will make the CC2538 erase its last flash page. This will, among other things, clear the "valid firmware present" indicator in said flash page, and will also set a flag that in combination will activate the integrated bootloader in the CC2538.
- then after a reset, the CC2538 will be in bootloader mode. Via its UART port, https://github.com/JelmerT/cc2538-bsl can be used to flash a full firmware image.
NOTE
: using a (slightly forked) variant of OpenOCD, erasing the flash page and then using the serial bootloader is obsolete: it has a flash loader for the CC2538. So flashing a firmware is now as easy as connecting JTAG and running OpenOCD. See this comment: https://gist.github.com/hwhw/fc43892785aa84913d03495c97b0f25a#gistcomment-3518720
In order to flash the CC2538 using OpenOCD, you need
- a reasonably recent version of OpenOCD (https://openocd.org/)
- an "interface" that OpenOCD can use. This can e.g. be FT2232 based USB/multiprotocol adapter, or in my case a Raspberry Pi (and in that case you would run OpenOCD on said RPi).
- some telnet client to interact with OpenOCDs CLI.
- https://github.com/JelmerT/cc2538-bsl for flashing after we used OpenOCD to prepare bootloader mode (not needed when using forked OpenOCD with CC2538 flash loader)
By the way: now is a good point in time to check if said bootloader mode is active on your CC2538 anyway (just wire up UART connection and run cc2538-bsl.py), which will save you lots of time since then you don't need to use OpenOCD.
From now on, information in the CC2538 reference manual is indispensible: http://www.ti.com/lit/ug/swru319c/swru319c.pdf When using OpenOCD in combination with a Raspberry Pi, you need to check the according OpenOCD interface configuration. By default, you'll find this in /usr/share/openocd/interface/raspberrypi2-native.cfg (for RPi >=2). The RPi pins used for JTAG communication are specified there. I suggest you go with the defaults. Note that there's two numbering schemes for RPi GPIOs, which will make things veeeeery awkward. At some point, you'll get this right, I guess. Default config is:
- JTAG TCK - GPIO 11 - Pin 23
- JTAG TMS - GPIO 25 - Pin 22
- JTAG TDI - GPIO 10 - Pin 19
- JTAG TDO - GPIO 9 - Pin 21
You need to wire up the full JTAG interface, as we will be using cJTAG just for switching over to JTAG mode. The CC2538 side is as follows:
- JTAG TCK = cJTAG TCK - dedicated TCK pin 47.
- JTAG TMS = cJTAG TMSC - dedicated TMS pin 46.
- JTAG TDI - PB6 (=pin 49)
- JTAG TDO - PB7 (=pin 48)
Also connect at least a common ground connection. You can also power the CC2538 using the RPi's 3.3V rail. Make double sure that you use the 3.3V rail, not the 5V rail.
On the RPi (or whatever host you are using), run
openocd -f /usr/share/openocd/interface/raspberrypi2-native.cfg -f /usr/share/openocd/target/cc2538.cfg
(adapt to the interface you're using if it is not a RPi >= 2.)
It should output some status that will basically tell you it communicated successfully with the CC2538. Well, probably it won't on try #1 and you need to re-check your cables and so on, but at some point it hopefully will.
In a different terminal - possibly even on another computer - run
telnet localhost 4444
You should be greeted by the OpenOCD command line then. The following commands are to be entered on this OpenOCD command line interface.
using a fork of OpenOCD, this is not needed anymore, see https://gist.github.com/hwhw/fc43892785aa84913d03495c97b0f25a#gistcomment-3518720
Set erase address:
mww 0x400D300C 0x7F800
(note that this was shifted in an earlier release of this document - with a current version of OpenOCD of today, this seems to work instead now)
Issue erase command (also set flags to unlock)
mww 0x400D3008 0x0205
That's it, the CCA should be erased and all flags should be cleared now. I tried to reconstruct this from my OpenOCD history. I sincerely hope that these are the right commands. You can check the CCA contents using the command mdb 0x0027F800 2048
and hopefully, everything is cleared. If you want to read up on what those register writes did, check the reference manual for the CC2538 and read up on the flash controller in the memory section.
using a fork of OpenOCD, this is not needed anymore, see https://gist.github.com/hwhw/fc43892785aa84913d03495c97b0f25a#gistcomment-3518720
Use whatever UART adapter you like - e.g. maybe the built-in UART of the RPi you used for flashing? Or a simple USB-UART adapter? Make sure it has an adequate voltage level, then connect it as follows:
- cc2538 UART RX is on PA0
- cc2538 UART TX is on PA1
- make sure you have a common ground connection and to power the cc2538 with a 3.3V source
https://github.com/JelmerT/cc2538-bsl will show you information about a connected cc2538 in bootloader mode and will allow flashing. Be sure that you've read the documentation. Especially about the bootloader backdoor configuration (also have a look into the cc2538 reference manual to learn about the exact way how to configure the backdoor enable pin). Note that using the JTAG method described above you do not need the bootloader backdoor. It might still be handy to enable it if possible, as this saves a step and some wiring.
In case anyone using raspberry pi 4 with Ubuntu.
I installed openocd on ubuntu 20.04 using
apt-get install openocd
. However this lackedbcm2835gpio
interface/adapter resulting in error:Error: The specified debug interface was not found (bcm2835gpio)
. So had to manually get openocd from github and compile it with that support. After removing openocd package:git clone https://git.code.sf.net/p/openocd/code openocd cd openocd ./bootstrap ./configure --enable-maintainer-mode --enable-bcm2835gpio --enable-sysfsgpio make -j4
Credit: https://movr0.com/2016/09/02/use-raspberry-pi-23-as-a-jtagswd-adapter/ (code blocks are white in that site, but if you select all text, text is revealed)
And then adjust config for Rpi4:
Change bcm2835gpio_peripheral_base and bcm2835gpio_speed_coeffs to these values:
Credit: https://www.raspberrypi.org/forums/viewtopic.php?t=252551