Introduction
This chapter describes the usage of the 12-bit analog-to-digital converter (ADC).
ADC is a successive-approximation register (SAR) ADC and the conversion result of ADC sample is stored in the result register and can be read on demand.
ADC characteristics are as follows:
Resolution:
12
bitENOB:
10
bitSample Frequency:
31.25
kHz ~250
kHz,166.67
kHz is recommended (default)Sample Capacitance:
2.4
pF ~2.93
pFReference Voltage: Fixed, cannot be externally supplied
Internal Impedance: About
500
kΩFIFO size:
64
x16
bitChannel switch list length:
1
~16
Description of channels:
Channel |
ADC channel ID |
Pin name |
Input voltage (V) |
---|---|---|---|
Normal channel |
CH0~CH6 |
PB13~PB19 |
0~3.3 |
BAT_MEAS channel |
CH7 |
BAT_MEAS |
0 ~ 5 |
Channel |
ADC channel ID |
Pin name |
Input voltage (V) |
---|---|---|---|
Normal channel |
CH0~CH5 |
PB5~PB0 |
0 ~ 3.3 |
Channel |
ADC channel ID |
Pin name |
Input voltage (V) |
---|---|---|---|
Normal channel |
CH0~CH5 |
PB5~PB0 |
0 ~ 3.3 |
Channel |
ADC channel ID |
Pin name |
Input voltage (V) |
---|---|---|---|
Normal channel |
CH0 ~ CH5 |
PA0 ~ PA5 |
0 ~ 1.8 |
BAT_MEAS channel |
CH6 |
BAT_MEAS |
0 ~ 5 |
Note
ADC multi-channel sample is time-division multiplexing on the same circuit. The sample frequency value listed above is specific to a single channel. When multiple channels are set in the channel switch list, the sample frequency will decrease accordingly.
ADC Sample Mode
ADC only supports sample mode: Continuous Mode. In this mode, ADC samples the channels in the channel list continuously and sequentially. Users can start it or stop it at any time. It is suitable for continuous voltage sampling over short periods.
ADC supports multi-channel sample. Users can configure channel switch list, including channel list length and channel switch order.
ADC works as: each time a trigger signal is generated, ADC will sample all the channels in channel switch list in order.
To use ADC continuous mode, the following steps are mandatory.
Enable clock and function of ADC module.
RCC_PeriphClockCmd(APBPeriph_ADC, APBPeriph_ADC_CLOCK, ENABLE); RCC_PeriphClockCmd(APBPeriph_CTC, APBPeriph_CTC_CLOCK, ENABLE);
Configure ADC pinmux according to the pinmux specification.
For example, call the following functions to use ADC0.
Pinmux_Config(ADC_CH0_PIN, PINMUX_FUNCTION_xxx); // Refer to pinmux table to get function ID PAD_InputCtrl(ADC_CH0_PIN, DISABLE); PAD_PullCtrl(ADC_CH0_PIN, GPIO_PuPd_NOPULL); PAD_SleepPullCtrl(ADC_CH0_PIN, GPIO_PuPd_NOPULL);
Note
Step2 could be skipped when user wants to use BAT_MEAS pin.
Initialize ADC parameters and modify ADC mode. ADC samples all the channels sequentially by default.
ADC_StructInit(ADC_InitTypeDef *ADC_InitStruct); ADC_InitStruct->ADC_OpMode = ADC_AUTO_MODE; // Set auto mode
Channel list and other parameters can be modified in ADC_InitStruct if needed.
For example, user sets channel switch list length to 3 and makes ADC sample ADC0/ADC2/ADC4 in order.
ADC_InitStruct->ADC_CvlistLen = 3 - 1; // Set channel switch list length ADC_InitStruct->ADC_Cvlist[0] = ADC_CH0; // Set the 1st channel ID ADC_InitStruct->ADC_Cvlist[1] = ADC_CH2; // Set the 2nd channel ID ADC_InitStruct->ADC_Cvlist[2] = ADC_CH4; // Set the 3rd channel ID
Initialize ADC and enable ADC.
ADC_Init(ADC_InitTypeDef *ADC_InitStruct); ADC_Cmd(ENABLE);
Read ADC sample data. Users can acquire ADC sample data in polling mode or interrupt mode.
Polling mode:
ADC_ReceiveBuf(u16 *pBuf, u32 len);
Interrupt mode:
ADC_INTConfig(ADC_BIT_IT_FIFO_OVER_EN | ADC_BIT_IT_FIFO_FULL_EN, ENABLE); InterruptRegister((IRQ_FUN)ADCIrqHandle, ADC_IRQ, NULL, INT_PRI_MIDDLE); InterruptEn(ADC_IRQ, INT_PRI_MIDDLE); ADC_AutoCSwCmd(ENABLE);
Where ADCIrqHandle is defined as
u32 ADCIrqHandle(void *para) { (void *)para; u32 status = ADC_GetISR(); if (status & ADC_BIT_IT_FIFO_FULL_STS) { while (ADC_Readable()) { u32 sample_value = ADC_Read(); // Read out sample value if (sample_cnt ++ > MAX_SAMPLE_CNT) { // Get enough sample value ADC_AutoCSwCmd(DISABLE); ADC_INTConfig(ADC_BIT_IT_FIFO_OVER_EN | ADC_BIT_IT_FIFO_FULL_EN, DISABLE); InterruptDis(ADC_IRQ); break; } } } ADC_INTClearPendingBits(status); return 0; }
ADC supports the following sample modes:
Continuous mode: ADC samples the channels in the channel list continuously and sequentially. Users can start it or stop it at any time. It is suitable for continuous voltage sampling over short periods.
Software-trigger mode: ADC outputs a set of sample data each time trigger is set by software.
Timer-trigger mode: ADC outputs a set of sample data when time expires.
ADC supports multi-channel sample. Users can configure channel switch list, including channel list length and channel switch order. ADC works as the following:
When sample is triggered for the first time, ADC samples the first channel in the channel list.
Each time a trigger signal is generated, ADC will automatically switch to the next channel.
When sample of the last channel in the channel list is completed, ADC will sample the first channel in the channel list again upon the next trigger signal.
To use ADC continuous mode, the following steps are mandatory.
Enable clock and function of ADC module.
RCC_PeriphClockCmd(APBPeriph_ADC, APBPeriph_ADC_CLOCK, ENABLE); RCC_PeriphClockCmd(APBPeriph_CTC, APBPeriph_CTC_CLOCK, ENABLE);
Configure ADC pinmux according to the pinmux specification.
For example, call the following functions to use ADC0.
Pinmux_Config(ADC_CH0_PIN, PINMUX_FUNCTION_xxx); // Refer to pinmux table to get function ID PAD_InputCtrl(ADC_CH0_PIN, DISABLE); PAD_PullCtrl(ADC_CH0_PIN, GPIO_PuPd_NOPULL); PAD_SleepPullCtrl(ADC_CH0_PIN, GPIO_PuPd_NOPULL);
Initialize ADC parameters and modify ADC mode. ADC samples all the channels sequentially by default.
ADC_StructInit(ADC_InitTypeDef *ADC_InitStruct); ADC_InitStruct->ADC_OpMode = ADC_AUTO_MODE; // Set auto mode
Channel list and other parameters can be modified in ADC_InitStruct if needed.
For example, user sets channel switch list length to 3 and makes ADC sample ADC0/ADC2/ADC4 in order.
ADC_InitStruct->ADC_CvlistLen = 3 - 1; // Set channel switch list length ADC_InitStruct->ADC_Cvlist[0] = ADC_CH0; // Set the 1st channel ID ADC_InitStruct->ADC_Cvlist[1] = ADC_CH2; // Set the 2nd channel ID ADC_InitStruct->ADC_Cvlist[2] = ADC_CH4; // Set the 3rd channel ID
Initialize ADC and enable ADC.
ADC_Init(ADC_InitTypeDef *ADC_InitStruct); ADC_Cmd(ENABLE);
Read ADC sample data. Users can acquire ADC sample data in polling mode or interrupt mode.
Polling mode:
ADC_ReceiveBuf(u16 *pBuf, u32 len);
Interrupt mode:
ADC_INTConfig(ADC_BIT_IT_FIFO_OVER_EN | ADC_BIT_IT_FIFO_FULL_EN, ENABLE); InterruptRegister((IRQ_FUN)ADCIrqHandle, ADC_IRQ, NULL, INT_PRI_MIDDLE); InterruptEn(ADC_IRQ, INT_PRI_MIDDLE); ADC_AutoCSwCmd(ENABLE);
Where ADCIrqHandle is defined as
u32 ADCIrqHandle(void *para) { (void *)para; u32 status = ADC_GetISR(); if (status & ADC_BIT_IT_FIFO_FULL_STS) { while (ADC_Readable()) { u32 sample_value = ADC_Read(); // Read out sample value if (sample_cnt ++ > MAX_SAMPLE_CNT) { // Get enough sample value ADC_AutoCSwCmd(DISABLE); ADC_INTConfig(ADC_BIT_IT_FIFO_OVER_EN | ADC_BIT_IT_FIFO_FULL_EN, DISABLE); InterruptDis(ADC_IRQ); break; } } } ADC_INTClearPendingBits(status); return 0; }
To use ADC software-trigger mode, the following steps are mandatory.
Enable clock and function of ADC module.
RCC_PeriphClockCmd(APBPeriph_ADC, APBPeriph_ADC_CLOCK, ENABLE); RCC_PeriphClockCmd(APBPeriph_CTC, APBPeriph_CTC_CLOCK, ENABLE);
Configure ADC pinmux according to the pinmux specification.
For example, call the following functions to use ADC0.
Pinmux_Config(ADC_CH0_PIN, PINMUX_FUNCTION_xxx); // Refer to pinmux table to get function ID PAD_InputCtrl(ADC_CH0_PIN, DISABLE); PAD_PullCtrl(ADC_CH0_PIN, GPIO_PuPd_NOPULL); PAD_SleepPullCtrl(ADC_CH0_PIN, GPIO_PuPd_NOPULL);
Initialize ADC parameters and modify ADC mode. ADC samples all the channels sequentially by default.
ADC_StructInit(ADC_InitTypeDef *ADC_InitStruct); ADC_InitStruct->ADC_OpMode = ADC_SW_TRI_MODE; // Set software trigger mode
Channel list and other parameters can be modified in ADC_InitStruct if needed.
For example, user sets channel switch list length to 3 and makes ADC sample ADC0/ADC2/ADC4 in order.
ADC_InitStruct->ADC_CvlistLen = 3 - 1; // Set channel switch list length ADC_InitStruct->ADC_Cvlist[0] = ADC_CH0; // Set the 1st channel ID ADC_InitStruct->ADC_Cvlist[1] = ADC_CH2; // Set the 2nd channel ID ADC_InitStruct->ADC_Cvlist[2] = ADC_CH4; // Set the 3rd channel ID
Initialize ADC and enable ADC.
ADC_Init(ADC_InitTypeDef *ADC_InitStruct); ADC_Cmd(ENABLE);
Read ADC sample data.
ADC_SWTrigCmd(ENABLE); while(ADC_Readable() == 0); ADC_SWTrigCmd(DISABLE); u32 sample_data = ADC_Read();
To use ADC timer-trigger mode, the following steps are mandatory.
Enable clock and function of ADC module.
RCC_PeriphClockCmd(APBPeriph_ADC, APBPeriph_ADC_CLOCK, ENABLE); RCC_PeriphClockCmd(APBPeriph_CTC, APBPeriph_CTC_CLOCK, ENABLE);
Configure ADC pinmux according to the pinmux specification.
For example, call the following functions to use ADC0.
Pinmux_Config(ADC_CH0_PIN, PINMUX_FUNCTION_xxx); // Refer to pinmux table to get function ID PAD_InputCtrl(ADC_CH0_PIN, DISABLE); PAD_PullCtrl(ADC_CH0_PIN, GPIO_PuPd_NOPULL); PAD_SleepPullCtrl(ADC_CH0_PIN, GPIO_PuPd_NOPULL);
Initialize ADC parameters and modify ADC mode. ADC samples all the channels sequentially by default.
ADC_StructInit(ADC_InitTypeDef *ADC_InitStruct); ADC_InitStruct->ADC_OpMode = ADC_TIM_TRI_MODE; // Set timer trigger mode
Channel list and other parameters can be modified in ADC_InitStruct if needed.
For example, user sets channel switch list length to 3 and makes ADC sample ADC0/ADC2/ADC4 in order.
ADC_InitStruct->ADC_CvlistLen = 3 - 1; // Set channel switch list length ADC_InitStruct->ADC_Cvlist[0] = ADC_CH0; // Set the 1st channel ID ADC_InitStruct->ADC_Cvlist[1] = ADC_CH2; // Set the 2nd channel ID ADC_InitStruct->ADC_Cvlist[2] = ADC_CH4; // Set the 3rd channel ID
Initialize ADC and enable ADC.
ADC_Init(ADC_InitTypeDef *ADC_InitStruct); ADC_Cmd(ENABLE);
Select basic timer index and set timer period. Read ADC sample data in ADC IRQ handler.
ADC_INTConfig(ADC_BIT_IT_CV_END_EN | ADC_BIT_IT_CVLIST_END_EN, ENABLE); InterruptRegister((IRQ_FUN)ADCIrqHandle, ADC_IRQ, tim_idx, INT_PRI_MIDDLE); InterruptEn(ADC_IRQ, INT_PRI_MIDDLE); ADC_TimerTrigCmd(tim_idx, period, ENABLE);
Where ADCIrqHandle is defined as
u32 ADCIrqHandle(void *para) { u32 tim_idx = (u32)para; u32 status = ADC_GetISR(); u32 value; if (status & ADC_BIT_IT_CV_END_STS) { while (ADC_Readable() == 0); value = ADC_Read(); printf("ch:%d, data:%d\n", ADC_GET_CH_NUM_GLOBAL(value), ADC_GET_DATA_GLOBAL(value)); } if (status & ADC_BIT_IT_CVLIST_END_STS) { ADC_TimerTrigCmd(tim_idx, 0, DISABLE); printf("All conversion finished in channel list\n"); } ADC_INTClearPendingBits(status); return 0; }
ADC supports the following sample modes:
Continuous mode: ADC samples the channels in the channel list continuously and sequentially. Users can start it or stop it at any time. It is suitable for continuous voltage sampling over short periods.
Software-trigger mode: ADC outputs a set of sample data each time trigger is set by software.
Timer-trigger mode: ADC outputs a set of sample data when time expires.
ADC supports multi-channel sample. Users can configure channel switch list, including channel list length and channel switch order. ADC works as the following:
When sample is triggered for the first time, ADC samples the first channel in the channel list.
Each time a trigger signal is generated, ADC will automatically switch to the next channel.
When sample of the last channel in the channel list is completed, ADC will sample the first channel in the channel list again upon the next trigger signal.
To use ADC continuous mode, the following steps are mandatory.
Enable clock and function of ADC module.
RCC_PeriphClockCmd(APBPeriph_ADC, APBPeriph_ADC_CLOCK, ENABLE); RCC_PeriphClockCmd(APBPeriph_CTC, APBPeriph_CTC_CLOCK, ENABLE);
Configure ADC pinmux according to the pinmux specification.
For example, call the following functions to use ADC0.
Pinmux_Config(ADC_CH0_PIN, PINMUX_FUNCTION_xxx); // Refer to pinmux table to get function ID PAD_InputCtrl(ADC_CH0_PIN, DISABLE); PAD_PullCtrl(ADC_CH0_PIN, GPIO_PuPd_NOPULL); PAD_SleepPullCtrl(ADC_CH0_PIN, GPIO_PuPd_NOPULL);
Initialize ADC parameters and modify ADC mode. ADC samples all the channels sequentially by default.
ADC_StructInit(ADC_InitTypeDef *ADC_InitStruct); ADC_InitStruct->ADC_OpMode = ADC_AUTO_MODE; // Set auto mode
Channel list and other parameters can be modified in ADC_InitStruct if needed.
For example, user sets channel switch list length to 3 and makes ADC sample ADC0/ADC2/ADC4 in order.
ADC_InitStruct->ADC_CvlistLen = 3 - 1; // Set channel switch list length ADC_InitStruct->ADC_Cvlist[0] = ADC_CH0; // Set the 1st channel ID ADC_InitStruct->ADC_Cvlist[1] = ADC_CH2; // Set the 2nd channel ID ADC_InitStruct->ADC_Cvlist[2] = ADC_CH4; // Set the 3rd channel ID
Initialize ADC and enable ADC.
ADC_Init(ADC_InitTypeDef *ADC_InitStruct); ADC_Cmd(ENABLE);
Read ADC sample data. Users can acquire ADC sample data in polling mode or interrupt mode.
Polling mode:
ADC_ReceiveBuf(u16 *pBuf, u32 len);
Interrupt mode:
ADC_INTConfig(ADC_BIT_IT_FIFO_OVER_EN | ADC_BIT_IT_FIFO_FULL_EN, ENABLE); InterruptRegister((IRQ_FUN)ADCIrqHandle, ADC_IRQ, NULL, INT_PRI_MIDDLE); InterruptEn(ADC_IRQ, INT_PRI_MIDDLE); ADC_AutoCSwCmd(ENABLE);
Where ADCIrqHandle is defined as
u32 ADCIrqHandle(void *para) { (void *)para; u32 status = ADC_GetISR(); if (status & ADC_BIT_IT_FIFO_FULL_STS) { while (ADC_Readable()) { u32 sample_value = ADC_Read(); // Read out sample value if (sample_cnt ++ > MAX_SAMPLE_CNT) { // Get enough sample value ADC_AutoCSwCmd(DISABLE); ADC_INTConfig(ADC_BIT_IT_FIFO_OVER_EN | ADC_BIT_IT_FIFO_FULL_EN, DISABLE); InterruptDis(ADC_IRQ); break; } } } ADC_INTClearPendingBits(status); return 0; }
To use ADC software-trigger mode, the following steps are mandatory.
Enable clock and function of ADC module.
RCC_PeriphClockCmd(APBPeriph_ADC, APBPeriph_ADC_CLOCK, ENABLE); RCC_PeriphClockCmd(APBPeriph_CTC, APBPeriph_CTC_CLOCK, ENABLE);
Configure ADC pinmux according to the pinmux specification.
For example, call the following functions to use ADC0.
Pinmux_Config(ADC_CH0_PIN, PINMUX_FUNCTION_xxx); // Refer to pinmux table to get function ID PAD_InputCtrl(ADC_CH0_PIN, DISABLE); PAD_PullCtrl(ADC_CH0_PIN, GPIO_PuPd_NOPULL); PAD_SleepPullCtrl(ADC_CH0_PIN, GPIO_PuPd_NOPULL);
Initialize ADC parameters and modify ADC mode. ADC samples all the channels sequentially by default.
ADC_StructInit(ADC_InitTypeDef *ADC_InitStruct); ADC_InitStruct->ADC_OpMode = ADC_SW_TRI_MODE; // Set software trigger mode
Channel list and other parameters can be modified in ADC_InitStruct if needed.
For example, user sets channel switch list length to 3 and makes ADC sample ADC0/ADC2/ADC4 in order.
ADC_InitStruct->ADC_CvlistLen = 3 - 1; // Set channel switch list length ADC_InitStruct->ADC_Cvlist[0] = ADC_CH0; // Set the 1st channel ID ADC_InitStruct->ADC_Cvlist[1] = ADC_CH2; // Set the 2nd channel ID ADC_InitStruct->ADC_Cvlist[2] = ADC_CH4; // Set the 3rd channel ID
Initialize ADC and enable ADC.
ADC_Init(ADC_InitTypeDef *ADC_InitStruct); ADC_Cmd(ENABLE);
Read ADC sample data.
ADC_SWTrigCmd(ENABLE); while(ADC_Readable() == 0); ADC_SWTrigCmd(DISABLE); u32 sample_data = ADC_Read();
To use ADC timer-trigger mode, the following steps are mandatory.
Enable clock and function of ADC module.
RCC_PeriphClockCmd(APBPeriph_ADC, APBPeriph_ADC_CLOCK, ENABLE); RCC_PeriphClockCmd(APBPeriph_CTC, APBPeriph_CTC_CLOCK, ENABLE);
Configure ADC pinmux according to the pinmux specification.
For example, call the following functions to use ADC0.
Pinmux_Config(ADC_CH0_PIN, PINMUX_FUNCTION_xxx); // Refer to pinmux table to get function ID PAD_InputCtrl(ADC_CH0_PIN, DISABLE); PAD_PullCtrl(ADC_CH0_PIN, GPIO_PuPd_NOPULL); PAD_SleepPullCtrl(ADC_CH0_PIN, GPIO_PuPd_NOPULL);
Initialize ADC parameters and modify ADC mode. ADC samples all the channels sequentially by default.
ADC_StructInit(ADC_InitTypeDef *ADC_InitStruct); ADC_InitStruct->ADC_OpMode = ADC_TIM_TRI_MODE; // Set timer trigger mode
Channel list and other parameters can be modified in ADC_InitStruct if needed.
For example, user sets channel switch list length to 3 and makes ADC sample ADC0/ADC2/ADC4 in order.
ADC_InitStruct->ADC_CvlistLen = 3 - 1; // Set channel switch list length ADC_InitStruct->ADC_Cvlist[0] = ADC_CH0; // Set the 1st channel ID ADC_InitStruct->ADC_Cvlist[1] = ADC_CH2; // Set the 2nd channel ID ADC_InitStruct->ADC_Cvlist[2] = ADC_CH4; // Set the 3rd channel ID
Initialize ADC and enable ADC.
ADC_Init(ADC_InitTypeDef *ADC_InitStruct); ADC_Cmd(ENABLE);
Select basic timer index and set timer period. Read ADC sample data in ADC IRQ handler.
ADC_INTConfig(ADC_BIT_IT_CV_END_EN | ADC_BIT_IT_CVLIST_END_EN, ENABLE); InterruptRegister((IRQ_FUN)ADCIrqHandle, ADC_IRQ, tim_idx, INT_PRI_MIDDLE); InterruptEn(ADC_IRQ, INT_PRI_MIDDLE); ADC_TimerTrigCmd(tim_idx, period, ENABLE);
Where ADCIrqHandle is defined as
u32 ADCIrqHandle(void *para) { u32 tim_idx = (u32)para; u32 status = ADC_GetISR(); u32 value; if (status & ADC_BIT_IT_CV_END_STS) { while (ADC_Readable() == 0); value = ADC_Read(); printf("ch:%d, data:%d\n", ADC_GET_CH_NUM_GLOBAL(value), ADC_GET_DATA_GLOBAL(value)); } if (status & ADC_BIT_IT_CVLIST_END_STS) { ADC_TimerTrigCmd(tim_idx, 0, DISABLE); printf("All conversion finished in channel list\n"); } ADC_INTClearPendingBits(status); return 0; }
ADC supports the following sample modes:
Continuous mode: ADC samples the channels in the channel list continuously and sequentially. Users can start it or stop it at any time. It is suitable for continuous voltage sampling over short periods.
Software-trigger mode: ADC outputs a set of sample data each time trigger is set by software.
Timer-trigger mode: ADC outputs a set of sample data when time expires.
ADC supports multi-channel sample. Users can configure channel switch list, including channel list length and channel switch order. ADC works as the following:
When sample is triggered for the first time, ADC samples the first channel in the channel list.
Each time a trigger signal is generated, ADC will automatically switch to the next channel.
When sample of the last channel in the channel list is completed, ADC will sample the first channel in the channel list again upon the next trigger signal.
To use ADC continuous mode, the following steps are mandatory.
Enable clock and function of ADC module.
RCC_PeriphClockCmd(APBPeriph_ADC, APBPeriph_ADC_CLOCK, ENABLE); RCC_PeriphClockCmd(APBPeriph_CTC, APBPeriph_CTC_CLOCK, ENABLE);
Configure ADC pinmux according to the pinmux specification.
For example, call the following functions to use ADC0.
Pinmux_Config(ADC_CH0_PIN, PINMUX_FUNCTION_xxx); // Refer to pinmux table to get function ID PAD_InputCtrl(ADC_CH0_PIN, DISABLE); PAD_PullCtrl(ADC_CH0_PIN, GPIO_PuPd_NOPULL); PAD_SleepPullCtrl(ADC_CH0_PIN, GPIO_PuPd_NOPULL);
Note
Step2 could be skipped when user wants to use BAT_MEAS pin.
Initialize ADC parameters and modify ADC mode. ADC samples all the channels sequentially by default.
ADC_StructInit(ADC_InitTypeDef *ADC_InitStruct); ADC_InitStruct->ADC_OpMode = ADC_AUTO_MODE; // Set auto mode
Channel list and other parameters can be modified in ADC_InitStruct if needed.
For example, user sets channel switch list length to 3 and makes ADC sample ADC0/ADC2/ADC4 in order.
ADC_InitStruct->ADC_CvlistLen = 3 - 1; // Set channel switch list length ADC_InitStruct->ADC_Cvlist[0] = ADC_CH0; // Set the 1st channel ID ADC_InitStruct->ADC_Cvlist[1] = ADC_CH2; // Set the 2nd channel ID ADC_InitStruct->ADC_Cvlist[2] = ADC_CH4; // Set the 3rd channel ID
Initialize ADC and enable ADC.
ADC_Init(ADC_InitTypeDef *ADC_InitStruct); ADC_Cmd(ENABLE);
Read ADC sample data. Users can acquire ADC sample data in polling mode or interrupt mode.
Polling mode:
ADC_ReceiveBuf(u16 *pBuf, u32 len);
Interrupt mode:
ADC_INTConfig(ADC_BIT_IT_FIFO_OVER_EN | ADC_BIT_IT_FIFO_FULL_EN, ENABLE); InterruptRegister((IRQ_FUN)ADCIrqHandle, ADC_IRQ, NULL, INT_PRI_MIDDLE); InterruptEn(ADC_IRQ, INT_PRI_MIDDLE); ADC_AutoCSwCmd(ENABLE);
Where ADCIrqHandle is defined as
u32 ADCIrqHandle(void *para) { (void *)para; u32 status = ADC_GetISR(); if (status & ADC_BIT_IT_FIFO_FULL_STS) { while (ADC_Readable()) { u32 sample_value = ADC_Read(); // Read out sample value if (sample_cnt ++ > MAX_SAMPLE_CNT) { // Get enough sample value ADC_AutoCSwCmd(DISABLE); ADC_INTConfig(ADC_BIT_IT_FIFO_OVER_EN | ADC_BIT_IT_FIFO_FULL_EN, DISABLE); InterruptDis(ADC_IRQ); break; } } } ADC_INTClearPendingBits(status); return 0; }
To use ADC software-trigger mode, the following steps are mandatory.
Enable clock and function of ADC module.
RCC_PeriphClockCmd(APBPeriph_ADC, APBPeriph_ADC_CLOCK, ENABLE); RCC_PeriphClockCmd(APBPeriph_CTC, APBPeriph_CTC_CLOCK, ENABLE);
Configure ADC pinmux according to the pinmux specification.
For example, call the following functions to use ADC0.
Pinmux_Config(ADC_CH0_PIN, PINMUX_FUNCTION_xxx); // Refer to pinmux table to get function ID PAD_InputCtrl(ADC_CH0_PIN, DISABLE); PAD_PullCtrl(ADC_CH0_PIN, GPIO_PuPd_NOPULL); PAD_SleepPullCtrl(ADC_CH0_PIN, GPIO_PuPd_NOPULL);
Note
Step2 could be skipped when user wants to use BAT_MEAS pin.
Initialize ADC parameters and modify ADC mode. ADC samples all the channels sequentially by default.
ADC_StructInit(ADC_InitTypeDef *ADC_InitStruct); ADC_InitStruct->ADC_OpMode = ADC_SW_TRI_MODE; // Set software trigger mode
Channel list and other parameters can be modified in ADC_InitStruct if needed.
For example, user sets channel switch list length to 3 and makes ADC sample ADC0/ADC2/ADC4 in order.
ADC_InitStruct->ADC_CvlistLen = 3 - 1; // Set channel switch list length ADC_InitStruct->ADC_Cvlist[0] = ADC_CH0; // Set the 1st channel ID ADC_InitStruct->ADC_Cvlist[1] = ADC_CH2; // Set the 2nd channel ID ADC_InitStruct->ADC_Cvlist[2] = ADC_CH4; // Set the 3rd channel ID
Initialize ADC and enable ADC.
ADC_Init(ADC_InitTypeDef *ADC_InitStruct); ADC_Cmd(ENABLE);
Read ADC sample data.
ADC_SWTrigCmd(ENABLE); while(ADC_Readable() == 0); ADC_SWTrigCmd(DISABLE); u32 sample_data = ADC_Read();
To use ADC timer-trigger mode, the following steps are mandatory.
Enable clock and function of ADC module.
RCC_PeriphClockCmd(APBPeriph_ADC, APBPeriph_ADC_CLOCK, ENABLE); RCC_PeriphClockCmd(APBPeriph_CTC, APBPeriph_CTC_CLOCK, ENABLE);
Configure ADC pinmux according to the pinmux specification.
For example, call the following functions to use ADC0.
Pinmux_Config(ADC_CH0_PIN, PINMUX_FUNCTION_xxx); // Refer to pinmux table to get function ID PAD_InputCtrl(ADC_CH0_PIN, DISABLE); PAD_PullCtrl(ADC_CH0_PIN, GPIO_PuPd_NOPULL); PAD_SleepPullCtrl(ADC_CH0_PIN, GPIO_PuPd_NOPULL);
Note
Step2 could be skipped when user wants to use BAT_MEAS pin.
Initialize ADC parameters and modify ADC mode. ADC samples all the channels sequentially by default.
ADC_StructInit(ADC_InitTypeDef *ADC_InitStruct); ADC_InitStruct->ADC_OpMode = ADC_TIM_TRI_MODE; // Set timer trigger mode
Channel list and other parameters can be modified in ADC_InitStruct if needed.
For example, user sets channel switch list length to 3 and makes ADC sample ADC0/ADC2/ADC4 in order.
ADC_InitStruct->ADC_CvlistLen = 3 - 1; // Set channel switch list length ADC_InitStruct->ADC_Cvlist[0] = ADC_CH0; // Set the 1st channel ID ADC_InitStruct->ADC_Cvlist[1] = ADC_CH2; // Set the 2nd channel ID ADC_InitStruct->ADC_Cvlist[2] = ADC_CH4; // Set the 3rd channel ID
Initialize ADC and enable ADC.
ADC_Init(ADC_InitTypeDef *ADC_InitStruct); ADC_Cmd(ENABLE);
Select basic timer index and set timer period. Read ADC sample data in ADC IRQ handler.
ADC_INTConfig(ADC_BIT_IT_CV_END_EN | ADC_BIT_IT_CVLIST_END_EN, ENABLE); InterruptRegister((IRQ_FUN)ADCIrqHandle, ADC_IRQ, tim_idx, INT_PRI_MIDDLE); InterruptEn(ADC_IRQ, INT_PRI_MIDDLE); ADC_TimerTrigCmd(tim_idx, period, ENABLE);
Where ADCIrqHandle is defined as
u32 ADCIrqHandle(void *para) { u32 tim_idx = (u32)para; u32 status = ADC_GetISR(); u32 value; if (status & ADC_BIT_IT_CV_END_STS) { while (ADC_Readable() == 0); value = ADC_Read(); printf("ch:%d, data:%d\n", ADC_GET_CH_NUM_GLOBAL(value), ADC_GET_DATA_GLOBAL(value)); } if (status & ADC_BIT_IT_CVLIST_END_STS) { ADC_TimerTrigCmd(tim_idx, 0, DISABLE); printf("All conversion finished in channel list\n"); } ADC_INTClearPendingBits(status); return 0; }
Note
ADC sample result consists of 4-bit Channel ID and 12-bit sample data. User could get them by the following macros:
ADC_GET_CH_NUM_GLOBAL(sample_data): Get 4-bit channel ID
ADC_GET_DATA_GLOBAL(sample_data): Get 12-bit sample value
ADC Calibration
ADC Voltage Calibration
To improve the linearity of ADC input/output characteristics, the chip undergoes nonlinear calibration during factory programming. This calibration reduces ADC gain errors and offset errors.
The ADC calibration employs a 5-point curve fitting method, calculating calibration parameters A/B/C based on the principle of minimum variance. After calibration, each chip stores its unique parameters A/B/C in OTP memory.
After obtaining ADC sampled values, calculate the current voltage (in mV) using the following formula, where \(x\) represents the ADC sampled value:
\[\begin{split}\begin{aligned} y &= ax^2 + bx + c \\ &= \frac{A}{2^{26}}x^2 + \frac{B}{2^{15}}x + \frac{C}{2^{6}} \end{aligned}\end{split}\]Normal channels and BAT_MEAS channel have different calibration parameters. Users can directly retrieve voltages via the following APIs:
ADC_GetVoltage(u32 chan_data): Get normal voltage value.
ADC_GetVBATVoltage(u32 vbat_data): Get battery voltage value.
ADC Internal Resistor Calibration
Variations in the ADC internal resistor
R
among chips affect measurement accuracy:ADC internal resistor
Ideal voltage: \(V_{ideal} = \frac{V_{cc} \times R_{2}}{R_{1} + R_{2}}\)
Actual voltage: \(V_{actual} = \frac{V_{cc} \times R_{2}//R}{R_{1} + R_{2}//R}\)
Relationship: Due to voltage division of internal resistor, \(V_{actual} < V_{ideal}\)
Conclusion: Larger \(\frac{R_{1} \times R_{2}}{R}\) leads to greater deviation between actual and ideal voltages.
Users can directly retrieve internal resistor value via
ADC_GetInterR()
ADC Digital Comparator
The ADC includes a digital comparator that generates an interrupt when the sampled value of a specified channel falls within a configured range. Users can perform specific operations (e.g., reading the current voltage) in the interrupt handler.
Digital comparator can work well under all the ADC sample modes.
Digital comparator supports the following comparison modes:
Below Low Threshold
Above High Threshold
Within Range: Greater than or equal to low threshold AND less than or equal to high threshold
Outside Range: Less than low threshold OR greater than high threshold
Note
Low threshold and high threshold values must be in the range 0
to 4095
. Low threshold should not be greater than high threshold.
For example, if user wants to use ADC digital comparator under continuous sample mode, the following steps are mandatory.
Enable clock and function of ADC module as shown above.
Configure ADC pinmux according to the pinmux specification as shown above.
Assign a channel and configure the low/high thresholds and comparison mode of digital comparator.
For example, call the function below to set comparison mode of ADC0. An interrupt will arise when the sampled value is between 2000 and 3000.
ADC_SetComp(ADC_CH0, 3000, 2000, ADC_COMP_WITHIN_THL_AND_THH); InterruptRegister((IRQ_FUN)ADCIrqHandle, ADC_IRQ, NULL, INT_PRI_MIDDLE); InterruptEn(ADC_IRQ, INT_PRI_MIDDLE);
Where ADCIrqHandle is defined as
u32 ADCIrqHandle(void *para) { (void)para; u32 status = ADC_GetISR(); u32 value; if (status & ADC_BIT_IT_COMP_ALL_STS) { printf("ISR:0x%x, Comp status:0x%x\n", status, ADC->ADC_COMP_STS); // ADC sample data is in predefined range while (ADC_Readable() == 0); value = ADC_Read(); } ADC_INTClearPendingBits(status); return 0; }
Initialize ADC parameters and modify ADC mode as shown above.
Channel list and other parameters can be modified in
ADC_InitStruct
if needed.Initialize ADC and enable ADC as shown above.
It is suggested to read ADC sample result by polling mode.
ADC Troubleshooting
Sample Offset
Phenomenon |
Sample offset |
Cause |
|
Solution |
|
Note
Calibrate the whole application circuit together if possible.
Refer to the hardware design specification to obtain ADC internal impedance.
Channel Interference
Phenomenon |
Channel interference |
Cause |
|
Solution |
|
Sampling Voltage Interference
Phenomenon |
Sampling voltage interference |
Cause |
Unreasonable PCB layout |
Solution |
|