]> rtime.felk.cvut.cz Git - mf624-simulink.git/blob - mf624_SIMULINK.c
mf624_blocks_demo adapt for IRC extension and include PWM blocks.
[mf624-simulink.git] / mf624_SIMULINK.c
1 /*
2  * Application using MF624 UIO driver
3  *
4  * Copyright (C) 2011 Rostislav Lisovy <lisovy@gmail.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20
21 #include <stdio.h>
22 #include <errno.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <dirent.h>
28 #include <sys/types.h>
29 #include <sys/mman.h>
30 #include <stdint.h> // uintX_t
31 #include <inttypes.h>
32 #include <unistd.h>
33 #include <alloca.h>
34 #include <stdlib.h>
35
36 #include "mf624_SIMULINK.h"
37
38 /****************************************************************/
39
40
41 /* Which uio device node to use */
42 #define UIO "uio0"
43
44 mf624_state_t* mfst=NULL;
45 unsigned mfst_refcnt = 0;
46
47 static int bar_mapping_fill(bar_mapping_t *barmap, const char *uio_dev, int map_nr)
48 {
49         FILE *file;
50         void *s;
51         int ssiz;
52         static size_t page_size;
53
54         page_size = sysconf(_SC_PAGESIZE);
55
56         ssiz = snprintf(NULL, 0, "/sys/class/uio/%s/maps/map%i/", uio_dev, map_nr);
57         if (ssiz < 0)
58                 return -1;
59         /* add reserve to store each size addr, name, offset, size */
60         ssiz += 6 + 1;
61         s = alloca(ssiz);
62
63         snprintf(s, ssiz, "/sys/class/uio/%s/maps/map%i/addr", uio_dev, map_nr);
64         file = fopen(s, "rb");
65         if (file == NULL)
66           return -1;
67         fscanf(file, "%"SCNiPTR, &barmap->phys_addr);
68         fclose(file);
69
70         snprintf(s, ssiz, "/sys/class/uio/%s/maps/map%i/offset", uio_dev, map_nr);
71         file = fopen(s, "rb");
72         if (file == NULL) {
73                 barmap->offset = barmap->phys_addr & (page_size - 1);
74         } else {
75                 fscanf(file, "%"SCNi32, &barmap->offset);
76                 fclose(file);
77         }
78
79         snprintf(s, ssiz, "/sys/class/uio/%s/maps/map%i/size", uio_dev, map_nr);
80         file = fopen(s, "rb");
81         if (file == NULL)
82           return -1;
83         fscanf(file, "%"SCNi32, &barmap->size);
84         fclose(file);
85
86         barmap->mmap_offset = page_size * map_nr + barmap->offset;
87
88         return 0;
89 }
90
91 static int bar_mapping_setup(bar_mapping_t *barmap, int device_fd)
92 {
93         static size_t page_mask = 0;
94         off_t mmap_start;
95         size_t mmap_size;
96
97         if (!page_mask)
98                  page_mask = sysconf(_SC_PAGESIZE) - 1;
99
100         mmap_start = barmap->mmap_offset & ~page_mask;
101         mmap_size = barmap->mmap_offset + barmap->size + page_mask - mmap_start;
102         mmap_size &= ~page_mask;
103
104         barmap->mmap_addr = mmap(0, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, device_fd, mmap_start);
105         if (barmap->mmap_addr == MAP_FAILED) {
106                 return -1;
107         }
108
109         barmap->virt_addr = (uintptr_t)barmap->mmap_addr;
110         barmap->virt_addr += barmap->mmap_offset & page_mask;
111
112         return 0;
113 }
114
115 static int bar_mapping_destroy(bar_mapping_t *barmap)
116 {
117         off_t mmap_start;
118         size_t mmap_size;
119         size_t page_mask = sysconf(_SC_PAGESIZE) - 1;
120
121         mmap_start = barmap->mmap_offset & ~page_mask;
122         mmap_size = barmap->mmap_offset + barmap->size + page_mask - mmap_start;
123         mmap_size &= ~page_mask;
124
125         return munmap(barmap->mmap_addr, mmap_size);
126 }
127
128 /****************************************************************/
129
130 #define BUFF_SMALL              32
131 #define BUFF_MID                256
132 #define min(a, b)               ((a) > (b) ? (b) : (a))
133
134
135
136
137
138 mf624_state_t mf624_state;
139
140 /* Print one byte as binary number */
141 static void print_8bin(int nr)
142 {
143         int i;
144         for (i = 7; i >= 0; i--) {
145                 printf("%d" , ((nr & (1 << i)) > 0));
146         }
147
148         printf("\n");
149 }
150
151
152
153 void DIO_write(mf624_state_t* mfst, int16_t val)
154 {
155         mf624_write16(val, MFST2REG(mfst, 2, DOUT_reg));
156 }
157
158 uint16_t DIO_read(mf624_state_t* mfst)
159 {
160         return mf624_read16(MFST2REG(mfst, 2, DIN_reg)) & 0xFF;
161 }
162
163 void DAC_enable(mf624_state_t* mfst)
164 {
165         // Setting DACEN and LDAC bits in GPIO register influences all DACs
166         mf624_write32((mf624_read32(MFST2REG(mfst, 0, GPIOC_reg))
167                 | GPIOC_DACEN_mask) // enable output
168                 & ~GPIOC_LDAC_mask, // enable conversion
169                 MFST2REG(mfst, 0, GPIOC_reg));
170 }
171
172
173
174 int DAC_write(mf624_state_t* mfst, dac_channel_t channel, int val)
175 {
176         if (channel > sizeof(dac_channel2reg)/sizeof(*dac_channel2reg))
177                 return -1;
178
179         mf624_write16(val, MFST2REG(mfst, 2, dac_channel2reg[channel]));
180         return 0;
181 }
182
183 int ADC_enable(mf624_state_t* mfst, adc_channel_t channel)
184 {
185         mfst->ADC_enabled = 0;
186
187         if (channel > sizeof(adc_channel2reg)/sizeof(*adc_channel2reg))
188                 return -1;
189
190         mfst->ADC_enabled = (1 << channel);
191
192         mfst->ADC_enabled &= 0xFF;
193         mf624_write16(mfst->ADC_enabled, MFST2REG(mfst, 2, ADCTRL_reg));
194         //print_8bin(ADC_enabled);
195
196         return 0;
197 }
198
199 /* This function blocks until conversion is finished */
200 double ADC_read(mf624_state_t* mfst, adc_channel_t channel)
201 {
202         volatile int i;
203         int result;
204
205         // Activate trigger to start conversion
206         mf624_read16(MFST2REG(mfst, 2, ADSTART_reg));
207
208         // Check if conversion has finished
209         while((mf624_read32(MFST2REG(mfst, 0, GPIOC_reg)) & GPIOC_EOLC_mask)) {
210                 for (i = 0; i < 1000; i++) {} // small wait
211         }
212
213         ADC_enable(mfst, channel);
214         result = mf624_read16(MFST2REG(mfst, 2, ADDATA0_reg));
215
216         return 10.0 * ((int16_t) (result << 2)) / (double) 0x8000;
217 }
218
219 extern uint32_t IRC_mode_change(mf624_state_t* mfst, uint32_t change_mask, uint32_t change_val)
220 {
221         /* This sequence should be protected by mutex to protect changes in multirate systems */
222         mfst->IRC_mode = (mfst->IRC_mode & ~change_mask) | (change_val & change_mask);
223         mf624_write32(mfst->IRC_mode, MFST2REG(mfst, 4, IRCCTRL_reg));
224         /*printf("change_mask 0x%08x, change_val 0x%08x\n", change_mask, change_val);*/
225         /*printf("IRC mode set to %08lx\n", mfst->IRC_mode);*/
226         return mfst->IRC_mode;
227 }
228
229
230 static int open_device(char* path) {
231         int device_fd;
232
233         device_fd = open(path, O_RDWR | O_SYNC);
234         if (device_fd == -1) {
235                 perror(path);
236                 return -1;
237         }
238
239         return device_fd;
240 }
241
242 static void wait_for_interrupts(int device_fd)
243 {
244         read(device_fd, NULL, 1);
245 }
246
247 static int disable_interrupts(int device_fd)
248 {
249         uint32_t control_value = 0;
250         int status;
251
252         status = write(device_fd, &control_value, sizeof(uint32_t));
253         if (status == -1) {
254                 perror("write()");
255                 return -1;
256         }
257
258         return status;
259 }
260
261 static int enable_interrupts(int device_fd)
262 {
263         uint32_t control_value = 1;
264         int status;
265
266         status = write(device_fd, &control_value, sizeof(uint32_t));
267         if (status == -1) {
268                 perror("write()");
269                 return -1;
270         }
271
272         return status;
273 }
274
275 static void list_available_mem_regions(char* device)
276 {
277         int status;
278         char path[] = "/sys/class/uio/";
279         char subdir[] = "/maps/";
280         char directory[BUFF_MID];
281         memset(directory, '\0', BUFF_MID);
282
283         DIR *dip;
284         struct dirent *dit;
285
286         strncat(directory, path, strlen(path));
287         strncat(directory, device, min(strlen(device), 8));
288         strncat(directory, subdir, strlen(subdir));
289
290         dip = opendir(directory);
291         if (dip == NULL) {
292                 perror("opendir");
293                 return;
294         }
295
296         while ((dit = readdir(dip)) != NULL) {
297                 if (strcmp(dit->d_name, ".") && strcmp(dit->d_name, "..")) {
298                         printf(" %s\n", dit->d_name);
299                 }
300         }
301
302         status = closedir(dip);
303         if (status == -1) {
304                 perror("closedir()");
305                 return;
306         }
307
308 }
309
310
311 static void list_available_io_ports(char *device)
312 {
313         int status;
314         char path[] = "/sys/class/uio/";
315         char subdir[] = "/portio/";
316         char directory[BUFF_MID];
317         memset(directory, '\0', BUFF_MID);
318
319         DIR *dip;
320         struct dirent *dit;
321
322         strncat(directory, path, strlen(path));
323         strncat(directory, device, min(strlen(device), 8));
324         strncat(directory, subdir, strlen(subdir));
325
326         status = access(directory, F_OK);
327         if (status == -1) {
328                 printf(" There are no IO port available\n");
329                 return;
330         }
331
332         dip = opendir(directory);
333         if (dip == NULL) {
334                 perror("opendir");
335                 return;
336         }
337
338         while ((dit = readdir(dip)) != NULL) {
339                 if (strcmp(dit->d_name, ".") && strcmp(dit->d_name, "..")) {
340                         printf(" %s\n", dit->d_name);
341                 }
342         }
343
344         status = closedir(dip);
345         if (status == -1) {
346                 perror("closedir()");
347                 return;
348         }
349
350 }
351
352
353 static void run_simple_tests(char* dev_name)
354 {
355         int status;
356         int device_fd;
357         char buff[BUFF_SMALL];
358         memset(buff, '\0', BUFF_SMALL);
359
360         strncat(buff, "/dev/", 5);
361         strncat(buff, dev_name, min(strlen(dev_name), 8));
362
363         printf("Opening %s\n", buff);
364
365         device_fd = open_device(buff);
366         if (device_fd != -1) {
367                 printf("Tring to enable interrupts\n");
368                 status = enable_interrupts(device_fd);
369                 if (status != -1) {
370                         printf(" Probably OK\n");
371                 }
372
373                 printf("Tring to disable interrupts\n");
374                 status = disable_interrupts(device_fd);
375                 if (status != -1) {
376                         printf(" Probably OK\n");
377                 }
378         }
379
380
381         printf("Checking for available memory regions exported by the UIO driver\n");
382         list_available_mem_regions(dev_name);
383
384         printf("Checking for available IO ports exported by the UIO driver\n");
385         list_available_io_ports(dev_name);
386 }
387
388 static int mmap_regions(mf624_state_t* mfst)
389 {
390         if (bar_mapping_fill(&mfst->bar0, mfst->uio_dev, 0) < 0) {
391                 fprintf(stderr, "%s bar0 mapping fill failed\n", mfst->uio_dev);
392                 return -1;
393         }
394
395         if (bar_mapping_fill(&mfst->bar2, mfst->uio_dev, 1) < 0) {
396                 fprintf(stderr, "%s bar2 mapping fill failed\n", mfst->uio_dev);
397                 return -1;
398         }
399
400         if (bar_mapping_fill(&mfst->bar4, mfst->uio_dev, 2) < 0) {
401                 fprintf(stderr, "%s bar4 mapping fill failed\n", mfst->uio_dev);
402                 return -1;
403         }
404
405         if (bar_mapping_setup(&mfst->bar0, mfst->device_fd) < 0) {
406                 fprintf(stderr, "%s bar0 mapping setup failed\n", mfst->uio_dev);
407                 return -1;
408         }
409
410         if (bar_mapping_setup(&mfst->bar2, mfst->device_fd) < 0) {
411                 fprintf(stderr, "%s bar2 mapping setup failed\n", mfst->uio_dev);
412                 return -1;
413         }
414
415         if (bar_mapping_setup(&mfst->bar4, mfst->device_fd) < 0) {
416                 fprintf(stderr, "%s bar4 mapping setup failed\n", mfst->uio_dev);
417                 return -1;
418         }
419
420 #if 1
421         printf("bar0.phys_addr = %"PRIxPTR"\n", mfst->bar0.phys_addr);
422         printf("bar2.phys_addr = %"PRIxPTR"\n", mfst->bar2.phys_addr);
423         printf("bar4.phys_addr = %"PRIxPTR"\n", mfst->bar4.phys_addr);
424
425         printf("bar0.virt_addr = %"PRIxPTR"\n", mfst->bar0.virt_addr);
426         printf("bar2.virt_addr = %"PRIxPTR"\n", mfst->bar2.virt_addr);
427         printf("bar4.virt_addr = %"PRIxPTR"\n", mfst->bar4.virt_addr);
428 #endif
429
430         return 0;
431 }
432
433
434
435 /**
436  * Try to initialize the MF624 UIO driver.
437  *
438  * If and only if S is not NULL and initialization fails, then
439  * ssSetErrorStatus() gets called to report the error via Simulink
440  * interface.
441  *
442  * @param S Pointer to SimStruct for error reporting or NULL.
443  *
444  * @return Zero in case of success, -1 in case of error.
445  */
446 int mf624_init(SimStruct *S)
447 {
448         if (mfst==NULL) {
449                 mfst = malloc(sizeof(mf624_state_t));
450                 char fn[32];
451                 mfst->uio_dev = UIO;
452                 snprintf(fn, sizeof(fn), "/dev/%s", mfst->uio_dev);
453
454                 mfst->device_fd = open_device(fn);
455                 if (mfst->device_fd < 0) {
456                         if (S) ssSetErrorStatus(S,"/dev/" UIO ": open failed");
457                         goto free;
458                 }
459                 if (mmap_regions(mfst) < 0) {
460                         if (S) ssSetErrorStatus(S,"/dev/" UIO ": mmap_regions failed");
461                         goto close;
462                 }
463         }
464         mfst_refcnt++;
465         return 0;
466 close:
467         close(mfst->device_fd);
468 free:
469         free(mfst);
470         mfst = NULL;
471         return -1;
472 }
473
474 int mf624_done()
475 {
476         if (mfst) {
477                 if (--mfst_refcnt == 0) {
478                         close(mfst->device_fd);
479                         bar_mapping_destroy(&mfst->bar0);
480                         bar_mapping_destroy(&mfst->bar2);
481                         bar_mapping_destroy(&mfst->bar4);
482                         free(mfst);
483                         mfst = NULL;
484                 }
485         }
486
487         return 0;
488 }
489
490
491 /**
492  * Check whether MF624 card is initialized.
493  *
494  * @param S
495  *
496  * @return Zero if MF624 is initialized, -1 othewise.
497  */
498 int mf624_check(SimStruct *S)
499 {
500         if (mfst==NULL) {
501                 if (S) ssSetErrorStatus(S, "MF624 is not initialized");
502                 return -1;
503         }
504         else
505                 return 0;
506 }
507
508 /*int main(int argc, char* argv[])
509 {
510         mf624_state_t* mfst = &mf624_state;
511         char buff[BUFF_SMALL];
512         memset(buff, '\0', BUFF_SMALL);
513
514         if (argc < 2) {
515                 printf("Usage: %s UIO_DEVICE\n   UIO_DEVICE\tname of uio device in /dev\n", argv[0]);
516                 return 1;
517         }
518
519         mfst->uio_dev = argv[1];
520
521         strncat(buff, "/dev/", 5);
522         strncat(buff, mfst->uio_dev, sizeof(buff) - 6);
523
524         mfst->device_fd = open_device(buff);
525         if (mfst->device_fd < 0) {
526                 fprintf(stderr, "%s open failed (%s)!\n", mfst->uio_dev, strerror(errno));
527                 return 2;
528         }
529         if (mmap_regions(mfst) < 0) {
530                 fprintf(stderr, "%s mmap_regions failed (%s)!\n", mfst->uio_dev, strerror(errno));
531                 return 2;
532         }
533
534         DAC_enable(mfst);
535
536         while (1){
537                 printf("Reading DIO: ");
538                 print_8bin(DIO_read(mfst));
539                 sleep(1);
540
541                 printf("Setting DA1 to 10 V\n");
542                 DAC_write(mfst, DA1, 0x3FFF);
543                 sleep(1);
544
545                 printf("Reading ADC0: ");
546                 printf("%f V\n", ADC_read(mfst, AD0));
547                 sleep(1);
548
549                 printf("Reading ADC1: ");
550                 printf("%f V\n", ADC_read(mfst, AD1));
551                 sleep(1);
552
553                 printf("Setting DIO to 0xff\n");
554                 DIO_write(mfst, 0xff);
555                 sleep(1);
556
557                 printf("Setting DIO to 0x00\n");
558                 DIO_write(mfst, 0x00);
559                 sleep(1);
560
561                 printf("Setting DA1 to 5 V\n");
562                 DAC_write(mfst, DA1, 0x3000);
563                 sleep(1);
564
565                 printf("Reading ADC0: ");
566                 printf("%f V\n", ADC_read(mfst, AD0));
567                 sleep(1);
568
569                 printf("Reading ADC1: ");
570                 printf("%f V\n", ADC_read(mfst, AD1));
571                 sleep(1);
572                 printf("----------------------\n\n");
573         }
574
575
576         return 0;
577 }*/