STM32与C#上位机串口通信示例

2023-05-16

1.概述

串口通信是一种非常常用的通信方式,本文首先介绍了串口硬件和协议的相关内容,然后给出一个STM32与C#上位机通过串口通信的示例。

2.串口介绍

参考这份文档

3.通信协议

为了方便数据传输,定义了一个简单的通信协议。利用该协议可以传输不定长数据帧,另外还加入了累加和校验,可以识别出传输过程中的错误。数据帧格式如下:
在这里插入图片描述
数据帧由数据帧头、数据长度、数据、累加和组成,比如数据帧(0xFF 0x02 0x05 06 0x0C)解析为:
在这里插入图片描述
在发送方发送数据时把要发的数据和数据帧头、数据长度、累加和打包到一起,如果接收方比对发现校验和正确就提取出数据帧中的数据部分。定义了数据帧,就可以在上面定义通信协议了。在本例中,单片机中有4个参数(1-4),上位机发命令设置这些参数,单片机收到之后通过串口反馈出来。另外单片机还接了一个led,用上位机可以控制改led的亮灭。协议具体内容如下:
在这里插入图片描述
在上位机发送给单片机的数据中,第一字节代表了该帧数据的功能,比如Byte0=1时表示设置参数1,参数1从Byte1开始占4个字节。单片机发送的协议与此类似:
在这里插入图片描述

3.单片机实现

单片机用的是stm32f103rbt6(其他型号也都类似),开发环境是STM32CubeIDE 1.4.0,调试工具是Stlink V2。

3.1硬件实现

硬件借用了之前做的USBCAN加一个USB转串口模块,原理图如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.2软件实现

配置为外部晶振,频率为8M,主频设为72M:
在这里插入图片描述
在这里插入图片描述
配置调试接口为stlink:
在这里插入图片描述
开启Timer2中断,中断周期为1ms:
在这里插入图片描述
在这里插入图片描述
配置串口为异步通信,波特率9600:
在这里插入图片描述
配置led引脚:
在这里插入图片描述
部分程序介绍如下:

//串口打包发送,按照前面的协议将data打包通过串口发送出去
void Uart1Transmit(uint8_t* data,int len)
{
	uint8_t checkSum = 0;

	memset(u1TxBuffer,0,len + 3);
	u1TxBuffer[0] = 0xFF;
	u1TxBuffer[1] = len;
	checkSum+=u1TxBuffer[0];
	checkSum+=u1TxBuffer[1];
	for(int i=0;i<len;i++)
	{
		u1TxBuffer[2+i] = data[i];
		checkSum+=data[i];
	}
	u1TxBuffer[len+2] = checkSum;
	HAL_UART_Transmit_IT(&huart1, u1TxBuffer,len + 3);
}
//串口接收用的是空闲中断当总线空闲或缓存区满的时候会产生一个中断,在中断中解析数据,
//收到数据后查找数据头0xFF的位置,然后读取一包数据,如果后面有不完整的数据包,把
//它们移到缓存区的最前面,等下次接收时处理
uint32_t startByteIndex = 0;
static uint8_t buffer[500];
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
	int headerIndex = 0;
	uint8_t noHeader = 0;
	uint8_t checkSum = 0;
	uint8_t msgLength = 0;

	if(huart->Instance==USART1)
	{
		memcpy(&buffer[startByteIndex],u1RxBuffer,Size);
		result = HAL_UARTEx_ReceiveToIdle_IT(&huart1, u1RxBuffer,500);
		if(result!=0)
		{
			__NOP();
		}
		do
		{
			noHeader = 1;
			for(int i=headerIndex;i<(startByteIndex+Size);i++)
			{
				if(buffer[i] == 0xFF)
				{
					headerIndex = i;
					noHeader = 0;
					break;
				}
			}
			if(noHeader == 1)
			{
				startByteIndex  = 0;
				break;
			}
			if((headerIndex+1) < (startByteIndex+Size))
			{
				msgLength = buffer[headerIndex+1];
			}
			else
			{
				memcpy(buffer,&buffer[headerIndex],startByteIndex + Size - headerIndex);
				startByteIndex = startByteIndex + Size - headerIndex;
				break;
			}

			if((headerIndex + msgLength + 3) <= (startByteIndex + Size))
			{
				checkSum = 0;
				for(int i=0;i<msgLength + 2;i++)
				{
					checkSum += buffer[headerIndex+i];
				}
				if(checkSum == buffer[headerIndex + msgLength + 2])
				{
					gRxDataType.Unpack(&buffer[headerIndex+2],msgLength);
					headerIndex = headerIndex + msgLength + 3;
				}
				else
				{
					startByteIndex  = 0;
					break;
				}
			}
			else
			{
				memcpy(buffer,&buffer[headerIndex],startByteIndex + Size - headerIndex);
				startByteIndex = startByteIndex + Size - headerIndex;
				break;
			}
		}while(1);
	}
}

