Usart的单线半双工模式(stm32F10x系列)

  这两天折腾CTS/RTS硬件流控,看到说232协议的CTS/RTS只是用来做半双工换向使用的。正好手头上有块stm32的板子,看了看stm32的Usart,竟然发现支持的是单线半双工。232里面毕竟4根线,支持半双工也是各自独立地物理信道(大胆猜测,回头回顾一下以前草草使用的双线485,看看它的半双工)。第一次注意到。之所以引起我的兴趣,是因为,我好奇stm32的单线半双工有2点。

  第一:有啥用。结果上网一搜,还真有人用它来控制AX-12数字舵机。

  第二:怎么实现的。我印象中stm32的io口是需要配置方向的。单线半双工需要两端即当输入又当输出。操作不当很容易把IO口的mos管烧掉啊。

  于是折腾了半天。最后确实调通了。管子也没烧。

  参考手册上关于这块,很简单说的。

  单线半双方模式通过设置USART_CR3寄存器的HDSEL位选择。在这个模式里,下面的位必须保持清零状态:
    ● USART_CR2寄存器的LINEN和CLKEN位
    ● USART_CR3寄存器的SCEN和IREN位
  USART可以配置成遵循单线半双工协议。在单线半双工模式下,TX和RX引脚在芯片内部互连。使用控制位”HALF DUPLEX SEL”(USART_CR3中的HDSEL位)选择半双工和全双工通信。
  当HDSEL为’1’时
    ● RX不再被使用
    ● 当没有数据传输时,TX总是被释放。因此,它在空闲状态的或接收状态时表现为一个标准I/O口。这就意味该I/O在不被USART驱动时,必须配置成悬空输入(或开漏的输出高)
  除此以外,通信与正常USART模式类似。由软件来管理线上的冲突(例如通过使用一个中央仲裁器)。特别的是,发送从不会被硬件所阻碍。当TE位被设置时,只要数据一写到数据寄存器上,发送就继续。

基本上比较模糊,但是也能猜出个大概。也就是双方的USart通过TX-Tx相连。而且Tx管脚IO状态设置成悬空输入(或开漏的输出高)。这个地方其实出现了个问题,因为手册第9章GPIO配置的地方明确写道:

Tx管脚在用作半双工模式时,GPIO应该设为推挽复用输出。所以这个悬空输入其实是有问题的。因为手册上写道:

当I/O端口配置为输入时:

● 输出缓冲器被禁止

● 施密特触发输入被激活

● 根据输入配置(上拉,下拉或浮动)的不同,弱上拉和下拉电阻被连接

● 出现在I/O脚上的数据在每个APB2时钟被采样到输入数据寄存器

● 对输入数据寄存器的读访问可得到I/O状态

也就是说输出根本输出不来。所以这里使用复用推挽输出或者复用开漏输出。我估计很多人就是管脚没配置对所以没调通。。。而且既然一定要外接上拉电阻至3.3V,不仅是开漏输出,复用推挽也是,实践证明,不然通信失败。

开漏输出好理解,加上拉后可以输出正常高低电平,而且stm32的IO口设置成开漏输出后,其实是双向的,这点可以从下图看出。手册上说配置成输出后,采样输入数据寄存器里的值即可得到IO状态。

从图上还可以看到。IO管脚内部是有稳压管的。有的管脚会被稳压到3.3VVDD,有的会被稳压到5V,VDD_FT,所以网上说什么上拉至5V其实指得是在FT管脚上才能实现的,不然再上拉也拉不上去。顶多3.3,再多就烧了。

单向半双工(Half Deplux)的实现也比较简单,代码如下。

