我想做一个宠物智能投喂系统的仿真,现在我设计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 代码文件:
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);
}
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)
{
uint8_t i;
uint16_t j;
uint16_t k;
// 正转 (模拟投喂动作)
GPIO_SetBits(MOTOR_PORT, MOTOR_PIN1);
GPIO_ResetBits(MOTOR_PORT, MOTOR_PIN2);
// 延时 (1秒 = 1000毫秒)
for(i = 0; i < seconds; i++) {
for(j = 0; j < 1000; j++) {
for(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 i;
uint8_t response[4];
response[0] = CMD_HEADER;
response[1] = cmdType;
response[2] = status;
response[3] = CMD_END;
for(i = 0; i < 4; i++) {
// 等待发送寄存器空
while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, response[i]);
}
}
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);
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;
}
}
}
现在出现的问题是单片机收到指令后执行模拟投喂操作时两个灯不亮,两灯的正极接gpio口,负极接地,我怀疑是单片机没有正确解析传输的命令,请你重点分析一下keil的代码
最新发布