历史上的今天
今天是:2025年04月02日(星期三)
2019年04月02日 | 模拟串口UART的实现
2019-04-02 来源:eefocus
最近在调的MCU的型号为STM32F030,配置芯片相较之前的MCU都比较简单,功能配置很顺利。但是在写串口程序的时候,发现串口一直不通,使用示波器也没有波形。因为基本的串口通讯线只有Tx和Rx两根线,配置也相对简单,8位数位,1位停止位,9600波特率。协议结构为 起始位(低电平)+8位数据(低位在前)+1位停止位(高电平),例如发送字节0x55,即电平为低 高低高低高低高低 高。电平转换的间隔时间为1s/9600 = 104us
以上均为理论分析过程,检查代码对串口的配置都没有发现错误。最终排查的结果是硬件工程师画原理图和PCB图时将串口的Tx和Rx画反了!由于某些原因板子已经量产了,故只能通过改软件来实现串口的功能,在网上找了一下发现模拟串口可行性可以,故动手写了一下模拟串口。
串口通讯需要模拟两根线(Tx和Rx)的时序,模拟串口的主要思路如下:
发送部分比较简单,按照 起始位(低电平)+8位数据(低位在前)+1位停止位(高电平),间隔时间104us,即可。
接收部分有点复杂,需要配置一个外部中断,用于检测低电平信号,还需要一个定时器,用于读取有效数据。
下面将代码附上:
发送IO口初始化
/*!
* @brief 模拟串口1 TX IO口配置
* @param none
* @return none
* @note Tx(PA10)
*/
void MUSART1_TX_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA, GPIO_Pin_10);
}
接收IO口初始化
/*!
* @brief 模拟串口1 RX IO口配置
* @param none
* @return none
* @note Rx(PA9)
*/
void MUSART1_RX_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); //!<外部中断时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource9);
EXTI_InitStructure.EXTI_Line=EXTI_Line9;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿中断
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=EXTI4_15_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority=0x01;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
定时器初始化
/*!
* @brief 定时器14初始化
* @param
* @return NONE
* @note 103us定时器,用于串口数据采样
*/
void Time14Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimerBaseStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14, ENABLE); //!<时钟使能
TIM_DeInit(TIM14); //! TIM_TimerBaseStruct.TIM_Period=103; //!<设置重载寄存器初值 (设置为103,即:定时104us) TIM_TimerBaseStruct.TIM_Prescaler=7; //!<使用内部8M时钟,分频8(7+1),8M/8 = 1000000,故数1000000(999999+1)下,达1秒 TIM_TimerBaseStruct.TIM_ClockDivision=0; //!<不分频 TIM_TimerBaseStruct.TIM_CounterMode=TIM_CounterMode_Up; //!<设置计数器向上计数模式 TIM_TimeBaseInit(TIM14,&TIM_TimerBaseStruct); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel=TIM14_IRQn; NVIC_InitStructure.NVIC_IRQChannelPriority=2; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); TIM_ClearITPendingBit(TIM14, TIM_FLAG_Update); TIM_ITConfig(TIM14,TIM_IT_Update,ENABLE); //!<使能TIM1中断源 TIM_Cmd(TIM14,DISABLE); //!<禁能TIM1定时器 } 发送数据函数 uint32 delayTime =99; //!<9600,理论值为104但实际测下来99时效果最好 /*! * @brief 模拟串口1发送一个字节 * @param * @return none * @note 数据低位在前高位在后 */ void MUSART1_SendData(uint8 data) { uint8 i = 0; TX_L(); //!<起始位 delay_us(delayTime); for(i = 0; i < 8; i++){ if(data & 0x01) TX_H(); else TX_L(); delay_us(delayTime); data >>= 1; } TX_H(); //!<停止位 delay_us(delayTime); } 接收数据,外部中断起始接收 /*! * @brief 串口接收IO中断处理函数 * @param none * @return NONE * @note none */ void EXTI4_15_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line9) != RESET){ if(RX_READ() == 0x00){ if(rx_state >= STATE_STOP){ recvData = 0; rx_state = STATE_START; delay_us(50); TIM_Cmd(TIM14, ENABLE); //!<打开定时器,接收数据 } } EXTI_ClearITPendingBit(EXTI_Line9); } } 接收数据,定时器中断接收数据 /*! * @brief 定时器1中断处理函数 * @param * @return NONE * @note */ void TIM14_IRQHandler(void) { if(TIM_GetITStatus(TIM14, TIM_IT_Update) != RESET){ rx_state++; //!<改变状态机 if(rx_state == STATE_STOP){ TIM_Cmd(TIM14, DISABLE); //!<关闭定时器 usart_getByte(); //!<接收到停止位之后,处理数据recvData return; //!<返回 } if(RX_READ()){ recvData |= (1 << (rx_state - 1)); }else{ recvData &= ~(1 <<(rx_state - 1)); } TIM_ClearITPendingBit(TIM14, TIM_FLAG_Update); } } 其他说明 typedef enum{ STATE_START=0, STATE_BIT0, STATE_BIT1, STATE_BIT2, STATE_BIT3, STATE_BIT4, STATE_BIT5, STATE_BIT6, STATE_BIT7, STATE_STOP }RX_STATE; RX_STATE rx_state = STATE_STOP; uint8 recvData=0; //!<接收的一个字节数据,全局变量 至此,模拟串口的代码及原理均已描述完成。单独的串口通讯并没有问题,但是在实际应用中采取了一种特殊的“总线”形式。 本次写的是从机部分的代码,从机接收数据并没有问题,但是在发送数据时,由于所有的从机Tx都挂载在同一根Tx上,并且从机Tx空闲状态时一直是高电平,导致指定从机的起始信号发不出去。故需要再做以下处理,解决以上问题。 当接收到的数据包中的ID为本从机ID时将Tx拉高,否则拉低,这样能够保证当指定ID的从机发送数据时有且只有一个从机再总线上发送数据(其他从机的Tx主动离线)。 好了就记录这么多。
下一篇:STM32F2位带操作
史海拾趣
|
;--------------------------------------- ; 四位共阳LED动态扫描显示程序 ; P0为段码口,P2为位选口(低电平有效) ; 参数为要显示的字符串指针 ;--------------------------------------- & ...… 查看全部问答> |
|
美资公司LSI上海研发中心诚聘 Test Engineer 内部推荐,有意者请将中英文简历发送至:asic_tapeout@hotmail.com Test Engineer DESCRIPTION OF DUTIES IN ADDITION TO THOSE IN JOB DESCRIPTION: - To support prototype and production relea ...… 查看全部问答> |
|
我用到液晶显示,但是由于液晶本身的CGRAM=2K远远不够存入我要显示的自定义字符数组(大约有三百个中文字,按照16*16*300来计算,要用到将近10K的空间.所以我想把这些数组放到FLASH中,请教应该如何设置才能实现此功能? 万分感谢!… 查看全部问答> |
|
求证:"TI MSP430大学计划教材"中,5V的液晶显示器可与MSP430的I/O口直接相连,可以吗??? "TI MSP430大学计划教材"-"MSP430系列16位超低功耗单片机原理与应用"一书中,第306页和309页的电路图均表示5V的液晶显示器可与MSP430的I/O口直接相连,可以吗???,这对我很重要,因我要将51系统改为用MSP430.… 查看全部问答> |
|
TI送的MSP430软件开发指南的P66页的这周期计算为什么要乘以2呢?我觉得应该不要呀 TI送的MSP430软件开发指南的P66页的这周期计算为什么要乘以2呢?我觉得应该不要呀!书上写的是增计数模式周期应该是CCR0/f呀!求指导 … 查看全部问答> |
|
本帖最后由 风雪天殇 于 2014-7-25 15:28 编辑 前几天看到这个活动,这种鼠标一点就有奖励神马的最有爱了,果断点了链接,本着申请不浪费的原则,选了个TPS65980,虽然仍有很大可能用不上....当然那个SN74LVC1G08也有可能用得上,但是太便宜了的 ...… 查看全部问答> |
|
信号为50mhz固定,要用分频来实现不同的型号输入。 设计一个周期、占空比可调的脉冲发生器。脉冲产生过程中,可对脉冲的周期、高电平和低电平的延续时间进行调整;调整步长自定;并同时显示已经输出的脉冲的个数,若脉冲个数达到了数码管显示的极 ...… 查看全部问答> |




