Quantcast
Channel: Raspberry Pi Forums
Viewing all articles
Browse latest Browse all 4455

C/C++ • RPi4 GPClock no output

$
0
0
I'm attempting to generate a clock signal to drive external device. However I'm unable to see a signal with the below given code after several hours of trying different changes. Would someone be able to do a sanity check to see if I'm missing something obvious?

rpi4.hpp

Code:

/** * NOTE: PI4 ONLY!!!! (BCM2711) * convience methods for setting RPI 4 registers that are not exposed in the generic linux gpiod library * * influence for clocks taken from below link, modified for pi4 * https://forums.raspberrypi.com/viewtopic.php?t=22274&sid=86e37b8b410e76419751ebaa1879d719 */#pragma once#include <cmath>#include <fcntl.h>#include <stdexcept>#include <stdint.h>#include <sys/mman.h>#include <unistd.h>class Rpi4 {public:    enum class GP_CLOCK {        GPC_CLOCK0 = 0x70,        GPC_CLOCK1 = 0x78,        GPC_CLOCK2 = 0x80    };    enum class CLOCK_SOURCE {        CS_OSCILLATOR = 1, // 54 MHz        CS_PLLC = 5,       // 1000 MHz        CS_PLLD = 6,       // 750 MHz        CS_HDMI_AUX = 7    // 216 MHz    };    enum class GPIO_MODE {        GM_INPUT = 0,        GM_OUTPUT = 1,        GM_ALT0 = 4,        GM_ALT1 = 5,        GM_ALT2 = 6,        GM_ALT3 = 7,        GM_ALT4 = 3,        GM_ALT5 = 2    };    enum class GPIO_LEVEL {        GL_OFF = 0,        GL_ON = 1    };    Rpi4() {        this->initialize();    }    void gpioSetLevel(const uint8_t gpio, const GPIO_LEVEL level) {        if (level == GPIO_LEVEL::GL_OFF) {            *(this->_gpioRegisters + GPCLR0 + (gpio >> 5)) = (1 << (gpio & 0x1F));        } else {            *(this->_gpioRegisters + GPSET0 + (gpio >> 5)) = (1 << (gpio & 0x1F));        }    }    void gpioSetMode(const uint8_t gpio, const GPIO_MODE mode) {        uint32_t reg = gpio / 10;         // each sel register covers 10 pins each w/ 3 bits (32 bits total)        uint32_t shift = (gpio % 10) * 3; // we will shift to appropriate location for this gpio in register (3 bit values)        uint32_t value = *(this->_gpioRegisters + reg);        uint32_t shftdMode = ((uint32_t)mode) << shift;        uint32_t registerClr = ~(7 << shift);        *(this->_gpioRegisters + reg) = (*(this->_gpioRegisters + reg) & registerClr) | shftdMode;    }    GPIO_MODE gpioGetMode(const uint8_t gpio) {        uint32_t reg = gpio / 10;        uint32_t shift = (gpio % 10) * 3;        uint32_t value = (*(this->_gpioRegisters + reg) >> shift) & 7;        return static_cast<GPIO_MODE>(value);    }    /**     * this method attempts to get the clock as close to the target frequency as possible. Please NOTE that it doesn't attempt to utilize mash divider to get     * fractional clock divider (TODO: add feature in future), and therefore the clock rate will be as close as possible using whole number dividers.     */    int startClock(const GP_CLOCK clock, const uint32_t targetFrequencyHz) {        // since lowest divI value allowed is 2 and highest is 4095, we need to restrict the requested frequencies        if (targetFrequencyHz < 13187 || targetFrequencyHz > 125e6) {            throw std::runtime_error("target frequency requires fractional dividers that have not been implemented. Please select a frequency between 13187 Hz and 125 MHz");        }        // TODO: improve as this will not always select the best clock for getting the desired target frequency        // determine best clock source        CLOCK_SOURCE clkSrc = CLOCK_SOURCE::CS_OSCILLATOR;        CLOCK_SOURCE clkSrcMap[4] = {            CLOCK_SOURCE::CS_OSCILLATOR,            CLOCK_SOURCE::CS_PLLC,            CLOCK_SOURCE::CS_PLLD,            CLOCK_SOURCE::CS_HDMI_AUX        };        uint32_t clkIdx = 0;        for (clkIdx = 0; clkIdx < 4; clkIdx++) {            if (targetFrequencyHz < CLOCK_SOURCE_FREQUENCIES[clkIdx]) {                clkSrc = clkSrcMap[clkIdx];                break;            }        }        // calculate divider required to get closest to target frequency        uint32_t divI = std::ceil(CLOCK_SOURCE_FREQUENCIES[clkIdx] / targetFrequencyHz);        uint32_t freq = CLOCK_SOURCE_FREQUENCIES[clkIdx] / divI;        std::cout << "frequency will be " << std::to_string(freq) << " Hz" << std::endl;        if (divI < 2 || divI > 4095) {            throw std::runtime_error("invalid clock divI value provided (2-4095)");        }        // TODO: implement        uint32_t divF = 0;        uint32_t mash = 0;        if (divF < 0 || divF > 4095) {            throw std::runtime_error("invlaid clock divF value provided (0-4095)");        }        if (mash < 0 || mash > 3) {            throw std::runtime_error("invalid clock mash value provided (0-3)");        }        uint32_t clkCtl = (int)clock;        uint32_t clkDiv = clkCtl + 4; // div is 4 more than the ctl address        this->printClockCtlValue();        this->_clkRegisters[clkCtl] = CLK_PASSWD & CLK_CTL_DISABLE;        this->printClockCtlValue();        while (this->_clkRegisters[clkCtl] & CLK_CTL_BUSY) {            usleep(10);        }        uint32_t divValue = Rpi4::getClockDivIntValue(divI);        divValue |= Rpi4::getClockDivFractionalValue(divF);        divValue |= CLK_PASSWD;        this->_clkRegisters[clkDiv] = divValue;        usleep(10);        this->printClockCtlValue();        uint32_t ctlValue = Rpi4::getClockMashValue(mash);        ctlValue |= Rpi4::getClockSourceValue(static_cast<uint32_t>(clkSrc));        ctlValue |= CLK_PASSWD;        this->_clkRegisters[clkCtl] = ctlValue;        this->printClockCtlValue();        usleep(10);        this->_clkRegisters[clkCtl] |= (CLK_PASSWD | CLK_CTL_ENABLE);        this->printClockCtlValue();    }    void printClockCtlValue() {        uint32_t value = this->_clkRegisters[112];        std::cout << "clock 0: " << std::to_string(value) << std::endl;    }    int stopClock(const GP_CLOCK clock) {        uint32_t clkCtl = (int)clock;        this->_clkRegisters[clkCtl] = CLK_PASSWD | CLK_CTL_KILL;        while (this->_clkRegisters[clkCtl] & CLK_CTL_BUSY) {            usleep(10);        }    }private:    static constexpr uint32_t CLOCK_BASE_ADDRESS = 0x7E101000;    static constexpr uint32_t GPIO_BASE_ADDRESS = 0x7E200000;    static constexpr uint32_t CLOCK_MEMORY_LENGTH = 0xA8;    static constexpr uint32_t GPIO_MEMORY_LENGTH = 0xB4;    static constexpr uint32_t CLOCK_SOURCE_FREQUENCIES[4] = {        // frequency index must align with above clock source enum index        54e6,   // oscillator        1000e6, // PLLC        750e6,  // PLLD        216e6   // HDMI AUX    };    static constexpr uint8_t CLK_GP0_CTL = 0x70;    static constexpr uint8_t CLK_GP0_DIV = 0x74;    static constexpr uint8_t CLK_GP1_CTL = 0x78;    static constexpr uint8_t CLK_GP1_DIV = 0x7C;    static constexpr uint8_t CLK_GP2_CTL = 0x80;    static constexpr uint8_t CLK_GP2_DIV = 0x84;    static constexpr uint32_t CLK_PASSWD = 0x5A << 24;    static constexpr uint32_t CLK_CTL_BUSY = 0x01 << 7;    static constexpr uint32_t CLK_CTL_KILL = 0x01 << 5;    static constexpr uint32_t CLK_CTL_ENABLE = 0x01 << 4;    static constexpr uint32_t CLK_CTL_DISABLE = ~(0x01 << 4);    static constexpr uint8_t GPCLR0 = 10;    static constexpr uint8_t GPSET0 = 7;    uint32_t* _gpioRegisters = (uint32_t*)MAP_FAILED;    uint32_t* _clkRegisters = (uint32_t*)MAP_FAILED;    static uint32_t getClockMashValue(uint32_t mash) {        return mash << 9;    }    static uint32_t getClockSourceValue(uint32_t src) {        return src << 0;    }    static uint32_t getClockDivIntValue(uint32_t divI) {        return divI << 12;    }    static uint32_t getClockDivFractionalValue(uint32_t divF) {        return divF << 0;    }    static uint32_t* initializeMemoryMap(const uint32_t fd, const uint32_t address, const uint32_t length) {        return (uint32_t*)mmap(0, length, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_LOCKED, fd, address);    }    uint32_t initialize() {        int fd = open("/dev/gpiomem", O_RDWR | O_SYNC); // /dev/gpiomem doesn't require root whereas /dev/mem does (just be member of gpio group)        if (fd < 0) {            throw std::runtime_error("failed to access gpio memory map");        }        this->_gpioRegisters = Rpi4::initializeMemoryMap(fd, GPIO_BASE_ADDRESS, GPIO_MEMORY_LENGTH);        close(fd);        int genMemFd = open("/dev/mem", O_RDWR | O_SYNC); // unfortunately requires run as sudo        if (fd < 0) {            throw std::runtime_error("failed to access memory map");        }        this->_clkRegisters = Rpi4::initializeMemoryMap(genMemFd, CLOCK_BASE_ADDRESS, CLOCK_MEMORY_LENGTH);        close(genMemFd);        if (this->_gpioRegisters == MAP_FAILED || this->_clkRegisters == MAP_FAILED) {            throw std::runtime_error("register memory map failed");        }        return 0;    }};
main.cpp

