Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // IGEP v2 pwm-demo rev.F --- Demonstrate usage of the dm3730-pwm library
- // Based on the omap3730-pwm-demo by Mark A. Yoder
- // Modified by Pavel Kopylov 29-June-2016
- // Copyright (c) 2010 Thomas W. Most <twm@freecog.net>
- //
- // The contents of this file may be used subject to the terms of either of the
- // following licenses:
- //
- // GNU LGPL 2.1 license:
- //
- // This library is free software; you can redistribute it and/or modify it
- // under the terms of the GNU Lesser General Public License as published by the
- // Free Software Foundation; either version 2.1 of the License, or (at your
- // option) any later version.
- //
- // This library 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 Lesser General Public License
- // for more details.
- //
- // You should have received a copy of the GNU Lesser General Public License
- // along with this library; if not, write to the Free Software Foundation,
- // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- //
- // MIT license:
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- #include <glib.h>
- #include <unistd.h>
- #include <stdio.h>
- #include <fcntl.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <sys/mman.h>
- #include <errno.h>
- #include "dm3730-pwm.h"
- // Clock configuration registers (TRM p. 470)
- #define CM_FCLKEN_PER 0x48005000
- #define FCLKEN_GPT8_MASK (1 << 9)
- #define CM_ICLKEN_PER 0x48005010
- #define ICLKEN_GPT8_MASK (1 << 9)
- #define CM_IDLEST_PER 0x48005020
- #define IDLEST_GPT8_MASK (1 << 9)
- #define CM_AUTOIDLE_PER 0x48005030
- #define AUTOIDLE_GPT8_MASK (1 << 9)
- #define CM_CLKSEL_PER 0x48005040
- #define CLKSEL_GPT8_MASK (1 << 6)
- // GPTIMER register offsets
- #define GPTIMER8 0x4903E000
- #define GPT_REG_TCLR 0x024
- #define GPT_REG_TCRR 0x028
- #define GPT_REG_TLDR 0x02c
- #define GPT_REG_TMAR 0x038
- // Get a guint32 pointer to the register in block `instance` at byte
- // offset `offset`.
- #define REG32_PTR(instance, offset) ((volatile guint32*) (instance + offset))
- // The default Linux page size is 4k and the GP timer register
- // blocks are aligned to 4k. Therefore it is convenient to just
- // assume that pages are aligned there for the purposes of mmap()
- // (since mmap only maps aligned pages). This function checks
- // that assumption and aborts if it is untrue.
- static void check_pagesize(void)
- {
- if (getpagesize() != 4096)
- {
- g_error("The page size is %d. Must be 4096.", getpagesize());
- }
- }
- // Simply a wrapper around mmap that passes the correct arguments
- // for mapping a register block. `instance_number` must be between
- // 1 and 12, or errno will be set to EDOM and MAP_FAILED returned.
- // Otherwise the return value is that of `mmap()`.
- guint8* pwm_mmap_instance(int mem_fd)
- {
- guint8* instance;
- instance = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, GPTIMER8);
- printf("%s: 0x%08X mmaped --> 0x%08X\n", __func__, GPTIMER8, (guint32)instance);
- return instance;
- }
- // The inverse of `pwm_mmap_instance()`, this is simply a wrapper
- // arount `munmap()`. It returns the underlying `munmap()` call's
- // return value.
- int pwm_munmap_instance(guint8 *instance)
- {
- return munmap(instance, 4096);
- }
- // Configure the clocks for GPTIMER8, which can be set to
- // use the 13 MHz system clock (otherwise they use the 32 kHz clock like
- // the rest of the timers). Return -1 on failure, with errno set.
- int pwm_config_clock(int mem_fd, gboolean gptimer_13mhz)
- {
- int page_addr = CM_FCLKEN_PER & 0xfffff000;
- int offset_FCLKEN = CM_FCLKEN_PER & 0xfff;
- int offset_ICLKEN = CM_ICLKEN_PER & 0xfff;
- int offset_IDLEST = CM_IDLEST_PER & 0xfff;
- int offset_AUTOIDLE = CM_AUTOIDLE_PER & 0xfff;
- int offset_CLKSEL = CM_CLKSEL_PER & 0xfff;
- guint32 value;
- printf("%s\n", __func__);
- guint8 *registers = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, page_addr);
- if (registers == MAP_FAILED)
- {
- return -1;
- }
- printf("%s: 0x%08X mmaped --> 0x%08X\n", __func__, page_addr, (guint32)registers);
- value = *REG32_PTR(registers, offset_FCLKEN);
- printf("%s(0x%08X): CM_FCLKEN_PER = 0x%08X, EN_GPT8 = %d\n",
- __func__, (guint32)registers + offset_FCLKEN, value, value & FCLKEN_GPT8_MASK ? 1 : 0);
- value = *REG32_PTR(registers, offset_ICLKEN);
- printf("%s(0x%08X): CM_ICLKEN_PER = 0x%08X, EN_GPT8 = %d\n",
- __func__, (guint32)registers + offset_ICLKEN, value, value & ICLKEN_GPT8_MASK ? 1 : 0);
- value = *REG32_PTR(registers, offset_IDLEST);
- printf("%s(0x%08X): CM_IDLEST_PER = 0x%08X, ST_GPT8 = %d\n",
- __func__, (guint32)registers + offset_IDLEST, value, value & IDLEST_GPT8_MASK ? 1 : 0);
- value = *REG32_PTR(registers, offset_AUTOIDLE);
- printf("%s(0x%08X): CM_AUTOIDLE_PER = 0x%08X, AUTO_GPT8 = %d\n",
- __func__, (guint32)registers + offset_AUTOIDLE, value, value & AUTOIDLE_GPT8_MASK ? 1 : 0);
- value = *REG32_PTR(registers, offset_CLKSEL);
- printf("%s(0x%08X): CM_CLKSEL_PER = 0x%08X, CLKSEL_GPT8 = %d\n",
- __func__, (guint32)registers + offset_CLKSEL, value, value & CLKSEL_GPT8_MASK ? 1 : 0);
- /* -------------------------- */
- value = *REG32_PTR(registers, offset_FCLKEN);
- value |= FCLKEN_GPT8_MASK;
- *REG32_PTR(registers, offset_FCLKEN) = value;
- value = *REG32_PTR(registers, offset_ICLKEN);
- value |= ICLKEN_GPT8_MASK;
- *REG32_PTR(registers, offset_ICLKEN) = value;
- value = *REG32_PTR(registers, offset_CLKSEL);
- value &= ~(CLKSEL_GPT8_MASK);
- if (gptimer_13mhz) value |= CLKSEL_GPT8_MASK;
- *REG32_PTR(registers, offset_CLKSEL) = value;
- printf("--------------------------\n");
- value = *REG32_PTR(registers, offset_FCLKEN);
- printf("%s(0x%08X): CM_FCLKEN_PER = 0x%08X, EN_GPT8 = %d\n",
- __func__, (guint32)registers + offset_FCLKEN, value, value & FCLKEN_GPT8_MASK ? 1 : 0);
- value = *REG32_PTR(registers, offset_ICLKEN);
- printf("%s(0x%08X): CM_ICLKEN_PER = 0x%08X, EN_GPT8 = %d\n",
- __func__, (guint32)registers + offset_ICLKEN, value, value & ICLKEN_GPT8_MASK ? 1 : 0);
- value = *REG32_PTR(registers, offset_IDLEST);
- printf("%s(0x%08X): CM_IDLEST_PER = 0x%08X, ST_GPT8 = %d\n",
- __func__, (guint32)registers + offset_IDLEST, value, value & IDLEST_GPT8_MASK ? 1 : 0);
- value = *REG32_PTR(registers, offset_AUTOIDLE);
- printf("%s(0x%08X): CM_AUTOIDLE_PER = 0x%08X, AUTO_GPT8 = %d\n",
- __func__, (guint32)registers + offset_AUTOIDLE, value, value & AUTOIDLE_GPT8_MASK ? 1 : 0);
- value = *REG32_PTR(registers, offset_CLKSEL);
- printf("%s(0x%08X): CM_CLKSEL_PER = 0x%08X, CLKSEL_GPT8 = %d\n",
- __func__, (guint32)registers + offset_CLKSEL, value, value & CLKSEL_GPT8_MASK ? 1 : 0);
- return munmap(registers, 4096);
- }
- // Calculate the resolution of the PWM (the number of clock ticks
- // in the period), which is passed to `pwm_config_timer()`.
- guint32 pwm_calc_resolution(int pwm_frequency, int clock_frequency)
- {
- float pwm_period = 1.0 / pwm_frequency;
- float clock_period = 1.0 / clock_frequency;
- printf("%s\n", __func__);
- return (guint32) (pwm_period / clock_period);
- }
- // Initialize the control registers of the specified timer
- // instance for PWM at the specified resolution.
- void pwm_config_timer(guint8 *registers, guint32 resolution, float duty_cycle)
- {
- guint32 counter_start = 0xffffffff - resolution;
- guint32 dc = 0xffffffff - ((guint32) (resolution * duty_cycle));
- guint32 value;
- // Edge condition: the duty cycle is set within two units of the overflow
- // value. Loading the register with this value shouldn't be done (TRM 16.2.4.6).
- if (0xffffffff - dc <= 2)
- {
- dc = 0xffffffff - 2;
- }
- // Edge condition: TMAR will be set to within two units of the overflow
- // value. This means that the resolution is extremely low, which doesn't
- // really make sense, but whatever.
- if (0xffffffff - counter_start <= 2)
- {
- counter_start = 0xffffffff - 2;
- }
- *REG32_PTR(registers, GPT_REG_TCLR) = 0; // Turn off
- value = *REG32_PTR(registers, GPT_REG_TCLR);
- printf("%s: GPT_REG_TCLR (0x%08X) = 0x%08X\n", __func__, (guint32)registers + GPT_REG_TCLR, value);
- *REG32_PTR(registers, GPT_REG_TCRR) = counter_start;
- value = *REG32_PTR(registers, GPT_REG_TCRR);
- printf("%s: GPT_REG_TCLR (0x%08X) = 0x%08X\n", __func__, (guint32)registers + GPT_REG_TCRR, value);
- *REG32_PTR(registers, GPT_REG_TLDR) = counter_start;
- value = *REG32_PTR(registers, GPT_REG_TLDR);
- printf("%s: GPT_REG_TLDR (0x%08X) = 0x%08X\n", __func__, (guint32)registers + GPT_REG_TLDR, value);
- *REG32_PTR(registers, GPT_REG_TMAR) = dc;
- value = *REG32_PTR(registers, GPT_REG_TMAR);
- printf("%s: GPT_REG_TMAR (0x%08X) = 0x%08X\n", __func__, (guint32)registers + GPT_REG_TMAR, value);
- *REG32_PTR(registers, GPT_REG_TCLR) = (
- (1 << 0) | // ST -- enable counter
- (1 << 1) | // AR -- autoreload on overflow
- (1 << 6) | // CE -- compare enabled
- (1 << 7) | // SCPWM -- invert pulse
- (2 << 10) | // TRG -- overflow and match trigger
- (1 << 12) // PT -- toggle PWM mode
- );
- value = *REG32_PTR(registers, GPT_REG_TCLR);
- printf("%s: GPT_REG_TCLR (0x%08X) = 0x%08X\n", __func__, (guint32)registers + GPT_REG_TCLR, value);
- }
- int pwm_open_devmem(void)
- {
- check_pagesize();
- return open("/dev/mem", O_RDWR | O_SYNC);
- }
- void pwm_close_devmem(int dev_fd)
- {
- /* This function is useful! */
- close(dev_fd);
- }
Add Comment
Please, Sign In to add comment