aboutsummaryrefslogtreecommitdiffstats
path: root/demos/modules/gaudin/gwinosc.c
blob: a5de7e387fd3721337dc5f962fcf8568f25ea3ad (plain)
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
190
/*
    ChibiOS/GFX - Copyright (C) 2012, 2013
                 Joel Bodenmann aka Tectu <joel@unormal.org>

    This file is part of ChibiOS/GFX.

    ChibiOS/GFX is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    ChibiOS/GFX is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/**
 * --------------------------- Our Custom GWIN Oscilloscope ---------------
 *
 * This GWIN superset implements a simple audio oscilloscope using the GAUDIN module.
 *
 * It makes many assumptions, the most fundamental of which is that the audio device
 * produces unsigned integer samples.
 *
 * The GMISC module with GMISC_NEED_ARRAYOPS could be used to process the samples more
 * correctly if we were really building something generic.
 */

#include "ch.h"
#include "hal.h"
#include "gfx.h"

#include "gwinosc.h"

/* Include internal GWIN routines so we can build our own superset class */
#include "gwin/internal.h"

/* Our GWIN identifier */
#define GW_SCOPE				(GW_FIRST_USER_WINDOW+0)

/* The size of our dynamically allocated audio buffer */
#define AUDIOBUFSZ				64*2

/* How many flat-line sample before we trigger */
#define FLATLINE_SAMPLES		8

GHandle gwinCreateScope(GScopeObject *gs, coord_t x, coord_t y, coord_t cx, coord_t cy, uint16_t channel, uint32_t frequency) {
	/* Initialise the base class GWIN */
	if (!(gs = (GScopeObject *)_gwinInit((GWindowObject *)gs, x, y, cx, cy, sizeof(GScopeObject))))
		return 0;

	/* Initialise the scope object members and allocate memory for buffers */
	gs->gwin.type = GW_SCOPE;
	chBSemInit(&gs->bsem, TRUE);
	gs->nextx = 0;
	if (!(gs->lastscopetrace = (coord_t *)chHeapAlloc(NULL, gs->gwin.width * sizeof(coord_t))))
		return 0;
	if (!(gs->audiobuf = (adcsample_t *)chHeapAlloc(NULL, AUDIOBUFSZ * sizeof(adcsample_t))))
		return 0;
#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP
	gs->lasty = gs->gwin.height/2;
#elif TRIGGER_METHOD == TRIGGER_MINVALUE
	gs->lasty = gs->gwin.height/2;
	gs->scopemin = 0;
#endif

	/* Start the GADC high speed converter */
	gaudinInit(channel, frequency, gs->audiobuf, AUDIOBUFSZ, AUDIOBUFSZ/2);
	gaudinSetBSem(&gs->bsem, &gs->myEvent);
	gaudinStart();

	return (GHandle)gs;
}

void gwinWaitForScopeTrace(GHandle gh) {
	#define 		gs	((GScopeObject *)(gh))
	int				i;
	coord_t			x, y;
	coord_t			yoffset;
	audin_sample_t	*pa;
	coord_t			*pc;
#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP
	bool_t			rdytrigger;
	int				flsamples;
#elif TRIGGER_METHOD == TRIGGER_MINVALUE
	bool_t			rdytrigger;
	int				flsamples;
	coord_t			scopemin;
#endif

	/* Wait for a set of audio conversions */
	chBSemWait(&gs->bsem);

	/* Ensure we are drawing in the right area */
	#if GDISP_NEED_CLIP
		gdispSetClip(gh->x, gh->y, gh->width, gh->height);
	#endif

	yoffset = gh->height/2 + (1<<SCOPE_Y_BITS)/2;
	x = gs->nextx;
	pc = gs->lastscopetrace+x;
	pa = gs->myEvent.buffer;
#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP
	rdytrigger = FALSE;
	flsamples = 0;
#elif TRIGGER_METHOD == TRIGGER_MINVALUE
	rdytrigger = FALSE;
	flsamples = 0;
	scopemin = 0;
#endif

	for(i = gs->myEvent.count; i; i--) {

		/* Calculate the new scope value - re-scale using simple shifts for efficiency, re-center and y-invert */
		#if GAUDIN_BITS_PER_SAMPLE > SCOPE_Y_BITS
			y = yoffset - (*pa++ >> (GAUDIN_BITS_PER_SAMPLE - SCOPE_Y_BITS));
		#else
			y = yoffset - (*pa++ << (SCOPE_Y_BITS - GAUDIN_BITS_PER_SAMPLE));
		#endif

#if TRIGGER_METHOD == TRIGGER_MINVALUE
		/* Calculate the scopemin ready for the next trace */
		if (y > scopemin)
			scopemin = y;
#endif

		/* Have we reached the end of a scope trace? */
		if (x >= gh->width) {

#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP || TRIGGER_METHOD == TRIGGER_MINVALUE
			/* Handle triggering - we trigger on the next sample minimum (y value maximum) or a flat-line */

			#if TRIGGER_METHOD == TRIGGER_MINVALUE
				/* Arm when we reach the sample minimum (y value maximum) of the previous trace */
				if (!rdytrigger && y >= gs->scopemin)
					rdytrigger = TRUE;
			#endif

			if (y == gs->lasty) {
				/* Trigger if we get too many flat-line samples regardless of the armed state */
				if (++flsamples < FLATLINE_SAMPLES)
					continue;
				flsamples = 0;
			} else if (y > gs->lasty) {
				gs->lasty = y;
				flsamples = 0;
				#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP
					/* Arm the trigger when samples fall (y increases) ie. negative slope */
					rdytrigger = TRUE;
				#endif
				continue;
			} else {
				/* If the trigger is armed, Trigger when samples increases (y decreases) ie. positive slope */
				gs->lasty = y;
				flsamples = 0;
				if (!rdytrigger)
					continue;
			}

			/* Ready for a the next trigger cycle */
			rdytrigger = FALSE;
#endif

			/* Prepare for a scope trace */
			x = 0;
			pc = gs->lastscopetrace;
		}

		/* Clear the old scope pixel and then draw the new scope value */
		gdispDrawPixel(gh->x+x, gh->y+pc[0], gh->bgcolor);
		gdispDrawPixel(gh->x+x, gh->y+y, gh->color);

		/* Save the value */
		*pc++ = y;
		x++;
		#if TRIGGER_METHOD == TRIGGER_POSITIVERAMP || TRIGGER_METHOD == TRIGGER_MINVALUE
			gs->lasty = y;
		#endif
	}
	gs->nextx = x;
#if TRIGGER_METHOD == TRIGGER_MINVALUE
	gs->scopemin = scopemin;
#endif

	#undef gs
}