Sunday 20 November 2016

A 1.8V-safe amplifier

When working with embedded development boards (such as the Beaglebone and the TM4C Launchpad), it's common to need to restrict any voltages you apply to analog-input pins to a 'safe' range (typically less than 1.8V).

Figure 1 shows one way I came up to do this:



The STMicro LF15ABDT is a low dropout voltage regulator: you give it a voltage between 2.5V and 16V and it outputs a fixed 1.5V.  This 1.5V is used to power an MCP6041 op-amp. - a  rail-to-rail amplifier able to run off a supply as low as 1.4V.  The amplifier's output voltage can obviously never get larger that the 1.5V supply.

Here it is for real (the centre board) with the input (in this case a thermistor signal) coming in on the blue wire and going out to my TM4C Launchpad on the white. The flying leads and lack of ground planes etc. means it isn't going to win any prizes for precision analog design (!), but hey, as a quick way to safely feed the Launchpad's sensitive analog inputs, it does the job.


Saturday 29 October 2016

Constructing Structures at Compile-time in C

In embedded software, it's common to need to pass board-specific configuration data to a function.

In such cases, it's tempting to declare a set of const's (typically structures) to hold all the configuration values .

The downside of this approach is that memory is precious in embedded; d
efining lots of constants, not all of which may be used, is undesirable.

To give a concrete example


Suppose we're writing a library function to start-up a microprocessor's analog-to-digital converter:


fStartADC( pConfigData );

Here pConfigData is a pointer to a C-Structure containing configuration data for the ADC (for example the pin on the board you want the ADC to connect to).

If there are seven (say) possible ADC configurations, its tempting to declare a set of const. structures CONFIG0 ... CONFIG6, for users of your library function.

Should a user only make use of (say) CONFIG3 however, you risk having six unused structures bloating memory.

Instead, you'd like to provide users of your library a set of predefined structures CONFIG0 ... CONFIG6, but have the compiler only create (instantiate) those that the user actually uses.

C++ offers you such things as constructors to help with this. In C it takes a little more work, but here's one way:

To keep things simple, suppose our ADC CONFIG structure needs to hold a single integer, x (in real life, configuring the ADC need require multiple e.g. pin, port, oscillator... etc. values)

First, we declare a type for our structure and a pointer to it.

typedef struct { int x; } ConfigData_t, *pConfigData_t

Next, we define a function that takes an integer, creates a structure containing the integer, and returns a pointer to it.

pConfigStruc_t fConstructor( int x ) {
static ConfigStruc_t CONFIGn;
CONFIGn.x = x;
return &CONFIGn; }


Finally, we define some macro's

#define CONFIG0 ( fConstructor ( 7) )
#define CONFIG1 ( fConstructor (67) )
..etc.


where 7, 67 are examples of board-specific configuration values.

We can now offer users of our library, a set of predefined configurations CONFIG0,CONFIG1 insulating (abstracting) them from any messy details of our board hardware. The user is free to write a line of code like:

fStartADC( CONFIG0 );


At compile-time, in accordance with our #define, the compiler will dutifully replace CONFIG0 with fConstructor(7)

fConstructor (7) will then do its job, returning a pointer to a structure containing '7'.

Notice however, that since our user never wrote CONFIG1, a structure holding 67 was never created!

We've met our goal of providing users with a set of predefined CONIG's  but at the same time made sure that only those they use will be compiled into memory. Neat huh!

To see a 'real world' example of this technique, check-out my gitHub code here

Tuesday 25 October 2016

Programming the Launchpad UART

Having immersed ourselves in the complexities of CAN recently, communicating by UART is like coming up for air!

UART is dead simple: 
       One wire (plus ground)
       Agree a data (baud) rate data
      ...and start sending 0's and 1's!

