Jump to content
Frankiesayrelax

ADC interrupts for Tiva C Series

Recommended Posts

I am using Keil uVision 5 IDE and the microcontroller is the TM4C123GH6M.

 

At the moment, all I am trying to do is to get some analogue to digital conversion happening on a single input pin. I am using the datasheet with this tutorial on youtube as a guide, here: 

 

 

I understand all the steps except for using an interrupt to know when each A to D conversion has ended. I have read the Keil user guide on what interrupts are and how you can use them:

http://www.keil.com/support/man/docs/c51/c51_le_interruptfuncs.htm

And as usual, I really don't understand what it said. It gave me a general idea on what interrupts are but not on how to use them. In the past I have found Keil, both the IDE itself and the "help and support" webpage they give to be extremely user unfriendly and entirely unhelpful. Or maybe I am just terrible at programming!

 

I also looked up the address of the interrupt in the interrupt vector address table in the datasheet and the one I want to use is for the ADC 1 Sequencer 3, the address is 0x0000.010C. It is interrupt number 51 in the register.

The tutorial mentions finding a cstartup_M.c file at around 15 minute, 40 second mark, including it in the source group and modifying it, but I can't find it anywhere in any of the project folders or directories.

 

Basically, the most important thing is I want to know how to use interrupts and if any of you have the time, I would like to know what exactly he is doing when he starts talking about interrupts at the 15.25 mark and including this source file. Thanks a lot for reading.

Share this post


Link to post
Share on other sites

I'm not sure I can help, but I'll give it a try.

 
First, that link was for the Intel 8051, a 8-bit microprocessor from the 1980s. Some general notions of interrupts applied, but I found it pretty confusing since none of the details concerned us.
 
Have you loaded the TM4C "pack" for Keil's product?  Just checking. There's bound to be some relevant files under there.
 
I found some interesting pages on Keil's site. When you do a search, there's an 'advanced' search option. Using that, I searched under the ARM products only, for 'cortex interrupt' and some likely articles turned up.
 
Here's one for the ADC on the STM32F : http://www.keil.com/support/man/docs/gsac/gsac_adc.htm
 
Not exactly what we wanted, but it shows the things we'll have to do, since it's basically the same core with different peripherals.
 
And maybe bookmark this, it looks very useful for using their IDE: http://www.keil.com/support/man/docs/gsac/gsac_intro.htm
 
If you haven't already, install TI's Tivaware package - or at least unpack it; it has many examples for you under SW-EK-TM4C123GXL-2.1.0.12573/examples/boards/ek-tm4c123gxl
 
TI has some good support videos and documents over here:  http://www.ti.com/TM4C123G-Launchpad-Workshop
There's a video specifically for interrupts; and see pages 4-7, 4-17, 4-18 of the TM4C123G_LaunchPad_Workshop_Workbook.pdf
 
Again, it's not a perfect match, since it uses TI's CCS instead of Keil's uVision.  Oh, well, I'm using gcc on OS X, so they're both foreign to me.
 
An interrupt is just the hardware making a subroutine call for us, behind our backs, unexpectedly. The interrupt handler has to be careful not to trash any variables that other parts of our code might be using. And, other parts of our code might have to be careful not to assume they know the values of the data that the interrupt routine touched.
 
I don't think you have to play with the priority register at first. Just get a plain interrupt happening; later tweak the priorities if you need to.
 
All we do is:
- set the bits in the ADC registers to allow interrupts
- define a interrupt handler
- put the name of the interrupt handler in the startup code
 
I'm guessing that cstartup_M.c file was for the 8051. In the Tivawave examples, the file is startup_rvmdk.S for the Keil compiler.  Just find the line that has 'ADC1 Sequence 3' and replace 'IntDefaultHander' with the name of your ADC1 sequence 3 handler.
;; startup_rvmdk.S - Startup code for use with Keil's uVision.
  ...
DCD     IntDefaultHandler           ; ADC1 Sequence 3
replace with
DCD     ADC1Seq3handler ; ADC1 Sequence 3
After all that, here's my contribution. It measures the internal temperature sensor on ADC0 sequencer 3 at about 1KHz, and prints the temperature to the debug UART port about every two seconds. For some reason I clocked everything slowly and used delays so that we can watch it easier on a scope. I'm not sure that makes any sense now.
 
It uses the Tivaware functions, so if you're using direct register access, it won't map directly to your needs.
 
I hope this helps. Good luck!
 
/*
 *	adci.c
 *	adc with interrupt to measure temp
 *	$Id:$
 *
 *	NOTE! add ADC0handler to startup_gcc.c (or whatever your IDE uses)
 *		void ADC0handler(void);		// declare it
 *		...
 *		ADC0handler,			// ADC Sequence 3
 *	
 *	LED indicators
 *	  red    PF1  triggered conversion, waiting for conversion to finish
 *	  blue   PF2  inside ADC interuppt handler, reading conversion data
 *	  green  PF3  got conversion
 *
 *	To make things take longer, to maybe see better on a scope,
 *	both the processor and ADC run from from slower clocks,
 *	and the ADC uses hardware averaging.
 *
 *	The repetition rate of the loop is about 775Hz.
 *	It waits about 137 usec for a conversion complete.
 *	The ADC interrupt routine takes about 9.5 usec.
 *
 *	About every two seconds the loop prints a line to UART0
 *		$08dc 2268    10 C   50 F
 *
 *	Warm up (or cool off) your thumb, and hold against the chip,
 *	to see the temperature change.
 */

