-
Notifications
You must be signed in to change notification settings - Fork 0
/
my_adc.c
92 lines (85 loc) · 2.4 KB
/
my_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
#include "my_adc.h"
#include <avr/io.h>
#include <Arduino.h>
#include <util/atomic.h>
/*
* Callback which will be invoked with the result of an ADC conversion. If
* no conversion is currently running, its value is NULL.
*
* This is also used as a "flag" which tells us whether a conversion is running.
* In theory, we could rely on the flag ADCS on ADCSRA; however, when this flag
* is cleared, the callback might not have been scheduled yet, and thus the ADC
* conversion might not be over from a software perspective.
*/
static volatile my_task__func_t _conversion_complete_func = NULL;
ISR(ADC_vect)
{
my_task__queue_new(
(my_task__task_t){
.func = _conversion_complete_func, .arg._ushort = ADC
}
);
_conversion_complete_func = NULL;
}
void my_adc__init()
{
/*
* Reference voltage AV_CC, conversion result is right-adjusted, reading from
* ADC0.
*/
ADMUX = bit(REFS0);
/* ADC enabled, no conversion started (yet), conversion complete interrupt
* enabled, ADC clock divider 128.
*/
ADCSRA = bit(ADEN) | bit(ADIE) | bit(ADPS2) | bit(ADPS1) | bit(ADPS0);
/*
* Digital input buffers disabled (I won't need them, might as well save on
* power).
*/
DIDR0 =
bit(ADC5D) | bit(ADC4D) | bit(ADC3D) | bit(ADC2D) | bit(ADC1D) | bit(ADC0D);
}
return_status_byte_t my_adc__start_conversion(my_task__func_t func)
{
return_status_byte_t status = return_status__ok;
// A NULL function pointer is not valid.
if (NULL == func)
{
status = return_status__my_adc__bad_parameter;
}
if (return_status__ok == status)
{
/*
* If no callback was saved previously, that means there's no active
* conversion: save our callback.
*
* Function pointers are 16 bit wide and cannot be read atomically, thus we
* need an atomic block for performing the aforementioned check.
*/
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
if (NULL != _conversion_complete_func)
{
status = return_status__my_adc__other;
}
else
{
_conversion_complete_func = func;
}
}
}
if (return_status__ok == status)
{
// Start the ADC conversion.
bitSet(ADCSRA, ADSC);
}
return status;
}
bool my_adc__is_conversion_active()
{
/*
* Keep in mind that we're running inside an outer atomic block, so we can
* read a function pointer without any problems.
*/
return NULL != _conversion_complete_func;
}