Window C++模拟单片机控制TFT屏幕和SD卡

发布于:2025-03-07 ⋅ 阅读:(15) ⋅ 点赞:(0)

因为每次都要做大量的测试,上传到单片机实在是太费事,所以写了这个模拟项目用来测试
很多方法我没有补充进去,因为太多了,如果有需要请自行补充

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 INPUT_PULLUP 0
#define HIGH 1
#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 print(const char *t) {
		printf("%s", t);
	};
};
void pinMode(int, int);
void digitalWrite(int, int);

SD.h

#pragma once
#include"stdafx.h"
#include"FS.h"
#define CARD_NONE 0
#pragma warning(disable:4996)
#define MN_PATH "E:\\模拟SD卡"
class String :public std::string
{
public:
	String() : std::string() {}
	String(const char*t) :std::string(t) {}
	String(std::string &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(uint8_t *addr, int size)
	{
		return fread(addr, size, 1, this->fp);
	}
};
class sd {
public:
	sd() {};
	bool begin(int, SPIClass&t) { return true; }
	bool begin(int) { 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);
int digitalRead(int yj);

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_SCA1 2
#define TFT_SCA 2

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_SCA1, TFT_HEIGHT*TFT_SCA1, 24);
		::SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 100, 100,  SWP_NOSIZE);
	}
	void setRotation(int) {};
	void fillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color)
	{
		HDC hdc = img.GetDC();
		RECT rect;
		rect.left = x* TFT_SCA1;
		rect.top = y * TFT_SCA1;
		rect.right = x * TFT_SCA1 + (w * TFT_SCA1);
		rect.bottom = y * TFT_SCA1 + (h * TFT_SCA1);
		HBRUSH hb = CreateSolidBrush(Get_real_col(color));
		HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, hb);
		FillRect(hdc, &rect, hb);
		SelectObject(hdc, hOldBrush);
		DeleteObject(hb);
		img.ReleaseDC();
	}
	void fillCircle(int prevX1, int prevY1, int R, int BG_COLOR)
	{
		int prevX = prevX1 * TFT_SCA1;
		int prevY = prevY1 * TFT_SCA1;
		HDC hdc = img.GetDC();
		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_SCA1;
		Ellipse(hdc, prevX - r, prevY - r, prevX + r, prevY + r);

		// 恢复原来的画笔并删除新画笔
		SelectObject(hdc, hOldPen);
		SelectObject(hdc, hOldBrush);
		DeleteObject(hTransparentPen);
		DeleteObject(hb);
		img.ReleaseDC();
	}
	void fillScreen(uint16_t col)
	{
		RECT rect;
		rect.left = 0;
		rect.top = 0;
		rect.right = TFT_WIDTH* TFT_SCA1;
		rect.bottom = TFT_HEIGHT* TFT_SCA1;
		HBRUSH hb = CreateSolidBrush(Get_real_col(col));
		HDC hdc = img.GetDC();
		FillRect(hdc, &rect, hb);
		ReleaseDC(hwnd, hdc);
		DeleteObject(hb);
		img.ReleaseDC();
	}
	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_SCA1;
		rect.top = y* TFT_SCA1;
		rect.right = TFT_WIDTH * TFT_SCA1;
		rect.bottom = TFT_HEIGHT * TFT_SCA1;
		HDC hdc = img.GetDC();
		SetTextColor(hdc, this->col);
		LOGFONT lf;
		memset(&lf, 0, sizeof(LOGFONT));
		lf.lfHeight = -MulDiv(fontsize* TFT_SCA1 * 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);
		img.ReleaseDC();
	}
	void startWrite()
	{
		img_ch = true;
	}
	int x, y, w, h;
	void setAddrWindow(int x, int y, int w, int h)
	{
		this->h = h*TFT_SCA1;
		this->x = x*TFT_SCA1;
		this->y = y*TFT_SCA1;
		this->w = w*TFT_SCA1;
	}
	void pushPixels(uint16_t*rowBuffer, int size)
	{
		unsigned char* rgb1 = (unsigned char*)img.GetBits();
		int pitch1 = img.GetPitch();
		
		for (int i = this->y; i < this->y + TFT_SCA1;i++)
		for (int j1 = 0; j1 < size; j1++)
		{
			int j2 = this->x + j1*TFT_SCA1;
			uint16_t c = rowBuffer[j1];
			c = (c << 8) | (c >> 8);
			unsigned char r, g, b;
			r = ((c >> 11) & 0x1F) * 8;
			g = ((c >> 5 )& 0x3F) * 4;
			b = ((c & 0x1F)) * 8;
			for (int j = j2; j < j2+TFT_SCA1; j++)
			{
				*(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 = img.GetDC();
		TFT_eSPI::img.StretchBlt(hdc, 0, 0, TFT_SCA1*TFT_WIDTH, TFT_SCA1 * TFT_HEIGHT);
		img.ReleaseDC();
	}
};
int constrain(int value, int min_value, int max_value);

WiFi.h

#pragma once
#include"stdafx.h" 
#include"SD.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() {
	};
	void begin(const char*, const char*) {
		WSADATA wsaData;
		WSAStartup(MAKEWORD(2, 2), &wsaData); 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);
class WiFiClient
{
public:
	bool is_con,canr;
	SOCKET sock;
	String hc;
	WiFiClient() {
		is_con = false;
		canr = false;
		sock = INVALID_SOCKET;
		if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
			std::cerr << "Socket creation failed: " << WSAGetLastError() << std::endl;
		}
	}
	bool connect(const char* host, int port)
	{
		struct sockaddr_in server;
		std::string response;
		// 创建socket

		// 设置服务器地址
		// 设置服务器地址
		if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
			std::cerr << "Socket creation failed: " << WSAGetLastError() << std::endl;
			return false;
		}
		server.sin_family = AF_INET;
		server.sin_port = htons(port); // HTTP默认端口
		server.sin_addr.s_addr = inet_addr(host); // 直接使用IP地址

												  // 连接到服务器
		if (::connect(sock, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR) {
			std::cerr << "Connection failed: " << WSAGetLastError() << std::endl;
			closesocket(sock);
			return false;
		}
		is_con = true;
		return true;
	}
	void print(String request)
	{
		if (send(sock, request.c_str(), request.length(), 0) == SOCKET_ERROR) {
			std::cerr << "Send failed: " << WSAGetLastError() << std::endl;
			closesocket(sock);
		}
		canr = true;
	}
	bool available()
	{
		return canr;
	}
	String readStringUntil(const char *ends)
	{
		String h="";
		int s = hc.indexOf(ends);
		h = hc.substr(0, s == -1 ? hc.length() : s);
		canr = false;
		return h;
	}
	int read(uint8_t*addr, int size)
	{
		int bytesReceived = recv(sock, (char*)addr, size, 0);
		if (bytesReceived <= 0)
		{
			canr = false;
			return -1;
		}
		return bytesReceived;
	}
	void stop()
	{
		is_con = false;
		closesocket(sock);
	}
	~WiFiClient()
	{
		if(is_con)
		this->stop();
	}
};

