/
adc.c
142 lines (114 loc) · 3.43 KB
/
adc.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/**
@file adc.c
@brief ADC routines
@author Matej Kogovsek
@copyright LGPL 2.1
@note This file is part of mat-stm32f1-lib
*/
#include <stm32f10x.h>
#include <stm32f10x_adc.h>
#define ADC_NCH 18 /**< Max number of ADC channels */
static volatile uint16_t adc_res[ADC_NCH]; /**< Buffer of averaged results for all possible channels */
static volatile uint8_t adc_curch; /**< Channel currently converting */
static uint32_t adc_ench; /**< Enabled channel */
static uint8_t adc_freerun = 0; /**< Freerun or not (bool) */
static uint8_t adc_navg = 16; /**< how many ADC samples to average */
/**
@brief Init ADC.
@param[in] ench Bitmask of enabled channels (bits 0 to 17)
@param[in] navg Number of samples to average (keep this a power of two, i.e. 1,2,4,8,16,...)
*/
void adc_init(uint32_t ench, uint8_t navg)
{
adc_ench = ench;
adc_curch = 255; // set to invalid value
adc_navg = navg;
if( adc_navg == 0 ) adc_navg = 1;
// PCLK2 is the APB2 clock, ADCCLK = PCLK2/6 = 72/6 = 12MHz
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
// Enable ADC1 clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
// ADC1 Configuration
ADC_InitTypeDef adis;
adis.ADC_Mode = ADC_Mode_Independent;
adis.ADC_ScanConvMode = DISABLE;
adis.ADC_ContinuousConvMode = DISABLE;
adis.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
adis.ADC_DataAlign = ADC_DataAlign_Right;
adis.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &adis);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while( ADC_GetResetCalibrationStatus(ADC1) );
ADC_StartCalibration(ADC1);
while( ADC_GetCalibrationStatus(ADC1) );
ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);
// ADC interrupt setup
NVIC_InitTypeDef ictd;
ictd.NVIC_IRQChannel = 18; // ADC1_IRQn or ADC1_2_IRQn
ictd.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&ictd);
}
/**
@brief Start next conversion.
Use if you want to control when conversions are started. Do not call if free running.
*/
void adc_startnext(void)
{
//if( ADC_GetSoftwareStartConvStatus(ADC1) == SET ) return;
if( adc_ench ) { // sanity check
do {
adc_curch++;
if( adc_curch >= ADC_NCH ) adc_curch = 0;
} while( (adc_ench & (1 << adc_curch)) == 0 );
ADC_RegularChannelConfig(ADC1, adc_curch, 1, ADC_SampleTime_13Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
}
/**
@brief Start free running ADC conversions.
After a conversion is finished, a new conversion is automatically started.
*/
void adc_startfree(void)
{
adc_freerun = 1;
adc_startnext();
}
/**
@brief Stop free running ADC conversions.
*/
void adc_stopfree(void)
{
adc_freerun = 0;
}
/**
@brief Get a channel's averaged ADC value.
@param[in] ch Channel to get
@return Averaged ADC value for channel
*/
uint16_t adc_get(const uint8_t ch)
{
if( ch >= ADC_NCH ) return 0;
uint32_t g = __get_PRIMASK();
__disable_irq();
uint16_t r = adc_res[ch];
__set_PRIMASK(g);
return r;
}
/** @privatesection */
void ADC1_2_IRQHandler(void)
{
static uint16_t sum = 0;
static uint8_t samp = 0;
sum += ADC_GetConversionValue(ADC1); // add new conversion result to sum
samp++; // increase sample counter
if( samp == adc_navg ) {
sum /= adc_navg; // calc average
adc_res[adc_curch] = sum; // store averaged conversion result
samp = 0; // reset variables
sum = 0;
if( adc_freerun ) adc_startnext();
} else {
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
}