Skip to content
This repository has been archived by the owner on Jul 18, 2024. It is now read-only.

Writing Firmware for a Simple Peripheral

Nick Walker edited this page Aug 14, 2014 · 4 revisions

Planning

We're going to get off the ground with the simplest possible profile; a single, read/write, boolean value. We'll make that boolean control a blinking LED on pin 1.

Organization of a CC2541 Project

TI's example projects are densely documented, to the point that it can actually be hard to tell what's going on at a glance. This is especially true within IAR, which is a mediocre IDE. Let's start from the Blinky example project from this repository. Drag it into your ble/projects folder in your TI installation if you want to compile it. Here's the condensed version of what the documentation will explain to you:

  • OnBoard.c defines the capabilities of the evaluation hardware. If you specify that you're building your code to run on any of TI's first party dev-kits, you'll have access to a bunch of helpful functions already.
  • Main.c is the entry point for the application and initializes the hardware and operating system abstraction layers.
  • OSAL.c starts all of the processes needed to manage the BLE stack. Your application process will be initialized at the very bottom.
  • BlinkyPeripheral.c/.h contains lot of callbacks that define the way the peripheral will work. The init function should be your main starting place for writing any application specific code.
  • BlinkyProfile.c/.h defines the GATT configuration of our very simple profile.
  • DevInfo.c/.h is a special GATT profile that's standard and generally a good thing to have in any project. It will broadcast the device name, serial number, etc..

Exploring Blinky

I've kindly removed all of the code that's not absolutely necessary to make this peripheral function, but there's still a lot to look through. Let's start with BlinkyPeripheral.c. We've just been handed the reins by the operating system. Notice how the Init function and all functions in the file are namespaced with BlinkPeripheral_. This is a common pattern to prevent accidentally redeclaring functions in other profiles and applications. The init function will configure broadcasting parameters, pulling in much of the top half of the file's worth of constants, set up the connection manager and then finally add everything onto the GATT server. The init function wraps up by setting some default values to the profiles and configuring the pins on the chip.

Don't worry too much about the developer information service. You're unlikely to need to modify it beyond simply changing the strings it broadcasts. Instead, lets look at the blinkyProfile. The AddService function pulls in some constants and passes them to the GATT server. The attribute table is the what actually defines the characteristics and their metadata. You can tweak the permissions of each characteristic and change the pointer that provides the backing store to any value.

A single parameter getter and a single parameter setter provide a way for other files to query for the state of the profile's values. The more important functions are the read/write attribute callbacks. Whenever a characteristic is changed via the radio, these functions will be called with a set of identifying parameters. A lot of data validation goes on, but eventually, both of the callbacks boil down to a passed in pointer being assigned to or read to the local backing variable. The write callback will also call a profile call back if one has been set.

The magic sauce that will actually make the LED blink is back in the application file (BlinkyPeripheral). During initialization we also set a repeating timer with the operating system abstraction layer. This function will toggle pin one in perpetuity, as long as the the single boolean characteristic isn't set to 0.