模拟串口UART的实现

发布者:RadiantExplorer最新更新时间:2019-04-02 来源: eefocus关键字:模拟串口  UART 手机看文章 扫描二维码
随时随地手机看文章

最近在调的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主动离线)。


好了就记录这么多。


关键字:模拟串口  UART 引用地址:模拟串口UART的实现

上一篇:STM32 IO 模拟IIC I2C
下一篇:STM32F2位带操作

推荐阅读最新更新时间:2024-03-16 16:26

STM32L1XX使用HAL_UART_Transmit_DMA发送串口数据
使用STM32CubeMX生成初始化代码。 问题: HAL_UART_Transmit_DMA函数只能调用一次,第二次就返回状态HAL_UART_STATE_BUSY 0x02。 原因: stm32l1xx_hal_uart.c开头有描述 (##) DMA Configuration if you need to use DMA process (HAL_UART_Transmit_DMA() and HAL_UART_Receive_DMA() APIs): (+++) Declare a DMA handle structure for the Tx/Rx channel. (+
[单片机]
STM32L1XX使用HAL_<font color='red'>UART</font>_Transmit_DMA发送<font color='red'>串口</font>数据
MSP430学习总结——UART串口
一、MSP430串口 我用的这个单片机是MSP430F5529,这个单片机有两个串口,分别是USCI_A0和USCI_A1,以下是关于MSP430串口的配置以及接收函数和发送函数。 二、串口配置 说到串口,那肯定离不开起始位、数据位、校验位、停止位以及波特率。 先来说一下常用的寄存器。 串口控制寄存器0 这个寄存器可以设置数据位,停止位和校验位等 串口控制寄存器1 这里可以设置时钟源和中断使能等 波特率设置寄存器 波特率设置涉及到三个寄存器UCA0BR,UCA0BR1,UCA0MCTL 具体的设置方法在芯片手册上有写,也可以参考一下这位大佬的博客https://blog.csdn.net/weixin_27070451
[单片机]
MSP430学习总结——<font color='red'>UART</font><font color='red'>串口</font>
MSP430F5438学习笔记 UART SMCLK 115200-8-N-1
MSP430中 UCBR0、UCBR1和UCBRF的设置值可以通过计算,也通过通过查表获得。我个人倾向于查表法。具体可以参考以下两张图表。默认情况,ACLK为32768MHZ,而SMCLK为1048576。选择ACLK时波特率最大为9600,大于9600的情况只能使用SMCLK,波特率的设置和SMCLK的时钟频率有关,我个人更倾向于设置SMCLK和MCLK为8MHz。 // 时钟默认情况 // FLL时钟 FLL选择 XT1 // 辅助时钟 ACLK选择 XT1 32768Hz // 主系统时钟 MCLK选择 DCOCLKDIV 8000000Hz // 子系统时钟 SMCLK选
[单片机]
MSP430F5438学习笔记 <font color='red'>UART</font> SMCLK 115200-8-N-1
对MSP430F5438的时钟及uart配置
对MSP430F5438的时钟及uart配置 在使用的过程中,一直认为比较简单,也一直没有深究,忽然这几天卡壳了,出现问题是操作UART发送出的数据给232显示,并不是想得到的数据。这里涉及了两个地方,一是时钟配置,二是对UART的配置。 时钟配置 需求:考虑到功耗及使用场景,MCLK=2M,SMCLK=1M,ACLK=32768 P7SEL |= BIT0 + BIT1; //开XT1 __bis_SR_register( SCG0 ); // Disable the FLL control loop UCSCTL0 = 0x0000; // Set low
[单片机]
关于MSP430f149Ti官方例子-UART01
官方源码 //****************************************************************************** // MSP-FET430P140 Demo - USART0, UART 115200 Echo ISR, HF XTAL ACLK // // Description: Echo a received character, RX ISR used. Normal mode is LPM0, // USART0 RX interrupt triggers TX Echo. // ACLK = MCLK = UCLK0 = LFXT1 = 8MHz // Bau
[单片机]
关于MSP430f149Ti官方例子-<font color='red'>UART</font>01
MSP430Ware学习笔记 UART SMCLK 115200-8-N-1
1.初始化UART0之前需要先初始化ACLK、SMCLK和MCLK。示例代码中使用XT1,ACLK为32768,SMCLK和MCLK约为8MHZ。 2.UART的时钟可以参考ACLK或者SMCLK,本例参考SMCLK。波特率提高至115200 3.MSP430波特率的产生有两种模式,低频波特率产生和过采样波特率产生。代码中使用过采样波特率产生。其实,两种模式可以达到相似的效果。 4.代码的开头调用了stdio,在函数中宏重写了putchar函数,定向到UART单字节输出。 5.代码初始化之后输出 Hello MSP430Ware,随后直接反射串口接收到的数据,例如发送123456即返回123456. #include i
[单片机]
基于S3C2440的UART传输小结
这里简单地介绍一下通过寄存器来控制S3C2440的UART传输。 查看S3C2440的芯片手册可以发现UART有着发送和接收的缓存区,在缓存区有数据后其便一个字节一个字节地将数据传输到对应端口处。 这里介绍其中使用到的几个寄存器。 ULCONx:用于设置数据的数据位、校验位、停止位之类的信息。 UCONx:设置查询方式以及UART的时钟源。 UFCONx:是否使用FIFO之类的方式。 UMCONx:是否使用流控 UBRDIVx:设置UART的波特率, 一般有两类:一种是标准的串口波特,为921600的约数,如300、600、1200、2400、4800、9600、19200、38400、43000、
[单片机]
基于S3C2440的<font color='red'>UART</font>传输小结
基于单片机通用引脚的软件UART设计
引言 随着单片机应用技术的不断深入,由单片机构成的多机系统取得了长足的发展,多个单片机之间以串口进行数据传输,构成复杂的主从式通讯网。在多机系统中的有一些单片机承担着复杂的通讯任务,当计算机的串口不能满足需要,就必须对串口进行扩展。如多参数医用监护仪、小区防盗报警系统、RS485总线控制系统等。 目前扩展串口的方法主要有以下方法, ①、采用串口扩展芯片实现,如ST16C550、ST16C554、SP2538、MAX3110等,虽然成本较高, 但系统的可靠性得到了保证,适用于数据量较大、串口需求较多的系统;②、采用分时切换的方法将一个串口扩展与多个串口设备通信,分时复用的方法成本低, 但只适用于数据量不大的场合, 并且只能由这个
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved