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管被短路的可能性存在。但是手冊上沒有更詳細的介紹了。所以只能是個人猜測。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM