-
-
Notifications
You must be signed in to change notification settings - Fork 108
/
adccmds.c
138 lines (130 loc) · 3.9 KB
/
adccmds.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
// Commands for controlling GPIO analog-to-digital input pins
//
// Copyright (C) 2016 Kevin O'Connor <[email protected]>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "basecmd.h" // oid_alloc
#include "board/gpio.h" // struct gpio_adc
#include "board/irq.h" // irq_disable
#include "command.h" // DECL_COMMAND
#include "sched.h" // DECL_TASK
struct analog_in {
struct timer timer;
uint32_t rest_time, sample_time, next_begin_time;
uint16_t value, min_value, max_value;
struct gpio_adc pin;
uint8_t invalid_count, range_check_count;
uint8_t state, sample_count;
};
static struct task_wake analog_wake;
static uint_fast8_t
analog_in_event(struct timer *timer)
{
struct analog_in *a = container_of(timer, struct analog_in, timer);
uint32_t sample_delay = gpio_adc_sample(a->pin);
if (sample_delay) {
a->timer.waketime += sample_delay;
return SF_RESCHEDULE;
}
uint16_t value = gpio_adc_read(a->pin);
uint8_t state = a->state;
if (state >= a->sample_count) {
state = 0;
} else {
value += a->value;
}
a->value = value;
a->state = state+1;
if (a->state < a->sample_count) {
a->timer.waketime += a->sample_time;
return SF_RESCHEDULE;
}
if (a->range_check_count > 0) {
if (likely(a->value >= a->min_value && a->value <= a->max_value)) {
a->invalid_count = 0;
} else {
a->invalid_count++;
if (a->invalid_count >= a->range_check_count) {
try_shutdown("ADC out of range");
a->invalid_count = 0;
}
}
}
sched_wake_task(&analog_wake);
a->next_begin_time += a->rest_time;
a->timer.waketime = a->next_begin_time;
return SF_RESCHEDULE;
}
void
command_config_analog_in(uint32_t *args)
{
struct gpio_adc pin = gpio_adc_setup(args[1]);
struct analog_in *a = oid_alloc(
args[0], command_config_analog_in, sizeof(*a));
a->timer.func = analog_in_event;
a->pin = pin;
a->state = 1;
}
DECL_COMMAND(command_config_analog_in, "config_analog_in oid=%c pin=%u");
void
command_query_analog_in(uint32_t *args)
{
struct analog_in *a = oid_lookup(args[0], command_config_analog_in);
sched_del_timer(&a->timer);
gpio_adc_cancel_sample(a->pin);
a->next_begin_time = args[1];
a->timer.waketime = a->next_begin_time;
a->sample_time = args[2];
a->sample_count = args[3];
a->state = a->sample_count + 1;
a->rest_time = args[4];
a->min_value = args[5];
a->max_value = args[6];
a->range_check_count = args[7];
if (! a->sample_count)
return;
sched_add_timer(&a->timer);
}
DECL_COMMAND(command_query_analog_in,
"query_analog_in oid=%c clock=%u sample_ticks=%u sample_count=%c"
" rest_ticks=%u min_value=%hu max_value=%hu range_check_count=%c");
void
analog_in_task(void)
{
if (!sched_check_wake(&analog_wake))
return;
uint8_t oid;
struct analog_in *a;
foreach_oid(oid, a, command_config_analog_in) {
if (a->state != a->sample_count)
continue;
irq_disable();
if (a->state != a->sample_count) {
irq_enable();
continue;
}
uint16_t value = a->value;
uint32_t next_begin_time = a->next_begin_time;
a->state++;
irq_enable();
sendf("analog_in_state oid=%c next_clock=%u value=%hu"
, oid, next_begin_time, value);
}
}
DECL_TASK(analog_in_task);
void
analog_in_shutdown(void)
{
uint8_t i;
struct analog_in *a;
foreach_oid(i, a, command_config_analog_in) {
gpio_adc_cancel_sample(a->pin);
if (a->sample_count) {
a->state = a->sample_count + 1;
a->next_begin_time += a->rest_time;
a->timer.waketime = a->next_begin_time;
sched_add_timer(&a->timer);
}
}
}
DECL_SHUTDOWN(analog_in_shutdown);