(Actually, there are a few other things you can add such as check-bits etc. But we'll ignore these)


The Texas Instruments TM4C123 Launchpad is a low cost ARM micro-controller I introduced here

TI have free software (an API) for UART communication, but I decided to write my own (partly for fun, partly 'cos I wasn't 100% clear about my licence-freedom to show TI's code, and partly 'cos I wanted to use the ARM CMSIS code standard).

My code is here


The functions I've defined do the obvious...

startUART(UART0, UART0_CFG, baud115200) starts a UART.

fprintstringUART(UART0, "blah, blah...") prints a string.

fprintcharUART(UART0, myChar) prints a character ('myChar')

fprintUint32UART(UART0, 32, DEC) prints '32'

fprintUint32UART(UART0, 32, HEX) prints '0x20'

Using a UART monitor such as the awesome PuTTY, UART_example.c results in



The code has limitations:

At present it's limited to baud9600 or baud115200 (see the enum's in uart_helper.h). Also, when starting a UART its necessary to pass in configuration data. You do this by passing the fstartUART() one of the predefined configurations "UARTn_CFG" (my next post, and the comments in uart_helper.h explain how this works). So far I've only gotten around to defining UART0_CFG.


Finally fprintUint32UART() assumes you're doing the right thing passing it an unsigned integer - there's no size checking etc.
The code-flow basically follows the Lauchpad datasheet (DS-TM4C123GH6PM-15842.2741). So, for example, the datasheet tells you on p.902, that if you want to use the UART, the first thing to do is "Enable the UART module using the RCGCUART register"

Hence the line
     SYSCTL->RCGCUART |= RCGCUART_R0;


in my fstartUART() function. Etc.

For those just getting started I gave an intro. to the concept of registers here

For those unfamiliar with "->" notation, this comes from the so-called "ARM CMSIS". This is a standard way to talk to ARM chips. More about this another time.

Definitions of all the Launchpad's registers (RCGCUART etc. etc. ) are held in a board file (here TM4C123GH6PM.h) you can download from TI. I couldn't find a file listing the individual register-bit-fields however (anyone?) so I started my own tm4c_register_fields.h.

That's all for today. If you spot bugs (there are bound to be some!) or just have a comment generally, drop me a line.

Monday 24 October 2016

CAN Bus Project Part 8 : Summary

This is Part 8 in my project to send data from my Arduino (/Genuino) to my TI Launchpad.

It's time to summarize. Here's my setup:


- In Part 2 we connected the Arduino to a thermistor.

- Part 3 connected the Arduino an MCP2515 CAN Controller.

- Parts 6,7 connected a TI Launchpad to a MCP2551 to receive CAN data.

Part 5 gave a CAN overview.

All the code is on my github page.

I didn't describe the Launchpad thermometer icon I created using the TI's "GUI Composer". 


To create it I followed some TI "GUI Composer" instruction videos. You simply select icons (here I chose a thermometer and text box) and tell GUI Composer what variable(s) you want the icon to display - in my case g_ui32MsgCount and g_temperature in the Part 7 code. 

I also didn't describe setting up the Launchpad vector table. I'll do a more detailed post about vector tables in future. For now, for those of familiar with such things you simply need to tell the CAN0 interrupt to call CANIntHandler().

Leave a comment  and enjoy!

Thursday 20 October 2016

Chip Chat No.2 - A low cost function generator


I recently stumbled across a function generator kit on Amazon. On the basis that an electronics ‘den’ can never have too many frequency sources, I ordered one. 

For around $10 you get a pre-drilled PCB, a bag of components and a basic enclosure. Photo 1 shows the end result once I’d soldered it together.

The kit instructions are pretty basic and it helps to have a ohm-meter to check resistor values etc. as you go along. Overall however, things are clear enough.

You need to supply the board with between 9V and 12V (the datasheet warns of instability above 12V). The first thing I did was to plug in a supply of the wrong polarity, “popping” one of electrolytic cap’s and frying the IC. Grrrrr! Fortunately both were cheap to replace.

