Monday, 11 August 2014

Creating a 5 bit successive approximation (sar) ADC using the LPC810's Comparator

The only analog peripheral the LPC810 has it the analog comparator which can compare various external and internal voltages. The analogs pin are fixed pins in the LPC800 series of micro-controllers and exist on the same pins as PIO0_0 and PIO0_1


 In this example we are going to use the PIO0_1 pin which is ACMP_I2


and we have the potentiometer connected to PIO0_1.A word of warning, be careful not to have the POT turned to ground when you try to restart the LPC810 after programming or the LPC810 will boot into ISP mode again.

The steps in setting up the comparator is similar to the other peripherals, first we must turn on the clock to the comparator then select it in the switch matrix. We also need to power up the comparator like so:

LPC_SYSCON->PDRUNCFG &= ~(( 1  <<  15 )); // power up ACMP

Again replace the main.c in from the previous project with the one below :-


#include "LPC8xx.h"

#define BAUDRATE 57600
#define UART_CFG_DATALEN_8      (0x01 << 2)  /* UART 8 bit length mode */
#define UART_CFG_PARITY_NONE    (0x00 << 4)  /* No parity */
#define UART_CFG_STOPLEN_1      (0x00 << 6)  /* UART One Stop Bit Select */
#define UART_STAT_RXRDY         (0x01 << 0)  /* Receiver ready */
#define UART_STAT_TXRDY         (0x01 << 2)  /* Transmitter ready for data */
#define UART_CFG_ENABLE         (0x01 << 0)

uint32_t SystemCoreClock = 12000000; /* Value @ Reset */
uint32_t SystemMainClock = 12000000; /* Value @ Reset */

#define halt_clk(c) LPC_MRT->Channel[3].INTVAL = (c-3)

int uart0PollChar() {
 if (!(LPC_USART0->STAT & UART_STAT_RXRDY))
  return -1;
 return LPC_USART0->RXDATA;
}
void uart0putc(unsigned char c) {
 /* Wait until we're ready to send */
 while (!(LPC_USART0->STAT & UART_STAT_TXRDY))
  ;
 LPC_USART0->TXDATA = c;
}

void uart0puti(unsigned int n) {
 unsigned int rem = n % 10;
 unsigned int quot = n / 10;
 if (quot > 0)
  uart0puti(quot);
 uart0putc(rem + '0');
}

/* Use ACMP_2 as a simple 5 bit SAR adc */
int read_adc2() {
 int min = 0, max = 63, value = 31, count, compstat, laststat;
 LPC_CMP->CTRL = (0x2 << 8) | (0x3 << 25); /* positive input ACMP0_I2 , negative input voltage ladder , 20mV hysteresis */
 while (!((value == min) || (value == max))) {
  count = 0;
  LPC_CMP->LAD = 1 | (value); /* ladder Enable | Vref=Vdd | value */
  halt_clk(12 * 15); /* 15us worst case for ladder to settle */
  laststat = (LPC_CMP->CTRL >> 21) & 1;
  /* wait till reading is the same 3 times in row */
  do {
   halt_clk(6); /* wait 0.5us */
   compstat = (LPC_CMP->CTRL >> 21) & 1;
   if (compstat == laststat)
    count++;
   else
    count = 0;
   laststat = compstat;
  } while (count < 3);
  /* Binary divide */
  if (compstat) {
   min = value;
   value = value + ((max - value) >> 1);
  } else {
   max = value;
   value = value - ((value - min) >> 1);
  }
 }
 return value >> 1;
}

int main(void) {
 /* Enable AHB clock to the Switch Matrix , UART0 , GPIO , IOCON, MRT , ACMP */
 LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 7) | (1 << 14) /*| (1 << 6)*//* | (1 << 18)*/
   | (1 << 10) | (1 << 19);

 /* Configure Switch Matrix so that Pin2 = U0_TXD and Pin8 = U0_RXD */
 LPC_SWM->PINASSIGN0 = 0xffff0004UL;

 /* On reset pio_2(swdio), pio_3(swclk) , pio_5(reset) are selected as fixed function.
  * We disable all fixed functions here so they will become GPIO unless a movable function
  * has been assigned in the switch matrix by the line above */
 LPC_SWM->PINENABLE0 = 0xffffffffUL & (~0x2); /* Enable fixed function ACMP_I2. This function is enabled on pin PIO0_1 */

 /* Set the UART clock divisor to 1  */
 LPC_SYSCON->UARTCLKDIV = 1; // note on reset the value is 0. i.e clock is disabled
 /* Configure UART0 baud rate */
 LPC_USART0->BRG = ((SystemMainClock / LPC_SYSCON->UARTCLKDIV) >> 4)
   / BAUDRATE - 1;
 LPC_SYSCON->UARTFRGMULT =
   ((((SystemMainClock / LPC_SYSCON->UARTCLKDIV) >> 4) * (0x100))
     / (BAUDRATE * (LPC_USART0->BRG + 1))) - (0x100);
 LPC_SYSCON->UARTFRGDIV = 0xFF; //note on reset the value is 0

 /* Set UART0 to 8N1 and finally enable */
 LPC_USART0->CFG = UART_CFG_DATALEN_8 | UART_CFG_PARITY_NONE
   | UART_CFG_STOPLEN_1 | UART_CFG_ENABLE;

 LPC_MRT->Channel[3].CTRL = (0x02 << 1); /* MRT3 (halt_clk) bus stall mode , no interrupts */

 /* Initialise the comparator ICMP_2 */
 LPC_SYSCON->PDRUNCFG &= ~((1 << 15)); // power up ACMP

 /* wait for the keypress 'a' */
 while (1) {
  switch (uart0PollChar()) {
  case 'a': {
   int v = read_adc2();
   uart0puti(v);
   uart0putc('\n');
   uart0putc('\r');
  }
   break;
  }
 }
 return 0;
}


The function read_adc2() does most of the work here and implements a simple 5 bit SAR ADC using the programmable voltage divider of the LPC800 series mcu's. It should be noted that theoretically this routine could get into an infinite loop if there was to much noise but it seems to work well for me.


No comments:

Post a Comment