转:CCriticalSection

本文介绍了MFC中的CCriticalSection类,这是一种用于实现线程同步的临界区对象。通过实例展示了如何使用CCriticalSection来控制多线程对共享资源的访问,确保同一时刻只有一个线程能够访问这些资源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

类CCriticalSection的对象表示一个“临界区”,它是一个用于同步的对象,同一时刻只允许一个线程存取资源或代码区。临界区在控制一次只能有一个线程修改数据或其它的控制资源时非常有用。例如在链表中增加一个节点就中允许一次一个线程进行。通过使用CCriticalSection对象来控制链表,就可以达到这个目的。

在运行性能比较重要而且资源不会跨进程使用时,建议采用临界区代替信号灯。有关在MFC中使用信号灯的详细信息,请参阅CMutex。使用CCriticalSection对象之前,需要构造它。在构造函数返回后,就可以使用临界区了。在使用完之后要调用UnLock函数。  

 

      存取由CCriticalSection控制的资源时,要在资源的存取函数中定义一个CSingleLock型的变量。然后调用加锁对象的Lock成员函数(如CSingleLock::Lock)。此时,调用的线程要么获得对资源的存取权,要么等待他人释放资源等待加锁,或者等待他人释放资源,但又因为超时而加锁失败。这样就保证了一次只有一个线程在存取临界资源。释放资源只需调用成员函数UnLock(例如CSingleLock:Unlock),或让锁对象在作用范围之外。  

 

      此外,可以单独地建立一个CCriticalSection对象,并在存取临界资源之前显式地存取它。这种方式有助于保持代码的清晰,但是更容易出错,因为程序员要记住在存取临界资源前加锁,存取之后开锁。  #include <afxmt.h>

 

 

使隶属于同一进程的各线程协调一致地工作称为线程的同步。MFC提供了多种同步对象,最常用的四种:

  • 临界区(CCriticalSection)
  • 事件(CEvent)
  • 互斥量(CMutex)
  • 信号量(CSemaphore)

本人目前只学习了临界区(CCriticalSection) 的使用,简单介绍如下:

      当多个线程访问一个独占性共享资源时,可以使用“临界区”对象。任一时刻只有一个线程可以拥有临界区对象,拥有临界区的线程可以访问被保护起来的资源或代码段,其他希望进入临界区的线程将被挂起等待,直到拥有临界区的线程放弃临界区时为止,这样就保证了不会在同一时刻出现多个线程访问共享资源。

CCriticalSection类的用法非常简单,步骤如下:
 

  1. 定义CCriticalSection类的一个全局对象(以使各个线程均能访问),如CCriticalSection critical_section;
  2. 在访问需要保护的资源或代码之前,调用CCriticalSection类的成员Lock()获得临界区对象:
    critical_section.Lock();
    
    在线程中调用该函数来使线程获得它所请求的临界区。如果此时没有其它线程占有临界区对象,则调用Lock()的线程获得临界区;否则,线程将被挂起,并放入到一个系统队列中等待,直到当前拥有临界区的线程释放了临界区时为止。
  3. 访问临界区完毕后,使用CCriticalSection的成员函数Unlock()来释放临界区:
    critical_section.Unlock();
    
    再通俗一点讲,就是线程A执行到critical_section.Lock();语句时,如果其它线程(B)正在执行critical_section.Lock();语句后且critical_section. Unlock();语句前的语句时,线程A就会等待,直到线程B执行完critical_section. Unlock();语句,线程A才会继续执行。

下面再通过一个实例进行演示说明。


