个人电脑控制单片机,主要通过以下步骤:
1)建立个人电脑和单片机的通信连接
普通的单片机通常有UART、SPI、IIC、USB等通信模块。
和电脑通信最常用的是UART,可以有两种方式和电脑建立连接(现在的笔记本电脑很少有支持RS232的DB9接口,所以需要使用USB转TTL,或者USB转485的数据线):
从网上购买USB转TTL的串口线:
通过UART-TTL,将单片机的串口TX,RX引出,加限流保护电阻之后,将串口线的地和单片机的地接到一起,将单片机的串口TX接到串口线的RX,将单片机的串口RX接到串口线的TX。
还可以通过RS485总线与电脑连接;
仍然从网上购买USB转RS485的数据线:
RX,TX,以及发送/接收使能控制脚如下图连接至485芯片(如MAX485),将单片机的串口转成485总线。
除了串口之外,还可以从网上购买支持UART接口的以太网模块或者wifi模块。
通过以太网模块,或者wifi模块内置的TCP/IP协议栈,采用TCP/IP通信与个人电脑建立连接。
单片机与模块之间的控制(如初始化、建立TCP客户端/服务端,发送/接收TCP/UDP报文等)可以通过标准的AT指令实现。
2)制定协议以及编写软件
如果是通过RS485连接,建议采用MODBUS通信协议。
个人自定义协议,我通常采用如下格式:
1字节帧头,2字节数据长度,1字节命令字,n字节payload,1字节crc8校验
包括单片机软件和上位机软件,
不管是串口/RS485通信,还是TCP/IP通信,都是单片机串口的通信程序。
包括发送和接收程序,一般开辟发送/接收两块缓存,在串口中断接收程序中,将串口接收到的数据放在环行队列,在主程序中根据协议从环行队列中取出数据进行解析,当解析到有效数据之后,再抛给应用层程序进行相关处理(如IO口控制,IO口读取,FLASH读写等)。
当应用层需要发送数据时,请求串口发送,将数据填入发送缓存,之后串口程序串口发送中断程序中将数据逐字节送入串口发送寄存器。
以下是我在产品中实际使用的串口程序。
#include Uart.h
#include IO.h
#include crc.h
#include Timer.h
#include Strs.h
#include WiFi.h
#define P_UA_TX_P GPIOA
#define P_UA_TX_V 9
#define P_UA_RX_P GPIOA
#define P_UA_RX_V 10
#define UART_WIFI ((USART_TypeDef *) USART1_BASE)
STRUARecType g_ua_stRecRegs;
STRUASendType g_ua_stSendRegs;
#define ENABLE_UART_TX() {\
UART_WIFI->CR1 |= USART_CR1_TXEIE;\
}
//禁止发送中断,使能接收
#define DISABLE_UART_TX() {\
UART_WIFI->CR1 &= ~USART_CR1_TXEIE;\
g_ua_stSendRegs.m_uchSendTimer = 0;\
}
#define ENABLE_UART_INT() {\
NVIC_EnableIRQ(USART1_IRQn);\
}
void fnUA_Init(void);
void fnUA_RealTime(void);
void fnUA_IOInit(void);
void fnUA_RegInit(void);
void fnUA_IOInit(void)
{
//使能外设时钟
U16 bandrate;
SET_IO_AFMODE_PP(P_UA_TX_P, P_UA_TX_V);
SET_IO_IN_WITHOUTPULLUP(P_UA_RX_P, P_UA_RX_V);
//复位UART1外设模块
RCC->APB2RSTR |= RCC_APB2RSTR_USART1RST;
RCC->APB2RSTR &= ~RCC_APB2RSTR_USART1RST;
//使能外设时钟
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
//使能UART功能
UART_WIFI->CR1 = 0; //OVER8=0:16bit sample;M=0:8data bits;PCE=0:parity disable;
UART_WIFI->CR2 = 0; //ABREN=0:Auto baud rate detection is disabled;one stop bit;
UART_WIFI->CR3 = 0; //OVRDIS=0: Overrun enable;ONEBIT=0: Three sample bit method;EIE=0: Error interrupt enable
// 24M FCK 配置波特率为19200U; 24000000/19200=1250(0x04e2)
bandrate = ((U32)SYSCLK_SYS_FREQ / 2 / (U32)115200);
UART_WIFI->BRR = bandrate;//);
UART_WIFI->SR |= (USART_SR_PE |USART_SR_FE|USART_SR_NE |USART_SR_ORE|USART_SR_TC);
//接收/接收中断使能
UART_WIFI->CR1 |= USART_CR1_RE | USART_CR1_RXNEIE;
//发送使能
UART_WIFI->CR1 |= USART_CR1_TE ; //TCIE = 0;TXEIE = 0;IDLEIE = 0;
//使能UART外设模块
UART_WIFI->CR1 |= USART_CR1_UE;
ENABLE_UART_INT();
}
void fnUA_RegInit(void)
{
memset(&g_ua_stRecRegs, 0, sizeof(g_ua_stRecRegs));
memset(&g_ua_stSendRegs, 0, sizeof(g_ua_stSendRegs));
}
void fnUA_Init(void)
{
fnUA_RegInit();
fnUA_IOInit();
}
void USART1_IRQHandler(void)
{
unsigned char temp =0;
static U8 crc = 0;
//Receive Int
if(UART_WIFI->CR1 & USART_CR1_RXNEIE)
{
while(UART_WIFI->SR & USART_SR_RXNE)
{
temp = UART_WIFI->DR;
g_ua_stRecRegs.m_uchRingBuff[g_ua_stRecRegs.m_uchInP & (UA_RX_RINGBUFF_SIZE - 1)] = temp;
g_ua_stRecRegs.m_uchInP++;
}
}
//Transmitter Int
if(UART_WIFI->CR1 & USART_CR1_TXEIE)
{
if(UART_WIFI->SR & USART_SR_TXE)
{//UART发送中断
if(g_ua_stSendRegs.m_uchIsSending)
{
if(0 == g_ua_stSendRegs.m_uchIndex)
{
crc = 0;
}
if(g_ua_stSendRegs.m_uchIndex
{
temp = g_ua_stSendRegs.m_uchBuff[g_ua_stSendRegs.m_uchIndex];
if(g_ua_stSendRegs.m_uchIndex == (g_ua_stSendRegs.m_uchCount - 1))
{
temp = crc;
}
else
{
crc += temp;
}
UART_WIFI->DR = temp;
g_ua_stSendRegs.m_uchIndex ++;
}
else
{
DISABLE_UART_TX();
g_ua_stSendRegs.m_uchIsSending = FALSE;
}
}
}
}
}
#define UA_RX_CHARTIME 8
void fnUA_RecMsg(void)
{
U16 outp;
U8 temp;
static U8 crc = 0;
if(g_tm_stTimerFlag.Bits.bTimer16ms)
{
if(g_ua_stRecRegs.m_uchCharTime)
{
g_ua_stRecRegs.m_uchCharTime--;
if(0 == g_ua_stRecRegs.m_uchCharTime)
{
g_ua_stRecRegs.m_uchPointer = 0;
}
}
}
if(FALSE == g_ua_stRecRegs.m_uchReceived)
{
while(g_ua_stRecRegs.m_uchInP != g_ua_stRecRegs.m_uchOutP)
{
outp = g_ua_stRecRegs.m_uchOutP & (UA_RX_RINGBUFF_SIZE - 1);
g_ua_stRecRegs.m_uchOutP++;
temp = g_ua_stRecRegs.m_uchRingBuff[outp];
if(0 == g_ua_stRecRegs.m_uchPointer)
{
if(0x55 == temp)
{
crc = 0;
g_ua_stRecRegs.m_uchPointer = 1;
}
}
else if(1 == g_ua_stRecRegs.m_uchPointer)
{
if(0xFF == temp)
{
g_ua_stRecRegs.m_uchPointer = 2;
}
else if(0x55 != temp)
{
g_ua_stRecRegs.m_uchPointer = 0;
}
}
else if(g_ua_stRecRegs.m_uchPointer
{
g_ua_stRecRegs.m_uchBuff[g_ua_stRecRegs.m_uchPointer - 2] = temp;
g_ua_stRecRegs.m_uchLen = 0;
g_ua_stRecRegs.m_uchPointer ++;
}
else if(g_ua_stRecRegs.m_uchPointer
{
g_ua_stRecRegs.m_uchLen = g_ua_stRecRegs.m_uchLen
g_ua_stRecRegs.m_uchLen |= (U16)temp;
g_ua_stRecRegs.m_uchBuff[g_ua_stRecRegs.m_uchPointer - 2] = temp;
g_ua_stRecRegs.m_uchPointer ++;
if(g_ua_stRecRegs.m_uchLen >= (UA_RX_BUFF_SIZE - 5)) //msgid, length;
{
g_ua_stRecRegs.m_uchPointer = 0;
}
}
else
{
g_ua_stRecRegs.m_uchBuff[g_ua_stRecRegs.m_uchPointer - 2] = temp;
g_ua_stRecRegs.m_uchPointer++;
if(g_ua_stRecRegs.m_uchPointer >= (g_ua_stRecRegs.m_uchLen + 7))
{
g_ua_stRecRegs.m_uchPointer = 0;
if(crc == temp)
{
g_ua_stRecRegs.m_uchRecvTimer = 6;
g_ua_stRecRegs.m_uchLen += 5;
fnWF_ResetDeadTime();
g_ua_stRecRegs.m_uchReceived = TRUE;
}
break;
}
}
crc += temp;
}
}
}
void fnUA_Monitor(void)
{
if(g_tm_stTimerFlag.Bits.bTimer16ms)
{
if(g_ua_stSendRegs.m_uchSendTimer)
{
g_ua_stSendRegs.m_uchSendTimer --;
if(0 == g_ua_stSendRegs.m_uchSendTimer)
{
fnUA_Init();
}
}
}
}
void fnUA_RealTime(void)
{
U32 data;
fnUA_Monitor();
fnUA_RecMsg();
if(g_tm_stTimerFlag.Bits.bTimer100ms){
if(g_ua_stRecRegs.m_uchRecvTimer)
{
g_ua_stRecRegs.m_uchRecvTimer--;
if(0 == g_ua_stRecRegs.m_uchRecvTimer)
{
g_ua_stRecRegs.m_uchReceived = FALSE;
}
}
}
}
U8 fnUA_SendReq(U16 msgid, U16 len)
{
U8 res = FALSE;
g_ua_stSendRegs.m_uchBuff[0] = 0x55;
g_ua_stSendRegs.m_uchBuff[1] = 0xFF;
g_ua_stSendRegs.m_uchBuff[2] = (U8)(msgid >> 8);
g_ua_stSendRegs.m_uchBuff[3] = (U8)msgid;
g_ua_stSendRegs.m_uchBuff[4] = (U8)(len >> 8);
g_ua_stSendRegs.m_uchBuff[5] = (U8)len;
g_ua_stSendRegs.m_uchCount = len + 7;
g_ua_stSendRegs.m_uchSendTimer = (200);
g_ua_stSendRegs.m_uchIndex = 0;
g_ua_stSendRegs.m_uchIsSending = TRUE;
ENABLE_UART_TX();
res = TRUE;
return(res);
}
U8 fnUA_IsSend(void)
{
U8 res = TRUE;
if(FALSE == g_ua_stSendRegs.m_uchIsSending)
{
res = FALSE;
}
return res;
}
简单的调试,可以用串口调试工具,采用MODBUS协议的工业控制可以用wincc等组态软件。
对于上位机软件,需要选择集成开发环境和编程语言。
这里选择非常多,可以用C语言的VC, PASCAL语言的delphi,脚本语言python,甚至可以用图形化编程语言labview.
我个人比较喜欢用delphi,接下来讲一下delphi的编程实现。
先在delphi上设计人机界面,比如下图的界面。
采用MSCOMM或者SPCOMM实现串口通信。
根据用户的操作发送数据给单片机,接收单片机的数据显示在界面上,有些基本需要通过数据库控件,采用access或者mysql数据库,将采集到的数据保存下来。