嵌入式C语言的一些基本操作,读取串口接收数据,通过Printf打印结果--printf重定位

嵌入式C语言的一些基本操作,读取串口接收数据,通过Printf打印结果--printf重定位 以stm32f107VC为例展示串口打印过程串口初始化以USART3为例放在usart.c中void USART3_Init(uint32_t baudrate) { USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; USART_RCC_Configuration(); //static void USART_RCC_Configuration(void) //static 可以使函数被固化不被其他文件找到 //{ // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE); //使能GPIOC时钟\AFIO功能时钟 // RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE); // 使能USART3时钟 //}USART需要的初始化设置波特率数据格式8、16标准 USART 硬件设计为传输数据位可以是 8 位常用或 9 位用于地址识别或多处理器通信。停止位在 UART/USART 通信中每一帧数据通常包含起始位 数据位5~9位 可选校验位 停止位。停止位的作用是让接收方确认当前帧的结束并为下一帧的起始位提供时间间隙。保证收发双方时钟同步的容错能力。异步通信没有独立的时钟线接收方根据起始位的下降沿同步然后按约定的波特率采样数据。停止位提供一段高电平时间。奇偶校验位硬件流控制简单来说就是不会丢失数据的一种模式在双方频率不同时使用。但是使用场景少要多两条线硬件外设还要支持这种控制使能收发相当于打开双工控制补充说明初始化后要使能通常在此之后还需要调用USART_Cmd(USART3, ENABLE);才能真正启动 USART3 的收发功能。如果代码中没有这一行串口将不会工作。需要GPIO 配置要正常使用 USART3还需配置对应的 TX 和 RX 引脚为复用功能例如 PC10、PC11 或 PB10、PB11 等具体取决于芯片型号。这部分代码通常单独完成。NVIC-中断配置配置USART3外设中断EXTI15_10_IRQn为外部中断线含义USART3 外设的专用中断向量。触发源USART3 内部产生的事件如接收完成、发送完成、空闲帧、校验错误等。中断服务函数USART3_IRQHandler()。对比项USART3_IRQnEXTI15_10_IRQn中断源类型外设内部事件串口外部引脚边沿/电平变化中断向量专用仅 USART3共享多条 EXTI 线共用一个入口中断服务函数USART3_IRQHandler()EXTI15_10_IRQHandler()服务函数内容读取 USART 状态寄存器判断具体事件读取 EXTI 挂起寄存器判断是哪条线典型应用串口收发数据、错误处理按键、传感器信号捕获、唤醒抢占优先级NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority1子优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority0IRQ通道使能NVIC_InitStructure .NVIC_IRQChannelCmd ENABLE;mapNVIC_Init(NVIC_InitStructure);//USART 初始化设置 USART_DeInit(USART3); USART_InitStructure.USART_BaudRate baudrate; // 串口波特率 USART_InitStructure.USART_WordLength USART_WordLength_8b; // 字长为8位数据格式 USART_InitStructure.USART_StopBits USART_StopBits_1; // 一个停止位 USART_InitStructure.USART_Parity USART_Parity_No; // 无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode USART_Mode_Rx | USART_Mode_Tx; // 使能收发模式 USART_Init(USART3, USART_InitStructure); // 初始化串口 // 中断配置 NVIC_InitStructure .NVIC_IRQChannel USART3_IRQn; NVIC_InitStructure .NVIC_IRQChannelPreemptionPriority1; // 抢占优先级 NVIC_InitStructure .NVIC_IRQChannelSubPriority 0; // 子优先级 NVIC_InitStructure .NVIC_IRQChannelCmd ENABLE; // IRQ通道使能 NVIC_Init(NVIC_InitStructure); #ifdef USE_DMA USART_ITConfig(USART3, USART_IT_IDLE, ENABLE); #else USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); #endif USART_Cmd(USART3, ENABLE); // 使能串口, 开始工作 USART_ClearFlag(USART3,USART_FLAG_TC); //清除USARTx的待处理标志位 USART3_GPIO_Configuration(); //USART GPIO设置 #ifdef USE_DMA DMA_USART3_RX_Config(); USART3_Enable_DMA_RX(); #endif }详细解释#ifdef USE_DMA USART_ITConfig(USART3, USART_IT_IDLE, ENABLE); #else USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); #endif空闲中断USART_IT_IDLE当接收线上出现空闲帧即一个完整帧的时间没有新数据时触发。通常与 DMA 配合DMA 负责自动将接收数据搬运到内存缓冲区空闲中断标志一帧数据的结束可在中断中停止 DMA、计算接收长度并处理数据避免了每收到一个字节就中断一次提高了效率。触发条件接收线RX上持续一个完整帧时间起始位数据位校验位停止位处于高电平状态硬件自动置位 IDLE 标志并产生中断。用途配合 DMA 实现不定长数据接收标记一帧数据结束让软件知道“本次数据已收完可以处理了”。跟看门狗的区别本质系统级的硬件定时器用于监控程序是否正常运行。工作方式启动后开始倒计时软件必须在超时前“喂狗”重置计时器否则看门狗会触发系统复位或中断。用途防止程序跑飞、死循环或死锁提高系统可靠性。归属属于独立或系统外设IWDG、WWDG与通信无关。接收中断USART_IT_RXNE当接收数据寄存器DR中有新数据时触发。用于非 DMA 方式每收到一个字节就会进入中断在中断服务程序中手动读取数据并存储。这种条件编译允许同使能USART_Cmd(USART3, ENABLE)在此之前所有配置波特率、字长、停止位、中断使能等都已写入寄存器但只有调用此函数后USART3 才能真正发送和接收数据3. 清除发送完成标志USART_ClearFlag(USART3, USART_FLAG_TC)清除 USART3 的TCTransmission Complete发送完成标志位。TC 标志在 USART 使能后可能处于不确定状态尤其是复位后发送第一帧数据前清除它可以确保后续发送完成中断或状态判断的准确性。注意有些库中直接操作USART_ClearFlag()有些则使用USART_ClearITPendingBit()但实际效果相同。4. 配置 USART3 的 GPIO 引脚USART3_GPIO_Configuration(); //USART GPIO设置static void USART3_GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; // GPIO_TX引脚配置 GPIO_InitStructure.GPIO_Pin GPIO_Pin_10; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; // TX引脚 GPIO_Init (GPIOC, GPIO_InitStructure); // GPIO_RX引脚配置 GPIO_InitStructure.GPIO_Pin GPIO_Pin_11; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING ; // RX引脚 //将USART3的TXD和RXD重映射到PC10和PC11 GPIO_PinRemapConfig(GPIO_PartialRemap_USART3,ENABLE); //部分重映像 GPIO_Init (GPIOC, GPIO_InitStructure); }5. 如果使用 DMA配置 DMA 并启用 DMA 接收#ifdef USE_DMA DMA_USART3_RX_Config(); USART3_Enable_DMA_RX(); #endifDMA_USART3_RX_Config()用户自定义函数用于配置 DMA 通道以接收 USART3 的数据。典型步骤void DMA_USART3_RX_Config(void) { RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_InitTypeDef DMA_InitStructure; DMA_DeInit(DMA1_Channel3); DMA_InitStructure.DMA_PeripheralBaseAddr (uint32_t)(USART3-DR); DMA_InitStructure.DMA_MemoryBaseAddr (uint32_t)USART3_RxBuffer; DMA_InitStructure.DMA_DIR DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize USART3_RX_BUFFER_SIZE; DMA_InitStructure.DMA_PeripheralInc DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode DMA_Mode_Normal; DMA_InitStructure.DMA_Priority DMA_Priority_Medium; DMA_InitStructure.DMA_M2M DMA_M2M_Disable; DMA_Init(DMA1_Channel3, DMA_InitStructure); DMA_Cmd(DMA1_Channel3, ENABLE); }USART3_Enable_DMA_RX()使能 USART3 的 DMA 接收功能。实际上是调用库函数USART_DMACmd(USART3, USART_DMAReq_Rx, ENABLE)将 USART3 的控制寄存器中的 DMAR 位置 1使能 DMA 接收请求。此后每当 USART3 收到一个字节硬件会自动向 DMA 控制器发送请求DMA 将数据从USART3-DR搬运到内存缓冲区无需 CPU 干预。void USART3_Enable_DMA_RX(void) { USART_DMACmd(USART3, USART_DMAReq_Rx, ENABLE); } void USART3_IRQHandler(void) { if (USART_GetITStatus(USART3, USART_IT_IDLE) ! RESET) { volatile uint16_t temp USART3-SR; temp USART3-DR; (void)temp; DMA_Cmd(DMA1_Channel3, DISABLE); USART3_RxCount USART3_RX_BUFFER_SIZE - DMA_GetCurrDataCounter(DMA1_Channel3); USART3_RxFinished 1; } }最终就是printf的重定向了这个能复制粘贴就行还是尽量背下来main.c函数NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); USART3_Init(UART3_BAUD); // 串口初始化USART3(115200-N-8-1), 且工程已把printf重定向至USART3输出; SysTick_Init(); #if 1 printf(Hello,World!\r\n); #endif # if 1 while(1) { //读取串口接收数据 #ifdef USE_DMA if(USART3_RxFinished 1){ show_menu(); USART3_RxFinished 0; printf(uart3_rcv %s\r\n, USART3_RxBuffer); memset(USART3_RxBuffer, 0, USART3_RX_BUFFER_SIZE);//关键去除上一次残留 DMA_SetCurrDataCounter(DMA1_Channel3, USART3_RX_BUFFER_SIZE); DMA_Cmd(DMA1_Channel3, ENABLE); } #else printf(uart3_rcv %c\r\n, uart3_cmd); #endif SysTick_delay_ms(1000); } # endif while(1);