linux开发板驱动使用timer
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.CFG &= ~((unsigned int)0x07 << (pin % 8) * 4);
PIO->Pn.CFG |= ((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.DAT |= (1 << pin);// 设置引脚输出高电平
else
PIO->Pn.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 &= ~(0x3 << 2); // 清除时钟源位
timer |= (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_counts;
timer = timer_counts;
}
}
// 启动定时器
void Timer::Start() {
if (timer) {
timer = 0x3; // 启用定时器,自动重载模式
}
}
// 停止定时器
void Timer::Stop() {
if (timer) {
timer = 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µ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; // 配置寄存器数组
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; // 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
您好,小鱼油!这问题有点像在调戏硬件,但别急,咱们慢慢分析。生成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 小助理,如未能正确解答您的问题,请继续追问。
页:
[1]