例程:  MultiThread8

  1. 建立一个基于对话框的工程MultiThread8,在对话框IDD_MULTITHREAD8_DIALOG中加入两个按钮和两个编辑框控件,两个按钮的ID分别为IDC_WRITEW和IDC_WRITED,标题分别为“写‘W’”和“写‘D’”;两个编辑框的ID分别为IDC_W和IDC_D,属性都选中Read-only;
  2. 在MultiThread8Dlg.h文件中声明两个线程函数:
    UINT WriteW(LPVOID pParam);
    UINT WriteD(LPVOID pParam);
    
  3. 使用ClassWizard分别给IDC_W和IDC_D添加CEdit类变量m_ctrlW和m_ctrlD;
  4. 在MultiThread8Dlg.cpp文件中添加如下内容:

    为了文件中能够正确使用同步类,在文件开头添加:
    #include "afxmt.h"
    
    定义临界区和一个字符数组,为了能够在不同线程间使用,定义为全局变量:
    CCriticalSection critical_section;
    char g_Array[10];
    
    添加线程函数:
    UINT WriteW(LPVOID pParam)
    {
    	CEdit *pEdit=(CEdit*)pParam;
    	pEdit->SetWindowText("");
    	critical_section.Lock();
    	//锁定临界区,其它线程遇到critical_section.Lock();语句时要等待
    	//直至执行critical_section.Unlock();语句
    	for(int i=0;i<10;i++)
    	{
    		g_Array[i]=''W'';
    	    pEdit->SetWindowText(g_Array);
    		Sleep(1000);
    	}
    	critical_section.Unlock();
    	return 0;
    
    }
    
    UINT WriteD(LPVOID pParam)
    {
    	CEdit *pEdit=(CEdit*)pParam;
    	pEdit->SetWindowText("");
    	critical_section.Lock();
    	//锁定临界区,其它线程遇到critical_section.Lock();语句时要等待
    	//直至执行critical_section.Unlock();语句
    	for(int i=0;i<10;i++)
    	{
    		g_Array[i]=''D'';
    	    pEdit->SetWindowText(g_Array);
    		Sleep(1000);
    	}
    	critical_section.Unlock();
    	return 0;
    
    }
  5. 分别双击按钮IDC_WRITEW和IDC_WRITED,添加其响应函数:
    void CMultiThread8Dlg::OnWritew() 
    {
    	CWinThread *pWriteW=AfxBeginThread(WriteW,
    		&m_ctrlW,
    		THREAD_PRIORITY_NORMAL,
    		0,
    		CREATE_SUSPENDED);
    	pWriteW->ResumeThread();
    }
    
    void CMultiThread8Dlg::OnWrited() 
    {
    	CWinThread *pWriteD=AfxBeginThread(WriteD,
    		&m_ctrlD,
    		THREAD_PRIORITY_NORMAL,
    		0,
    		CREATE_SUSPENDED);
    	pWriteD->ResumeThread();
    	
    }
    由于代码较简单,不再详述。编译、运行该例程,您可以连续点击两个按钮,观察体会临界类的作用。

转载于:https://www.cnblogs.com/killer-xc/p/6612482.html