The board is based around an Exar Corporation XR2206 Monolithic Function Generator IC. The chip datasheet describes applications of this chip as function generators (it’s role here) and communication instrumentation (in particular FSK applications; more about this another time). The XR2206 comprises a voltage controlled oscillator (VCO) oscillating at a frequency set by an external resistor + capacitor. On the board, this resistor is a couple of pots. A row of jumpers allows you to change the capacitor range. On-chip wave-shaping allows you to output a square, triangle or sine wave.

As you’d expect for the price, there are limits to the quality of the output waveforms: On the up-side, you can generate waves from a few Hz to 1MHz. Adjusting frequency and amplitude is as easy as twiddling the knobs. Using these I was able to adjust frequencies to within a few 10th's of a %.

On the downside, the datasheet advises you’re limited to 0.5% sine wave distortion with a maximum amplitude variation of  4800 ppm/degC, set partly by external resistors. The DC value of the output waveforms is centred around 50% of your power rails and I found the sine- and triangle waves began to clip around 4Vp-p. The square wave amplitude isn't adjustable. Finally, I noticed some peak-to-peak amplitude asymmetry (especially at low frequency). 

As every electronics designer knows, there are times when you need precision. Other times however, you just need some waveform and desperately don’t want to have to wait a week for your £5000 function-generator software to boot! You’re not going to be running your atomic-clock off this baby, but overall I’m happy with what I got for my $10.

Finally, for those interested, here're some of the output waveforms: 

1kHz sine 







From here on I switched the oscilloscope to AC coupling.
1kHz triangle...








1kHz square...







1kHz sine starting to clip above 4Vp-p...







600kHz Sine...









10Hz sine...

Monday 17 October 2016

Chip Chat No.1 - a protoboard power supply

A short post today to mention a handy little board you might appreciate having in your tool kit.

The photo shows a 'YuRobot' board (I've no connection with the company) I picked off ebay recently for a couple of quid (that's 'bucks' to my US readers).  





It has pins on the underside which plug into a prototyping breadboard, creating two sets of breadboard power rails. The little yellow jumpers allow you select 3.3V or 5V outputs.

You supply power to the YuRobot through a 9V supply (the datasheet for the voltage regulators say they have a 15V absolute max.) or from your PC's USB port (you'll need a male-to-male USB cable).

The board features a couple of AMS1117 voltage regulators in SOT-223 packages. The AMS1117 datasheet says these are rated to 1A, with an output voltage accurate to 1.5%. My voltmeter measured 3.296V and 5.015V - pretty good for the price.

Overall, a handy little widget I can recommend.

Saturday 15 October 2016

CAN Bus Project Part 7 - Programming the Lauchpad

This is part 7 in my project to get my Arduino (/Genuino) talking with my TI Launchpad via CAN.

Today's post is fairly long, so to begin, I thought it'd be useful context to show you where we're heading:

Here's a screenshot of my PC, with my Arduino and Launchpad hooked-up for CAN messaging.



Top-left is thermistor temperature data being sent by the Genuino. Top-right is the Lauchpad receiving it. I also send the data received to a thermometer widget I created using TI's (free) "GUI Composer" software. 

In previous posts I've described sending CAN messages to the Launchpad. Today I want to talk about configuring it to accept them.

As I described previously, the Launchpad comes with (two) CAN controllers built-in. Having the Launchpad receive/transmit CAN comes down to writing a bunch of values to its registers (if you’re new to embedded, I wrote an introduction to registers here).

The bad news is that there are lots of these registers :-(

The good news is, TI have free code that does all the hard work for you :-)

To run TI's code, you don’t strictly need to understand how the Launchpad handles CAN. For most of us however, the fun of electronics is learning how stuff works, so here’s a brief overview. My aim isn’t to get into details, just to help kick-start making sense of the Launchpad datasheet and software.

A Launchpad CAN controller is made up of some CAN logic and memory for storing messages. You don’t get direct access to either of these however, instead you communicate with the Launchpad's CAN module by reading and writing to registers. 


