Window C++模拟单片机控制TFT屏幕和SD卡
因为每次都要做大量的测试,上传到单片机实在是太费事,所以写了这个模拟项目用来测试
很多方法我没有补充进去,因为太多了,如果有需要请自行补充
stdafx.h
#pragma once
#include<iostream>
#include<atlimage.h>
#include<time.h>
#include<string>
#include <ws2tcpip.h>
#include<Windows.h>
FS.h
#pragma once
#include"stdafx.h"
#define HSPI 0
#define OUTPUT 0
#define HIGH 0
#define LOW 0
class SPIClass
{
public:
SPIClass(int t) {};
void begin(int, int, int, int) {};
};
class serial {
public:
serial() {};
void begin(int) {};
void println(const char *t) {
printf("%s\n", t);
};
};
void pinMode(int, int);
void digitalWrite(int, int);
SD.h
#pragma once
#include"stdafx.h"
#define CARD_NONE 0
#pragma warning(disable:4996)
#define MN_PATH "D:\\Program Files\\all\\desktop\\SD"
class String :public std::string
{
public:
String() : std::string() {}
String(const char*t) :std::string(t) {}
int indexOf(const char *st)
{
return this->find(st, 0);
}
String substring(int _off, int ct = -1)
{
if (ct == -1)
{
ct = this->length();
}
String h= this->substr(_off, ct).c_str();
return h;
}
};
class File
{
private:
FILE* fp;
public:
File(const char *path)
{
this->fp = fopen(path, "rb");
}
explicit operator bool() const { // 防止隐式转换到其他类型
return fp!=NULL; /* 根据对象状态返回true/false */;
}
String readStringUntil(char ends)
{
String t = "";
char t1;
int lna = 0;
while ((lna = fread(&t1, 1, 1, this->fp))>0)
{
t+=t1;
if (t1 == ends)
break;
}
return t;
}
void close()
{
fclose(this->fp);
}
void seek(long pos)
{
fseek(this->fp, pos, 0);
}
int read(void *addr, int size)
{
return fread(addr, size, 1, this->fp);
}
};
class sd {
public:
sd() {};
bool begin(int, SPIClass&t) { return true; }
int cardType() { return 1; }
File open(const char*path)
{
int len = strlen(path) + 1;
char *t = new char[len];
memset(t, 0, sizeof(len));
strcat(t,path);
for (int i = 0; i < len; i++)
if (t[i] == '/')
t[i] = '\\';
std::string p = MN_PATH;
p += t;
File file(p.c_str());
return file;
}
};
SPI.h
#pragma once
#include"stdafx.h"
void delay(int min);
long millis();
void loop();
void setup();
void setmillis(long t);
TFT_eSPI.h
#pragma once
#include"stdafx.h"
#pragma warning(disable:4996)
#define TFT_BLUE 0x1F
#define TFT_GREEN 0x3E0
#define TFT_BLACK 0x0000
#define TFT_WHITE 0x7FFF
#define TFT_WIDTH 160
#define TFT_HEIGHT 128
#define TFT_SCA 4
LRESULT CALLBACK GLWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
DWORD WINAPI CTF(void*E);
int Get_real_col(uint16_t c1);
class TMp2
{
public:
HWND*hwnd;
HINSTANCE *hInstance;
bool *ok;
};
class TFT_eSPI
{
public:
static HINSTANCE hInstance;
static HWND hwnd;
static bool ok,canrun;
static void setH(HINSTANCE hInstance)
{
TFT_eSPI::hInstance = hInstance;
}
static CImage img;
static bool img_ch;
static void CT()
{
if (ok)return;
TMp2 *t=new TMp2();
t->hwnd = &(TFT_eSPI::hwnd);
t->hInstance = &(TFT_eSPI::hInstance);
t->ok = &(TFT_eSPI::ok);
CreateThread(0, 0, CTF, t, 0, 0);
}
int col;
TFT_eSPI() {
}
void init()
{
CT();
Sleep(400);
while (!ok)
{
Sleep(400);
}
img.Create(TFT_WIDTH, TFT_HEIGHT, 24);
}
void setRotation(int) {};
void fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color)
{
HDC hdc = GetDC(hwnd);
RECT rect;
rect.left = x* TFT_SCA;
rect.top = y * TFT_SCA;
rect.right = x * TFT_SCA +(w * TFT_SCA);
rect.bottom = y * TFT_SCA +(h * TFT_SCA);
HBRUSH hb = CreateSolidBrush(Get_real_col(color));
HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, hb);
FillRect(hdc, &rect, hb);
SelectObject(hdc, hOldBrush);
DeleteObject(hb);
ReleaseDC(hwnd, hdc);
}
void fillCircle(int prevX1, int prevY1, int R, int BG_COLOR)
{
int prevX = prevX1 * TFT_SCA;
int prevY = prevY1 * TFT_SCA;
HDC hdc = GetDC(hwnd);
HPEN hTransparentPen = CreatePen(PS_NULL, 1, RGB(0, 0, 0));
HPEN hOldPen = (HPEN)SelectObject(hdc, hTransparentPen);
// 设置画笔颜色和样式
HBRUSH hb = CreateSolidBrush( Get_real_col(BG_COLOR));
HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, hb);
// 绘制圆,左上角坐标(100, 100),半径100
int r = R *TFT_SCA;
Ellipse(hdc, prevX-r, prevY-r, prevX+r, prevY+r);
// 恢复原来的画笔并删除新画笔
SelectObject(hdc, hOldPen);
SelectObject(hdc, hOldBrush);
DeleteObject(hTransparentPen);
DeleteObject(hb);
ReleaseDC(hwnd, hdc);
}
void fillScreen(uint16_t col)
{
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = TFT_WIDTH* TFT_SCA;
rect.bottom = TFT_HEIGHT* TFT_SCA;
HBRUSH hb= CreateSolidBrush(Get_real_col(col));
HDC hdc = GetDC(hwnd);
FillRect(hdc, &rect, hb);
ReleaseDC(hwnd,hdc);
DeleteObject(hb);
}
void setTextColor(uint16_t col)
{
this->col = Get_real_col(col);
}
void drawString(const char *str, int x, int y, int fontsize=1)
{
RECT rect;
rect.left = x* TFT_SCA;
rect.top = y* TFT_SCA;
rect.right = TFT_WIDTH * TFT_SCA;
rect.bottom = TFT_HEIGHT * TFT_SCA;
HDC hdc = GetDC(hwnd);
SetTextColor(hdc, this->col);
LOGFONT lf;
memset(&lf, 0, sizeof(LOGFONT));
lf.lfHeight = -MulDiv(fontsize* TFT_SCA *5, GetDeviceCaps(hdc, LOGPIXELSY), 72); // 设置字体高度(20磅)
lf.lfWeight = FW_NORMAL; // 字体粗细
strcpy(lf.lfFaceName, "Arial"); // 字体名称
HFONT hFont = CreateFontIndirect(&lf);
HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);
int nOldMode = SetBkMode(hdc, TRANSPARENT);
DrawText(hdc, str, strlen(str), &rect, DT_LEFT);
SetBkMode(hdc, nOldMode);
SelectObject(hdc, hOldFont);
DeleteObject(hFont);
ReleaseDC(hwnd, hdc);
}
void startWrite()
{
img_ch = true;
}
int x,y,w,h;
void setAddrWindow(int x, int y, int w, int h)
{
this->h = h;
this->x = x;
this->y = y;
this->w = w;
}
void pushPixels(uint16_t*rowBuffer, int size)
{
unsigned char* rgb1 = (unsigned char*)img.GetBits();
int pitch1 = img.GetPitch();
int i = this->y;
for (int j1 = 0; j1 < size; j1 ++)
{
int j = this->x + j1;
uint16_t c = rowBuffer[j1];
c = (c << 8) | (c >> 8);
c = c >> 1;
unsigned char r, g, b;
r = (c >> 10&0x1F) * 8;
g = (c >> 5 & 0x1F) * 8;
b = (c & 0x1F) * 8;
*(rgb1 + (j * 3) + (i * pitch1) + 0) = b;
*(rgb1 + (j * 3) + (i * pitch1) + 1) = g;
*(rgb1 + (j * 3) + (i * pitch1) + 2) = r;
}
}
void endWrite()
{
HDC hdc = GetDC(TFT_eSPI::hwnd);
TFT_eSPI::img.StretchBlt(hdc, 0, 0, TFT_SCA*TFT_WIDTH, TFT_SCA * TFT_HEIGHT);
ReleaseDC(hwnd, hdc);
}
};
int constrain(int value, int min_value, int max_value);
WiFi.h
#pragma once
#include"stdafx.h"
#pragma comment(lib, "ws2_32.lib")
#define WL_CONNECTED 0
class Tmp
{
public:
Tmp()
{
}
const char* toString()
{
return "192.168.0.123";
}
};
class wifi
{
public:
static __time64_t _time_net;
wifi() {
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
};
void begin(const char*, const char*) { Sleep(1000); }
int status()
{
return 0;
}
Tmp localIP()
{
Tmp t;
return t;
}
};
void configTime(long gmtOffset_sec, int daylightOffset_sec, const char* ntpServer);
bool getLocalTime(tm* tm1);
other.cpp
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#include "TFT_eSPI.h"
#include "WiFi.h"
void delay(int min)
{
}
long t1 = 0;
void setmillis(long t)
{
t1 = t;
}
long millis()
{
return t1;
}
void digitalWrite(int, int) {}
void pinMode(int, int)
{
}
__time64_t wifi::_time_net = 0;
uint64_t tryNtpServer(const char* server) {
SOCKET sock = INVALID_SOCKET;
char buffer[48] = { 0 };
struct addrinfo hints = { 0 }, * res = nullptr;
// 解析DNS
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
if (getaddrinfo(server, "123", &hints, &res) != 0) {
std::cerr << "[DNS Error] " << server << std::endl;
return 0;
}
// 遍历所有解析到的IP地址
for (auto* ptr = res; ptr != nullptr; ptr = ptr->ai_next) {
sock = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (sock == INVALID_SOCKET) continue;
// 配置套接字选项
// setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&NTP_TIMEOUT, sizeof(NTP_TIMEOUT));
// 构造NTP请求包
memset(buffer, 0, 48);
buffer[0] = 0x1B; // NTPv3客户端模式
// 发送请求
if (sendto(sock, buffer, 48, 0,
ptr->ai_addr, (int)ptr->ai_addrlen) <= 0) {
closesocket(sock);
continue;
}
// 接收响应
int bytes = recv(sock, buffer, 48, 0);
if (bytes > 0) {
uint32_t seconds = ntohl(*((uint32_t*)(buffer + 40)));
freeaddrinfo(res);
closesocket(sock);
return seconds - 2208988800ULL; // 转换为Unix时间戳
}
closesocket(sock);
}
freeaddrinfo(res);
return 0;
}
void configTime(long gmtOffset_sec, int daylightOffset_sec, const char* ntpServer)
{
wifi::_time_net = tryNtpServer(ntpServer);
}
bool getLocalTime(tm* tm1)
{
long long t = (wifi::_time_net +( t1/1000));
tm* tm_t =localtime(&t);
memcpy(tm1, tm_t, sizeof(tm));
return true;
}
HINSTANCE TFT_eSPI::hInstance=NULL;
HWND TFT_eSPI::hwnd=NULL;
bool TFT_eSPI::ok=false;
bool TFT_eSPI::canrun = true;
bool TFT_eSPI::img_ch = false;
CImage TFT_eSPI::img;
LRESULT CALLBACK GLWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CLOSE:
PostQuitMessage(0);
TFT_eSPI::canrun = false;
return 0;
default:
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
DWORD WINAPI CTF(void*e)
{
TMp2*E = (TMp2*)e;
WNDCLASSEX wndclass;
wndclass.cbClsExtra = 0;
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.cbWndExtra = 0;
wndclass.hbrBackground = NULL;
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hIcon = NULL;
wndclass.hIconSm = NULL;
wndclass.hInstance = *(E->hInstance);
wndclass.lpfnWndProc = GLWindowProc;
wndclass.lpszClassName = "GLWindow";
wndclass.lpszMenuName = NULL;
wndclass.style = CS_VREDRAW | CS_HREDRAW;
ATOM atom = RegisterClassEx(&wndclass);
if (!atom)
{
MessageBox(NULL, "Notice", "Error", MB_OK);
return 0;
}
HWND hwnd = CreateWindowEx(NULL, "GLWindow", "TFT模拟", WS_OVERLAPPEDWINDOW, 0, 0, TFT_WIDTH*TFT_SCA+16, TFT_HEIGHT * TFT_SCA+37, NULL, NULL, *(E->hInstance), NULL);
*(E->hwnd) = hwnd;
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
*(E->ok) = true;
//程序持续运行
MSG msg;
while (true)
{
if (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 0;
}
int Get_real_col(uint16_t c)
{
unsigned char r, g, b;
r = (c >> 10 & 0x1F) * 8;
g = (c >> 5 & 0x1F) * 8;
b =( c & 0x1F )* 8;
//printf("%d %d %d %d\n",c, r,g,b);
return RGB(r,g,b);
}
int constrain(int value, int min_value, int max_value) {
return min(max(value, min_value), max_value);
}
#ifdef _CONSOLE
int main()
#elif
int WINAPI WinMain(HINSTANCE h1, HINSTANCE h2, LPSTR cmd, int show)
#endif
{
#ifdef _CONSOLE
HINSTANCE h1 = GetModuleHandle(NULL);
#endif
TFT_eSPI::setH(h1);
setup();
long t = 0;
while (1)
{
if (!TFT_eSPI::canrun)
break;
loop();
/*if (TFT_eSPI::img_ch)
{
TFT_eSPI::img_ch = false;
}*/
Sleep(50);
t = t + 50;
setmillis(t);
}
}
测试用.cpp
#include "SPI.h"
#include "wifi.h"
#include "FS.h"
#include "SD.h"
#include "TFT_eSPI.h"
#ifdef WIN32
serial Serial;
wifi WiFi;
sd SD;
#endif
const char* ntpServer = "time.windows.com";
const long gmtOffset_sec = 8 * 3600; // 东八区(北京时间)
const int daylightOffset_sec = 0;
#define HSPI_CLK 14
#define HSPI_MISO 12
#define HSPI_MOSI 13
#define HSPI_CS 15
#define SD_CS 15
#define TFT_CS 5 // TFT片选引脚
TFT_eSPI tft;
SPIClass spiSD(HSPI); // 创建HSPI对象
void setup() {
tft.init();
tft.setTextColor(TFT_WHITE);
tft.fillScreen(TFT_BLACK);
tft.fillRect(60-5, 20, 9, 32, TFT_GREEN);
tft.fillRect(100-5, 20, 9, 32, TFT_GREEN);
}
unsigned long lastTime = 0;
int y = 0;
#define AD 8
int ad = AD;
int t11 = 3;
void loop() {
if (t11 >= 3|| t11==2)
{
tft.fillRect(60 - 5, 20 + y, 9, AD, TFT_BLACK);
}
if (t11 >= 3 || t11 == 1)
{
tft.fillRect(100 - 5, 20 + y, 9, AD, TFT_BLACK);
}
y += ad;
if (t11 >= 3 || t11 == 2)
{
tft.fillRect(60 - 5, 20 + y, 9, 32 - y, TFT_GREEN);
}
if (t11 >= 3 || t11 == 1)
{
tft.fillRect(100 - 5, 20 + y, 9, 32 - y, TFT_GREEN);
}
if (y <= 0) {
ad = AD;
Sleep(rand() % 10*500);
t11 = rand() % 3 + 1;
}
else if (y >= 32)
{
ad = -AD;
}
}