int main(void)
{
    usart_Configuration();    

  while(NbrOfDataToRead2--)
  {
    /* Wait until end of transmit */
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)
    {

    }
    /* Write one byte in the USARTy Transmit Data Register */

    USART_SendData(USART1, TxBuffer1[TxCounter1++]);
    //    GPIO_SetBits(GPIOB,GPIO_Pin_0);
    /* Wait the byte is entirely received by USARTz */
    while(USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == RESET)
    {
    }
    /* Store the received byte in the RxBuffer2 */
    RxBuffer2[RxCounter2++] = USART_ReceiveData(USART2);
    //GPIO_SetBits(GPIOB,GPIO_Pin_0);
  }
   USART_ReceiveData(USART1);
  while(NbrOfDataToRead1--)
  {
    /* Wait until end of transmit */
    while(USART_GetFlagStatus(USART2, USART_FLAG_TXE)== RESET)
    {
    }
    /* Write one byte in the USARTz Transmit Data Register */
    USART_SendData(USART2, TxBuffer2[TxCounter2++]);

    /* Wait the byte is entirely received by USARTy */
    while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == RESET)
    {
    }
    /* Store the received byte in the RxBuffer1 */
    RxBuffer1[RxCounter1++] = USART_ReceiveData(USART1);
  }

    /* Check the received data with the send ones */
  TransferStatus1 = Buffercmp(TxBuffer1, RxBuffer2, TxBufferSize1);
  if(TransferStatus1)
  {GPIO_SetBits(GPIOB,GPIO_Pin_0);}
  /* TransferStatus = PASSED, if the data transmitted from USARTy and
     received by USARTz are the same */
  /* TransferStatus = FAILED, if the data transmitted from USARTy and
     received by USARTz are different */
  TransferStatus2 = Buffercmp(TxBuffer2, RxBuffer1, TxBufferSize2);
    if(TransferStatus2)
  {GPIO_SetBits(GPIOB,GPIO_Pin_1);}
  while (1)
  {
  }

//IO配置部分
void Rcc_Configuration(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
}

void UsartGPIO_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);
    GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);  //Tx1         

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    //GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOD, &GPIO_InitStructure);//Tx2

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);      //PB0---LED0
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
    GPIO_Init(GPIOB, &GPIO_InitStructure);      //PB1---LED1s

}

void usart_Configuration(void)
{
    USART_InitTypeDef USART_InitStructure;

    Rcc_Configuration();

    UsartGPIO_Configuration();

    USART_InitStructure.USART_BaudRate = 230400;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    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(USART1, &USART_InitStructure);
    USART_Init(USART2, &USART_InitStructure);

    USART_HalfDuplexCmd(USART1,ENABLE);
    USART_HalfDuplexCmd(USART2,ENABLE);

    USART_Cmd(USART1, ENABLE);
    USART_Cmd(USART2, ENABLE);
}

  最后,but也是我没想明白的地方。为什么设置成复用推挽输出,依旧能通信成功而且没烧掉mos管。这里我猜测,是由于虽然你设置了管脚位推挽输出,但是你也设置了HalfDelpux模式,其实Tx这时候默认其实是输入的。只有在你往发送数据寄存器TDR里面写入数据时,标志位TXE置1,然后由硬件将IO口设置成推挽输出。这样对面Tx仍然是输入,所以能够通信输出,不存在mos管被短路的可能性存在。但是手册上没有更详细的介绍了。所以只能是个人猜测。

时间: 2024-10-10 06:44:51

Usart的单线半双工模式(stm32F10x系列)的相关文章

STM32 uart 单线半双工模式(cube版本)

STM32 uart 单线半双工模式(cube版本) 1.引言 在某些场合下需要进行三线制串口通信(信号线只有一根),这就要求进行单线半双工的模式进行通信.在这种情况进行数据协议传输的过程中,信号端需要来回切换输入输出模式.或者可以将控制端口的发送和接口进行短接.那针对这种情况,STM32提供了half-duplex功能,只要在软件里面开启这项功能,芯片硬件层内部就会将发送和接收端口进行短接.具体的芯片如何寄存器操作这边不做赘述,可以自己手册usart篇进行查阅.我们下面来看看如何利用cubeM

策略模式--设计模式系列