The datasheet asks you to picture the message-memory as divided into (thirty-two) “message objects”. A “message object” consists of a message you want to transmit (/receive), plus a bunch of configuration values. For example, you might configure message object no.5 (say) such that any message held there has CAN priority-level 17 (if you don’t know about CAN message-priority I did an intro. here); or you might tell the controller you don’t want to use message object no.30 and to ignore it etc. Generally, the Launchpad gives you of lots of flexibility in how you configure message objects (you can attach FIFO’s, set up interrupts etc. etc.). 

Receiving a CAN message on the Launchpad is a three stage process.

- Firstly, you need to tell the Launchpad you want to use its built-in CAN controller, and it should devote some (GPIO) pins to CAN messaging. (This is typical in large embedded systems, where having every chip-feature running all-of-the-time would chew power and require too many pins).

- Second, with the CAN controller “turned on” you need to configure it and give it messages to send. This means deciding upon things like data-rate and setting-up the “message objects” above.

- Finally, you tell the Launchpad what you want to happen when it actually receives (/transmits) a CAN message.

TI make the first step easy by giving you C-functions that turn the CAN unit on. Search the code below for "CANInit" - the lines immediately beforehand show how it’s done.

TI also provide functions to handle the second step. You decide the speed you want your CAN link to run at; the priority you want your messages to have etc., and call TI functions accordingly. Search the code for "
CANEnable" and look at the code immediately after to see the approach.

The third step is where you have all the freedom. In a real application you want total flexibility to decide what happens when a CAN message is received (/transmits). Maybe you want an LED to flash, or a buzzer to sound, or the Launchpad to reply, or...

This creates a problem for TI / ARM's chip designers however: How do they design hardware that gives you total flexibility? The answer (as those of you familiar with embedded programming will know already) is the interrupt vector table (VTable for short).

VTable’s are a big topic. To avoid getting side-tracked here, let’s simply say the VTable is a list of “stuff” on the Launchpad: There’s a line in the VTable for the Launchpad’s USB controller; a line for each of its (two) CAN controllers, a line for...etc. 


Whenever a CAN unit “fires” (interrupts) the Launchpad checks the VTable to see what it should do. Your job is simply to paste your (e.g. LED-flashing-) software at the “CAN” line in the VTable. (Actually, instead of pasting your entire software into the VTable you just paste a pointer to your software). Now whenever a CAN message arrives, the Launchpad checks the VTable, finds a pointer to your software, and runs it. Neat huh?!

Unfortunately, the Launchpad’s VTable is a separate file to the simple_rx.c code below. Rather than clutter up this post with multiple files, I’ll show it in another time. The software the VTable calls is in the file below however: Search for CANIntHandler. Whatever you write into the CANIntHandler function is what will happen when a CAN message arrives. In my case, I added a few lines of code (marked“_VBGW_”) to print some stuff when messages arrived. 


This has been a long post! If you’ve made it to the end do please send me a comment to let me know if it was useful, and what I could do better.


Here's the Launchpad code. Please note the TI copyright.


