/* * pwm.c: Simple program control hardware PWM on Omega2 * * Copyright (C) 2017, Alexey 'Cluster' Avdyukhin (clusterrr@clusterrr.com) * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #define printerr(fmt,...) do { fprintf(stderr, fmt, ## __VA_ARGS__); fflush(stderr); } while(0) #define PWM_ENABLE 0x10005000 // PWM Enable register #define PWM0_CON 0x10005010 // PWM0 Control register #define PWM0_HDURATION 0x10005014 // PWM0 High Duration register #define PWM0_LDURATION 0x10005018 // PWM0 Low Duration register #define PWM0_GDURATION 0x1000501C // PWM0 Guard Duration register #define PWM0_SEND_DATA0 0x10005030 // PWM0 Send Data0 register #define PWM0_SEND_DATA1 0x10005034 // PWM0 Send Data1 register #define PWM0_WAVE_NUM 0x10005038 // PWM0 Wave Number register #define PWM0_DATA_WIDTH 0x1000503C // PWM0 Data Width register #define PWM0_THRESH 0x10005040 // PWM0 Thresh register #define PWM0_SEND_WAVENUM 0x10005044 // PWM0 Send Wave Number register #define CLKSEL_100KHZ 0b00000000 #define CLKSEL_40MHZ 0b00001000 #define CLKDIV_1 0b00000000 #define CLKDIV_2 0b00000001 #define CLKDIV_4 0b00000010 #define CLKDIV_8 0b00000011 #define CLKDIV_16 0b00000100 #define CLKDIV_32 0b00000101 #define CLKDIV_64 0b00000110 #define CLKDIV_128 0b00000111 uint32_t devmem(uint32_t target, uint8_t size, uint8_t write, uint32_t value) { int fd; void *map_base, *virt_addr; uint32_t pagesize = (unsigned)getpagesize(); /* or sysconf(_SC_PAGESIZE) */ uint32_t map_size = pagesize; uint32_t offset; uint32_t result; offset = (unsigned int)(target & (pagesize-1)); if (offset + size > pagesize ) { // Access straddles page boundary: add another page: map_size += pagesize; } fd = open("/dev/mem", O_RDWR | O_SYNC); if (fd == -1) { printerr("Error opening /dev/mem (%d) : %s\n", errno, strerror(errno)); exit(1); } map_base = mmap(0, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, target & ~((typeof(target))pagesize-1)); if (map_base == (void *) -1) { printerr("Error mapping (%d) : %s\n", errno, strerror(errno)); exit(1); } virt_addr = map_base + offset; if (write) { switch (size) { case 1: *((volatile uint8_t *) virt_addr) = value; break; case 2: *((volatile uint16_t *) virt_addr) = value; break; case 4: *((volatile uint32_t *) virt_addr) = value; break; } } switch (size) { case 1: result = *((volatile uint8_t *) virt_addr); break; case 2: result = *((volatile uint16_t *) virt_addr); break; case 4: result = *((volatile uint32_t *) virt_addr); break; } if (munmap(map_base, map_size) != 0) { printerr("ERROR munmap (%d) %s\n", errno, strerror(errno)); exit(1); } close(fd); return result; } static void usage(const char *cmd) { fprintf(stderr, "\nUsage:\t%s [duty]\n", cmd); } static uint32_t get_base_freq(uint16_t mode) { int i; uint32_t r; if (!(mode & CLKSEL_40MHZ)) r = 100000; else r = 40000000; for (i = 0; i < (mode & 0b00000111); i++) r /= 2; return r; } static void pwm_raw(uint8_t channel, uint16_t mode, uint16_t duration_h, uint16_t duration_l) { uint32_t enable = devmem(PWM_ENABLE, 4, 0, 0); enable &= ~((uint32_t)(1< 3)) { fprintf(stderr, "Invalid channel number\n"); exit(1); } if (sscanf(argv[2], "%d", &freq) != 1) { fprintf(stderr, "Invalid frequency number\n"); exit(1); } if (argc >= 4) { if ((sscanf(argv[3], "%d", &duty) != 1) || (duty > 100)) { fprintf(stderr, "Invalid duty number\n"); exit(1); } } return pwm(channel, freq, duty); }