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:
2026-05-25 10:31:55 +08:00
commit 28d9c6da58
52 changed files with 2599 additions and 0 deletions

64
hal/encoder.cpp Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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();
};