嵌入式驱动开发实践
RS-485 驱动(STM32)
c
// RS-485 半双工驱动实现
// 使用 DE/RE 引脚控制收发方向
#include "stm32h7xx_hal.h"
#define RS485_DE_PIN GPIO_PIN_12
#define RS485_DE_PORT GPIOA
// 方向控制宏
#define RS485_TX_MODE() HAL_GPIO_WritePin(RS485_DE_PORT, RS485_DE_PIN, GPIO_PIN_SET)
#define RS485_RX_MODE() HAL_GPIO_WritePin(RS485_DE_PORT, RS485_DE_PIN, GPIO_PIN_RESET)
// DMA 发送缓冲区
static uint8_t tx_buffer[256];
static uint8_t rx_buffer[256];
static volatile uint8_t tx_complete = 1;
// 初始化
void RS485_Init(void) {
// 配置 DE 引脚为输出
GPIO_InitTypeDef gpio = {
.Pin = RS485_DE_PIN,
.Mode = GPIO_MODE_OUTPUT_PP,
.Pull = GPIO_NOPULL,
.Speed = GPIO_SPEED_FREQ_HIGH
};
HAL_GPIO_Init(RS485_DE_PORT, &gpio);
RS485_RX_MODE(); // 默认接收模式
// 配置 UART(9600, 8N1)
huart2.Instance = USART2;
huart2.Init.BaudRate = 9600;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
HAL_UART_Init(&huart2);
// 启动 DMA 接收
HAL_UART_Receive_DMA(&huart2, rx_buffer, sizeof(rx_buffer));
}
// 发送数据(带方向控制)
HAL_StatusTypeDef RS485_Send(const uint8_t *data, uint16_t len) {
if (!tx_complete) return HAL_BUSY;
memcpy(tx_buffer, data, len);
tx_complete = 0;
RS485_TX_MODE(); // 切换到发送模式
// 小延迟确保方向切换稳定(根据硬件调整)
__NOP(); __NOP(); __NOP();
return HAL_UART_Transmit_DMA(&huart2, tx_buffer, len);
}
// 发送完成回调(DMA 中断)
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART2) {
// 等待最后一个字节发送完成(TC 标志)
while (__HAL_UART_GET_FLAG(huart, UART_FLAG_TC) == RESET);
RS485_RX_MODE(); // 切换回接收模式
tx_complete = 1;
}
}CAN 总线驱动
c
// STM32 CAN 驱动(用于 BMS 通信)
#include "stm32h7xx_hal.h"
FDCAN_HandleTypeDef hfdcan1;
void CAN_Init(void) {
hfdcan1.Instance = FDCAN1;
hfdcan1.Init.ClockDivider = FDCAN_CLOCK_DIV1;
hfdcan1.Init.FrameFormat = FDCAN_FRAME_CLASSIC; // 经典 CAN 2.0
hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;
hfdcan1.Init.AutoRetransmission = ENABLE;
hfdcan1.Init.TransmitPause = DISABLE;
hfdcan1.Init.ProtocolException = ENABLE;
// 波特率:250kbps(时钟 80MHz)
hfdcan1.Init.NominalPrescaler = 16;
hfdcan1.Init.NominalSyncJumpWidth = 1;
hfdcan1.Init.NominalTimeSeg1 = 13;
hfdcan1.Init.NominalTimeSeg2 = 6;
HAL_FDCAN_Init(&hfdcan1);
// 配置接收过滤器(接收所有扩展帧)
FDCAN_FilterTypeDef filter = {
.IdType = FDCAN_EXTENDED_ID,
.FilterIndex = 0,
.FilterType = FDCAN_FILTER_MASK,
.FilterConfig = FDCAN_FILTER_TO_RXFIFO0,
.FilterID1 = 0x00000000,
.FilterID2 = 0x00000000 // 掩码全0=接收所有
};
HAL_FDCAN_ConfigFilter(&hfdcan1, &filter);
HAL_FDCAN_ConfigGlobalFilter(&hfdcan1,
FDCAN_REJECT, FDCAN_REJECT,
FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE);
// 启动 CAN
HAL_FDCAN_Start(&hfdcan1);
HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0);
}
// 发送 CAN 帧
HAL_StatusTypeDef CAN_Send(uint32_t can_id, const uint8_t *data, uint8_t len) {
FDCAN_TxHeaderTypeDef tx_header = {
.Identifier = can_id,
.IdType = FDCAN_EXTENDED_ID,
.TxFrameType = FDCAN_DATA_FRAME,
.DataLength = len,
.ErrorStateIndicator = FDCAN_ESI_ACTIVE,
.BitRateSwitch = FDCAN_BRS_OFF,
.FDFormat = FDCAN_CLASSIC_CAN,
.TxEventFifoControl = FDCAN_NO_TX_EVENTS,
.MessageMarker = 0
};
return HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &tx_header, data);
}
// 接收中断回调
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs) {
FDCAN_RxHeaderTypeDef rx_header;
uint8_t rx_data[8];
while (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0,
&rx_header, rx_data) == HAL_OK) {
// 处理接收到的 CAN 帧
CAN_ProcessMessage(rx_header.Identifier, rx_data, rx_header.DataLength);
}
}SPI Flash 驱动
c
// W25Q128 SPI Flash 驱动(用于数据缓存)
#define W25Q_PAGE_SIZE 256
#define W25Q_SECTOR_SIZE 4096
#define W25Q_TOTAL_SIZE (16 * 1024 * 1024) // 16MB
// 命令定义
#define CMD_WRITE_ENABLE 0x06
#define CMD_READ_STATUS1 0x05
#define CMD_PAGE_PROGRAM 0x02
#define CMD_SECTOR_ERASE 0x20
#define CMD_READ_DATA 0x03
#define CMD_CHIP_ERASE 0xC7
static void SPI_CS_Low(void) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); }
static void SPI_CS_High(void) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); }
// 等待 Flash 就绪
static void W25Q_WaitBusy(void) {
uint8_t cmd = CMD_READ_STATUS1;
uint8_t status;
do {
SPI_CS_Low();
HAL_SPI_Transmit(&hspi1, &cmd, 1, 100);
HAL_SPI_Receive(&hspi1, &status, 1, 100);
SPI_CS_High();
} while (status & 0x01); // WIP 位
}
// 写使能
static void W25Q_WriteEnable(void) {
uint8_t cmd = CMD_WRITE_ENABLE;
SPI_CS_Low();
HAL_SPI_Transmit(&hspi1, &cmd, 1, 100);
SPI_CS_High();
}
// 扇区擦除(4KB)
void W25Q_EraseSector(uint32_t addr) {
W25Q_WriteEnable();
uint8_t cmd[4] = {
CMD_SECTOR_ERASE,
(addr >> 16) & 0xFF,
(addr >> 8) & 0xFF,
addr & 0xFF
};
SPI_CS_Low();
HAL_SPI_Transmit(&hspi1, cmd, 4, 100);
SPI_CS_High();
W25Q_WaitBusy();
}
// 页写入(最多 256 字节)
void W25Q_WritePage(uint32_t addr, const uint8_t *data, uint16_t len) {
W25Q_WriteEnable();
uint8_t cmd[4] = {
CMD_PAGE_PROGRAM,
(addr >> 16) & 0xFF,
(addr >> 8) & 0xFF,
addr & 0xFF
};
SPI_CS_Low();
HAL_SPI_Transmit(&hspi1, cmd, 4, 100);
HAL_SPI_Transmit(&hspi1, (uint8_t*)data, len, 1000);
SPI_CS_High();
W25Q_WaitBusy();
}
// 读取数据
void W25Q_Read(uint32_t addr, uint8_t *buf, uint32_t len) {
uint8_t cmd[4] = {
CMD_READ_DATA,
(addr >> 16) & 0xFF,
(addr >> 8) & 0xFF,
addr & 0xFF
};
SPI_CS_Low();
HAL_SPI_Transmit(&hspi1, cmd, 4, 100);
HAL_SPI_Receive(&hspi1, buf, len, 5000);
SPI_CS_High();
}低功耗设计
c
// STM32 低功耗模式管理
typedef enum {
POWER_MODE_RUN, // 正常运行
POWER_MODE_SLEEP, // 睡眠(CPU 停止,外设运行)
POWER_MODE_STOP2, // 停止2(最低功耗,保留 RAM)
POWER_MODE_STANDBY // 待机(最低功耗,RAM 丢失)
} PowerMode_t;
// 进入停止模式(等待中断唤醒)
void EnterStopMode(void) {
// 确保所有外设操作完成
HAL_UART_DeInit(&huart2);
// 配置唤醒源(RTC 闹钟)
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 30, RTC_WAKEUPCLOCK_CK_SPRE_16BITS);
// 进入停止模式
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
// 唤醒后重新初始化时钟
SystemClock_Config();
HAL_UART_Init(&huart2);
}
// 动态电压频率调节(DVFS)
void SetLowPerformanceMode(void) {
// 降低 CPU 频率(从 480MHz 降到 120MHz)
RCC_ClkInitTypeDef clk = {
.ClockType = RCC_CLOCKTYPE_SYSCLK,
.SYSCLKSource = RCC_SYSCLKSOURCE_HSI,
.AHBCLKDivider = RCC_SYSCLK_DIV1,
.APB1CLKDivider = RCC_HCLK_DIV2,
.APB2CLKDivider = RCC_HCLK_DIV2
};
HAL_RCC_ClockConfig(&clk, FLASH_LATENCY_2);
// 降低电压(VOS3 模式)
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE3);
}Linux 内核驱动(嵌入式 Linux)
c
// 简单字符设备驱动示例(RS485 控制)
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/of.h>
struct rs485_dev {
struct device *dev;
int de_gpio;
int re_gpio;
};
static int rs485_probe(struct platform_device *pdev) {
struct rs485_dev *priv;
struct device_node *np = pdev->dev.of_node;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) return -ENOMEM;
// 从设备树获取 GPIO
priv->de_gpio = of_get_named_gpio(np, "de-gpios", 0);
if (!gpio_is_valid(priv->de_gpio)) {
dev_err(&pdev->dev, "Invalid DE GPIO\n");
return -EINVAL;
}
// 申请 GPIO
devm_gpio_request_one(&pdev->dev, priv->de_gpio,
GPIOF_OUT_INIT_LOW, "rs485-de");
platform_set_drvdata(pdev, priv);
dev_info(&pdev->dev, "RS485 driver probed\n");
return 0;
}
static const struct of_device_id rs485_of_match[] = {
{ .compatible = "mycompany,rs485-ctrl" },
{}
};
MODULE_DEVICE_TABLE(of, rs485_of_match);
static struct platform_driver rs485_driver = {
.probe = rs485_probe,
.driver = {
.name = "rs485-ctrl",
.of_match_table = rs485_of_match,
},
};
module_platform_driver(rs485_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("RS485 Direction Control Driver");c
// 设备树节点(DTS)
rs485_ctrl: rs485-ctrl {
compatible = "mycompany,rs485-ctrl";
de-gpios = <&gpio1 12 GPIO_ACTIVE_HIGH>;
re-gpios = <&gpio1 13 GPIO_ACTIVE_LOW>;
status = "okay";
};