我想做一个宠物智能投喂系统的仿真,现在我设计l一个stm32f103c6单片机,其PA1连接了compim的RXD,PA2连接了TXD,PB0、PB1连接了发光二极管,发光二极管用来模拟电机动状态,我创建了一个mfc程序(PCMFCDLG),添加了5个Combo Box控件分别用来选择端口号(控件类,m_cbPort)、波特率(控件类,m_cbBaud)、校验位(控件类,m_cbParity)、数据位(控件类,m_cbDate)以及停止位(控件类,m_cbStop),4个按钮控件分别用来打开串口连接stm32单片机、关闭串口、立即投喂、定时投喂,3个Edit control控件(value类,m_strTime1/2/3)用来定时3个时间点实现定时投喂,最后还有一个List Box(控件类,m_listStatus)用来显示执行状态,在PC机单片机没有建立连接的时候除了连接按钮,其他按钮都不能点击,在定时按钮没有按下的时候edit control控件不能输入,在单片机执行完相应操作后,会返还给PC机信息,然后显示在list box里面如mfc界面点击立即投喂,单片机执行完操作后返回给pc端mfc界面一个标志,告诉pc端操作已完成,然后在mfc界面的list里面显示对应操作已完成。 请你根据我的需求帮我写出所有的代码,包括mfc的PCMFC.h代码文件以及PCMFC.cpp代码文件和SerialPort.h代码文件以及SerialPort.c代码文件,和keil里面的相应代码文件。 MFC 代码文件: SerialPort.h cpp #pragma once #include <windows.h> #include <vector> #include <afxwin.h> #include <afxstr.h> class CSerialPort { public: CSerialPort(); ~CSerialPort(); bool InitPort(CWnd* pOwner, LPCTSTR port, UINT baud, BYTE parity, BYTE databits, BYTE stopbits); bool OpenListenThread(); void ClosePort(); bool WriteToPort(const BYTE* buffer, DWORD length); bool IsConnected() const { return m_hComm != INVALID_HANDLE_VALUE; } static const UINT WM_COMM_ERR = WM_USER + 1; static const UINT WM_COMM_RECEIVED = WM_USER + 2; private: HANDLE m_hComm; bool m_bThreadRunning; CWnd* m_pOwner; CRITICAL_SECTION m_cs; static DWORD WINAPI ListenThread(LPVOID lpParam); void ProcessReceivedData(const BYTE* buffer, DWORD length); void ReportError(const CString& msg); void PurgeComm(); }; SerialPort.cpp cpp #include "pch.h" #include "SerialPort.h" CSerialPort::CSerialPort() : m_hComm(INVALID_HANDLE_VALUE) , m_bThreadRunning(false) , m_pOwner(nullptr) { InitializeCriticalSection(&m_cs); } CSerialPort::~CSerialPort() { ClosePort(); DeleteCriticalSection(&m_cs); } bool CSerialPort::InitPort(CWnd* pOwner, LPCTSTR port, UINT baud, BYTE parity, BYTE databits, BYTE stopbits) { m_pOwner = pOwner; // 打开串口 m_hComm = CreateFile(port, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (m_hComm == INVALID_HANDLE_VALUE) { ReportError(_T("无法打开串口")); return false; } // 设置串口缓冲区 if (!SetupComm(m_hComm, 1024, 1024)) { ReportError(_T("设置串口缓冲区失败")); CloseHandle(m_hComm); m_hComm = INVALID_HANDLE_VALUE; return false; } // 配置串口参数 DCB dcb = { 0 }; dcb.DCBlength = sizeof(DCB); if (!GetCommState(m_hComm, &dcb)) { ReportError(_T("获取串口状态失败")); CloseHandle(m_hComm); m_hComm = INVALID_HANDLE_VALUE; return false; } dcb.BaudRate = baud; dcb.ByteSize = databits; dcb.Parity = parity; dcb.StopBits = stopbits; dcb.fBinary = TRUE; dcb.fDtrControl = DTR_CONTROL_ENABLE; dcb.fRtsControl = RTS_CONTROL_ENABLE; if (!SetCommState(m_hComm, &dcb)) { ReportError(_T("设置串口参数失败")); CloseHandle(m_hComm); m_hComm = INVALID_HANDLE_VALUE; return false; } // 设置超时参数 COMMTIMEOUTS timeouts = { 0 }; timeouts.ReadIntervalTimeout = 50; timeouts.ReadTotalTimeoutConstant = 50; timeouts.ReadTotalTimeoutMultiplier = 10; timeouts.WriteTotalTimeoutConstant = 50; timeouts.WriteTotalTimeoutMultiplier = 10; if (!SetCommTimeouts(m_hComm, &timeouts)) { ReportError(_T("设置串口超时失败")); CloseHandle(m_hComm); m_hComm = INVALID_HANDLE_VALUE; return false; } PurgeComm(); return true; } void CSerialPort::PurgeComm() { if (m_hComm != INVALID_HANDLE_VALUE) { ::PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR); } } bool CSerialPort::OpenListenThread() { if (!IsConnected() || !m_pOwner) return false; PurgeComm(); m_bThreadRunning = true; HANDLE hThread = CreateThread(NULL, 0, ListenThread, this, 0, NULL); if (hThread == NULL) { m_bThreadRunning = false; return false; } CloseHandle(hThread); return true; } DWORD WINAPI CSerialPort::ListenThread(LPVOID lpParam) { CSerialPort* pThis = (CSerialPort*)lpParam; BYTE buffer[1024]; DWORD bytesRead = 0; OVERLAPPED ov = { 0 }; ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (ov.hEvent == NULL) { pThis->ReportError(_T("创建事件对象失败")); return 1; } while (pThis->m_bThreadRunning) { BOOL bReadStatus = ReadFile(pThis->m_hComm, buffer, sizeof(buffer), &bytesRead, &ov); if (!bReadStatus) { DWORD err = GetLastError(); if (err == ERROR_IO_PENDING) { WaitForSingleObject(ov.hEvent, INFINITE); if (GetOverlappedResult(pThis->m_hComm, &ov, &bytesRead, FALSE)) { if (bytesRead > 0) { pThis->ProcessReceivedData(buffer, bytesRead); } } } else { pThis->ReportError(_T("读取串口数据失败")); break; } } else { if (bytesRead > 0) { pThis->ProcessReceivedData(buffer, bytesRead); } } } CloseHandle(ov.hEvent); return 0; } void CSerialPort::ProcessReceivedData(const BYTE* buffer, DWORD length) { EnterCriticalSection(&m_cs); if (m_pOwner && m_pOwner->GetSafeHwnd()) { BYTE* pData = new BYTE[length]; memcpy(pData, buffer, length); m_pOwner->PostMessage(WM_COMM_RECEIVED, (WPARAM)length, (LPARAM)pData); } LeaveCriticalSection(&m_cs); } void CSerialPort::ReportError(const CString& msg) { EnterCriticalSection(&m_cs); if (m_pOwner && m_pOwner->GetSafeHwnd()) { CString* pMsg = new CString(msg); m_pOwner->PostMessage(WM_COMM_ERR, (WPARAM)pMsg, 0); } LeaveCriticalSection(&m_cs); } void CSerialPort::ClosePort() { m_bThreadRunning = false; Sleep(100); if (IsConnected()) { PurgeComm(); CloseHandle(m_hComm); m_hComm = INVALID_HANDLE_VALUE; } m_pOwner = nullptr; } bool CSerialPort::WriteToPort(const BYTE* buffer, DWORD length) { if (!IsConnected()) { ReportError(_T("串口未打开")); return false; } DWORD bytesWritten; OVERLAPPED ov = { 0 }; ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (ov.hEvent == NULL) { ReportError(_T("创建事件对象失败")); return false; } if (!WriteFile(m_hComm, buffer, length, &bytesWritten, &ov)) { DWORD err = GetLastError(); if (err == ERROR_IO_PENDING) { if (!GetOverlappedResult(m_hComm, &ov, &bytesWritten, TRUE)) { CloseHandle(ov.hEvent); ReportError(_T("写入串口失败 (等待)")); return false; } } else { CloseHandle(ov.hEvent); ReportError(_T("写入串口失败 (立即)")); return false; } } CloseHandle(ov.hEvent); return bytesWritten == length; } PCMFCDlg.h cpp #pragma once #include "SerialPort.h" #include "afxdialogex.h" class CPCMFCDlg : public CDialogEx { public: CPCMFCDlg(CWnd* pParent = nullptr); #ifdef AFX_DESIGN_TIME enum { IDD = IDD_PCMFC_DIALOG }; #endif protected: virtual void DoDataExchange(CDataExchange* pDX); virtual BOOL OnInitDialog(); HICON m_hIcon; DECLARE_MESSAGE_MAP() public: CComboBox m_cbPort; CComboBox m_cbBaud; CComboBox m_cbParity; CComboBox m_cbData; CComboBox m_cbStop; CListBox m_listStatus; CString m_strTime1; CString m_strTime2; CString m_strTime3; CSerialPort m_SerialPort; BOOL m_bConnected; UINT_PTR m_nTimerID; BOOL m_bTimerEnabled; CByteArray m_RecvBuffer; // 接收数据缓冲区 CCriticalSection m_csRecv; // 缓冲区临界区 BOOL ValidateTimeFormat(CString strTime); void SendFeedCommand(); void OpenSerialPort(); void CloseSerialPort(); void AddStatusMessage(CString message); void EnableControls(BOOL bEnable); void ParseResponse(const BYTE* buffer, DWORD length); void ProcessRecvBuffer(); afx_msg void OnBnClickedButtonOpen(); afx_msg void OnBnClickedButtonClose(); afx_msg void OnBnClickedButtonNowfeed(); afx_msg void OnBnClickedButtonTimefeed(); afx_msg void OnTimer(UINT_PTR nIDEvent); afx_msg LRESULT OnCommErr(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnCommReceived(WPARAM wParam, LPARAM lParam); afx_msg void OnDestroy(); }; PCMFCDlg.cpp cpp #include "pch.h" #include "framework.h" #include "PCMFC.h" #include "PCMFCDlg.h" #include "afxdialogex.h" #include <ctime> #ifdef _DEBUG #define new DEBUG_NEW #endif CPCMFCDlg::CPCMFCDlg(CWnd* pParent) : CDialogEx(IDD_PCMFC_DIALOG, pParent), m_strTime1(_T("08:00")), m_strTime2(_T("12:00")), m_strTime3(_T("18:00")), m_bConnected(FALSE), m_nTimerID(0), m_bTimerEnabled(FALSE) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CPCMFCDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_COMBO_PORT, m_cbPort); DDX_Control(pDX, IDC_COMBO_BAUD, m_cbBaud); DDX_Control(pDX, IDC_COMBO_PARITY, m_cbParity); DDX_Control(pDX, IDC_COMBO_DATA, m_cbData); DDX_Control(pDX, IDC_COMBO_STOP, m_cbStop); DDX_Control(pDX, IDC_LIST, m_listStatus); DDX_Text(pDX, IDC_EDIT_TIME1, m_strTime1); DDX_Text(pDX, IDC_EDIT_TIME2, m_strTime2); DDX_Text(pDX, IDC_EDIT_TIME3, m_strTime3); } BEGIN_MESSAGE_MAP(CPCMFCDlg, CDialogEx) ON_BN_CLICKED(IDC_BUTTON_OPEN, &CPCMFCDlg::OnBnClickedButtonOpen) ON_BN_CLICKED(IDC_BUTTON_CLOSE, &CPCMFCDlg::OnBnClickedButtonClose) ON_BN_CLICKED(IDC_BUTTON_NOWFEED, &CPCMFCDlg::OnBnClickedButtonNowfeed) ON_BN_CLICKED(IDC_BUTTON_TIMEFEED, &CPCMFCDlg::OnBnClickedButtonTimefeed) ON_WM_TIMER() ON_WM_DESTROY() ON_MESSAGE(CSerialPort::WM_COMM_ERR, &CPCMFCDlg::OnCommErr) ON_MESSAGE(CSerialPort::WM_COMM_RECEIVED, &CPCMFCDlg::OnCommReceived) END_MESSAGE_MAP() void CPCMFCDlg::AddStatusMessage(CString message) { CTime currentTime = CTime::GetCurrentTime(); CString timestamp = currentTime.Format(_T("[%H:%M:%S] ")); m_listStatus.AddString(timestamp + message); m_listStatus.SetCurSel(m_listStatus.GetCount() - 1); } void CPCMFCDlg::EnableControls(BOOL bEnable) { GetDlgItem(IDC_BUTTON_CLOSE)->EnableWindow(bEnable); GetDlgItem(IDC_BUTTON_NOWFEED)->EnableWindow(bEnable); GetDlgItem(IDC_BUTTON_TIMEFEED)->EnableWindow(bEnable); GetDlgItem(IDC_EDIT_TIME1)->EnableWindow(bEnable && m_bTimerEnabled); GetDlgItem(IDC_EDIT_TIME2)->EnableWindow(bEnable && m_bTimerEnabled); GetDlgItem(IDC_EDIT_TIME3)->EnableWindow(bEnable && m_bTimerEnabled); } BOOL CPCMFCDlg::OnInitDialog() { CDialogEx::OnInitDialog(); SetIcon(m_hIcon, TRUE); SetIcon(m_hIcon, FALSE); // 初始化串口选项 for (int i = 1; i <= 16; ++i) { CString port; port.Format(_T("COM%d"), i); m_cbPort.AddString(port); } m_cbPort.SetCurSel(0); m_cbBaud.AddString(_T("1200")); m_cbBaud.AddString(_T("2400")); m_cbBaud.AddString(_T("4800")); m_cbBaud.AddString(_T("9600")); m_cbBaud.AddString(_T("19200")); m_cbBaud.AddString(_T("38400")); m_cbBaud.AddString(_T("57600")); m_cbBaud.AddString(_T("115200")); m_cbBaud.SetCurSel(3); // 默认9600波特率 m_cbParity.AddString(_T("None")); m_cbParity.AddString(_T("Odd")); m_cbParity.AddString(_T("Even")); m_cbParity.SetCurSel(0); // 默认无校验 m_cbData.AddString(_T("5")); m_cbData.AddString(_T("6")); m_cbData.AddString(_T("7")); m_cbData.AddString(_T("8")); m_cbData.SetCurSel(3); // 默认8数据位 m_cbStop.AddString(_T("1")); m_cbStop.AddString(_T("1.5")); m_cbStop.AddString(_T("2")); m_cbStop.SetCurSel(0); // 默认1停止位 EnableControls(FALSE); AddStatusMessage(_T("系统启动完成")); AddStatusMessage(_T("请设置串口参数并打开连接")); return TRUE; } void CPCMFCDlg::OnDestroy() { if (m_nTimerID) { KillTimer(m_nTimerID); m_nTimerID = 0; } CloseSerialPort(); CDialogEx::OnDestroy(); } LRESULT CPCMFCDlg::OnCommErr(WPARAM wParam, LPARAM lParam) { CString* pMsg = (CString*)wParam; if (pMsg) { AddStatusMessage(_T("串口错误: ") + *pMsg); delete pMsg; } return 0; } LRESULT CPCMFCDlg::OnCommReceived(WPARAM wParam, LPARAM lParam) { DWORD length = (DWORD)wParam; BYTE* buffer = (BYTE*)lParam; if (buffer && length > 0) { // 将数据添加到接收缓冲区 m_csRecv.Lock(); for (DWORD i = 0; i < length; i++) { m_RecvBuffer.Add(buffer[i]); } m_csRecv.Unlock(); // 处理接收缓冲区 ProcessRecvBuffer(); delete[] buffer; } return 0; } void CPCMFCDlg::ProcessRecvBuffer() { m_csRecv.Lock(); // 查找完整数据包 (4字节: AA CMD STATUS 55) int bufferSize = m_RecvBuffer.GetSize(); int i = 0; while (i <= bufferSize - 4) { if (m_RecvBuffer[i] == 0xAA && m_RecvBuffer[i+3] == 0x55) { // 提取完整数据包 BYTE cmd = m_RecvBuffer[i+1]; BYTE status = m_RecvBuffer[i+2]; // 处理命令 switch(cmd) { case 0x01: // 喂食命令 AddStatusMessage(status == 0x00 ? _T("设备响应: 喂食成功") : _T("设备响应: 喂食失败")); break; default: AddStatusMessage(_T("收到未知命令响应")); break; } // 从缓冲区移除已处理的数据 for (int j = 0; j < 4; j++) { m_RecvBuffer.RemoveAt(i); } bufferSize = m_RecvBuffer.GetSize(); } else { i++; } } m_csRecv.Unlock(); } void CPCMFCDlg::OpenSerialPort() { if (m_bConnected) { AddStatusMessage(_T("串口已连接,无需重复打开")); return; } CString strPort, strBaud; m_cbPort.GetWindowText(strPort); m_cbBaud.GetWindowText(strBaud); int baudRate = _ttoi(strBaud); int parity = m_cbParity.GetCurSel(); int dataBits = m_cbData.GetCurSel() + 5; int stopBits = m_cbStop.GetCurSel(); if (m_SerialPort.InitPort(this, strPort, baudRate, parity, dataBits, stopBits)) { if (m_SerialPort.OpenListenThread()) { m_bConnected = TRUE; EnableControls(TRUE); AddStatusMessage(_T("串口打开成功: ") + strPort + _T(" @") + strBaud); } else { m_SerialPort.ClosePort(); AddStatusMessage(_T("无法启动监听线程")); } } else { AddStatusMessage(_T("串口打开失败: ") + strPort); } } void CPCMFCDlg::CloseSerialPort() { if (m_bConnected) { m_SerialPort.ClosePort(); m_bConnected = FALSE; EnableControls(FALSE); AddStatusMessage(_T("串口已关闭")); } } BOOL CPCMFCDlg::ValidateTimeFormat(CString strTime) { if (strTime.GetLength() != 5) return FALSE; if (strTime[2] != _T(':')) return FALSE; int hour = _ttoi(strTime.Left(2)); int min = _ttoi(strTime.Mid(3)); return (hour >= 0 && hour < 24 && min >= 0 && min < 60); } void CPCMFCDlg::SendFeedCommand() { if (!m_bConnected) { AddStatusMessage(_T("错误: 串口未连接")); return; } BYTE cmd[] = { 0xAA, 0x01, 0x00, 0x55 }; CString strBytes; for (int i = 0; i < sizeof(cmd); i++) { strBytes.AppendFormat(_T("%02X "), cmd[i]); } AddStatusMessage(_T("发送指令: ") + strBytes); if (m_SerialPort.WriteToPort(cmd, sizeof(cmd))) { AddStatusMessage(_T("喂食指令已发送")); } else { AddStatusMessage(_T("错误: 指令发送失败")); } } void CPCMFCDlg::OnBnClickedButtonOpen() { OpenSerialPort(); } void CPCMFCDlg::OnBnClickedButtonClose() { CloseSerialPort(); } void CPCMFCDlg::OnBnClickedButtonNowfeed() { SendFeedCommand(); } void CPCMFCDlg::OnBnClickedButtonTimefeed() { UpdateData(TRUE); if (!ValidateTimeFormat(m_strTime1) || !ValidateTimeFormat(m_strTime2) || !ValidateTimeFormat(m_strTime3)) { AddStatusMessage(_T("错误: 时间格式无效 (使用 HH:MM 格式)")); return; } if (m_nTimerID) { KillTimer(m_nTimerID); m_nTimerID = 0; m_bTimerEnabled = FALSE; AddStatusMessage(_T("定时喂食已停止")); } else { m_nTimerID = SetTimer(1, 60000, NULL); // 每分钟检查一次 m_bTimerEnabled = TRUE; CString msg; msg.Format(_T("定时喂食已启动: %s, %s, %s"), m_strTime1, m_strTime2, m_strTime3); AddStatusMessage(msg); } EnableControls(m_bConnected); } void CPCMFCDlg::OnTimer(UINT_PTR nIDEvent) { if (nIDEvent == 1) { CTime currentTime = CTime::GetCurrentTime(); CString strCurrent = currentTime.Format(_T("%H:%M")); if (strCurrent == m_strTime1) { AddStatusMessage(_T("到达定时喂食时间1: ") + m_strTime1); SendFeedCommand(); } else if (strCurrent == m_strTime2) { AddStatusMessage(_T("到达定时喂食时间2: ") + m_strTime2); SendFeedCommand(); } else if (strCurrent == m_strTime3) { AddStatusMessage(_T("到达定时喂食时间3: ") + m_strTime3); SendFeedCommand(); } } CDialogEx::OnTimer(nIDEvent); } Keil 单片机代码文件: hardware.h c #pragma once #include "stm32f10x.h" // 引脚定义 #define MOTOR_PORT GPIOB #define MOTOR_PIN1 GPIO_Pin_0 #define MOTOR_PIN2 GPIO_Pin_1 // 协议定义 #define CMD_HEADER 0xAA #define CMD_FEED 0x01 #define CMD_END 0x55 // 状态反馈 #define STATUS_OK 0x00 #define STATUS_ERROR 0xFF // 函数声明 void RCC_Configuration(void); void Hardware_Init(void); void Motor_Feed(uint8_t seconds); void SendResponse(uint8_t status, uint8_t cmdType); hardware.c c #include "hardware.h" #include "stm32f10x_gpio.h" #include "stm32f10x_usart.h" #include "stm32f10x_rcc.h" // 系统时钟配置 (使用外部8MHz晶振,PLL到48MHz) void RCC_Configuration(void) { RCC_DeInit(); // 使能外部高速晶振 RCC_HSEConfig(RCC_HSE_ON); while(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET); // 配置PLL (8MHz * 6 = 48MHz) RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_6); RCC_PLLCmd(ENABLE); while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); // 设置系统时钟 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); while(RCC_GetSYSCLKSource() != 0x08); // 设置AHB、APB1和APB2时钟 RCC_HCLKConfig(RCC_SYSCLK_Div1); RCC_PCLK1Config(RCC_HCLK_Div2); // APB1 = 24MHz RCC_PCLK2Config(RCC_HCLK_Div1); // APB2 = 48MHz // 使能外设时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); } // 硬件初始化 void Hardware_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; // 配置USART2引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; // TX (PA2) GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; // RX (PA1) GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置电机控制引脚 GPIO_InitStructure.GPIO_Pin = MOTOR_PIN1 | MOTOR_PIN2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init(MOTOR_PORT, &GPIO_InitStructure); // 配置USART2 (9600, 8N1) USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART2, &USART_InitStructure); // 配置USART中断 NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); USART_Cmd(USART2, ENABLE); } // 电机控制函数 (模拟喂食动作) void Motor_Feed(uint8_t seconds) { // 正 (模拟投喂动作) GPIO_SetBits(MOTOR_PORT, MOTOR_PIN1); GPIO_ResetBits(MOTOR_PORT, MOTOR_PIN2); // 延时 (1秒 = 1000毫秒) for(uint8_t i = 0; i < seconds; i++) { for(uint16_t j = 0; j < 1000; j++) { for(uint16_t k = 0; k < 72; k++); // 约1ms延时 } } // 停止电机 GPIO_ResetBits(MOTOR_PORT, MOTOR_PIN1); GPIO_ResetBits(MOTOR_PORT, MOTOR_PIN2); } // 发送响应给PC void SendResponse(uint8_t status, uint8_t cmdType) { uint8_t response[4] = { CMD_HEADER, cmdType, status, CMD_END }; for(uint8_t i = 0; i < 4; i++) { // 等待发送寄存器空 while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET); USART_SendData(USART2, response[i]); } } main.c c #include "stm32f10x.h" #include "hardware.h" // 接收缓冲区 uint8_t rxBuffer[4] = {0}; uint8_t rxIndex = 0; int main(void) { // 系统初始化 RCC_Configuration(); Hardware_Init(); // 初始状态: 关闭电机 GPIO_ResetBits(MOTOR_PORT, MOTOR_PIN1); GPIO_ResetBits(MOTOR_PORT, MOTOR_PIN2); while(1) { // 主循环 (低功耗模式) __WFI(); } } // USART2中断服务程序 void USART2_IRQHandler(void) { // 处理接收中断 if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) { uint8_t data = USART_ReceiveData(USART2); // 状态机解析协议 switch(rxIndex) { case 0: // 等待帧头 if(data == CMD_HEADER) { rxBuffer[rxIndex++] = data; } break; case 1: // 等待命令 if(data == CMD_FEED) { rxBuffer[rxIndex++] = data; } else { rxIndex = 0; // 无效命令,重置 } break; case 2: // 等待参数 rxBuffer[rxIndex++] = data; break; case 3: // 等待帧尾 if(data == CMD_END) { rxBuffer[rxIndex] = data; // 验证完整指令 if(rxBuffer[0] == CMD_HEADER && rxBuffer[1] == CMD_FEED && rxBuffer[3] == CMD_END) { // 执行喂食操作 (2秒) Motor_Feed(2); // 发送成功响应 SendResponse(STATUS_OK, CMD_FEED); } else { // 发送错误响应 SendResponse(STATUS_ERROR, CMD_FEED); } } rxIndex = 0; // 重置接收状态 break; default: rxIndex = 0; // 异常状态重置 break; } } } Rebuild target 'STM32103' compiling core_cm3.c... assembling startup_stm32f10x_md.s... compiling system_stm32f10x.c... compiling misc.c... compiling stm32f10x_adc.c... compiling stm32f10x_bkp.c... compiling stm32f10x_can.c... compiling stm32f10x_cec.c... compiling stm32f10x_crc.c... compiling stm32f10x_dac.c... compiling stm32f10x_dbgmcu.c... compiling stm32f10x_dma.c... compiling stm32f10x_exti.c... compiling stm32f10x_flash.c... compiling stm32f10x_fsmc.c... compiling stm32f10x_gpio.c... compiling stm32f10x_i2c.c... compiling stm32f10x_iwdg.c... compiling stm32f10x_pwr.c... compiling stm32f10x_rcc.c... compiling stm32f10x_rtc.c... compiling stm32f10x_sdio.c... compiling stm32f10x_spi.c... compiling stm32f10x_tim.c... compiling stm32f10x_usart.c... compiling stm32f10x_wwdg.c... compiling main.c... src\hardware.h(22): warning: #1-D: last line of file ends without a newline void SendResponse(uint8_t status, uint8_t cmdType); src\main.c(81): warning: #1-D: last line of file ends without a newline } src\main.c: 2 warnings, 0 errors compiling stm32f10x_it.c... compiling led.c... compiling delay.c... compiling hardware.c... src\hardware.h(22): warning: #1-D: last line of file ends without a newline void SendResponse(uint8_t status, uint8_t cmdType); src\hardware.c(86): error: #254: type name is not allowed for(uint8_t i = 0; i < seconds; i++) { src\hardware.c(86): error: #65: expected a ";" for(uint8_t i = 0; i < seconds; i++) { src\hardware.c(86): error: #20: identifier "i" is undefined for(uint8_t i = 0; i < seconds; i++) { src\hardware.c(87): error: #254: type name is not allowed for(uint16_t j = 0; j < 1000; j++) { src\hardware.c(87): error: #65: expected a ";" for(uint16_t j = 0; j < 1000; j++) { src\hardware.c(87): error: #20: identifier "j" is undefined for(uint16_t j = 0; j < 1000; j++) { src\hardware.c(88): error: #254: type name is not allowed for(uint16_t k = 0; k < 72; k++); // 约1ms延时 src\hardware.c(88): error: #65: expected a ";" for(uint16_t k = 0; k < 72; k++); // 约1ms延时 src\hardware.c(88): error: #20: identifier "k" is undefined for(uint16_t k = 0; k < 72; k++); // 约1ms延时 src\hardware.c(102): error: #28: expression must have a constant value cmdType, src\hardware.c(103): error: #28: expression must have a constant value status, src\hardware.c(107): error: #254: type name is not allowed for(uint8_t i = 0; i < 4; i++) { src\hardware.c(107): error: #65: expected a ";" for(uint8_t i = 0; i < 4; i++) { src\hardware.c(107): error: #20: identifier "i" is undefined for(uint8_t i = 0; i < 4; i++) { src\hardware.c(112): warning: #1-D: last line of file ends without a newline } src\hardware.c: 2 warnings, 14 errors ".\Objects\project_1.axf" - 14 Error(s), 4 Warning(s). Target not created. Build Time Elapsed: 00:00:17 这是keil代码出现的问题
最新发布
06-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值