1. 通过UART串口进行通信
STM32 端代码(UART通信):
#include "stm32f1xx_hal.h"
UART_HandleTypeDef huart1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
char message[] = "Hello from STM32!\r\n";
while (1)
{
HAL_UART_Transmit(&huart1, (uint8_t*)message, sizeof(message), HAL_MAX_DELAY);
HAL_Delay(1000); // 发送间隔1秒
}
}
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 9600;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
HAL_UART_Init(&huart1);
}
static void SystemClock_Config(void)
{
// 时钟配置代码
}
static void MX_GPIO_Init(void)
{
// GPIO初始化代码
}
C# 端代码(通过串口读取STM32的数据):
using System;
using System.IO.Ports;
class Program
{
static void Main()
{
SerialPort port = new SerialPort("COM3", 9600); // 使用串口3, 波特率9600
port.Open();
port.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
Console.WriteLine("等待数据...");
Console.ReadLine(); // 保持程序运行
port.Close();
}
private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string inData = sp.ReadExisting(); // 读取接收到的数据
Console.WriteLine($"接收到的数据: {inData}");
}
}
2. 通过USB-CDC进行通信
STM32 端代码(配置为USB CDC虚拟串口):
- 使用STM32CubeMX生成USB CDC代码。
- 在
usbd_cdc_if.c
文件中修改发送数据的部分:
#include "usbd_cdc_if.h"
uint8_t usb_data[] = "Hello from STM32 via USB!\r\n";
void send_usb_data(void)
{
CDC_Transmit_FS(usb_data, sizeof(usb_data)); // 通过USB发送数据
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_USB_DEVICE_Init(); // 初始化USB设备
while (1)
{
send_usb_data();
HAL_Delay(1000); // 每秒发送一次
}
}
C# 端代码(通过虚拟串口与STM32通信):
using System;
using System.IO.Ports;
class Program
{
static void Main()
{
SerialPort usbPort = new SerialPort("COM4", 115200); // 设置虚拟串口COM4
usbPort.Open();
usbPort.DataReceived += new SerialDataReceivedEventHandler(USBDataReceivedHandler);
Console.WriteLine("USB 连接已打开...");
Console.ReadLine(); // 保持程序运行
usbPort.Close();
}
private static void USBDataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string inData = sp.ReadExisting();
Console.WriteLine($"通过USB接收到的数据: {inData}");
}
}
3. 通过I²C通信(使用FTDI USB-I²C适配器)
STM32 端代码(配置为I²C主机):
#include "stm32f1xx_hal.h"
I2C_HandleTypeDef hi2c1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
uint8_t data[] = {0x01, 0x02, 0x03}; // 要发送的I2C数据
HAL_I2C_Master_Transmit(&hi2c1, 0x68 << 1, data, sizeof(data), HAL_MAX_DELAY); // 发送数据到I2C从设备地址0x68
while (1) {}
}
static void MX_I2C1_Init(void)
{
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000;
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
HAL_I2C_Init(&hi2c1);
}
static void SystemClock_Config(void)
{
// 时钟配置代码
}
static void MX_GPIO_Init(void)
{
// GPIO初始化代码
}
C# 端代码(使用USB-I²C适配器读取STM32发送的数据):
C# 端需要使用FTDI提供的库,例如 D2XX
驱动库。
// 假设使用 FTDI I2C 适配器,调用 FTDI 的 D2XX 库
4. 通过以太网进行TCP通信
STM32 端代码(配置为TCP服务器):
- 使用 STM32CubeMX 生成 LWIP 网络栈,配置以太网和 TCP 服务器。
- 在主程序中实现TCP通信。
#include "lwip/opt.h"
#include "lwip/tcp.h"
#include "lwip.h"
#include <string.h>
void tcp_server_init(void);
err_t recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
if (p != NULL)
{
tcp_recved(tpcb, p->tot_len);
tcp_write(tpcb, "Hello from STM32 TCP Server!", strlen("Hello from STM32 TCP Server!"), 1);
pbuf_free(p);
}
return ERR_OK;
}
void tcp_server_init(void)
{
struct tcp_pcb *pcb;
pcb = tcp_new();
tcp_bind(pcb, IP_ADDR_ANY, 80); // 绑定到端口80
pcb = tcp_listen(pcb);
tcp_accept(pcb, recv_callback);
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_LWIP_Init(); // 初始化以太网栈
tcp_server_init(); // 启动TCP服务器
while (1)
{
MX_LWIP_Process(); // 处理以太网数据
}
}
C# 端代码(作为TCP客户端连接到STM32):
using System;
using System.Net.Sockets;
using System.Text;
class Program
{
static void Main()
{
TcpClient client = new TcpClient("192.168.0.10", 80); // STM32的IP地址和端口
NetworkStream stream = client.GetStream();
byte[] data = Encoding.ASCII.GetBytes("Hello STM32 via Ethernet");
stream.Write(data, 0, data.Length);
byte[] response = new byte[256];
int bytes = stream.Read(response, 0, response.Length);
Console.WriteLine($"收到的回复: {Encoding.ASCII.GetString(response, 0, bytes)}");
stream.Close();
client.Close();
}
}
当然,下面是关于通过CAN总线通信(使用USB-CAN适配器)的完整示例,包括STM32端和C#端的代码。
5. 通过CAN总线通信(使用USB-CAN适配器)
STM32 端代码(配置为CAN节点):
#include "stm32f1xx_hal.h"
CAN_HandleTypeDef hcan1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_CAN1_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_CAN1_Init();
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailbox;
uint8_t TxData[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
TxHeader.StdId = 0x321; // 标准ID
TxHeader.ExtId = 0x01; // 扩展ID(如果需要)
TxHeader.RTR = CAN_RTR_DATA; // 数据帧
TxHeader.IDE = CAN_ID_STD; // 标准ID
TxHeader.DLC = 8; // 数据长度
while (1)
{
HAL_CAN_AddTxMessage(&hcan1, &TxHeader, TxData, &TxMailbox); // 发送CAN数据
HAL_Delay(1000); // 每秒发送一次
}
}
static void MX_CAN1_Init(void)
{
hcan1.Instance = CAN1;
hcan1.Init.Prescaler = 16; // 设置分频器
hcan1.Init.Mode = CAN_MODE_NORMAL; // 正常模式
hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ; // 同步跳宽
hcan1.Init.TimeSeg1 = CAN_BS1_8TQ; // 时间段1
hcan1.Init.TimeSeg2 = CAN_BS2_3TQ; // 时间段2
hcan1.Init.AutoBusOff = DISABLE; // 自动总线关闭
hcan1.Init.AutoWakeUp = DISABLE; // 自动唤醒
hcan1.Init.ReceiveFifoLocked = DISABLE; // 接收FIFO锁定
hcan1.Init.TransmitFifoPriority = DISABLE; // 发送FIFO优先级
HAL_CAN_Init(&hcan1);
}
static void SystemClock_Config(void)
{
// 时钟配置代码
}
static void MX_GPIO_Init(void)
{
// GPIO初始化代码
}
C# 端代码(通过USB-CAN适配器读取CAN数据):
using System;
using System.IO.Ports;
class Program
{
static void Main()
{
// 假设使用特定厂商的CAN库与USB-CAN适配器通信
SerialPort canPort = new SerialPort("COM5", 115200); // 根据实际连接的COM端口
canPort.Open();
while (true)
{
string message = canPort.ReadLine(); // 假设CAN数据以行分隔
Console.WriteLine($"接收到的CAN数据: {message}");
}
canPort.Close();
}
}
总结
-
STM32代码:
- STM32配置为CAN节点,周期性地发送数据帧。
- 使用
HAL_CAN_AddTxMessage
函数发送CAN数据。
-
C#代码:
- C#端通过USB-CAN适配器接收CAN数据,假设使用
SerialPort
进行串口通信。 - 可以根据实际需要将
COM5
替换为相应的串口号。
- C#端通过USB-CAN适配器接收CAN数据,假设使用
注意事项:
- 确保STM32的CAN总线连接正确,并且终端电阻已正确配置。
- C#端可能需要特定的CAN库,具体取决于所使用的USB-CAN适配器。
- 根据实际应用,可能需要对发送的数据格式进行调整。
好的,以下是更多关于STM32外设与C#的实战示例,继续展示不同的外设连接和通信方式:
6. 通过SPI通信
STM32 端代码(配置为SPI主机):
#include "stm32f1xx_hal.h"
SPI_HandleTypeDef hspi1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI1_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SPI1_Init();
uint8_t dataToSend[3] = {0xAA, 0xBB, 0xCC}; // 要发送的数据
uint8_t receivedData[3];
while (1)
{
HAL_SPI_TransmitReceive(&hspi1, dataToSend, receivedData, sizeof(dataToSend), HAL_MAX_DELAY);
HAL_Delay(1000); // 每秒发送一次
}
}
static void MX_SPI1_Init(void)
{
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
HAL_SPI_Init(&hspi1);
}
static void SystemClock_Config(void)
{
// 时钟配置代码
}
static void MX_GPIO_Init(void)
{
// GPIO初始化代码
}
C# 端代码(使用SPI与STM32通信):
using System;
using System.Device.Spi;
using System.Device.Spi.Drivers;
class Program
{
static void Main()
{
// 假设使用 SPI 设备, SPI 0 为主设备
using (SpiDevice spiDevice = new SpiDevice(new SpiConnectionSettings(0, 0)))
{
byte[] dataToSend = { 0xAA, 0xBB, 0xCC }; // 要发送的数据
byte[] receivedData = new byte[dataToSend.Length];
while (true)
{
spiDevice.Write(dataToSend); // 发送数据
spiDevice.Read(receivedData); // 读取返回的数据
Console.WriteLine($"接收到的数据: {BitConverter.ToString(receivedData)}");
System.Threading.Thread.Sleep(1000); // 每秒发送一次
}
}
}
}
7. 通过PWM控制LED
STM32 端代码(配置为PWM输出):
#include "stm32f1xx_hal.h"
TIM_HandleTypeDef htim2;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM2_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM2_Init();
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // 启动PWM
while (1)
{
for (uint32_t pulse = 0; pulse < 1000; pulse += 10)
{
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, pulse); // 设置PWM占空比
HAL_Delay(100); // 延时
}
}
}
static void MX_TIM2_Init(void)
{
TIM_OC_InitTypeDef sConfigOC = {0};
htim2.Instance = TIM2;
htim2.Init.Prescaler = 72 - 1; // 设置时钟
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 1000 - 1; // 设置PWM周期
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&htim2);
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0; // 初始占空比
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1);
}
static void SystemClock_Config(void)
{
// 时钟配置代码
}
static void MX_GPIO_Init(void)
{
// GPIO初始化代码
}
C# 端代码(控制PWM的占空比):
using System;
using System.IO.Ports;
class Program
{
static void Main()
{
SerialPort port = new SerialPort("COM3", 9600);
port.Open();
while (true)
{
for (int pulse = 0; pulse <= 1000; pulse += 10)
{
port.WriteLine(pulse.ToString()); // 发送PWM占空比
System.Threading.Thread.Sleep(100); // 延时
}
}
port.Close();
}
}
8. 通过ADC读取模拟传感器数据
STM32 端代码(配置为ADC读取):
#include "stm32f1xx_hal.h"
ADC_HandleTypeDef hadc1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC1_Init();
uint32_t adcValue;
while (1)
{
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
adcValue = HAL_ADC_GetValue(&hadc1); // 读取ADC值
HAL_ADC_Stop(&hadc1);
HAL_Delay(1000); // 每秒读取一次
}
}
static void MX_ADC1_Init(void)
{
hadc1.Instance = ADC1;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = DISABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
HAL_ADC_Init(&hadc1);
}
static void SystemClock_Config(void)
{
// 时钟配置代码
}
static void MX_GPIO_Init(void)
{
// GPIO初始化代码
}
C# 端代码(读取ADC数据并处理):
using System;
using System.IO.Ports;
class Program
{
static void Main()
{
SerialPort port = new SerialPort("COM3", 9600);
port.Open();
while (true)
{
string adcValue = port.ReadLine(); // 读取ADC值
Console.WriteLine($"ADC值: {adcValue}");
}
port.Close();
}
}
9. 通过RTC获取实时时钟
STM32 端代码(配置RTC):
#include "stm32f1xx_hal.h"
RTC_HandleTypeDef hrtc;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_RTC_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_RTC_Init();
RTC_TimeTypeDef sTime;
RTC_DateTypeDef sDate;
while (1)
{
HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
HAL_Delay(1000); // 每秒获取一次时间
}
}
static void MX_RTC_Init(void)
{
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
hrtc.Init.AsynchPrediv = 127;
hrtc.Init.SynchPrediv = 255;
HAL_RTC_Init(&hrtc);
}
static void SystemClock_Config(void)
{
// 时钟配置代码
}
static void MX_GPIO_Init(void)
{
// GPIO初始化代码
}
C# 端代码(获取RTC数据):
using System;
using System.IO.Ports;
class Program
{
static void Main()
{
SerialPort port = new SerialPort("COM3", 9600);
port.Open();
while (true)
{
string timeData = port.ReadLine(); // 读取RTC数据
Console.WriteLine($"当前时间: {timeData}");
}
port.Close();
}
}
10. 通过SD卡进行文件读写
STM32 端代码(使用FATFS读写SD卡):
#include "ff.h" // FATFS头文件
#include "stm32f1xx_hal.h"
FATFS FatFs; // FatFs工作区
FIL fil; // 文件对象
char buffer[100];
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
f_mount(&FatFs, "", 1); //
挂载文件系统
f_open(&fil, "test.txt", FA_WRITE | FA_CREATE_ALWAYS); // 打开文件
f_write(&fil, "Hello, STM32!", sizeof("Hello, STM32!"), NULL); // 写入数据
f_close(&fil); // 关闭文件
while (1);
}
static void SystemClock_Config(void)
{
// 时钟配置代码
}
static void MX_GPIO_Init(void)
{
// GPIO初始化代码
}
C# 端代码(读取SD卡文件):
using System;
using System.IO;
class Program
{
static void Main()
{
string path = @"\\path_to_sd_card\\test.txt"; // SD卡路径
while (true)
{
if (File.Exists(path))
{
string content = File.ReadAllText(path); // 读取文件内容
Console.WriteLine($"文件内容: {content}");
}
System.Threading.Thread.Sleep(1000); // 每秒检查一次
}
}
}
总结
这些示例展示了如何使用STM32微控制器的多种外设进行实际应用,同时与C#进行数据通信。可以根据实际需求,选择合适的外设和通信方式进行开发。作为一版笔记记录下来!