Hello,
can anyone help me,how can I program the AD-Converter of the MCB4300. The program works with one channel (AD0 - Channel 1), but I need 2 channels (AD0 - Channel 1 and AD0 Channel 0).
int main(void) { int adcVal;
ADC_Initialize(); ADC_StartConversion();
while(ADC_ConversionDone () < 0);
adcVal = ADC_GetValue(); }
/* This is the program code from the Example of Keil for the MCB4300 */ #include "LPC43xx.h" #include "SCU_LPC43xx.h" #include "Board_ADC.h"
#define ADC_RESOLUTION 10 /* Number of A/D converter bits */
/* Clock Control Unit register bits */ #define CCU_CLK_CFG_RUN (1 << 0) #define CCU_CLK_CFG_AUTO (1 << 1) #define CCU_CLK_STAT_RUN (1 << 0)
static volatile uint16_t AD_last; /* Last converted value */ static volatile uint8_t AD_done; /* AD conversion done flag */
int32_t ADC_Initialize (void) {
/* Enable ADC0 clock */ LPC_CCU1->CLK_APB3_ADC0_CFG = CCU_CLK_CFG_AUTO | CCU_CLK_CFG_RUN; while (!(LPC_CCU1->CLK_APB3_ADC0_STAT & CCU_CLK_STAT_RUN));
/* Configure ADC0_1 */ LPC_ADC0->CR = (1 << 1) | /* Select ADC0_1 pin for conversion */ (2 << 8) | /* 12MHz / (2+1) = 4MHz */ (1 << 21) ; /* ADC is operational */
/* Enable ADC0 Channel 1 interrupt */ LPC_ADC0->INTEN |= (1 << 1); NVIC_EnableIRQ (ADC0_IRQn);
return 0; }
int32_t ADC_Uninitialize (void) {
/* Disable ADC0 Channel 1 interrupt */ NVIC_DisableIRQ (ADC0_IRQn); LPC_ADC0->INTEN &= ~(1 << 1);
/* Disable ADC0 clock */ LPC_CCU1->CLK_APB3_ADC0_CFG = 0;
int32_t ADC_StartConversion (void) {
LPC_ADC0->CR |= (1 << 24); /* Start conversion */
int32_t ADC_ConversionDone (void) { return (AD_done ? 0 : -1); }
int32_t ADC_GetValue (void) {
if (AD_done) { AD_done = 0; return AD_last; } return -1; }
uint32_t ADC_GetResolution (void) { return ADC_RESOLUTION; }
void ADC0_IRQHandler (void) {
LPC_ADC0->GDR; /* Clear IRQ request flag */ AD_last = (LPC_ADC0->DR[1] >> 6) & 0x3FF; /* Read value and clear IRQ */ AD_done = 1; }
-----> LPC_ADC0->CR = (1 << 1) | (2 << 8) | (1 << 21) ; <-----
It says in the datasheet that for ADC0-CR where I can select the Channel for the AD0: "In software-controlled mode, only one of these bits should be 1. In hardware scan mode, any value containing 1 t0 8 ones is allowed"
My problem is, if I want to use Channel 0 and 1 of the ADC0 at the same time, how can I do this.
Thank you in advance for your help.
You can't use both channels _at the same time_.
You can either manually scan the two channels every second time.
Or you can configure the ADC to automatically switch between channel 0 and channel 1.
Now it should be quite trivial to continue with the information you have already retrieved from the data sheet...
Thank you very much for your answer.
If I want to switch between channel 0 and 1 automatically, do I need for example a "for-loop" from i=0 to 1. Should I modify the functions such as ADC_Initialize, ADC0_IRQHandler etc. as below, or do I need (additionally) a timer, or do I need a timer only if I want to get the ADC-value periodic in real-time.
I'm new in this things but I'm working since monday with this problems unsuccessfully. Luckily I find this forum to get answers to my questions with the hope.
For example:
int main(void) { int adcVal[1];
for(i=0; i<=1; i++) { ADC_Initialize(i); ADC_StartConversion();
while(ADC_ConversionDone () < 0); adcVal[i] = ADC_GetValue(); }
int ADC_Initialize (int i) {
/* Configure ADC0_1 */ LPC_ADC0->CR = (1 << i) | /* Select ADC0_1 pin for conversion */ (2 << 8) | /* 12MHz / (2+1) = 4MHz */ (1 << 21) ; /* ADC is operational */
/* Enable ADC0 Channel 1 interrupt */ LPC_ADC0->INTEN |= (1 << i); NVIC_EnableIRQ (ADC0_IRQn);
If you need a timer or not depends on how often you need ADC values. In some situations, you can adjust the conversion speed of the ADC so you just let the ADC create interrupts when there is data ready.
If you need the values at a slower pace, then you use an interrupt to start the conversions - so you might start a new batch conversion every 10 ms. And the ADC interrupt can then be configured to trig when all channels has been converted. Or you can have the ADC trig whenever a single channel has been converted.
With only two channels used, it's totally up to you if you want to use a loop or just have two read operations to read out the values for channel 0 and channel 1.
Okey thank you very much for your answer. I will test it.
So at last I finished the programm. Now I can use 2 channels. But my next problem is, how can I write a timer, if I want real-time values periodic from my adc. I want to load my program only once to the Controller but the adc should converts every time.
Have anyone an idea? I looked in the datasheed for the timer0, but it's very complicated. Do I combine timer0 with AD0 or can I write separately a timer0 in my main-program, which repeat the program code every time, without loading two times on controller?
The timer interrupt just activates an ADC conversion. Then the ADC interrupt will happen when the conversion is done.
So the main part of the program just initialize everything and then enters an infinite loop (it's possible to sleep the processor between each interrupt to reduce power consumption) while the timer and ADC interrupts gets repeatedly activated. The ADC interrupt could optionally set a volatile flag so the main loop knows it can pick up a new set of AD values from some volatile global variables.
There are thousands of examples of how to use the timers with different NXP processors - and lots of the NXP chips are very similar which makes it meaningful to look at examples from other NXP chips and then fall back to the datasheet for the exact register names and bit within the registers.
Okey thanks! I will look in other NXP examples. I hope I will find a similar programm.
Similar program? Why? It's enough to find an example that makes use of a timer to generate a periodic timer interrupt. There you can insert your own single instruction to start an ADC conversion.
The world has seen billions of different programs written. You don't get anywhere by expecting to always find examples doing the exact same thing. You use examples to view concepts. Then you adapt these concepts to your own requirements. So it's irrelevant if the timer example contains any use of any ADC or not. And it's irrelevant if any ADC sample contains any timer code or not. You are the one who must merge the concepts from different examples into a single program adapted to your specific needs. By reading and understanding the examples - after having cross-referenced the example code with the datasheet documentation.
Yes of course you are right, I meant, I hope I will find a similar program which use a timer.
Now I found in the demo programm of MCB4300 an timer:
/* Periodic timer definition */ void Timer_Callback (void const *arg); osTimerDef (PeriodicTimer, Timer_Callback); osTimerId TimerId;
void Timer_Callback (void const *arg) { static uint8_t TickLed, TickBtn, TickTh;
switch (TickLed++) { case 1: LEDOn = 1; break; case 6: LEDOff = 1; break; case 10: TickLed = 0; break; }
if (BtnTick == 0) { TickBtn++; if (TickBtn == 50) { BtnTick = 1; TickBtn = 0; } } else TickBtn = 0;
TickTh++; if (TickTh > 200) { TickTh = 0; Measure = 1; } }
int main (void) { TimerId = osTimerCreate (osTimer(PeriodicTimer), osTimerPeriodic, NULL);
if (TimerId) { osTimerStart (TimerId, 10); } }
But I can't understand how I can use it for my AD-Converter. I understand the main program. At first an TimerId wil created and then the timer starts with 10ms.
But what must I do in the Timer_Callback function. Have you any idea?
By the way, sorry for my english!
In the timer interrupt, you must do exactly what I have already suggested. Start the AD conversion. If you don't start any AD conversion, you will not get any ADC interrupt on conversion completion.
So, my program works. At first thank you for your help.
The timer works, and I see on the LCD periodic the adc-values. But when the timer expire, normally it has to start again. But it does not. Why? Do I need a while-loop?
void Timer_Callback (void const *arg); osTimerDef (Timer, Timer_Callback); osTimerId TimerId;
uint32_t exec; uint32_t timerDelay;
void Timer_Callback (void const *arg) {
int adcVal[3];
ADC_Initialize();
ADC_StartConversion();
while(ADC_ConversionDone() < 0);
adcVal[0] = ADC_GetValue(); }
int main(void) { int adcVal[3];
exec = 2; TimerId = osTimerCreate (osTimer(Timer), osTimerPeriodic, &exec);
if (TimerId != NULL) { timerDelay = 1000; osTimerStart (TimerId, timerDelay); } }
I have debug the program and the problem is caused by: os_idle_demon
But how can I solve this problem?
"It is running when no other task is ready to run (idle situation)"
/*--------------------------- os_idle_demon ---------------------------------*/
__task void os_idle_demon (void) { /* The idle demon is a system task. It is running when no other task is */ /* ready to run (idle situation). It must not terminate. Therefore it */ /* should contain at least an endless loop. */
for (;;) { _idle_(); /* enter low-power mode */ } } /* end of os_idle_demon */
Ok I have solve the problem. It had nothing to do with the timer.
Why do you initialize the ADC again and again?
It's enough that you initialize it once and then just start a new conversion when the timer ticks.
By the way - if you take a closer look at the information directly above the message input box when you are about to make new posts, there are clear instructions how to post source code.
It's much easier to read code problerly tagged:
#include <stdio.h> void main(void) { printf("Hello world!\n"); while (1) ; }
Sorry, with the source code, i will do it next time.
Yes you're right, I can initialize the ADC only once in the main-function. I will correct it.
By the way, I have an 10-Bit ADC. If I get the converted value from my ADC, I have to convert this value in voltage. The formula is below:
Voltage = (ADC-Value * Uref) / 1024
But where can I get the value of the reference voltage Uref (in datasheet?)? Or rather, which means Uref?