完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
记得点赞,有问题请留言前言 在嵌入式的开发中,经常要用到通过串口打印调试信息。有时为了节省成本,而没有多余的串口可用。因此打印调试信息可以采用: 方式1:工程中添加SEGGER_RTT调试打印 方式2:模拟串口 UART工作原理 UART即通用异步收发器,是一种串行通信方式。数据在传输过程中是通过一位一位地进行传输来实现通信的,串行通信方式具有传输线少,成本底等优点,缺点是速度慢。串行通信分为两种类型:同步通信方式和异步通信方式。但一般多用异步通信方式,主要因为接受和发送的时钟是可以独立的这样有利于增加发送与接收的灵活性。异步通信是一个字符接着一个字符传输,一个字符的信息由起始位、数据位、奇偶校验位和停止位组成。 每一个字符的传输靠起始位来同步,字符的前面一位是起始位,用下降沿通知收方开始传输,紧接着起始位之后的是数据位,传输时低位在前高位在后,字符本身由5~8位数据位组成。数据位后面是奇偶校验位,最后是停止位,停止位是用高电平来标记一个字符的结束,并为下一个字符的传输做准备。停止位后面是不同长度的空闲位。停止位和空闲位都规定为高电平,这样可以保证起始位有一个下降沿。UART的帧格式如图 UART的帧格式包括线路空闲状态(idle,高电平)、起始位(start bit,低电平)、5~8位数据位(data bits)、校验位(parity bit,可选)和停止位(stop bit,位数可为1、1.5、2位)。 UART模拟原理 UART的模拟方式基本就是定时器+IO口实现。 方案1:只打印不接收 如果在实际使用中只是为了打印log而不接收数据,可以采用DWT加普通IO口的方式; #define VCOM_BOUND 115200 #define VCOM_PIN GPIO_Pin_11 #define VCOM_PORT GPIOA #define VCOM_PIN_HIGH VCOM_PORT->BSRR = VCOM_PIN #define VCOM_PIN_LOW VCOM_PORT->BRR = VCOM_PIN #define BSP_REG_DEM_CR (*(volatile unsigned int *)0xE000EDFC) //DEMCR寄存器 #define BSP_REG_DWT_CR (*(volatile unsigned int *)0xE0001000) //DWT控制寄存器 #define BSP_REG_DWT_CYCCNT (*(volatile unsigned int *)0xE0001004) //DWT时钟计数寄存器 #define BSP_REG_DBGMCU_CR (*(volatile unsigned int *)0xE0042004) #define DEF_BIT_00 0x01u #define DEF_BIT_24 0x01000000u #define BSP_BIT_DEM_CR_TRCENA DEF_BIT_24 #define BSP_BIT_DWT_CR_CYCCNTENA DEF_BIT_00 static unsigned int sys_clock = 48000000; inline void dwt_start(void) { BSP_REG_DEM_CR |= (unsigned int)BSP_BIT_DEM_CR_TRCENA; BSP_REG_DWT_CYCCNT = (unsigned int)0u; //初始化CYCCNT寄存器 BSP_REG_DWT_CR |= (unsigned int)BSP_BIT_DWT_CR_CYCCNTENA; //开启CYCCNT } inline void dwt_stop(void) { BSP_REG_DWT_CR = 0; } void vcom_pin_init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitStructure.GPIO_Pin = VCOM_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(VCOM_PORT, &GPIO_InitStructure); GPIO_SetBits(VCOM_PORT,VCOM_PIN); VCOM_PIN_HIGH; } void vcom_put_char(char ch) { int i; int dat[8]; uint32_t sys_clk, bit_width; volatile uint32_t time_stamp; sys_clk = sys_clock/1000000; bit_width = 1000000*sys_clk/VCOM_BOUND; for(i=0; i<8; i++) { if(ch & 0x01) dat = 1; else dat = 0; ch >>= 1; } OS_CPU_SR cpu_sr; enter_critical();//以下代码进行临界保护,防止被中断打断造成发送误码 dwt_start(); VCOM_PIN_LOW; //发送起始位 time_stamp = BSP_REG_DWT_CYCCNT; while(BSP_REG_DWT_CYCCNT < (time_stamp+bit_width)); for(i=0; i<8; i++) { if(dat) VCOM_PIN_HIGH; else VCOM_PIN_LOW; time_stamp = BSP_REG_DWT_CYCCNT; while(BSP_REG_DWT_CYCCNT < (time_stamp+bit_width)); //发8bit 数据位 } VCOM_PIN_HIGH; time_stamp = BSP_REG_DWT_CYCCNT; while(BSP_REG_DWT_CYCCNT < (time_stamp+bit_width)); //发停止位 dwt_stop(); exit_critical(); } void vcom_printf(const char *fmt, ...) { char buf[0x80]; int i; va_list ap; memset(buf, 0x00, sizeof(buf)); va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); i = 0; while(buf) { vcom_put_char(buf); i++; } } 方案2:半双工UART 实现方式: 普通定时器+普通IO口中断+fifo /** *软件串口的实现(IO模拟串口) * 波特率:9600 1-8-N * TXD : PC13 * RXD : PB14 * 使用外部中断对RXD的下降沿进行触发,使用定时器4按照9600波特率进行定时数据接收。 * Demo功能: 接收11个数据,然后把接收到的数据发送出去 */ #define OI_TXD PCout(13) #define OI_RXD PBin(14) #define BuadRate_9600 100 u8 len = 0; //接收计数 u8 USART_buf[11]; //接收缓冲区 enum{ COM_START_BIT, COM_D0_BIT, COM_D1_BIT, COM_D2_BIT, COM_D3_BIT, COM_D4_BIT, COM_D5_BIT, COM_D6_BIT, COM_D7_BIT, COM_STOP_BIT, }; u8 recvStat = COM_STOP_BIT; u8 recvData = 0; void IO_TXD(u8 Data) { u8 i = 0; OI_TXD = 0; delay_us(BuadRate_9600); for(i = 0; i < 8; i++) { if(Data&0x01) OI_TXD = 1; else OI_TXD = 0; delay_us(BuadRate_9600); Data = Data>>1; } OI_TXD = 1; delay_us(BuadRate_9600); } void USART_Send(u8 *buf, u8 len) { u8 t; for(t = 0; t < len; t++) { IO_TXD(buf[t]); } } void IOConfig(void) { GPIO_InitTypeDef GPIO_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; EXTI_InitTypeDef EXTI_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC, ENABLE); //使能PB,PC端口时钟 //SoftWare Serial TXD GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_SetBits(GPIOC,GPIO_Pin_13); //SoftWare Serial RXD GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14); EXTI_InitStruct.EXTI_Line = EXTI_Line14; EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt; EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿触发中断 EXTI_InitStruct.EXTI_LineCmd=ENABLE; EXTI_Init(&EXTI_InitStruct); NVIC_InitStructure.NVIC_IRQChannel= EXTI15_10_IRQn ; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; NVIC_InitStructure.NVIC_IRQChannelSubPriority =2; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); } void TIM4_Int_Init(u16 arr,u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //时钟使能 //定时器TIM4初始化 TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位 TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update); TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断 //中断优先级NVIC设置 NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM4中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先占优先级1级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //从优先级1级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能 NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器 } int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级 delay_init(); IOConfig(); TIM4_Int_Init(107, 71); //1M计数频率 while(1) { if(len > 10) { len = 0; USART_Send(USART_buf,11); } } } void EXTI15_10_IRQHandler(void) { if(EXTI_GetFlagStatus(EXTI_Line14) != RESET) { if(OI_RXD == 0) { if(recvStat == COM_STOP_BIT) { recvStat = COM_START_BIT; TIM_Cmd(TIM4, ENABLE); } } EXTI_ClearITPendingBit(EXTI_Line14); } } void TIM4_IRQHandler(void) { if(TIM_GetFlagStatus(TIM4, TIM_FLAG_Update) != RESET) { TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update); recvStat++; if(recvStat == COM_STOP_BIT) { TIM_Cmd(TIM4, DISABLE); USART_buf[len++] = recvData; return; } if(OI_RXD) { recvData |= (1 << (recvStat - 1)); }else{ recvData &= ~(1 << (recvStat - 1)); } } } 参考代码:https://github.com/sckuck-bit/SoftWareSerial 方案2:全双工UART 实现思想:采用定时器的capture和compare,要求定时器与IO对应 代码:略 结论 串口打印会占用CPU,调试完成记得关闭调试信息。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1632 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1559 浏览 1 评论
985 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
688 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1605 浏览 2 评论
1869浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
653浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
523浏览 3评论
539浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
508浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-25 14:28 , Processed in 0.765084 second(s), Total 78, Slave 62 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号