-
Notifications
You must be signed in to change notification settings - Fork 0
/
adc.c
189 lines (166 loc) · 4.83 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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/*
* device_adc.c
*
* Created: 7/18/2013 10:26:40 PM
* Author: Greg Cook
*/
#include <stdlib.h>
#include <assert.h>
#include <avr/interrupt.h>
#include <util/atomic.h>
#include "adc.h"
// function prototypes for task states
static task_slice_result_t adc_state_init(task_t*);
static task_slice_result_t adc_state_start(task_t*);
static task_slice_result_t adc_state_sample(task_t*);
static task_slice_result_t adc_state_record(task_t*);
static const task_slice_callback_fp const adc_task_slices[] = {
adc_state_init,
adc_state_start,
adc_state_sample,
adc_state_record
};
static adc_dev_t adc_device = {
.lock.owner = NULL,
.current = NULL,
.regs = (adc_atmega_regs_t*)0x78
};
adc_dev_t * const ADC_DEV = &adc_device;
static task_t adc_tasks[8];
static adc_task_data_t adc_task_data[8];
const adc_channel_t ADC_CH0 = 0;
const adc_channel_t ADC_CH1 = 1;
const adc_channel_t ADC_CH2 = 2;
const adc_channel_t ADC_CH3 = 3;
const adc_channel_t ADC_CH4 = 4;
const adc_channel_t ADC_CH5 = 5;
const adc_channel_t ADC_CH6 = 6;
const adc_channel_t ADC_CH7 = 7;
#define TASK_STATE_START 1
#define TASK_STATE_SAMPLE 2
#define TASK_STATE_RECORD 3
/**
* adc task callback to initialize the channel
*
* @param task task object pased to the callback by the scheduler
* @return next state and schedule value
*/
static task_slice_result_t adc_state_init(task_t *task) { return (task_slice_result_t){0,0}; }
/**
* adc task callback to begin lock the device and set the channel
*
* @param task task object pased to the callback by the scheduler
* @return next state and schedule value
*/
static task_slice_result_t adc_state_start(task_t *task) {
task_slice_result_t result = { TASK_STATE_START, TASK_SCHED_IMMED };
if (!task->enabled) {
result.sched = TASK_END;
}
else {
// try to get lock
if (Mutex.try_lock(&ADC_DEV->lock, task)) {
adc_task_data_t *data = task->fdata;
adc_atmega_change_channel(ADC_DEV->regs, data->channel);
result.next = TASK_STATE_SAMPLE;
ADC_DEV->current = task;
}
}
return result;
}
/**
* adc task callback to start the sampling process
*
* @param task task object pased to the callback by the scheduler
* @return next state and schedule value
*/
static task_slice_result_t adc_state_sample(task_t *task) {
task_slice_result_t result = { TASK_STATE_RECORD, TASK_END };
assert(Mutex.have_lock(&ADC_DEV->lock, task) && "Task does not own mutex");
adc_atmega_se_start(ADC_DEV->regs);
return result;
}
/**
* adc task callback to record the sampled value
*
* @param task task object pased to the callback by the scheduler
* @return next state and schedule value
*/
static task_slice_result_t adc_state_record(task_t *task) {
task_slice_result_t result = { TASK_STATE_START, TASK_RESCHED };
assert(Mutex.have_lock(&ADC_DEV->lock, task) && "Task does not own mutex");
adc_task_data_t *data = task->fdata;
ADC_DEV->data[data->channel] = adc_atmega_se_read(ADC_DEV->regs);
ADC_DEV->current = NULL;
if (!task->enabled) result.sched = TASK_END;
Mutex.unlock(&ADC_DEV->lock, task);
return result;
}
/**
* Start sampling an ADC channel
*
* @param ch channel
* @param interval sampling interval in system ticks
* @return void
* @note all channels are stopped by default
*/
static void adc_start_channel(adc_channel_t ch, tick_t interval) {
task_t *task = &adc_tasks[ch];
Task.set_ticks(task, interval);
Task.enable(task);
task->slice_idx = TASK_STATE_START;
Task.schedule(task, TASK_RESCHED);
}
/**
* Stop sampling an ADC channel
*
* @param ch channel to stop
* @return void
* @note the ADC may take one more sample on this channel if it has already
* started doing so
*/
static inline void adc_stop_channel(adc_channel_t ch) {
Task.disable(&adc_tasks[ch]);
}
/**
* Atomic get most recent value read on ADC channel
*
* @param ch channel to get
* @return adc value
*/
static inline adc_data_t adc_read_value(adc_channel_t ch) {
uint16_t result;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
result = ADC_DEV->data[ch];
}
return result;
}
/**
* Initialize adc device, tasks, and all channels
*
* @param void
* @return void
*/
static void adc_init(void) {
adc_atmega_init(ADC_DEV->regs);
Mutex.init(&ADC_DEV->lock);
ADC_DEV->current = NULL;
int i;
for (i = 0; i < 8; i++) {
adc_task_data[i].channel = i;
Task.init(&adc_tasks[i], adc_task_slices, &adc_task_data[i]);
Task.disable(&adc_tasks[i]);
}
}
ISR(ADC_vect) {
Task.schedule(ADC_DEV->current, TASK_SCHED_IMMED);
}
/**
* Public Interface Class for ADC device
*/
const adc_class_t ADC_ = {
.init = adc_init,
.start = adc_start_channel,
.stop = adc_stop_channel,
.read = adc_read_value
};