// This file is derived from // "simple_rx.c - Example demonstrating simple CAN message reception."
// Copyright (c) 2010-2016 Texas Instruments Incorporated. All rights reserved.
//
// It is reproduced here under the permissions granted in the software
// licence agreement below.
//
// Modifications to the original file are included, marked _VBGW_
//
// The file is displayed for educational purposes.
// The author claims no ownership or responsiblity for the contents.
//*****************************************************************************
//
// simple_rx.c - Example demonstrating simple CAN message reception.
//
// Copyright (c) 2010-2016 Texas Instruments Incorporated. All rights reserved.
// Software License Agreement
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the
// distribution.
//
// Neither the name of Texas Instruments Incorporated nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// This is part of revision 2.1.3.156 of the Tiva Firmware Development Package.
//
//*****************************************************************************
#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_can.h"
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/can.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
#include "utils/uartstdio.h"
#include <string.h> // _VBGW_
//*****************************************************************************
//
//! \addtogroup can_examples_list
//! <h1>Simple CAN RX (simple_rx)</h1>
//!
//! This example shows the basic setup of CAN in order to receive messages
//! from the CAN bus. The CAN peripheral is configured to receive messages
//! with any CAN ID and then print the message contents to the console.
//!
//! This example uses the following peripherals and I/O signals. You must
//! review these and change as needed for your own board:
//! - CAN0 peripheral
//! - GPIO port B peripheral (for CAN0 pins)
//! - CAN0RX - PB4
//! - CAN0TX - PB5
//!
//! The following UART signals are configured only for displaying console
//! messages for this example. These are not required for operation of CAN.
//! - GPIO port A peripheral (for UART0 pins)
//! - UART0RX - PA0
//! - UART0TX - PA1
//!
//! This example uses the following interrupt handlers. To use this example
//! in your own application you must add these interrupt handlers to your
//! vector table.
//! - INT_CAN0 - CANIntHandler
//
//*****************************************************************************
//*****************************************************************************
//
// A counter that keeps track of the number of times the RX interrupt has
// occurred, which should match the number of messages that were received.
//
//*****************************************************************************
volatile uint32_t g_ui32MsgCount = 0;
//*********************************************************************** _VBGW_
// _VBGW_
// An integer variable to record the CAN ID of a received message. _VBGW_
// This variable will also be passed on to the GUI _VBGW_
// _VBGW_
//************************************************************************_VBGW_
uint32_t g_ui32MsgID; // _VBGW_
//*********************************************************************** _VBGW_
// _VBGW_
// An byte variable used to extract the received temperature value _VBGW_
// from the received CAN message. _VBGW_
// It will be passed on to the GUI _VBGW_
// _VBGW_
//************************************************************************_VBGW_
uint8_t g_temperature; // _VBGW_
//*****************************************************************************
//
// A flag for the interrupt handler to indicate that a message was received.
//
//*****************************************************************************
volatile bool g_bRXFlag = 0;
//*****************************************************************************
//
// A flag to indicate that some reception error occurred.
//
//*****************************************************************************
volatile bool g_bErrFlag = 0;
//*****************************************************************************
//
// This function sets up UART0 to be used for a console to display information
// as the example is running.
//
//*****************************************************************************
void
InitConsole(void)
{
//
// Enable GPIO port A which is used for UART0 pins.
// TODO: change this to whichever GPIO port you are using.
//
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
//
// Configure the pin muxing for UART0 functions on port A0 and A1.
// This step is not necessary if your part does not support pin muxing.
// TODO: change this to select the port/pin you are using.
//
GPIOPinConfigure(GPIO_PA0_U0RX);
GPIOPinConfigure(GPIO_PA1_U0TX);
//
// Enable UART0 so that we can configure the clock.
//
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
//
// Use the internal 16MHz oscillator as the UART clock source.
//
UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
//
// Select the alternate (UART) function for these pins.
// TODO: change this to select the port/pin you are using.
//
GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
//
// Initialize the UART for console I/O.
//
UARTStdioConfig(0, 115200, 16000000);
}
//*****************************************************************************
//
// This function is the interrupt handler for the CAN peripheral. It checks
// for the cause of the interrupt, and maintains a count of all messages that
// have been received.
//
//*****************************************************************************
void
CANIntHandler(void)
{
uint32_t ui32Status;
//
// Read the CAN interrupt status to find the cause of the interrupt
//
ui32Status = CANIntStatus(CAN0_BASE, CAN_INT_STS_CAUSE);
//
// If the cause is a controller status interrupt, then get the status
//
if(ui32Status == CAN_INT_INTID_STATUS)
{
//
// Read the controller status. This will return a field of status
// error bits that can indicate various errors. Error processing
// is not done in this example for simplicity. Refer to the
// API documentation for details about the error status bits.
// The act of reading this status will clear the interrupt.
//
ui32Status = CANStatusGet(CAN0_BASE, CAN_STS_CONTROL);
// _VBGW_ The following 13 'if' statements have been added to the TI code.
// _VBGW_ Their purpose is to print CAN error/status messages on the UART based
if(ui32Status & CAN_STATUS_BUS_OFF) { \
UARTprintf("ERROR: BUS OFF detected - Bus Off \n");}
if(ui32Status & CAN_STATUS_EWARN) { \
UARTprintf("ERROR: EWARN detected - Error count >=96 \n");}
if(ui32Status & CAN_STATUS_EPASS) { \
UARTprintf("ERROR: EPASS detected- Controller gone Error Passive \n");}
// if(ui32Status & CAN_STATUS_RXOK) { \
// UARTprintf("Launchpad received message (status RXOK).\n");}
// if(ui32Status & CAN_STATUS_TXOK) { \
// UARTprintf("Launchpad transmitted message (status TXOK) \n");}
if(ui32Status & CAN_STATUS_LEC_MSK) { \
UARTprintf("ERROR: LEC_MSK Mask of last 3 error code bits\n");}
// if(ui32Status & CAN_STATUS_LEC_NONE) { \
// UARTprintf("ERROR: LEC_NONE detected - No error\n");}
if(ui32Status & CAN_STATUS_LEC_STUFF) { \
UARTprintf("ERROR: LEC_STUFF detected - Stuffing error detected\n");}
if(ui32Status & CAN_STATUS_LEC_FORM) { \
UARTprintf("ERROR: LEC_FORM detected - Format error detected\n");}
if(ui32Status & CAN_STATUS_LEC_ACK) { \
UARTprintf("ERROR: LEC_ACK detected - Transmitted msg not acknowledged\n");}
if(ui32Status & CAN_STATUS_LEC_BIT1) { \
UARTprintf("ERROR: LEC_BIT1 detected Dominant found when sending recessive\n");}
if(ui32Status & CAN_STATUS_LEC_BIT0) {\
UARTprintf("ERROR: LEC_BIT0 detected Recessive found when sending dominant\n");}
if(ui32Status & CAN_STATUS_LEC_CRC) { \
UARTprintf("ERROR: LEC_CRC detected CRC error in received message\n");}
// _VBGW_ end of the 13 added 'if' statements
//
// Set a flag to indicate some errors may have occurred.
//
g_bErrFlag = 1;
}
//
// Check if the cause is message object 1, which what we are using for
// receiving messages.
//
else if(ui32Status == 1)
{
//
// Getting to this point means that the RX interrupt occurred on
// message object 1, and the message reception is complete. Clear the
// message object interrupt.
//
CANIntClear(CAN0_BASE, 1);
//
// Increment a counter to keep track of how many messages have been
// received. In a real application this could be used to set flags to
// indicate when a message is received.
//
g_ui32MsgCount++;
//
// Set flag to indicate received message is pending.
//
g_bRXFlag = 1;
//
// Since a message was received, clear any error flags.
//
g_bErrFlag = 0;
}
//
// Otherwise, something unexpected caused the interrupt. This should
// never happen.
//
else
{
//
// Spurious interrupt handling can go here.
//
}
}
//*****************************************************************************
//
// Configure the CAN and enter a loop to receive CAN messages.
//
//*****************************************************************************
int
main(void)
{
#if defined(TARGET_IS_TM4C129_RA0) || \
defined(TARGET_IS_TM4C129_RA1) || \
defined(TARGET_IS_TM4C129_RA2)
uint32_t ui32SysClock;
#endif
tCANMsgObject sCANMessage;
uint8_t pui8MsgData[8];
//
// Set the clocking to run directly from the external crystal/oscillator.
// TODO: The SYSCTL_XTAL_ value must be changed to match the value of the
// crystal used on your board.
//
#if defined(TARGET_IS_TM4C129_RA0) || \
defined(TARGET_IS_TM4C129_RA1) || \
defined(TARGET_IS_TM4C129_RA2)
ui32SysClock = SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
SYSCTL_OSC_MAIN |
SYSCTL_USE_OSC)
25000000);
#else
SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |
SYSCTL_XTAL_16MHZ);
#endif
//
// Set up the serial console to use for displaying messages. This is
// just for this example program and is not needed for CAN operation.
//
InitConsole();
UARTprintf("Starting Up...\n"); // _VBGW_
//
// For this example CAN0 is used with RX and TX pins on port B4 and B5.
// The actual port and pins used may be different on your part, consult
// the data sheet for more information.
// GPIO port B needs to be enabled so these pins can be used.
// TODO: change this to whichever GPIO port you are using
//
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
//
// Configure the GPIO pin muxing to select CAN0 functions for these pins.
// This step selects which alternate function is available for these pins.
// This is necessary if your part supports GPIO pin function muxing.
// Consult the data sheet to see which functions are allocated per pin.
// TODO: change this to select the port/pin you are using
//
GPIOPinConfigure(GPIO_PB4_CAN0RX);
GPIOPinConfigure(GPIO_PB5_CAN0TX);
//
// Enable the alternate function on the GPIO pins. The above step selects
// which alternate function is available. This step actually enables the
// alternate function instead of GPIO for these pins.
// TODO: change this to match the port/pin you are using
//
GPIOPinTypeCAN(GPIO_PORTB_BASE, GPIO_PIN_4 | GPIO_PIN_5);
//
// The GPIO port and pins have been set up for CAN. The CAN peripheral
// must be enabled.
//
SysCtlPeripheralEnable(SYSCTL_PERIPH_CAN0);
//
// Initialize the CAN controller
//
UARTprintf("Calling CANInit()..."); // _VBGW_
CANInit(CAN0_BASE);
UARTprintf("...CANInit() retuned.\n"); // _VBGW_
//
// Set up the bit rate for the CAN bus. This function sets up the CAN
// bus timing for a nominal configuration. You can achieve more control
// over the CAN bus timing by using the function CANBitTimingSet() instead
// of this one, if needed.
// In this example, the CAN bus is set to 500 kHz. In the function below,
// the call to SysCtlClockGet() or ui32SysClock is used to determine the
// clock rate that is used for clocking the CAN peripheral. This can be
// replaced with a fixed value if you know the value of the system clock,
// saving the extra function call. For some parts, the CAN peripheral is
// clocked by a fixed 8 MHz regardless of the system clock in which case
// the call to SysCtlClockGet() or ui32SysClock should be replaced with
// 8000000. Consult the data sheet for more information about CAN
// peripheral clocking.
//
#if defined(TARGET_IS_TM4C129_RA0) || \
defined(TARGET_IS_TM4C129_RA1) || \
defined(TARGET_IS_TM4C129_RA2)
CANBitRateSet(CAN0_BASE, ui32SysClock, 500000);
#else
UARTprintf("Calling CANBitRateSet()..."); // _VBGW_
CANBitRateSet(CAN0_BASE, SysCtlClockGet(), 125000); // set rate 125000 _VBGW_
UARTprintf("CANBitRateSet() retuned.\n"); // _VBGW_
#endif
//
// Enable interrupts on the CAN peripheral. This example uses static
// allocation of interrupt handlers which means the name of the handler
// is in the vector table of startup code. If you want to use dynamic
// allocation of the vector table, then you must also call CANIntRegister()
// here.
//
// CANIntRegister(CAN0_BASE, CANIntHandler); // if using dynamic vectors
//
CANIntEnable(CAN0_BASE, CAN_INT_MASTER | CAN_INT_ERROR | CAN_INT_STATUS);
//
// Enable the CAN interrupt on the processor (NVIC).
//
IntEnable(INT_CAN0);
//
// Enable the CAN for operation.
//
UARTprintf("Calling CANEnable()..."); // _VBGW_
CANEnable(CAN0_BASE);
UARTprintf("CANEnable() retuned.\n"); // _VBGW_
//
// Initialize a message object to be used for receiving CAN messages with
// any CAN ID. In order to receive any CAN ID, the ID and mask must both
// be set to 0, and the ID filter enabled.
//
sCANMessage.ui32MsgID = 0;
sCANMessage.ui32MsgIDMask = 0;
sCANMessage.ui32Flags = MSG_OBJ_RX_INT_ENABLE | MSG_OBJ_USE_ID_FILTER;
sCANMessage.ui32MsgLen = 8;
//
// Now load the message object into the CAN peripheral. Once loaded the
// CAN will receive any message on the bus, and an interrupt will occur.
// Use message object 1 for receiving messages (this is not the same as
// the CAN ID which can be any value in this example).
//
UARTprintf("Calling CANMessageSet()..."); // _VBGW_
CANMessageSet(CAN0_BASE, 1, &sCANMessage, MSG_OBJ_TYPE_RX);
UARTprintf("CANMessageSet() returned.\n"); // _VBGW_
UARTprintf("...............End of setup.................. \n"); // _VBGW_
UARTprintf("\n"); // _VBGW_
//
// Enter loop to process received messages. This loop just checks a flag
// that is set by the interrupt handler, and if set it reads out the
// message and displays the contents. This is not a robust method for
// processing incoming CAN data and can only handle one messages at a time.
// If many messages are being received close together, then some messages
// may be dropped. In a real application, some other method should be used
// for queuing received messages in a way to ensure they are not lost. You
// can also make use of CAN FIFO mode which will allow messages to be
// buffered before they are processed.
//
for(;;)
{
unsigned int uIdx;
//
// If the flag is set, that means that the RX interrupt occurred and
// there is a message ready to be read from the CAN
//
if(g_bRXFlag)
{
//
// Reuse the same message object that was used earlier to configure
// the CAN for receiving messages. A buffer for storing the
// received data must also be provided, so set the buffer pointer
// within the message object.
//
sCANMessage.pui8MsgData = pui8MsgData;
//
// Read the message from the CAN. Message object number 1 is used
// (which is not the same thing as CAN ID). The interrupt clearing
// flag is not set because this interrupt was already cleared in
// the interrupt handler.
//
CANMessageGet(CAN0_BASE, 1, &sCANMessage, 0);
//
// Clear the pending message flag so that the interrupt handler can
// set it again when the next message arrives.
//
g_bRXFlag = 0;
//
// Check to see if there is an indication that some messages were
// lost.
//
if(sCANMessage.ui32Flags & MSG_OBJ_DATA_LOST)
{
UARTprintf("CAN message loss detected\n");
}
// _VBGW_
// Store the message ID in g_ui32MsgID _VBGW_
// This variable is read by the GUI _VBGW_
// _VBGW_
g_ui32MsgID = sCANMessage.ui32MsgID; // _VBGW_
// _VBGW_
// Extract the temperature to g_temperature _VBGW_
// This variable is read by the GUI _VBGW_
// _VBGW_
g_temperature = pui8MsgData[0]; // _VBGW_
//
// Print out the contents of the message that was received.
//
UARTprintf("Msg ID=0x%08X len=%u data=0x",
sCANMessage.ui32MsgID, sCANMessage.ui32MsgLen);
for(uIdx = 0; uIdx < sCANMessage.ui32MsgLen; uIdx++)
{
UARTprintf("%02X ", pui8MsgData[uIdx]);
}
UARTprintf("total count=%u\n", g_ui32MsgCount);
UARTprintf("...................\n"); // _VBGW_
UARTprintf("\n"); // _VBGW_
}
}
//
// Return no errors
//
return(0);
}