/* Includes */
#include "main.h"
#include "Board.h"
#include "stdio.h"
#include "apm32f4xx_gpio.h"
#include "apm32f4xx_adc.h"
#include "apm32f4xx_misc.h"
#include "apm32f4xx_usart.h"
#include "apm32f4xx_tmr.h"
// 定义角色(取消注释其中一个)
//#define Master
#define Slave // 当前为从机模式
/* printf using USART1 */
#define DEBUG_USART USART1
#define APM_COMInit APM_TINY_COMInit
/* 初始化USART1,用于调试输出(连接串口助手) */
void USART1_Init()
{
/* USART1初始化 */
USART_Config_T usartConfigStruct;
/* 配置USART1参数 */
USART_ConfigStructInit(&usartConfigStruct); // 初始化默认配置
usartConfigStruct.baudRate = 115200; // 波特率115200
usartConfigStruct.mode = USART_MODE_TX_RX; // 收发模式
usartConfigStruct.parity = USART_PARITY_NONE; // 无奇偶校验
usartConfigStruct.stopBits = USART_STOP_BIT_1; // 1停止位
usartConfigStruct.wordLength = USART_WORD_LEN_8B; // 8位数据
usartConfigStruct.hardwareFlow = USART_HARDWARE_FLOW_NONE; // 无硬件流控制
/* 初始化COM1(对应USART1) */
APM_COMInit(COM1, &usartConfigStruct);
}
#ifdef Master
/* 主机模式:接收数据缓冲区和索引 */
volatile uint8_t rx_data[10], rx_idx = 0;
/* 初始化USART2,作为主机串口 */
void USART2_Init(void)
{
// 使能USART2和GPIOA时钟
RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_USART2); // 开启USART2时钟
RCM_EnableAHB2PeriphClock(RCM_AHB1_PERIPH_GPIOA); // 开启GPIOA时钟
// 配置PA2(TX)和PA3(RX)
GPIO_Config_T gpioConfigStruct;
gpioConfigStruct.mode = GPIO_MODE_AF; // 复用功能模式
gpioConfigStruct.otype = GPIO_OTYPE_PP; // 推挽输出
gpioConfigStruct.pin = GPIO_PIN_2 | GPIO_PIN_3; // PA2和PA3
gpioConfigStruct.pupd = GPIO_PUPD_NOPULL; // 无上下拉
gpioConfigStruct.speed = GPIO_SPEED_100MHz; // 高速
GPIO_Config(GPIOA, &gpioConfigStruct); // 应用GPIO配置
// 设置PA2和PA3的复用功能为USART2
GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_2, GPIO_AF_USART2); // PA2复用为USART2_TX
GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_3, GPIO_AF_USART2); // PA3复用为USART2_RX
// 配置USART2
USART_Config_T usartConfigStruct;
USART_ConfigStructInit(&usartConfigStruct); // 初始化默认配置
usartConfigStruct.baudRate = 115200; // 波特率115200
usartConfigStruct.mode = USART_MODE_TX_RX; // 收发模式
usartConfigStruct.parity = USART_PARITY_NONE; // 无奇偶校验
usartConfigStruct.stopBits = USART_STOP_BIT_1; // 1停止位
usartConfigStruct.wordLength = USART_WORD_LEN_8B; // 8位数据
usartConfigStruct.hardwareFlow = USART_HARDWARE_FLOW_NONE; // 无硬件流控制
USART_Config(USART2, &usartConfigStruct); // 应用USART2配置
USART_Enable(USART2); // 使能USART2
// 配置接收中断
NVIC_EnableIRQRequest(USART2_IRQn, 1, 1); // 使能USART2中断
USART_EnableInterrupt(USART2, USART_INT_RXBNE); // 使能接收非空中断
// 清除接收标志位,确保无残留状态
USART_ClearStatusFlag(USART2, USART_FLAG_RXBNE);
}
/* 发送单个字节通过USART2 */
void USART2_SendByte(uint8_t byte)
{
while (USART_ReadStatusFlag(USART2, USART_FLAG_TXBE) == RESET); // 等待发送缓冲区空
USART_TxData(USART2, byte); // 发送数据
}
/* 发送命令帧(地址+命令+校验) */
void SendCommand(uint8_t addr, uint8_t cmd)
{
uint8_t checksum = addr + cmd; // 计算校验和
USART2_SendByte(addr); // 发送地址
USART2_SendByte(cmd); // 发送命令
USART2_SendByte(checksum); // 发送校验和
}
/* 主机主函数 */
int main(void)
{
USART1_Init(); // 初始化调试串口
USART2_Init(); // 初始化主机串口
while (1)
{
SendCommand(0x01, 0x10); // 向从机(地址0x01)发送查询命令(0x10)
for (volatile int i = 0; i < 1000000; i++); // 简单延时
}
}
/* USART2中断处理函数 */
void USART2_IRQHandler(void)
{
if (USART_ReadIntFlag(USART2, USART_INT_RXBNE)) // 检查接收非空中断
{
rx_data[rx_idx++] = USART_RxData(USART2); // 读取接收数据
if (rx_idx >= 3) // 收到完整响应(地址+状态+校验)
{
printf("从机 0x%02X: 状态=0x%02X\n", rx_data[0], rx_data[1]); // 打印从机响应
rx_idx = 0; // 重置接收索引
}
}
}
#elif defined Slave
/* 从机模式:接收数据缓冲区、索引和首次接收标志 */
volatile uint8_t rx_data[10], rx_idx = 0;
volatile uint8_t first_data_received = 0;
/* 初始化USART2,作为从机串口 */
void USART2_Init(void)
{
// 使能USART2和GPIOA时钟
RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_USART2); // 开启USART2时钟
RCM_EnableAHB2PeriphClock(RCM_AHB1_PERIPH_GPIOA); // 开启GPIOA时钟
// 配置PA2(TX)和PA3(RX)
GPIO_Config_T gpioConfigStruct;
gpioConfigStruct.mode = GPIO_MODE_AF; // 复用功能模式
gpioConfigStruct.otype = GPIO_OTYPE_PP; // 推挽输出
gpioConfigStruct.pin = GPIO_PIN_2 | GPIO_PIN_3; // PA2和PA3
gpioConfigStruct.pupd = GPIO_PUPD_NOPULL; // 无上下拉
gpioConfigStruct.speed = GPIO_SPEED_100MHz; // 高速
GPIO_Config(GPIOA, &gpioConfigStruct); // 应用GPIO配置
// 设置PA2和PA3的复用功能为USART2
GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_2, GPIO_AF_USART2); // PA2复用为USART2_TX
GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_3, GPIO_AF_USART2); // PA3复用为USART2_RX
// 配置USART2
USART_Config_T usartConfigStruct;
USART_ConfigStructInit(&usartConfigStruct); // 初始化默认配置
usartConfigStruct.baudRate = 115200; // 波特率115200
usartConfigStruct.mode = USART_MODE_TX_RX; // 收发模式
usartConfigStruct.parity = USART_PARITY_NONE; // 无奇偶校验
usartConfigStruct.stopBits = USART_STOP_BIT_1; // 1停止位
usartConfigStruct.wordLength = USART_WORD_LEN_8B; // 8位数据
usartConfigStruct.hardwareFlow = USART_HARDWARE_FLOW_NONE; // 无硬件流控制
USART_Config(USART2, &usartConfigStruct); // 应用USART2配置
USART_Enable(USART2); // 使能USART2
// 配置接收中断
NVIC_EnableIRQRequest(USART2_IRQn, 1, 1); // 使能USART2中断
USART_EnableInterrupt(USART2, USART_INT_RXBNE); // 使能接收非空中断
// 清除接收标志位,确保无残留状态
USART_ClearStatusFlag(USART2, USART_FLAG_RXBNE);
}
/* 发送单个字节通过USART2 */
void USART2_SendByte(uint8_t byte)
{
while (USART_ReadStatusFlag(USART2, USART_FLAG_TXBE) == RESET); // 等待发送缓冲区空
USART_TxData(USART2, byte); // 发送数据
}
/* 进入静默模式 */
void Enter_Silent_Mode(void)
{
if (!USART_ReadStatusFlag(USART2, USART_FLAG_RXBNE)) // 确保无未处理接收数据
{
USART_EnableMuteMode(USART2); // 启用静默模式
printf("从机:进入静默模式\n"); // 调试输出
}
}
/* 退出静默模式 */
void Exit_Silent_Mode(void)
{
USART_DisableMuteMode(USART2); // 禁用静默模式
USART2->CTRL1_B.TXEN = 1; // 使能发送
printf("从机:退出静默模式\n"); // 调试输出
}
/* USART2中断处理函数 */
void USART2_IRQHandler(void)
{
if (USART_ReadIntFlag(USART2, USART_INT_RXBNE)) // 检查接收非空中断
{
rx_data[rx_idx++] = USART_RxData(USART2); // 读取接收数据
USART_ClearIntFlag(USART2, USART_INT_RXBNE); // 清除接收中断标志
// 首次接收数据后进入静默模式
if (!first_data_received)
{
first_data_received = 1; // 标记首次接收
Enter_Silent_Mode(); // 进入静默模式
rx_idx = 0; // 重置接收索引
return;
}
if (rx_idx == 1 && rx_data[0] != 0x01) // 检查地址(非本机地址0x01)
{
rx_idx = 0; // 重置接收索引
Enter_Silent_Mode(); // 重新进入静默模式
}
else if (rx_idx >= 3) // 收到完整命令(地址+命令+校验)
{
if (rx_data[0] == 0x01 && rx_data[1] == 0x10) // 验证地址和命令
{
uint8_t checksum = rx_data[0] + rx_data[1]; // 计算校验和
if (rx_data[2] == checksum) // 校验通过
{
Exit_Silent_Mode(); // 退出静默模式
USART2_SendByte(0x01); // 发送地址
USART2_SendByte(0x11); // 发送状态
USART2_SendByte(0x01 + 0x11); // 发送校验和
Enter_Silent_Mode(); // 重新进入静默模式
}
}
rx_idx = 0; // 重置接收索引
}
}
}
/* 从机主函数 */
int main(void)
{
USART1_Init(); // 初始化调试串口
USART2_Init(); // 初始化从机串口
while (1)
{
// 空循环,中断处理通信
}
}
#endif
#if defined (__CC_ARM) || defined (__ICCARM__) || (defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050))
/*!
* [url=home.php?mod=space&uid=247401]@brief[/url] Redirect C Library function printf to serial port.
* After Redirection, you can use printf function.
*
* @param ch: The characters that need to be send.
*
* @param *f: pointer to a FILE that can recording all information
* needed to control a stream
*
* @retval The characters that need to be send.
*
* @note
*/
int fputc(int ch, FILE* f)
{
/* send a byte of data to the serial port */
USART_TxData(DEBUG_USART, (uint8_t)ch);
/* wait for the data to be send */
while (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET);
return (ch);
}
#elif defined (__GNUC__)
/*!
* [url=home.php?mod=space&uid=247401]@brief[/url] Redirect C Library function printf to serial port.
* After Redirection, you can use printf function.
*
* @param ch: The characters that need to be send.
*
* @retval The characters that need to be send.
*
* @note
*/
int __io_putchar(int ch)
{
/* send a byte of data to the serial port */
USART_TxData(DEBUG_USART, ch);
/* wait for the data to be send */
while (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET);
return ch;
}
/*!
* [url=home.php?mod=space&uid=247401]@brief[/url] Redirect C Library function printf to serial port.
* After Redirection, you can use printf function.
*
* @param file: Meaningless in this function.
*
* @param *ptr: Buffer pointer for data to be sent.
*
* @param len: Length of data to be sent.
*
* @retval The characters that need to be send.
*
* @note
*/
int _write(int file, char* ptr, int len)
{
int i;
for (i = 0; i < len; i++)
{
__io_putchar(*ptr++);
}
return len;
}
#else
#warning Not supported compiler type
#endif
灵活使用串口的静默模式,提升代码的健壮性,作者细致的描述了串口静默模式的相关概念和相关应用,并加以实例演示,整体较佳。