This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

How to read Potentiometer and display conversion result

I would to ask how to read the potentiometer(Keil MCBSTR9U) input periodically with a timer interrupt? And also to display the conversion result on the LCD display. Please help. I don't know how to get started.

#include <91x_lib.h>
#include "LCD.h"


extern short AD_last;                     /* Last AD value read in interrupt  */

extern __irq void TIM3_IRQ_Handler(void); /* TIM3 interrupt routine           */
extern __irq void ADC_IRQ_Handler (void); /* ADC  interrupt routine           */


void wait (void)  {                       /* Wait function                    */
  int d;

  d = AD_last;                            /* Read AD_last value               */
  if (d != AD_last)                       /* Make sure that AD interrupt did  */
    d = AD_last;                          /* not interfere with value reading */

  d *= 500;
  d += 50000;                             /* Scale analog value for delay     */
                                          /* lower value -> longer delay      */
  while (d--);                            /* Only to delay for LED flashes    */
}


int main (void) {
  unsigned int i, n;
  unsigned short AD_old, AD_value;
  unsigned char text1[30];

  /* ADC Setup                                                                */
  SCU->GPIOIN[4]  |= 0x01;                /* P4.0 input  - mode 0             */
  SCU->GPIOOUT[4] &= 0xFFFC;              /* P4.0 output - mode 0             */
  GPIO4->DDR      &= 0xFE;                /* P4.0 direction - input           */
  SCU->GPIOANA    |= 0x0001;              /* P4.0 analog mode ON              */

  ADC->CR         |= 0x0002;              /* Set POR bit                      */
  for (n = 0; n < 100000; n ++);          /* Wait > 1 ms  (at 96 MHz)         */

  ADC->CR         &= 0xFFF7;              /* Clear STB bit                    */
  for (n = 0; n < 1500; n ++);            /* Wait > 15 us (at 96 MHz)         */

  ADC->CR         |= 0x0400;              /* Enable end of conversion interupt*/
  ADC->CCR         = 0x0003;              /* AD Conversion, No WDG on Ch 0    */

  SCU->GPIOOUT[7]  = 0x5555;              /* P7.0..7 output - mode 1          */
  GPIO7->DDR       = 0xFF;                /* P7.0..7 Outputs (LED Data)       */

  /* LCD Setup                                                                */
  GPIO8->DDR       = 0xFF;                /* P8.0..7 Outputs (LCD Data)       */
  GPIO9->DDR       = 0x07;                /* P9.0..2 Outputs (LCD Control)    */

  lcd_init();
  lcd_clear();
  lcd_print (" MCB-STR9 DEMO  ");
  set_cursor (0, 0);
  AD_value = ADC->DR0 & 0x03FF;
  sprintf(text1,"ADConversion: %5d",AD_value);
  lcd_print(text1);

  for (i = 0; i < 200; i++) wait();       /* Wait for initial display         */

  /* Configure and enable IRQ for A/D Converter (ADC)                         */
  VIC0->VAiR[15]  = (unsigned int)ADC_IRQ_Handler; /* Setup ADC IRQ Hndl addr */
  VIC0->VCiR[15] |= 0x20;                 /* Enable the vector interrupt      */
  VIC0->VCiR[15] |= 15;                   /* Specify the interrupt number     */
  VIC0->INTER    |= (1<<15);              /* Enable ADC interrupt             */

  /* Configure and enable IRQ for Timer (TIM3)                                */
  VIC0->VAiR[7]   = (unsigned int)TIM3_IRQ_Handler;/* Setup TIM3 IRQ Hndl addr*/
  VIC0->VCiR[7]  |= 0x20;                 /* Enable the vector interrupt      */
  VIC0->VCiR[7]  |= 7;                    /* Specify the interrupt number     */
  VIC0->INTER    |= (1<<7);               /* Enable TIM3 interrupt            */

  /* Timer 3 Configuration (TIM3)                                             */
  TIM3->CNTR      = 0x0000;               /* Setup TIM3 counter register      */
  TIM3->CR2      &= 0xFF00;               /* Clear prescaler value            */
  TIM3->CR2      |= 0x000F;               /* Setup TIM3 prescaler             */
  TIM3->CR2      |= 0x2000;               /* TIM3 timer overflow intrupt en   */
  TIM3->CR1      |= 0x8000;               /* TIM3 counter enable              */

  while (1) {                             /* Loop forever                     */
    for (n = 0x01; n <= 0xFF; n <<= 1) {
      GPIO7->DR[0x3FC] = n;               /* Turn on LED                      */
      wait();                             /* Delay                            */
      AD_value = AD_last;                 /* Read AD_last value               */
      if (AD_value != AD_last)            /* Make sure that AD interrupt did  */
        AD_value = AD_last;               /* not interfere with value reading */
      AD_value /= 13;                     /* Scale to AD_Value to 0 - 78      */
      if (AD_old != AD_value)  {          /* If AD value has changed          */
        set_cursor (0, 1);
        AD_old = AD_value;
        /*for (i = 0; i < 16; i++)  {       // Disp bargraph according to AD
          if (AD_value > 5)  {
            lcd_putchar (0x05);
            AD_value -= 5;
          }  else  {
            lcd_putchar (AD_value);
            AD_value = 0;
          }
        }
      }  */
                if (AD_value >= 0 && AD_value <= 13 ){
            lcd_print("0");
          }
                  else if (AD_value >= 14 && AD_value <= 26) {
                        lcd_print("1");
                  }
                  else if (AD_value >= 27 && AD_value <= 39) {
                        lcd_print("2");
                  }
                  else if (AD_value >= 40 && AD_value <= 52) {
                        lcd_print("3");
                  }
                  else if (AD_value >= 53 && AD_value <= 65)  {
                lcd_print("4");
          }
                  else if (AD_value >= 66 && AD_value <= 78) {
                        lcd_print("5");
                  }
        }
        }
  }
}

  • Woudlnt' you simply have the timer interrupt start an ADC conversion and then end?
    And the ADC interrupt pick up the ADC conversion result and then end?

    The issue here is that you also want to emit data to the LCD. That is normally not a good thing to do in an inteerrupt because it normally takes very long time to interact with the LCD. So the ADC interrupt would then just have to store the ADC value in a buffer and set a flag that there is a ADC value to emit. And your main loop would see the flag, pick up the value and display it.

    By the way: why so complicated code:

    if (AD_value >= 0 && AD_value <= 13 ){
        lcd_print("0");
    } else if (AD_value >= 14 && AD_value <= 26) {
        lcd_print("1");
    } else if (AD_value >= 27 && AD_value <= 39) {
        lcd_print("2");
    } else if (AD_value >= 40 && AD_value <= 52) {
        lcd_print("3");
    } else if (AD_value >= 53 && AD_value <= 65)  {
        lcd_print("4");
    } else if (AD_value >= 66 && AD_value <= 78) {
        lcd_print("5");
    }
    

    Do you expect the ADC to return negative values? Of course some ADC really can return results in two-complement format or with a sign bit but the majority of ADC only supports measuring a positive input signal.

    Next thing - do you expect your ADC value to be able to have a value > 26 but < 27? Why else do you have two conditions for each range? If there isn't a gap between then, you only need to have a series of "less than" comparisons to bin the ADC value into different bins.

    Next thing - if your bins are always the same size, you don't need to do as above. You can perform an integer division by 14. 13 is smaller than 14, so answer will be zero. 14..27 are less than 2 so but >= 1 so will return value 1.

    By the way - you did notice that your first bin is 14 numbers large, while all other bins are 13 large? A mistake ir really intended?

  • Hello Per Westermark, thank you for the reply.

    As I am very new to this, so pardon if i have mentioned anything "ridiculous".

    Woudlnt' you simply have the timer interrupt start an ADC conversion and then end?
    ->

    __irq void TIM3_IRQ_Handler (void) {    /* TIM3 timer interrupt routine       */
    
      if (!AD_in_progress)  {               /* If conversion not in progress      */
        AD_in_progress = 1;                 /* Flag that AD conversion is started */
        ADC->CR |= 0x0403;                  /* Set STR bit (Start Conversion)     */
                                            /* on Channel 0, with interrupt       */
                                            /* generation enabled                 */
      }
      TIM3->SR &= ~0x2000;                  /* Clear Timer Overflow interrupt flag*/
    
      VIC0->VAR = 0;                        /* Acknowledge Interrupt              */
      VIC1->VAR = 0;
    }
    

    And the ADC interrupt pick up the ADC conversion result and then end?
    ->

    
    __irq void ADC_IRQ_Handler (void) {     /* AD converter interrupt routine     */
    
      AD_last = ADC->DR0 & 0x03FF;          /* AD value for global usage (10 bit) */
    
      ADC->CR &= 0xFFFE;                    /* Clear STR bit (Start Conversion)   */
      ADC->CR &= 0x7FFF;                    /* Clear End of Conversion flag       */
    
      AD_value = ADC->DR0 & 0x3FF;           /* Read the ADC Value            */
    
      VIC0->VAR = 0;                        /* Acknowledge Interrupt              */
      VIC1->VAR = 0;
    
      AD_in_progress = 0;                   /* Clear flag, as AD conv finished    */
    }
    
    
    

    I am just trying to read the AD_conversion value and display it on LCD. I admit that the code written are way too complicated, any advice how i can do it??

  • Note that it's very hard to read this code:

    ADC->CR &= 0xFFFE;                    /* Clear STR bit (Start Conversion)   */
    ADC->CR &= 0x7FFF;                    /* Clear End of Conversion flag       */
    

    It's much better to write:

    ADC->CR &= ~0x0001;                    /* Clear STR bit (Start Conversion)   */
    ADC->CR &= ~0x8000;                    /* Clear End of Conversion flag       */
    


    Now, the specific bit cleared is visible. And another thing is that the ~ operation will be performed to full integer width. Wahat if you use 0xFFFE to a 32-bit wide register? Then you would also clear the 16 high bits without it being visible to the reader.

    Next thing is that it's a good idea to created named constants for your flags.

    enum {
        ADC_CR_STR = 0x0001u,
    };
    ADC->CR &= ~ADC_CR_STR;
    

    Now a reader can see that you are clearing the STR bit of the register, without having to read a comment.

    I normally create include files with these additional flags for the different processors I'm using so I would include:

    #include "lpc17xx_adc.h"
    #include "lpc17xx_uart.h"
    ...
    


    for my projects that are using a processor in the NXP LPC17xx family.

  • Just wondering how is the display work? For example when we are done with the AD conversion, what next is to display it to the LCD. How can it be done??

  • As I did mention in a previous post - by having the main loop see flag that there are a new value to print. Then call lcd_print() to emit it. An optimization would be to ignore display update if the new ADC value is the same as what was previously printed.

  • What i am trying to ask is how to display correctly the AD conversion value to the LCD? As from what i understand, the printing on the LCD is done using char variable encoded in ASCII character. As ADC_value is given as integer value, we would need to convert them into ASCII value, correct me if i am wrong. So the maximum value to be display is 1023. So I am thinking i might need to conver to ASCII before displaying.

    by having the main loop see flag that there are a new value to print. Then call lcd_print() to emit it.
    -> Would appreciate if you could tell me more how to do this?

  • If you have a number between 0 and 9, you can make it into ASCII by adding '0'.

    So if you have a number between 0 and 1023 you could do:

    char1 = '0' + (num / 1000);
    char2 = '0' + ((num / 100) % 10);
    char3 = '0' + ((num / 10) % 10);
    char4 = '0' + (num % 10);
    buf[0] = char1;
    buf[1] = char2;
    buf[2] = char3;
    buf[3] = char4;
    buf[4] = '\0';
    lcd_print(buf);
    

    Or you could use:

    sprintf(buf,"%u",num);
    lcd_print(buf);
    

    There are so many different ways you could select. Google or experiement instead of seeing problems.