WindowC++模拟单片机控制TFT屏幕和SD卡
- IT业界
- 2025-09-17 23:54:02

因为每次都要做大量的测试,上传到单片机实在是太费事,所以写了这个模拟项目用来测试 很多方法我没有补充进去,因为太多了,如果有需要请自行补充
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 "; 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; } }WindowC++模拟单片机控制TFT屏幕和SD卡由讯客互联IT业界栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“WindowC++模拟单片机控制TFT屏幕和SD卡”