鱼C论坛

 找回密码
 立即注册
查看: 339|回复: 1

linux开发板驱动使用timer

[复制链接]
发表于 2024-7-7 12:34:51 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
linux开发板驱动使用timer输出800khz,周期1.25us,占空比66%的方波。失败,用示波器测量的结果是周期为125us,占空比为50%。不论怎么改变high_time的值,占空比一直是50%不变,不知道是那有问题,还请小伙伴们指点,谢谢!








main.cpp:

#include "gpio.h"
#include <iostream>
#include <unistd.h>

void generate_pwm(GPIO &gpio, Timer &timer); // 声明 generate_pwm 函数
int main() {
    GPIO gpio;
    gpio.Init();
    Timer timer;
    timer.Init();
    gpio.ConfigPin(PORT::PE, 22, PIN_MODE::OUT);
    gpio.ConfigPin(PORT::PE, 21, PIN_MODE::OUT);

    int a = 0, b = -1;
    for (int i = 0; i < 2; i++) {

        generate_pwm(gpio, timer); // 生成PWM信号
        

        sleep(1);
    }

    gpio.Free();
    return 0;
}



gpio.cpp:


#include "gpio.h"
#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <thread>
#include <chrono>
#include <time.h>  // 添加这个头文件来使用 clock_nanosleep

#define TIMER_BASE 0x01C20C00  // 替换为你的定时器基地址
#define TIMER_CTRL_OFFSET 0x00
#define TIMER_INTV_VALUE_OFFSET 0x04
#define TIMER_CUR_VALUE_OFFSET 0x08

#define GPIO_BASE 0x01C20800  // 替换为你的GPIO基地址
#define GPIO_SET_OFFSET 0x1C
#define GPIO_CLR_OFFSET 0x28

#define PE22 (1 << 22)  // PE22引脚






// GPIO 类的构造函数和析构函数
GPIO::GPIO() : fd(-1), gpio_map(nullptr), PIO(nullptr) {}
// GPIO 类的析构函数
GPIO::~GPIO() {
    Free();// 调用 Free 方法释放资源
}

// 初始化 GPIO
void GPIO::Init() {
    // 打开 /dev/mem 设备文件,以读写方式打开
    if ((fd = open("/dev/mem", O_RDWR)) == -1) {
        std::cerr << "open error /dev/mem" << std::endl;
        return;
    }

    PageSize = sysconf(_SC_PAGESIZE);// 获取系统页面大小
    PageMask = (~(PageSize - 1));// 计算页掩码

    addr_start = PIO_BASE_ADDRESS & PageMask;// 计算映射起始地址
    addr_offset = PIO_BASE_ADDRESS & ~PageMask;// 计算地址偏移量

// 使用 mmap 将设备内存映射到用户空间
    gpio_map = (unsigned int *)mmap(NULL, PageSize * 2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr_start);
    if (gpio_map == MAP_FAILED) {
        std::cerr << "mmap error" << std::endl;
        close(fd);// 映射失败,关闭文件描述符
        return;
    }
//计算GPIO寄存器地址..
    PIO = (PIO_Map *)((unsigned int)gpio_map + addr_offset);
    close(fd);
}

// 配置指定引脚的模式
void GPIO::ConfigPin(PORT port, unsigned int pin, PIN_MODE mode) {
    if (gpio_map == nullptr) return; // 若 gpio_map 为 nullptr,直接返回

    PIO->Pn[port].CFG[pin / 8] &= ~((unsigned int)0x07 << (pin % 8) * 4);
    PIO->Pn[port].CFG[pin / 8] |= ((unsigned int)mode << (pin % 8) * 4);
}

// 设置指定引脚的电平
void GPIO::SetPin(PORT port, unsigned int pin, unsigned int level) {
    if (gpio_map == nullptr) return;

    if (level)
        PIO->Pn[port].DAT |= (1 << pin);// 设置引脚输出高电平
    else
        PIO->Pn[port].DAT &= ~(1 << pin);// 设置引脚输出低电平
}

// 释放 GPIO 资源
int GPIO::Free() {
    if (munmap(gpio_map, PageSize * 2) == 0) {
        std::cout << "unmap success!" << std::endl;
    } else {
        std::cerr << "unmap failed!" << std::endl;
    }
    return 0;
}





// 设置定时器
// Timer 类的构造函数和析构函数
Timer::Timer() : fd(-1), timer_map(nullptr), timer(nullptr) {}
Timer::~Timer() {
    Free(); // 调用 Free 方法释放资源
}