#include <stdbool.h>
#include <stdint.h>

#include "inc/tm4c123gh6pm.h"
#include "inc/hw_memmap.h"
#include "driverlib/adc.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"

#include "utils/uartstdio.h"

// define the peripheral/port/pins for the LEDs 
#define LED_PERIF	SYSCTL_PERIPH_GPIOF
#define LED_PORT	GPIO_PORTF_BASE
#define LED_RED		GPIO_PIN_1
#define LED_GREEN	GPIO_PIN_3
#define LED_BLUE	GPIO_PIN_2
#define LEDS		(LED_RED|LED_GREEN|LED_BLUE)
#define	LED_SET(X)	ROM_GPIOPinWrite(LED_PORT, LEDS, (X));

// this array must be big enough to hold the channels we convert
// (only need one entry for this example)
uint32_t rawtemp[4];

// this flag is set by the interrupt routine, and read by the wait loop
volatile uint32_t newtemp;

// this is the interrupt routine for the ADC
void ADC0handler(void)
{
	LED_SET(LED_BLUE);
	// clear the interrupt flag, grab the data, set the 'done' flag
	ROM_ADCIntClear(ADC0_BASE, 3);
	ROM_ADCSequenceDataGet(ADC0_BASE, 3, rawtemp);
	newtemp = 1;
	LED_SET(0);
}

void InitADC(void)
{
	// enable ADC0
	// set a slower conversion rate
	// use sequencer 3 (single sample), with manual trigger
	// seq 3, step 0, temperature-sensor, interrupt-enable, last-conversion
	// enable sequencer 3

	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
	ROM_SysCtlDelay(2);
	ADCClockConfigSet(ADC0_BASE, 
		ADC_CLOCK_RATE_EIGHTH | ADC_CLOCK_SRC_PIOSC, 1);
	ROM_ADCHardwareOversampleConfigure(ADC0_BASE, 16); // 64 32 16 8 4 2
	ROM_ADCSequenceConfigure(ADC0_BASE, 3, ADC_TRIGGER_PROCESSOR, 0);
	ROM_ADCSequenceStepConfigure(ADC0_BASE, 3, 0, 
		ADC_CTL_TS | ADC_CTL_IE | ADC_CTL_END);
	ROM_ADCSequenceEnable(ADC0_BASE, 3);

	// make sure interrupt flag is clear
	// enable the interrupt for the module and for the sequence
	ROM_ADCIntClear(ADC0_BASE, 3);
	ROM_IntEnable(INT_ADC0SS3);
	ROM_ADCIntEnable(ADC0_BASE, 3);
}

void InitConsole(void)
{
	// enable the port that has UART0 pins
	// configure the RX/TX port pins
	// enable the uart, set the clock, set the pins
	// set baud-rate
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
	ROM_SysCtlDelay(2);
	ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
	ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
	ROM_UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
	ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
	UARTStdioConfig(0, 115200, 16000000);
}

int main(void)
{
	uint32_t i, tempC, tempF;

	// clock at 8 MHz from the crystal
	ROM_SysCtlClockSet(SYSCTL_SYSDIV_2 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);

	ROM_SysCtlPeripheralEnable(LED_PERIF);
	ROM_SysCtlDelay(2);
	ROM_GPIOPinTypeGPIOOutput(LED_PORT, LEDS);
	LED_SET(0);

	InitConsole();
	InitADC();

	UARTprintf("\n\nADC internal temperature sensor\n");

	// allow interrupts
	ROM_IntMasterEnable();

	i = 0;
	while ( 1 ) {
		// clear the flag, show we're waiting
		newtemp = 0;
		LED_SET(LED_RED);

		// start a conversion
		ROM_ADCProcessorTrigger(ADC0_BASE, 3);

		// spin and wait for the interrupt handler to get the data
		while ( newtemp == 0 ) {
			__asm ("WFE\n	");
		}

		LED_SET(LED_GREEN);

		// print temps about every two seconds
		if ( ++i == 2000 ) {
			i = 0;
			tempC = (1475 - ((2475 * rawtemp[0])) / 4096)/10;
			tempF = ((tempC * 9) + 160) / 5;
			UARTprintf("$%04x %4d   %3d C  %3d F\n", 
				rawtemp[0], rawtemp[0], tempC, tempF);
		}
		// delay to slow down the loop to something around 1KHz
		ROM_SysCtlDelay(ROM_SysCtlClockGet()*3/8000);
	}
}

Share this post


Link to post
Share on other sites

Hi ,

which systemclock should I configure with ADC & timer module ( I am using both module in same code)..??

SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); freq = SysCtlClockGet(); // 80MHz
SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); freq = SysCtlClockGet(); // 50MHz
SysCtlClockSet(SYSCTL_SYSDIV_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); freq = SysCtlClockGet(); // 40MHz

If I dont write any of above, I get :: freq = SysCtlClockGet(); // 16MHz

My requirement is lowest execution time.
Should I configure to 80MHz..?? 

If I do so will both ADC and Timers will work fine..??
Is any extra configuration needed...?



Regards,
Krishnat

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×