other.cpp

#include "FS.h"
#include "SD.h"
#include "SPI.h"
#include "TFT_eSPI.h"
#include "WiFi.h"
void delay(int min)
{
	Sleep(min);
}
long t1 = 0;
void setmillis(long t)
{
	t1 = t;
}
long millis()
{
	return t1;
}
void digitalWrite(int, int) {}
void pinMode(int, int)
{

}
int digitalRead(int yj)
{
	return LOW;// rand() % 10 != 0;
}
__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 = time(NULL);// (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 = "Intermediate D3D Window";
	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, "Intermediate D3D Window", "QQ", WS_OVERLAPPEDWINDOW& ~WS_THICKFRAME & ~WS_MAXIMIZEBOX, 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()
#else
int WINAPI WinMain(HINSTANCE h1, HINSTANCE h2, LPSTR cmd, int show)
#endif
{
	//FILE*fp = fopen("E:\\模拟SD卡\\img.bin", "wb");
	//int num = 20;
	//fwrite(&num, 4, 1, fp);
	//fclose(fp);
#ifdef _CONSOLE
	HINSTANCE h1 = GetModuleHandle(NULL);
#endif
	TFT_eSPI::setH(h1);
	setup();
	long t = 0;
	long long tm = 0;
	while (1)
	{
		if (!TFT_eSPI::canrun)
			break;
		loop();
		/*if (TFT_eSPI::img_ch)
		{
		TFT_eSPI::img_ch = false;
		}*/
		Sleep(5);
		t = t + 8;
		setmillis(t);

		HDC dc = GetDC(TFT_eSPI::hwnd);
		TFT_eSPI::img.Draw(dc, 0, 0, TFT_WIDTH*TFT_SCA, TFT_HEIGHT*TFT_SCA);
		ReleaseDC(TFT_eSPI::hwnd, dc);
	}
}

测试用.cpp

#include "SPI.h"
#include "TFT_eSPI.h"
#include "wifi.h"
#include "FS.h"
#include "SD.h"
#ifdef WIN32
serial Serial;
wifi WiFi;
sd SD;
#endif

TFT_eSPI tft = TFT_eSPI();

#define SCREEN_WIDTH 160
#define SCREEN_HEIGHT 128
#define BALL_RADIUS 5
#define BALL_COLOR TFT_WHITE
#define BG_COLOR TFT_BLUE

int ballX = SCREEN_WIDTH / 2;
int ballY = SCREEN_HEIGHT / 2;
int ballSpeedX = 3;
int ballSpeedY = 3;
int prevX, prevY;

void setup() {
	tft.init();
	tft.setRotation(1); // 根据屏幕实际方向调整(0-3)
	tft.fillScreen(BG_COLOR);
	prevX = ballX;
	prevY = ballY;
}

void loop() {
	// 清除旧位置
	tft.fillCircle(prevX, prevY, BALL_RADIUS, BG_COLOR);

	// 更新位置
	prevX = ballX;
	prevY = ballY;
	ballX += ballSpeedX;
	ballY += ballSpeedY;

	// 边界碰撞检测
	if (ballX <= BALL_RADIUS || ballX >= SCREEN_WIDTH - BALL_RADIUS) {
		ballSpeedX = -ballSpeedX;
		ballX = constrain(ballX, BALL_RADIUS, SCREEN_WIDTH - BALL_RADIUS);
	}
	if (ballY <= BALL_RADIUS || ballY >= SCREEN_HEIGHT - BALL_RADIUS) {
		ballSpeedY = -ballSpeedY;
		ballY = constrain(ballY, BALL_RADIUS, SCREEN_HEIGHT - BALL_RADIUS);
	}

	// 绘制新位置
	tft.fillCircle(ballX, ballY, BALL_RADIUS, BALL_COLOR);

	delay(10); // 调整速度
}