Code:

#include <iostream>#include <unistd.h>#include "./rpi4.hpp"int main(int argc, const char** argv) {    // rpi clock    Rpi4 rpi4;    rpi4.startClock(Rpi4::GP_CLOCK::GPC_CLOCK0, 50000);    rpi4.gpioSetMode(4, Rpi4::GPIO_MODE::GM_ALT0); // pin 4 as gp clock    std::cout << "mode: " << std::to_string((uint32_t)rpi4.gpioGetMode(4)) << std::endl;    std::cout << "clock initialized" << std::endl;    // rpi4.gpioSetMode(4, Rpi4::GPIO_MODE::GM_OUTPUT); // pin 4 as gp clock    while (true) {        // rpi4.gpioSetLevel(4, Rpi4::GPIO_LEVEL::GL_ON);        // usleep(10);        // rpi4.gpioSetLevel(4, Rpi4::GPIO_LEVEL::GL_OFF);        // usleep(10);        usleep(100);    }}
Output:
file descriptor: 13
frequency will be 50000 Hz
clock 0: 1509949457
clock 0: 1509949440
clock 0: 1509949440
clock 0: 1509949441
clock 0: 1509949457
mode: 4
clock initialized

but no clock signal on my OScope.

Any help would be appreciated. I've check and double checked my register values for BCM2711, but that's all I can figure is I have an incorrect address maybe?

Statistics: Posted by weagle08 — Tue Nov 19, 2024 4:08 am — Replies 0 — Views 18



Viewing all articles
Browse latest Browse all 4455

Trending Articles