+/*
+ * Application using MF624 UIO driver
+ *
+ * Copyright (C) 2011 Rostislav Lisovy <lisovy@gmail.com>
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
#include <stdio.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <stdint.h> // uintX_t
+#include <inttypes.h>
#include <unistd.h>
+#include <alloca.h>
+
+/****************************************************************/
-#define BUFF_SMALL 32
-#define BUFF_MID 256
-#define min(a, b) ((a) > (b) ? (b) : (a))
+typedef struct bar_mapping_t {
+ uintptr_t virt_addr;
+ void * mmap_addr;
+ uintptr_t phys_addr;
+ uint32_t size;
+ uint32_t offset;
+} bar_mapping_t;
+
+int bar_mapping_fill(bar_mapping_t *barmap, const char *uio_dev, int map_nr)
+{
+ FILE *file;
+ void *s;
+ int ssiz;
+ ssiz = snprintf(NULL, 0, "/sys/class/uio/%s/maps/map%i/", uio_dev, map_nr);
+ if (ssiz < 0)
+ return -1;
+ /* add reserve to store each size addr, name, offset, size */
+ ssiz += 6 + 1;
+ s = alloca(ssiz + 6 + 1);
+
+ snprintf(s, ssiz, "/sys/class/uio/%s/maps/map%i/addr", uio_dev, map_nr);
+ file = fopen(s, "rb");
+ fscanf(file, "%"SCNiPTR, &barmap->phys_addr);
+ fclose(file);
+
+ snprintf(s, ssiz, "/sys/class/uio/%s/maps/map%i/offset", uio_dev, map_nr);
+ file = fopen(s, "rb");
+ fscanf(file, "%"SCNi32, &barmap->offset);
+ fclose(file);
+
+ snprintf(s, ssiz, "/sys/class/uio/%s/maps/map%i/size", uio_dev, map_nr);
+ file = fopen(s, "rb");
+ fscanf(file, "%"SCNi32, &barmap->size);
+ fclose(file);
+
+ return 0;
+}
+
+int bar_mapping_setup(bar_mapping_t *barmap, int device_fd)
+{
+ static size_t page_mask = 0;
+ off_t mmap_offset;
+ size_t mmap_size;
+
+ if (!page_mask)
+ page_mask = sysconf(_SC_PAGESIZE) - 1;
+
+ mmap_offset = barmap->offset & ~page_mask;
+ mmap_size = barmap->offset + barmap->size + page_mask - mmap_offset;
+ mmap_size &= ~page_mask;
+
+ barmap->mmap_addr = mmap(0, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, device_fd, mmap_offset);
+ if (barmap->mmap_addr == MAP_FAILED) {
+ return -1;
+ }
+
+ barmap->virt_addr = (uintptr_t)barmap->mmap_addr;
+ barmap->virt_addr += barmap->offset & page_mask;
+
+ return 0;
+}
+
+/****************************************************************/
+
+#define BUFF_SMALL 32
+#define BUFF_MID 256
+#define min(a, b) ((a) > (b) ? (b) : (a))
/* Hardware specific */
-#define DOUT_reg 0x10
+/* BAR0 */
+#define GPIOC_reg 0x54
+
+/* BAR2 */
+#define ADCTRL_reg 0x00
+#define ADDATA0_reg 0x00
+#define ADDATA1_reg 0x02
+#define ADDATA2_reg 0x04
+#define ADDATA3_reg 0x06
+#define ADDATA4_reg 0x08
+#define ADDATA5_reg 0x0a
+#define ADDATA6_reg 0x0c
+#define ADDATA7_reg 0x0e
+#define ADSTART_reg 0x20
+
+#define DOUT_reg 0x10
+#define DIN_reg 0x10
+#define DA0_reg 0x20
+#define DA1_reg 0x22
+#define DA2_reg 0x24
+#define DA3_reg 0x26
+#define DA4_reg 0x28
+#define DA5_reg 0x2A
+#define DA6_reg 0x2C
+#define DA7_reg 0x2E
+
+#define GPIOC_DACEN_mask (1 << 26)
+#define GPIOC_LDAC_mask (1 << 23)
+#define GPIOC_EOLC_mask (1 << 17)
+
+
+typedef enum {DA0, DA1, DA2, DA3, DA4, DA5, DA6, DA7} dac_channel_t;
+typedef enum {AD0, AD1, AD2, AD3, AD4, AD5, AD6, AD7} adc_channel_t;
+
+static uint32_t dac_channel2reg[] = {
+ [DA0] = DA0_reg,
+ [DA1] = DA1_reg,
+ [DA2] = DA2_reg,
+ [DA3] = DA3_reg,
+ [DA4] = DA4_reg,
+ [DA5] = DA5_reg,
+ [DA6] = DA6_reg,
+ [DA7] = DA7_reg,
+};
+
+static uint32_t adc_channel2reg[] = {
+ [AD0] = ADDATA0_reg,
+ [AD1] = ADDATA1_reg,
+ [AD2] = ADDATA2_reg,
+ [AD3] = ADDATA3_reg,
+ [AD4] = ADDATA4_reg,
+ [AD5] = ADDATA5_reg,
+ [AD6] = ADDATA6_reg,
+ [AD7] = ADDATA7_reg,
+};
+
+typedef struct mf624_state_t {
+ int device_fd;
+ char *uio_dev;
+ bar_mapping_t bar0;
+ bar_mapping_t bar2;
+ bar_mapping_t bar4;
+ int status;
+ int ADC_enabled; // Which ADCs are enabled
+} mf624_state_t;
-#define BAR0_offset (0 * sysconf(_SC_PAGESIZE))
-#define BAR2_offset (1 * sysconf(_SC_PAGESIZE))
-#define BAR4_offset (2 * sysconf(_SC_PAGESIZE))
+#define MFST2REG(mfst, bar_num, reg_offs) \
+ ((void *)(mfst->bar##bar_num.virt_addr + (reg_offs)))
-int status;
-void* mf624_BAR0 = NULL;
-void* mf624_BAR2 = NULL;
-void* mf624_BAR4 = NULL;
-int BAR0_phys_addr;
-int BAR2_phys_addr;
-int BAR4_phys_addr;
+mf624_state_t mf624_state;
+/* Print one byte as binary number */
void print_8bin(int nr)
{
int i;
printf("\n");
}
-static inline int16_t mf624_read16(uint16_t *ptr)
+static inline int16_t mf624_read16(void *ptr)
{
- return (volatile uint16_t) *ptr;
+ return *(volatile uint16_t*)ptr;
}
-static inline int32_t mf624_read32(uint32_t *ptr)
+static inline int32_t mf624_read32(void *ptr)
{
- return (volatile uint32_t) *ptr;
+ return *(volatile uint32_t*) ptr;
}
-static inline void mf624_write16(uint16_t val, uint16_t *ptr)
+static inline void mf624_write16(uint16_t val, void *ptr)
{
*(volatile uint16_t*) ptr = val;
}
-static inline void mf624_write32(uint32_t val, uint32_t *ptr)
+static inline void mf624_write32(uint32_t val, void *ptr)
{
*(volatile uint32_t*) ptr = val;
}
-void DIO_set(int16_t val)
+void DIO_write(mf624_state_t* mfst, int16_t val)
+{
+ mf624_write16(val, MFST2REG(mfst, 2, DOUT_reg));
+}
+
+uint16_t DIO_read(mf624_state_t* mfst)
+{
+ return mf624_read16(MFST2REG(mfst, 2, DIN_reg)) & 0xFF;
+}
+
+void DAC_enable(mf624_state_t* mfst)
+{
+ // Setting DACEN and LDAC bits in GPIO register influences all DACs
+ mf624_write32((mf624_read32(MFST2REG(mfst, 0, GPIOC_reg))
+ | GPIOC_DACEN_mask) // enable output
+ & ~GPIOC_LDAC_mask, // enable conversion
+ MFST2REG(mfst, 0, GPIOC_reg));
+}
+
+int DAC_write(mf624_state_t* mfst, dac_channel_t channel, int val)
+{
+ if (channel > sizeof(dac_channel2reg)/sizeof(*dac_channel2reg))
+ return -1;
+
+ mf624_write16(val, MFST2REG(mfst, 2, dac_channel2reg[channel]));
+ return 0;
+}
+
+int ADC_enable(mf624_state_t* mfst, adc_channel_t channel)
{
- mf624_write16(val, ((uint16_t*) mf624_BAR2) + (DOUT_reg/2));
+ mfst->ADC_enabled = 0;
+
+ if (channel > sizeof(adc_channel2reg)/sizeof(*adc_channel2reg))
+ return -1;
+
+ mfst->ADC_enabled = (1 << channel);
+
+ mfst->ADC_enabled &= 0xFF;
+ mf624_write16(mfst->ADC_enabled, MFST2REG(mfst, 2, ADCTRL_reg));
+ //print_8bin(ADC_enabled);
+
+ return 0;
}
-uint16_t DIO_read()
+/* This function blocks until conversion is finished */
+double ADC_read(mf624_state_t* mfst, adc_channel_t channel)
{
- return mf624_read16(((uint16_t*) mf624_BAR2) + (DOUT_reg/2)) & 0xF;
+ volatile int i;
+ int result;
+
+ // Activate trigger to start conversion
+ mf624_read16(MFST2REG(mfst, 2, ADSTART_reg));
+
+ // Check if conversion has finished
+ while((mf624_read32(MFST2REG(mfst, 0, GPIOC_reg)) & GPIOC_EOLC_mask)) {
+ for (i = 0; i < 1000; i++) {} // small wait
+ }
+
+ ADC_enable(mfst, channel);
+ result = mf624_read16(MFST2REG(mfst, 2, ADDATA0_reg));
+
+ return 10.0 * ((int16_t) (result << 2)) / (double) 0x8000;
}
+
int open_device(char* path) {
- status = open(path, O_RDWR);
- if (status == -1) {
+ int device_fd;
+
+ device_fd = open(path, O_RDWR | O_SYNC);
+ if (device_fd == -1) {
perror("open()");
return -1;
}
- return status;
+ return device_fd;
}
void wait_for_interrupts(int device_fd)
int disable_interrupts(int device_fd)
{
uint32_t control_value = 0;
+ int status;
status = write(device_fd, &control_value, sizeof(uint32_t));
if (status == -1) {
int enable_interrupts(int device_fd)
{
uint32_t control_value = 1;
+ int status;
status = write(device_fd, &control_value, sizeof(uint32_t));
if (status == -1) {
void list_available_mem_regions(char* device)
{
+ int status;
char path[] = "/sys/class/uio/";
char subdir[] = "/maps/";
char directory[BUFF_MID];
perror("closedir()");
return;
}
-
+
}
void list_available_io_ports(char *device)
{
+ int status;
char path[] = "/sys/class/uio/";
char subdir[] = "/portio/";
char directory[BUFF_MID];
void run_simple_tests(char* dev_name)
{
+ int status;
int device_fd;
char buff[BUFF_SMALL];
memset(buff, '\0', BUFF_SMALL);
if (status != -1) {
printf(" Probably OK\n");
}
-
+
printf("Tring to disable interrupts\n");
status = disable_interrupts(device_fd);
if (status != -1) {
list_available_io_ports(dev_name);
}
-void mmap_regions(int device_fd, char* uio_dev)
+int mmap_regions(mf624_state_t* mfst)
{
- char path[BUFF_MID];
- FILE *file;
-
- sprintf(path, "/sys/class/uio/%s/maps/map0/addr", uio_dev);
- file = fopen(path, "rb");
- fscanf(file, "%llx", &BAR0_phys_addr);
- fclose(file);
-
- sprintf(path, "/sys/class/uio/%s/maps/map1/addr", uio_dev);
- file = fopen(path, "rb");
- fscanf(file, "%llx", &BAR2_phys_addr);
- fclose(file);
-
- sprintf(path, "/sys/class/uio/%s/maps/map2/addr", uio_dev);
- file = fopen(path, "rb");
- fscanf(file, "%llx", &BAR4_phys_addr);
- fclose(file);
-
+ if (bar_mapping_fill(&mfst->bar0, mfst->uio_dev, 0) < 0) {
+ fprintf(stderr, "%s bar0 mapping fill failed\n", mfst->uio_dev);
+ return -1;
+ }
- //FIXME size of memory must be in multiples of memory pages
- // size = (size + PAGE_SIZE -1) / PAGE_SIZE * PAGE_SIZE; ?
- mf624_BAR0 = mmap(0, 1 * sysconf(_SC_PAGESIZE), PROT_READ | PROT_WRITE, MAP_SHARED, device_fd, BAR0_offset);
- if (mf624_BAR2 == MAP_FAILED) {
- perror("mmap()");
+ if (bar_mapping_fill(&mfst->bar2, mfst->uio_dev, 1) < 0) {
+ fprintf(stderr, "%s bar2 mapping fill failed\n", mfst->uio_dev);
+ return -1;
}
- mf624_BAR0 += (BAR0_phys_addr & (sysconf(_SC_PAGESIZE) - 1));
+ if (bar_mapping_fill(&mfst->bar4, mfst->uio_dev, 2) < 0) {
+ fprintf(stderr, "%s bar4 mapping fill failed\n", mfst->uio_dev);
+ return -1;
+ }
- mf624_BAR2 = mmap(0, 1 * sysconf(_SC_PAGESIZE), PROT_READ | PROT_WRITE, MAP_SHARED, device_fd, BAR2_offset);
- if (mf624_BAR2 == MAP_FAILED) {
- perror("mmap()");
+ if (bar_mapping_setup(&mfst->bar0, mfst->device_fd) < 0) {
+ fprintf(stderr, "%s bar0 mapping setup failed\n", mfst->uio_dev);
+ return -1;
}
- mf624_BAR2 += (BAR2_phys_addr & (sysconf(_SC_PAGESIZE) - 1));
+ if (bar_mapping_setup(&mfst->bar2, mfst->device_fd) < 0) {
+ fprintf(stderr, "%s bar2 mapping setup failed\n", mfst->uio_dev);
+ return -1;
+ }
- mf624_BAR4 = mmap(0, 1 * sysconf(_SC_PAGESIZE), PROT_READ | PROT_WRITE, MAP_SHARED, device_fd, BAR4_offset);
- if (mf624_BAR2 == MAP_FAILED) {
- perror("mmap()");
+ if (bar_mapping_setup(&mfst->bar4, mfst->device_fd) < 0) {
+ fprintf(stderr, "%s bar4 mapping setup failed\n", mfst->uio_dev);
+ return -1;
}
- mf624_BAR4 += (BAR4_phys_addr & (sysconf(_SC_PAGESIZE) - 1));
- /*
- printf("BAR0_phys_addr = %x\n", BAR0_phys_addr);
- printf("BAR2_phys_addr = %x\n", BAR2_phys_addr);
- printf("BAR4_phys_addr = %x\n", BAR4_phys_addr);
+#if 1
+ printf("bar0.phys_addr = %"PRIxPTR"\n", mfst->bar0.phys_addr);
+ printf("bar2.phys_addr = %"PRIxPTR"\n", mfst->bar2.phys_addr);
+ printf("bar4.phys_addr = %"PRIxPTR"\n", mfst->bar4.phys_addr);
+
+ printf("bar0.virt_addr = %"PRIxPTR"\n", mfst->bar0.virt_addr);
+ printf("bar2.virt_addr = %"PRIxPTR"\n", mfst->bar2.virt_addr);
+ printf("bar4.virt_addr = %"PRIxPTR"\n", mfst->bar4.virt_addr);
+#endif
- printf("mf624_BAR0 = %x\n", mf624_BAR0);
- printf("mf624_BAR2 = %x\n", mf624_BAR2);
- printf("mf624_BAR4 = %x\n", mf624_BAR4);
- */
+ return 0;
}
int main(int argc, char* argv[])
{
- int device_fd;
+ mf624_state_t* mfst = &mf624_state;
char buff[BUFF_SMALL];
memset(buff, '\0', BUFF_SMALL);
if (argc < 2) {
printf("Usage: %s UIO_DEVICE\n UIO_DEVICE\tname of uio device in /dev\n", argv[0]);
return 1;
- }
+ }
+
+ mfst->uio_dev = argv[1];
strncat(buff, "/dev/", 5);
- strncat(buff, argv[1], min(strlen(argv[1]), 8));
+ strncat(buff, mfst->uio_dev, sizeof(buff) - 6);
- device_fd = open_device(buff);
- mmap_regions(device_fd, argv[1]);
+ mfst->device_fd = open_device(buff);
+ if (mfst->device_fd < 0) {
+ fprintf(stderr, "%s open failed (%s)!\n", mfst->uio_dev, strerror(errno));
+ return 2;
+ }
+ if (mmap_regions(mfst) < 0) {
+ fprintf(stderr, "%s mmap_regions failed (%s)!\n", mfst->uio_dev, strerror(errno));
+ return 2;
+ }
- if (device_fd != -1) {
+ DAC_enable(mfst);
- while (1){
- printf("Reading DIO = ");
- print_8bin(DIO_read());
-
- printf("Setting DIO to 0xff\n");
- DIO_set(0xff);
- sleep(1);
-
- printf("Setting DIO to 0x00\n");
- DIO_set(0x00);
- sleep(1);
- }
+ while (1){
+ printf("Reading DIO: ");
+ print_8bin(DIO_read(mfst));
+ sleep(1);
+
+ printf("Setting DA1 to 10 V\n");
+ DAC_write(mfst, DA1, 0x3FFF);
+ sleep(1);
+
+ printf("Reading ADC0: ");
+ printf("%f V\n", ADC_read(mfst, AD0));
+ sleep(1);
+
+ printf("Reading ADC1: ");
+ printf("%f V\n", ADC_read(mfst, AD1));
+ sleep(1);
+
+ printf("Setting DIO to 0xff\n");
+ DIO_write(mfst, 0xff);
+ sleep(1);
+
+ printf("Setting DIO to 0x00\n");
+ DIO_write(mfst, 0x00);
+ sleep(1);
+
+ printf("Setting DA1 to 5 V\n");
+ DAC_write(mfst, DA1, 0x3000);
+ sleep(1);
+
+ printf("Reading ADC0: ");
+ printf("%f V\n", ADC_read(mfst, AD0));
+ sleep(1);
+
+ printf("Reading ADC1: ");
+ printf("%f V\n", ADC_read(mfst, AD1));
+ sleep(1);
+ printf("----------------------\n\n");
}