// 初始化定时器
void Timer::Init() {
    // 打开 /dev/mem 设备文件,以读写方式打开
    if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) {
        std::cerr << "open error /dev/mem" << std::endl;
        return;
    }

    PageSize = sysconf(_SC_PAGESIZE); // 获取系统页面大小
    PageMask = (~(PageSize - 1)); // 计算页掩码

    addr_start = TIMER_BASE & PageMask; // 计算映射起始地址
    addr_offset = TIMER_BASE & ~PageMask; // 计算地址偏移量

    // 使用 mmap 将设备内存映射到用户空间
    timer_map = (unsigned int *)mmap(NULL, PageSize * 2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr_start);
    if (timer_map == MAP_FAILED) {
        std::cerr << "mmap error" << std::endl;
        close(fd); // 映射失败,关闭文件描述符
        return;
    }

    // 计算定时器寄存器地址
    timer = (volatile unsigned int *)((unsigned int)timer_map + addr_offset);
    close(fd);
}





// 设置时钟源为OSC24M
void Timer::SetClockSource() {
    if (timer) {
        timer[TIMER_CTRL_OFFSET / 4] &= ~(0x3 << 2); // 清除时钟源位
        timer[TIMER_CTRL_OFFSET / 4] |= (0x1 << 2);  // 设置时钟源为OSC24M
    }
}






void Timer::SetPeriod(unsigned int period_ns) {
    if (timer) {
        // 计算计数值,每个计数单位大约为41.67纳秒
        unsigned int timer_counts = static_cast<unsigned int>(period_ns / 41.67/100);

        // 设置定时器的间隔值寄存器和当前值寄存器
        timer[TIMER_INTV_VALUE_OFFSET / 4] = timer_counts;
        timer[TIMER_CUR_VALUE_OFFSET / 4] = timer_counts;
    }
}

// 启动定时器
void Timer::Start() {
    if (timer) {
        timer[TIMER_CTRL_OFFSET / 4] = 0x3; // 启用定时器,自动重载模式
    }
}

// 停止定时器
void Timer::Stop() {
    if (timer) {
        timer[TIMER_CTRL_OFFSET / 4] = 0x0; // 停止定时器
    }
}

// 释放 Timer 资源
int Timer::Free() {
    if (timer_map && munmap((void *)timer_map, PageSize * 2) == 0) { // 确保 timer_map 有效
        std::cout << "unmap success!" << std::endl;
    } else {
        std::cerr << "unmap failed!" << std::endl;
    }
    return 0;
}





void generate_pwm(GPIO &gpio, Timer &timer) {
    const int period_ns = 1250; // 设置周期为1.25&#181;s
    const int high_time = 825; // 66% of 1250ns => 825ns
    const int low_time = period_ns - high_time; // 余下的时间为低电平

    gpio.ConfigPin(PORT::PE, 22, PIN_MODE::OUT);
    gpio.ConfigPin(PORT::PE, 21, PIN_MODE::OUT);

    while (true) {
        gpio.SetPin(PORT::PE, 21, 1);
        timer.SetPeriod(82); // 设置高电平时间
        timer.Start();
        std::this_thread::sleep_for(std::chrono::nanoseconds(high_time))
        timer.Stop();


        gpio.SetPin(PORT::PE, 21, 0);
        timer.SetPeriod(43); // 设置低电平时间
        timer.Start();
        std::this_thread::sleep_for(std::chrono::nanoseconds(low_time))
        timer.Stop();


    }
}




gpio.h:


#ifndef GPIO_H
#define GPIO_H




#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <cstdint> // 添加这个头文件来定义 uint32_t
#include <fcntl.h>

// 定义 PIO_BASE_ADDRESS 和页面大小
#define PIO_BASE_ADDRESS 0x01C20800

// 定义 PIO_Struct 结构体,描述每个 PIO 控制器的寄存器布局
typedef struct {
    unsigned int CFG[4]; // 配置寄存器数组
    unsigned int DAT;    // 数据寄存器
    unsigned int DRV0;   // 驱动能力寄存器0
    unsigned int DRV1;   // 驱动能力寄存器1
    unsigned int PUL0;   // 上拉寄存器0
    unsigned int PUL1;   // 上拉寄存器1
} PIO_Struct;

// 定义 PIO_Map 结构体,描述所有 PIO 控制器的映射
typedef struct {
    PIO_Struct Pn[7]; // PIO_Struct 数组,对应7个 PIO 控制器
} PIO_Map;

typedef enum {
    PA = 0,
    PB = 1,
    PC = 2,
    PD = 3,
    PE = 4,
    PF = 5,
    PG = 6,
} PORT;

typedef enum {
    IN = 0x00,
    OUT = 0x01,
    AUX = 0x02,
    INT = 0x06,
    DISABLE = 0x07,
} PIN_MODE;