4.上位机实现

上位机采用C#语言开发,开发环境为VS2017社区版。界面设计如下:
在这里插入图片描述

 /*固定周期接收数据,收到数据后查找数据头0xFF的位置,然后读取一包数据,如果后面有不完整的数据包,
   把它们移到缓存区的最前面,等下次接收时处理*/
private void timer_uartReceive_Tick(object sender, EventArgs e)
{
    byte[] data;
    int dataSize = 0;
    int msgLength = 0;
    byte noHeaderFlag = 0;
    byte checkSum = 0;
    int headerIndex = 0;

    if (serialPort.IsOpen)
    {
        result = serialPort.Receive(out data);
        if(result == 0)
        {
            dataSize = data.Length;
            if(dataSize > 0)
            {
                dataBuffer.AddRange(data);
                while(true)
                {
                    noHeaderFlag = 1;
                    for (int i = headerIndex; i < (startByteIndex + dataSize); i++)
                    {
                        if (dataBuffer[i] == 0xFF)
                        {
                            headerIndex = i;
                            noHeaderFlag = 0;
                            break;
                        }
                    }
                    if (noHeaderFlag == 1)
                    {
                        startByteIndex = 0;
                        dataBuffer.Clear();
                        break;
                    }
                    if((headerIndex + 1) < dataBuffer.Count)
                    {
                        msgLength = dataBuffer[headerIndex + 1];
                    }
                    else
                    {
                        dataBuffer.RemoveRange(0, headerIndex);
                        startByteIndex = startByteIndex + dataSize - headerIndex;
                        break;
                    }
                    if ((headerIndex + msgLength + 3) <= (startByteIndex + dataSize))
                    {
                        checkSum = 0;
                        for (int i = 0; i < msgLength + 2; i++)
                        {
                            checkSum += dataBuffer[headerIndex + i];
                        }
                        if (checkSum == dataBuffer[headerIndex + msgLength + 2])
                        {
                            byte[] recData;
                            result = Packet.UnPack(dataBuffer.Skip(0).Take(msgLength + 3).ToArray<byte>(), out recData);
                            if (result == 0)
                            {
                                result = protocol.Parse(recData);
                            }
                            else
                            {

                            }
                            headerIndex = headerIndex + msgLength + 3;
                        }
                        else
                        {
                            startByteIndex = 0;
                            dataBuffer.Clear();
                            break;
                        }
                    }
                    else
                    {
                        dataBuffer.RemoveRange(0, headerIndex);
                        startByteIndex = startByteIndex + dataSize - headerIndex;
                        break;
                    }
                }
            }
        }
    }
}

5.效果展示

运行单片机程序和上位机程序,修改参数后单片机能正确反馈参数,并且可以控制led,下方的记录区也能实时显示出发送的报文。
在这里插入图片描述

6.相关资源

完整STM32工程:https://download.csdn.net/download/m0_37782115/33372085
完整C#工程:https://download.csdn.net/download/m0_37782115/33372046

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32与C#上位机串口通信示例 的相关文章

随机推荐