1 /*
2 * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/
3 *
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the
15 * distribution.
16 *
17 * * Neither the name of Texas Instruments Incorporated nor the names of
18 * its contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
34 #include <sys/types.h>
35 #include <unistd.h>
36 #include <sys/stat.h>
37 #include <sys/mman.h>
38 #include <fcntl.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <unistd.h>
42 #include <stdint.h>
43 #include <math.h>
44 #include <time.h>
45 #include <string.h>
46 #include <sched.h>
47 #include <pthread.h>
48 #include <limits.h>
49 #include <ncurses.h>
51 #define PING_ADDR 0x9FFC0000
52 #define PONG_ADDR 0x9FFE0000
53 #define PING 0
54 #define PONG 1
55 #define DATA_READY 0x0000FFFF
56 #define IN_PROGRESS 0x00000000
57 #define NUM_CHANNELS 48
58 #define NUM_DEVICES 6
59 #define NUM_SAMPLES_PER_CYCLE 640
60 #define MAX_CSV_SAMPLES 65536
62 volatile float min[NUM_CHANNELS];
63 volatile float max[NUM_CHANNELS];
64 volatile float result[NUM_CHANNELS];
65 volatile uint16_t compensation, delay;
66 volatile int16_t csv_samples[MAX_CSV_SAMPLES];
67 volatile uint16_t csv_channel = 0;
68 volatile uint32_t csv_sample_num = 0;
69 volatile uint32_t csv_counter = 0;
71 struct buffer {
72 uint32_t flags;
73 uint32_t comp_delay;
74 uint32_t samples[NUM_CHANNELS*NUM_SAMPLES_PER_CYCLE];
75 };
77 void *capture_func(void *data)
78 {
79 int i, j;
80 struct buffer *ping_ptr, *pong_ptr;
81 uint32_t samples[NUM_CHANNELS*NUM_SAMPLES_PER_CYCLE];
82 uint64_t cur_square_sum = 0;
83 int16_t cur_min = INT16_MAX;
84 int16_t cur_max = INT16_MIN;
85 int16_t signed_val;
86 struct timespec sleep;
87 uint8_t curr_buff = PING;
89 /* Open the /dev/mem device to access the shared buffers */
90 int fd = open("/dev/mem",O_RDWR|O_SYNC);
91 if(fd < 0)
92 {
93 printf("Can't open /dev/mem\n");
94 return NULL;
95 }
96 /* mmap 128kB ping and pong buffers */
97 ping_ptr = (struct buffer *) mmap(0, getpagesize()*32, PROT_READ|PROT_WRITE, MAP_SHARED, fd, PING_ADDR);
98 pong_ptr = (struct buffer *) mmap(0, getpagesize()*32, PROT_READ|PROT_WRITE, MAP_SHARED, fd, PONG_ADDR);
99 if(ping_ptr == NULL || pong_ptr == NULL)
100 {
101 printf("Can't mmap\n");
102 return NULL;
103 }
105 /* put sine wave in memory */
106 /*
107 for(i = 0; i < NUM_SAMPLES_PER_CYCLE; i++) {
108 for(j = 0; j < NUM_CHANNELS; j++) {
109 ping_ptr->samples[i*NUM_CHANNELS+j] = sine_wave[i];
110 pong_ptr->samples[i*NUM_CHANNELS+j] = sine_wave[i];
111 }
112 }
113 */
115 /* Configure the sleep delay to 250us */
116 sleep.tv_sec = 0;
117 sleep.tv_nsec = 250000L;
119 /* Wait until PING buffer is in progress before beginning */
120 while (ping_ptr->flags != IN_PROGRESS)
121 nanosleep(&sleep, NULL);
123 while(1) {
124 /* Wait for the buffers to be ready and then memcpy the sample
125 * data to local memory. If the buffer is not ready yet, go to
126 * sleep for 250us before checking again. This thread is
127 * running at the highest priority so it needs to sleep when
128 * not in use or it will starve all other threads.
129 */
130 if (curr_buff == PING) {
131 while (ping_ptr->flags != DATA_READY)
132 nanosleep(&sleep, NULL);
133 compensation = ping_ptr->comp_delay >> 16;
134 delay = ping_ptr->comp_delay & 0xFFFF;
135 memcpy(samples, ping_ptr->samples, sizeof(samples));
136 curr_buff = PONG;
137 }
138 else {
139 while (pong_ptr->flags != DATA_READY)
140 nanosleep(&sleep, NULL);
141 compensation = pong_ptr->comp_delay >> 16;
142 delay = pong_ptr->comp_delay & 0xFFFF;
143 memcpy(samples, pong_ptr->samples, sizeof(samples));
144 curr_buff = PING;
145 }
147 /* Iterate through a full cycle for each channel */
148 for (j = 0; j < NUM_CHANNELS; j++) {
149 for(i = 0; i < NUM_SAMPLES_PER_CYCLE; i++) {
150 /* Convert the ADC code to it's signed equivalent */
151 signed_val = (uint16_t)samples[i*NUM_CHANNELS+j] - 0x8000;
152 if (j == csv_channel && csv_counter < csv_sample_num)
153 csv_samples[csv_counter++] = signed_val;
154 /* Check for min and max values throughout the cycle */
155 if(signed_val < cur_min)
156 cur_min = signed_val;
157 if(signed_val > cur_max)
158 cur_max = signed_val;
159 /* Accumulate the squares to be used in the RMS calc */
160 cur_square_sum += signed_val * signed_val;
161 }
162 /* Convert the min and max from ADC codes to voltages and store them */
163 min[j] = (float)cur_min * 0.0003125;
164 max[j] = (float)cur_max * 0.0003125;
165 /* Complete the Root Mean Square (RMS) calculation for this channel and store it */
166 result[j] = (sqrt(cur_square_sum / 640.0)) * 0.0003125;
167 /* Reset the current variables to defaults for the next channel iteration */
168 cur_min = INT16_MAX;
169 cur_max = INT16_MIN;
170 cur_square_sum = 0;
171 }
172 }
174 return NULL;
175 }
177 void *print_func(void *data)
178 {
179 int i;
180 float frequency;
182 /* Initialize the screen */
183 initscr();
184 /* Set the timeout to 250ms for waiting for user input */
185 timeout(250);
186 curs_set(0);
188 /* Print each channel's min, max, and RMS values to the screen */
189 while (1) {
190 for(i = 0; i < NUM_CHANNELS/2; i++) {
191 mvprintw(i, 0, "D%d:C%d Vpp:[%.3f,%.3f] ", i/8+1, i%8, min[i], max[i]);
192 mvprintw(i, 28, "RMS:%.3f", result[i]);
193 mvprintw(i, 39, "| D%d:C%d Vpp:[%.3f,%.3f] ", (i+NUM_CHANNELS/2)/8+1, i%8, min[i+NUM_CHANNELS/2], max[i+NUM_CHANNELS/2]);
194 mvprintw(i, 69, "RMS:%.3f", result[i+NUM_CHANNELS/2]);
195 }
197 frequency = (1.0 / ((delay * NUM_CHANNELS / NUM_DEVICES + compensation) * 0.000000005)) / NUM_SAMPLES_PER_CYCLE;
199 mvprintw(NUM_CHANNELS/2 + 1, 0, "Delay:%d, Compensation:%d", delay, compensation);
200 mvprintw(NUM_CHANNELS/2 + 2, 0, "Estimated Frequency:%.3f Hz", frequency);
201 mvprintw(NUM_CHANNELS/2 + 4, 0, "Press any key to exit");
202 /* Refresh the screen with the latest data */
203 refresh();
204 /* If a character is received then exit the while(1) loop */
205 if(getch() != -1)
206 break;
207 }
209 /* Close the window */
210 endwin();
212 return NULL;
213 }
215 int main(int argc, char* argv[])
216 {
217 struct sched_param param;
218 pthread_attr_t attr;
219 pthread_t capture_thread, print_thread;
220 int ret;
221 uint32_t i;
222 FILE *fp;
224 /* Check for command line arguments requesting to create a CSV file
225 * argv[1] - CSV file name
226 * argv[2] - Channel number to store results from
227 * argv[3] - Number of samples to store (max of 32000)
228 * example given:
229 * ./ARM_User_Space_App.out chan3_2048samples.csv 3 2048
230 */
231 if(argc == 4) {
232 csv_sample_num = atoi(argv[3]);
233 if (csv_sample_num > MAX_CSV_SAMPLES)
234 csv_sample_num = MAX_CSV_SAMPLES;
235 csv_channel = atoi(argv[2]);
236 if (csv_channel > NUM_CHANNELS)
237 csv_channel = 0;
238 }
241 /* Lock memory */
242 if(mlockall(MCL_CURRENT|MCL_FUTURE) == -1) {
243 printf("mlockall failed: %m\n");
244 exit(-2);
245 }
246 /* Initialize pthread attributes (default values) */
247 ret = pthread_attr_init(&attr);
248 if (ret) {
249 printf("init pthread attributes failed\n");
250 goto out;
251 }
252 /* Set the stack size, need at least 0x1E000 for the samples array */
253 ret = pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + 0x20000);
254 if (ret) {
255 printf("pthread setstacksize failed\n");
256 goto out;
257 }
258 /* Set scheduler policy and priority of pthread */
259 ret = pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
260 if (ret) {
261 printf("pthread setschedpolicy failed\n");
262 goto out;
263 }
264 param.sched_priority = 99;
265 ret = pthread_attr_setschedparam(&attr, ¶m);
266 if (ret) {
267 printf("pthread setschedparam failed\n");
268 goto out;
269 }
270 /* Use scheduling parameters of attr */
271 ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
272 if (ret) {
273 printf("pthread setinheritsched failed\n");
274 goto out;
275 }
276 /* Create a pthread with specified attributes (priority 99) */
277 ret = pthread_create(&capture_thread, &attr, capture_func, NULL);
278 if (ret) {
279 printf("create capture_thread failed\n");
280 goto out;
281 }
282 /* Create a pthread with lower priority to print the results */
283 ret = pthread_create(&print_thread, NULL, print_func, NULL);
284 if (ret) {
285 printf("create capture_thread failed\n");
286 goto out;
287 }
288 /* Join the print thread and wait until it is done (key press) */
289 ret = pthread_join(print_thread, NULL);
290 if (ret)
291 printf("join capture_thread failed: %m\n");
293 /* Write the samples to a csv file if requested */
294 if(argc == 4) {
295 fp = fopen(argv[1], "w+");
296 for (i = 0; i < csv_sample_num; i++) {
297 fprintf(fp, "%d\n", csv_samples[i]);
298 }
299 fclose(fp);
301 printf("\n%s file created with %d sample(s) from channel %d\n\n", argv[1], csv_sample_num, csv_channel);
302 }
304 out:
305 return ret;
306 }