// GPIO 类定义
class GPIO {
public:
    GPIO();   // 构造函数
    ~GPIO();  // 析构函数

    void Init();                    // 初始化 GPIO
    void ConfigPin(PORT port, unsigned int pin, PIN_MODE mode);  // 配置指定引脚的模式
    void SetPin(PORT port, unsigned int pin, unsigned int level);   // 设置指定引脚的电平
    int Free();                    // 释放 GPIO 资源

private:
    int fd;                         // 文件描述符
    unsigned int *gpio_map;         // 指向内存映射的 GPIO 控制器寄存器
    PIO_Map *PIO;                   // 指向 PIO_Map 的指针,用于访问 PIO 控制器的映射
    unsigned int addr_start;        // 映射起始地址
    unsigned int addr_offset;       // 地址偏移量
    unsigned int PageSize;          // 页面大小
    unsigned int PageMask;          // 页掩码
};














class Timer {
public:
    Timer();
    ~Timer();
    void Init();
    void SetPeriod(unsigned int period_ns);
    void Start();
    void Stop();
    void SetClockSource();
    bool CheckInterrupt();

private:
    int fd;
    unsigned int PageSize;
    unsigned int PageMask;
    unsigned int addr_start;
    unsigned int addr_offset;
    volatile unsigned int *timer_map;
    volatile unsigned int *timer;
    int Free();
};



void delay_ns(uint32_t ns);

void setup_timer();
void init_timer();
//void generate_pwm(gpio, timer);
void generate_pwm(GPIO &gpio, Timer &timer);
void delay2_ns(uint32_t ns);  // 声明纳秒级延时函数
void delay3_ns(long nanoseconds);  // 声明纳秒级延时函数
#endif // GPIO_H








想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2024-7-7 12:35:05 | 显示全部楼层
您好,小鱼油!这问题有点像在调戏硬件,但别急,咱们慢慢分析。生成800kHz、周期1.25us,占空比66%的方波,有点难度。但咱不怕,仔细看看代码和逻辑,找到问题的症结。

首先,咱需要了解你是如何设置和操作定时器的。你的代码展示了部分内容,但还不太完整。我们主要看的是 `generate_pwm` 函数如何生成PWM信号。目前你给出了一部分代码,我们需要更多信息。但是,我可以先给你一些调试建议:

1. **Timer 的配置**:
   - 确认你使用的定时器计数设置是否正确。如果周期是1.25us,那么计数值应该相应地缩小。
   - 例如,如果你的时钟频率是 24MHz,那么1.25us就对应30个时钟周期。
   
2. **占空比控制**:
   - 占空比66%意味着高电平时间约为0.825us(大约10个时钟周期),低电平时间约为0.425us(大约20个时钟周期)。
   - 确保你配置计数器的高低时间正确,并且都要在循环中设置。

3. **时序问题**:
   - 如果硬件配置和软件逻辑都有bug,可能导致输出波形不稳定。用示波器仔细检查实际输出,确认是否按你的预期变化。

4. **代码示例**:
   - 由于你提供的代码片段不完整,我无法准确判断。
   - 但是我能够给一个简化的示例来说明如何用定时器生成PWM。

假设我们有以下简化的 `generate_pwm` 函数,来用定时器实现简单的PWM:
#include <unistd.h>
#include <time.h>

// 简单的纳秒级精确延时函数
void delay_ns(long ns) {
    struct timespec req;
    req.tv_sec = 0;
    req.tv_nsec = ns;
    nanosleep(&req, NULL);
}

// generate_pwm 函数
void generate_pwm(GPIO &gpio, Timer &timer) {
    int high_time_ns = 825; // 高电平时间,单位纳秒
    int low_time_ns = 425;  // 低电平时间,单位纳秒

    while (true) {
        gpio.WritePin(PORT::PE, 22, true);  // 设置高电平
        delay_ns(high_time_ns);             // 高电平持续时间
        gpio.WritePin(PORT::PE, 22, false); // 设置低电平
        delay_ns(low_time_ns);              // 低电平持续时间
    }
}

- 注意,这只是一个非常简化的例子,仅适用于CPS不高且不需要其他复杂任务的情况。
- 实际工程中应使用硬件定时器或更低级别的寄存器操作,以减少上下文切换和提高实时性。

总的来说,要想实现高精度的PWM输出,你需要确保定时器配置正确,并且明确如何设置高低电平时间。尝试一步步验证,直到找到问题所在。

加油,小鱼油!希望这能帮到你,如果还有进一步的疑问,随时欢迎继续提问哦~
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-21 22:07

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表