Initial commit: SmartCar Framework v0.1 — 龙芯2K0300智能车开发框架\n\n- HAL: GPIO/PWM/Encoder/Framebuffer 驱动\n- Control: PID/IMU/Motor/Servo 控制\n- Vision: HSV双Otsu→4点标定IPM→洪泛填充→逐行搜线\n- Strategy: 三区前瞻偏差+速度策略\n- Debug: 文件热调参+LCD预览+cv截帧\n- Scheduler: 5ms timerfd+epoll 中央调度器
This commit is contained in:
64
hal/encoder.cpp
Normal file
64
hal/encoder.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "encoder.hpp"
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <cmath>
|
||||
|
||||
Encoder::Encoder(int pwm_channel, int dir_gpio, int direction_polarity)
|
||||
: _channel(pwm_channel)
|
||||
, _dir_gpio(dir_gpio)
|
||||
, _polarity(direction_polarity)
|
||||
, _reg_base(nullptr)
|
||||
, _mem_fd(-1)
|
||||
, _rps(0.0f)
|
||||
, _last_period(0)
|
||||
{
|
||||
_dir_gpio.setDirection("in");
|
||||
_mmapRegs();
|
||||
}
|
||||
|
||||
Encoder::~Encoder() { _munmapRegs(); }
|
||||
|
||||
void Encoder::_mmapRegs()
|
||||
{
|
||||
_mem_fd = open("/dev/mem", O_RDWR);
|
||||
if (_mem_fd < 0) return;
|
||||
|
||||
_reg_base = (volatile uint32*)mmap(nullptr, 0x10000, PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED, _mem_fd, ENCODER_BASE);
|
||||
}
|
||||
|
||||
void Encoder::_munmapRegs()
|
||||
{
|
||||
if (_reg_base && _reg_base != MAP_FAILED)
|
||||
munmap((void*)_reg_base, 0x10000);
|
||||
if (_mem_fd >= 0) { close(_mem_fd); _mem_fd = -1; }
|
||||
}
|
||||
|
||||
uint32 Encoder::_readRegister(unsigned int offset) const
|
||||
{
|
||||
if (!_reg_base || _reg_base == MAP_FAILED) return 0;
|
||||
return _reg_base[offset / sizeof(uint32)];
|
||||
}
|
||||
|
||||
int Encoder::getDirection() const
|
||||
{
|
||||
return _dir_gpio.getValue() * _polarity;
|
||||
}
|
||||
|
||||
void Encoder::update()
|
||||
{
|
||||
if (!_reg_base || _reg_base == MAP_FAILED) return;
|
||||
|
||||
uint32 period = _readRegister(REG_OFFSET + _channel * 0x100);
|
||||
if (period > 0 && period < 0xFFFFFF)
|
||||
{
|
||||
float freq = 50000000.0f / period; // 50MHz 基频
|
||||
_rps = (freq / 4.0f) * getDirection(); // 4倍频编码器
|
||||
_last_period = period;
|
||||
}
|
||||
else
|
||||
{
|
||||
_rps *= 0.6f; // 无脉冲时速度衰减
|
||||
}
|
||||
}
|
||||
26
hal/encoder.hpp
Normal file
26
hal/encoder.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include "types.hpp"
|
||||
#include "gpio.hpp"
|
||||
|
||||
class Encoder {
|
||||
public:
|
||||
Encoder(int pwm_channel, int dir_gpio, int direction_polarity);
|
||||
~Encoder();
|
||||
|
||||
void update();
|
||||
float getRPS() const { return _rps; }
|
||||
int getDirection() const;
|
||||
|
||||
private:
|
||||
int _channel;
|
||||
GPIO _dir_gpio;
|
||||
int _polarity; // 1 或 -1
|
||||
volatile uint32* _reg_base;
|
||||
int _mem_fd;
|
||||
float _rps;
|
||||
uint32 _last_period;
|
||||
static constexpr unsigned int REG_OFFSET = 0x0100;
|
||||
void _mmapRegs();
|
||||
void _munmapRegs();
|
||||
uint32 _readRegister(unsigned int offset) const;
|
||||
};
|
||||
60
hal/framebuffer.cpp
Normal file
60
hal/framebuffer.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#include "framebuffer.hpp"
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/fb.h>
|
||||
#include <cstring>
|
||||
|
||||
FrameBuffer::FrameBuffer() : _fd(-1), _buffer(nullptr), _width(0), _height(0), _screensize(0) {}
|
||||
|
||||
FrameBuffer::~FrameBuffer() { release(); }
|
||||
|
||||
bool FrameBuffer::init(const char* device)
|
||||
{
|
||||
_fd = open(device, O_RDWR);
|
||||
if (_fd < 0) return false;
|
||||
|
||||
fb_var_screeninfo vinfo;
|
||||
if (ioctl(_fd, FBIOGET_VSCREENINFO, &vinfo) < 0) return false;
|
||||
|
||||
_width = vinfo.xres;
|
||||
_height = vinfo.yres;
|
||||
_screensize = _width * _height * 2; // RGB565 = 2 bytes/pixel
|
||||
|
||||
_buffer = (uint16*)mmap(nullptr, _screensize, PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED, _fd, 0);
|
||||
return (_buffer != MAP_FAILED);
|
||||
}
|
||||
|
||||
void FrameBuffer::release()
|
||||
{
|
||||
if (_buffer && _buffer != MAP_FAILED)
|
||||
munmap(_buffer, _screensize);
|
||||
if (_fd >= 0) { close(_fd); _fd = -1; }
|
||||
_buffer = nullptr;
|
||||
}
|
||||
|
||||
void FrameBuffer::_rgb888_to_rgb565(const uint8* src, uint16* dst, int pixels)
|
||||
{
|
||||
for (int i = 0; i < pixels; ++i)
|
||||
{
|
||||
uint8 r = src[i * 3];
|
||||
uint8 g = src[i * 3 + 1];
|
||||
uint8 b = src[i * 3 + 2];
|
||||
dst[i] = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
|
||||
}
|
||||
}
|
||||
|
||||
bool FrameBuffer::write(const uint8* rgb888_data, int w, int h)
|
||||
{
|
||||
if (!_buffer || _buffer == MAP_FAILED) return false;
|
||||
|
||||
int copy_w = (w < _width) ? w : _width;
|
||||
int copy_h = (h < _height) ? h : _height;
|
||||
|
||||
for (int y = 0; y < copy_h; ++y)
|
||||
_rgb888_to_rgb565(rgb888_data + y * w * 3, _buffer + y * _width, copy_w);
|
||||
|
||||
return true;
|
||||
}
|
||||
23
hal/framebuffer.hpp
Normal file
23
hal/framebuffer.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include "../types.hpp"
|
||||
|
||||
class FrameBuffer {
|
||||
public:
|
||||
FrameBuffer();
|
||||
~FrameBuffer();
|
||||
|
||||
bool init(const char* device = "/dev/fb0");
|
||||
void release();
|
||||
int width() const { return _width; }
|
||||
int height() const { return _height; }
|
||||
uint16* buffer() const { return _buffer; }
|
||||
bool write(const uint8* rgb888_data, int w, int h);
|
||||
|
||||
private:
|
||||
int _fd;
|
||||
uint16* _buffer;
|
||||
int _width;
|
||||
int _height;
|
||||
int _screensize;
|
||||
void _rgb888_to_rgb565(const uint8* src, uint16* dst, int pixels);
|
||||
};
|
||||
43
hal/gpio.cpp
Normal file
43
hal/gpio.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "gpio.hpp"
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
GPIO::GPIO(int pin) : _pin(pin), _exported(false) { _export(); }
|
||||
|
||||
GPIO::~GPIO() { _unexport(); }
|
||||
|
||||
void GPIO::_export()
|
||||
{
|
||||
std::ofstream exp("/sys/class/gpio/export");
|
||||
if (exp.is_open()) { exp << _pin; _exported = true; }
|
||||
}
|
||||
|
||||
void GPIO::_unexport()
|
||||
{
|
||||
std::ofstream unexp("/sys/class/gpio/unexport");
|
||||
if (unexp.is_open()) unexp << _pin;
|
||||
}
|
||||
|
||||
void GPIO::setDirection(const std::string& dir)
|
||||
{
|
||||
std::string path = "/sys/class/gpio/gpio" + std::to_string(_pin) + "/direction";
|
||||
std::ofstream f(path);
|
||||
if (f.is_open()) f << dir;
|
||||
}
|
||||
|
||||
void GPIO::setValue(int val)
|
||||
{
|
||||
std::string path = "/sys/class/gpio/gpio" + std::to_string(_pin) + "/value";
|
||||
std::ofstream f(path);
|
||||
if (f.is_open()) f << val;
|
||||
}
|
||||
|
||||
int GPIO::getValue() const
|
||||
{
|
||||
std::string path = "/sys/class/gpio/gpio" + std::to_string(_pin) + "/value";
|
||||
std::ifstream f(path);
|
||||
int v = 0; if (f.is_open()) f >> v;
|
||||
return v;
|
||||
}
|
||||
17
hal/gpio.hpp
Normal file
17
hal/gpio.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
class GPIO {
|
||||
public:
|
||||
explicit GPIO(int pin);
|
||||
~GPIO();
|
||||
void setDirection(const std::string& dir);
|
||||
void setValue(int val);
|
||||
int getValue() const;
|
||||
|
||||
private:
|
||||
int _pin;
|
||||
bool _exported;
|
||||
void _export();
|
||||
void _unexport();
|
||||
};
|
||||
57
hal/pwm.cpp
Normal file
57
hal/pwm.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include "pwm.hpp"
|
||||
#include <fstream>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
PWM::PWM(int chip, int channel, unsigned int period_ns)
|
||||
: _chip(chip), _channel(channel), _period_ns(period_ns), _exported(false)
|
||||
{
|
||||
_export();
|
||||
setPeriod(period_ns);
|
||||
}
|
||||
|
||||
PWM::~PWM() { _unexport(); }
|
||||
|
||||
std::string PWM::_basePath() const
|
||||
{
|
||||
return "/sys/class/pwm/pwmchip" + std::to_string(_chip) +
|
||||
"/pwm" + std::to_string(_channel);
|
||||
}
|
||||
|
||||
void PWM::_export()
|
||||
{
|
||||
std::ofstream f("/sys/class/pwm/pwmchip" + std::to_string(_chip) + "/export");
|
||||
if (f.is_open()) { f << _channel; _exported = true; }
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
|
||||
void PWM::_unexport()
|
||||
{
|
||||
std::ofstream f("/sys/class/pwm/pwmchip" + std::to_string(_chip) + "/unexport");
|
||||
if (f.is_open()) f << _channel;
|
||||
}
|
||||
|
||||
void PWM::setDutyCycle(unsigned int duty_ns)
|
||||
{
|
||||
std::ofstream f(_basePath() + "/duty_cycle");
|
||||
if (f.is_open()) f << duty_ns;
|
||||
}
|
||||
|
||||
void PWM::setPeriod(unsigned int period_ns)
|
||||
{
|
||||
_period_ns = period_ns;
|
||||
std::ofstream f(_basePath() + "/period");
|
||||
if (f.is_open()) f << period_ns;
|
||||
}
|
||||
|
||||
void PWM::enable(bool on)
|
||||
{
|
||||
std::ofstream f(_basePath() + "/enable");
|
||||
if (f.is_open()) f << (on ? 1 : 0);
|
||||
}
|
||||
|
||||
void PWM::setPolarity(const char* pol)
|
||||
{
|
||||
std::ofstream f(_basePath() + "/polarity");
|
||||
if (f.is_open()) f << pol;
|
||||
}
|
||||
24
hal/pwm.hpp
Normal file
24
hal/pwm.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "types.hpp"
|
||||
|
||||
class PWM {
|
||||
public:
|
||||
PWM(int chip, int channel, unsigned int period_ns);
|
||||
~PWM();
|
||||
|
||||
void setDutyCycle(unsigned int duty_ns);
|
||||
void setPeriod(unsigned int period_ns);
|
||||
void enable(bool on = true);
|
||||
void setPolarity(const char* pol = "normal");
|
||||
unsigned int readPeriod() const { return _period_ns; }
|
||||
|
||||
private:
|
||||
int _chip;
|
||||
int _channel;
|
||||
unsigned int _period_ns;
|
||||
bool _exported;
|
||||
std::string _basePath() const;
|
||||
void _export();
|
||||
void _unexport();
|
||||
};
|
||||
Reference in New Issue
Block a user