基于STM32开发下位机和上位机(一、下位机篇)
什么是上位机和下位机?
以下截图为AI回答:

以下将以入门者的视角,来开发这一套系统。
首先,需要准备以下硬件:
1、STM32F103C8T6最小系统板(随个人视情况购买,通常不会超过¥15);
2、STLINK(随个人视情况购买,贵的便宜的都有,一样用);
3、USB转TTL模块(随个人视情况购买,贵的便宜的都有,¥10左右的就很好了);
4、一个插入式LED灯,一个20k电阻;(主要用来看功能是否实现,用其他代替也行,若条件有限,板子上PC13引脚自带的LED也行)
5、不需要别的了,这些够入门了;
再然后,安装STM32开发环境,个人推荐江科大的B站视频,跟着做就好了,应该是大部分嵌入式玩家的启蒙视频;
然后,点亮了PC13,就可以继续开始写以下代码了~
以下是我利用STM32 串口 +GPIO 控制开发的一个32单片机代码,这段代码实现了 STM32F10x 单片机基于 USART1 串口(115200 8 N 1)+DMA1_Channel5 的 4 字节指令帧接收功能,通过 USART1 的 IDLE 空闲中断标记帧接收完成,解析指令帧中指定的 GPIO 端口(A/B/C)、引脚(0-15)和电平状态(高 / 低)后,配置对应 GPIO 为推挽输出模式并控制其输出指定电平,同时通过串口回显接收的指令帧及执行结果(合法指令回传 'H'/'L' 表示高低电平,非法指令回传 'X'),且禁止操作 PA9/PA10(串口引脚)、PC13(可注释不编译) 等关键引脚以避免硬件冲突。
指令如下:
指令格式:命令帧 0/1(高或低) 时钟 引脚号
PORT A(GPIOA) 可用 13 根
PA0 高: AA 01 00 00 低: AA 00 00 00
PA1 高: AA 01 00 01 低: AA 00 00 01
PA2 高: AA 01 00 02 低: AA 00 00 02
PA3 高: AA 01 00 03 低: AA 00 00 03
PA4 高: AA 01 00 04 低: AA 00 00 04
PA5 高: AA 01 00 05 低: AA 00 00 05
PA6 高: AA 01 00 06 低: AA 00 00 06
PA7 高: AA 01 00 07 低: AA 00 00 07
PA8 高: AA 01 00 08 低: AA 00 00 08
PA11 高: AA 01 00 0B 低: AA 00 00 0B
PA12 高: AA 01 00 0C 低: AA 00 00 0C
PA15 高: AA 01 00 0F 低: AA 00 00 0F
PORT B(GPIOB) 可用 16 根
PB0 高: AA 01 01 00 低: AA 00 01 00
PB1 高: AA 01 01 01 低: AA 00 01 01
PB2 高: AA 01 01 02 低: AA 00 01 02
PB3 高: AA 01 01 03 低: AA 00 01 03
PB4 高: AA 01 01 04 低: AA 00 01 04
PB5 高: AA 01 01 05 低: AA 00 01 05
PB6 高: AA 01 01 06 低: AA 00 01 06
PB7 高: AA 01 01 07 低: AA 00 01 07
PB8 高: AA 01 01 08 低: AA 00 01 08
PB9 高: AA 01 01 09 低: AA 00 01 09
PB10 高: AA 01 01 0A 低: AA 00 01 0A
PB11 高: AA 01 01 0B 低: AA 00 01 0B
PB12 高: AA 01 01 0C 低: AA 00 01 0C
PB13 高: AA 01 01 0D 低: AA 00 01 0D
PB14 高: AA 01 01 0E 低: AA 00 01 0E
PB15 高: AA 01 01 0F 低: AA 00 01 0F
PORT C(GPIOC) 可用 8 根
PC0 高: AA 01 02 00 低: AA 00 02 00
PC1 高: AA 01 02 01 低: AA 00 02 01
PC2 高: AA 01 02 02 低: AA 00 02 02
PC3 高: AA 01 02 03 低: AA 00 02 03
PC4 高: AA 01 02 04 低: AA 00 02 04
PC5 高: AA 01 02 05 低: AA 00 02 05
PC6 高: AA 01 02 06 低: AA 00 02 06
PC7 高: AA 01 02 07 低: AA 00 02 07
详细代码如下:
#include "stm32f10x.h"
#define RX_FRAME_LEN 4
#define CMD_BYTE 0xAA
static uint8_t rxBuf[RX_FRAME_LEN];
static volatile uint8_t frameDone = 0;
static GPIO_TypeDef* const PORT_MAP[3] = {GPIOA, GPIOB, GPIOC};
static void usart_putc(uint8_t ch)
{
USART_SendData(USART1, ch);
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
}
static void usart1_init(void)
{
GPIO_InitTypeDef gpio;
USART_InitTypeDef usart;
DMA_InitTypeDef dma;
NVIC_InitTypeDef nvic;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
gpio.GPIO_Pin = GPIO_Pin_9;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
gpio.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &gpio);
gpio.GPIO_Pin = GPIO_Pin_10;
gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &gpio);
USART_StructInit(&usart);
usart.USART_BaudRate = 115200;
usart.USART_WordLength = USART_WordLength_8b;
usart.USART_StopBits = USART_StopBits_1;
usart.USART_Parity = USART_Parity_No;
usart.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
usart.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART1, &usart);
DMA_StructInit(&dma);
dma.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
dma.DMA_MemoryBaseAddr = (uint32_t)rxBuf;
dma.DMA_DIR = DMA_DIR_PeripheralSRC;
dma.DMA_BufferSize = RX_FRAME_LEN;
dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
dma.DMA_Mode = DMA_Mode_Circular;
dma.DMA_Priority = DMA_Priority_High;
dma.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel5, &dma);
DMA_Cmd(DMA1_Channel5, ENABLE);
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
nvic.NVIC_IRQChannel = USART1_IRQn;
nvic.NVIC_IRQChannelPreemptionPriority = 0;
nvic.NVIC_IRQChannelSubPriority = 0;
nvic.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic);
USART_Cmd(USART1, ENABLE);
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
}
void USART1_IRQHandler(void)
{
if (USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)
{
(void)USART1->DR;
frameDone = 1;
}
}
static void exec_cmd(void)
{
// 回显接收的指令帧
for (uint8_t i = 0; i < RX_FRAME_LEN; i++) usart_putc(rxBuf[i]);
uint8_t hl = rxBuf[1];
uint8_t port = rxBuf[2];
uint8_t pin = rxBuf[3];
// 非法参数校验
if (port > 4 || pin > 15 || (port == 0 && pin == 9) || (port == 0 && pin == 10) || (port == 2 && pin == 13))
{
usart_putc('X');
return;
}
static const uint32_t RCC_APB2Periph_Port[3] = {
RCC_APB2Periph_GPIOA, RCC_APB2Periph_GPIOB,
RCC_APB2Periph_GPIOC
};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_Port[port], ENABLE);
GPIO_InitTypeDef gpio = {
.GPIO_Pin = 1UL << pin,
.GPIO_Speed = GPIO_Speed_2MHz,
.GPIO_Mode = GPIO_Mode_Out_PP
};
GPIO_Init(PORT_MAP[port], &gpio);
// 控制GPIO电平
if (hl)
PORT_MAP[port]->BSRR = 1UL << pin;
else
PORT_MAP[port]->BRR = 1UL << pin;
// 回传执行结果
usart_putc(hl ? 'H' : 'L');
}
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE);
usart1_init();
while (1)
{
if (frameDone)
{
frameDone = 0;
exec_cmd();
}
}
}
注:跟着江科大的视频搭建号环境以后,将以上代码复制到main.c中便可编译烧录;
需要可执行文件,可以私信主包,知无不言言无不尽~欢迎同行交流
上位机(串口助手)分享请查看下一篇文章~