今天我们写一个鸭子类,首先分析一下鸭子有哪些特征呢? 鸭子:会叫,会游水,会飞,外观 现在有个需求:分两种鸭子,一种是外观是绿头,一种是红头,写下看: class Duck: def quack(self): print('呱呱叫') def swim(self): print('我会游泳') def display(self):pass def fly(self): print('我会飞') class RedDuck(Duck): def display(self): print('我是红头

策略模式 -- 设计模式系列文章(一)

概述 在日常开发工作中,适当的使用一些设计模式,可以让代码扩展性更强,能更好地拥抱变化,让代码更加优雅.本文主要介绍设计模式中的策略模式,并附上测试示例 Demo 供大家参考. 定义 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化. 个人理解 策略模式,针对那些动作因对象而异的情况下,把每一个动作都独立封装起来并实现同一个接口,通过组合的方式赋予对象相对应的动作,从而使得所有的动作都可以相互替换.通过策略模式,可以达到在运

stm32f10x寄存器设置脚本

1 #include "hw_config.h" 2 3 // <<< Use Configuration Wizard in Context Menu >>> 4 5 // <e> 电源控制(PWR) 6 // <h> 电源控制寄存器 (PWR_CR) 3.4.1 7 // <o1.8> DBP:取消后备区域的写保护 8 // <i> 在复位后,RTC和后备寄存器处于被保护状态以防意外写入. 9 //

菜鸟学设计模式系列笔记之建造者模式(Builder模式)

提供一种"封装机制"来隔离出"复杂对象的各个部分"的变化. 从而保持系统中的"稳定构建算法"不随需求变化而变化.----建造者模式 建造模式是对象的创建模式 建造模式可以将一个产品的内部表象与产品的生成过程分割开来, 从而可以是一个建造过程生成具有不同的内部表象的产品对象. Intent : 将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示 Motivation: 在复杂对象的构造过程中,允许同样的构造过程能够加入新的被

STM32F4之USART【库函数操作】

STM32F407xx内嵌四个通用同步/异步接收器??(USART1,USART2,USART3 和USART6)和两个通用异步收发器(UART4和UART5).这6个接口提供异步通信的IrDASIR ENDEC支持,多机通信模式,单线半双工通信模式LIN主/从功能. USART1和USART6接口能够速度高达10.5 Mbit / s的通信其他可用的接口通信高达5.25bit/s.USART1,USART2,USART3和USART6还提供硬件管理的CTS,RTS信号,智能卡的模式(ISO78

[STM32F10x] 利用定时器测量脉冲宽度

硬件:STM32F103C8T6 平台: ARM-MDk V5.11 前面一篇文章讲过如何利用定时器测量信号的频率(见[STM32F10x] 利用定时器测量频率),使用的是定时器的捕获/比较单元(Capture/compare),它也可以测量输入信号的脉冲宽度. 利用定时器测量脉冲宽度有两种方法. 方法1: 在捕获中断函数里改变捕获信号的触发沿(上升沿触发改为下降沿触发,或者下降沿触发改为上升沿触发),通过两次触发得到的计数器的差值,来计算出脉冲宽度.这种 方法需要定时器的配置和[STM32F1

设计模式之七:建造模式(Builder Pattern)

建造者模式就是将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示. 适用范围: 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时. 当构造过程必须允许被构造的对象有不同表示时. 建造者模式里面有四个角色: Builder: 给出一个抽象接口,以规范产品对象的各个组成部分的建造.一般而言,此接口独立于应用程序的业务逻辑.模式中直接创建产品对象的具体创建者角色.具体创建者角色必须实现这个接口的所有方法:一个是建造方法,另一个是结果返还方法. ConcreteBu

什么是半双工和双工通信

全双工(Full Duplex)是指在发送数据的同时也能够接收数据,两者同步进行,这好像我们平时打电话一样,说话的同时也能够听到对方的声音.目前的网卡一般都支持全双工. 半双工(Half Duplex),所谓半双工就是指一个时间段内只有一个动作发生,举个简单例子,一条窄窄的马路,同时只能有一辆车通过,当目前有两量车对开,这种情况下就只能一辆先过,等到头儿后另一辆再开,这个例子就形象的说明了半双工的原理.早期的对讲机.以及早期集线器等设备都是基于半双工的产品.随着技术的不断进步,半双工会逐渐退出历