鱼C论坛

 找回密码
 立即注册

<STM32系列>串口的应用

已有 854 次阅读2013-11-12 15:40 |个人分类:stm32

stm32F103VE拥有5个串口,可以使用查询、中断和DMA模式(目前用到的)。

1. 查询模式:阻塞方式传输,占用CPU资源大,适合小数据传送。

当前用于和AD进行通讯。
[code]
//引脚配置
//配置USART4串口的引脚
GPIO_InitStructure.GPIO_Pin = TXD2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIO_USART45, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = RXD2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIO_USART45, &GPIO_InitStructure);

//配置USART5串口的引脚
GPIO_InitStructure.GPIO_Pin = TXD1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIO_USART45, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = RXD1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIO_USART5, &GPIO_InitStructure);
//时钟配置
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART5, ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE | RCC_APB2Periph_GPIOB |
RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO);
//功能配置
void USART4_Config(void)
{
USART_InitTypeDef USART_InitStructure;

USART_DeInit(UART4);
USART_InitStructure.USART_BaudRate = 115200;        //波特率
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;                                 //一个数据字为8位
  USART_InitStructure.USART_StopBits = USART_StopBits_1;                     //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(UART4, &USART_InitStructure);

  USART_Cmd(UART4, ENABLE);
}

void USART5_Config(void)
{
USART_InitTypeDef USART_InitStructure;

USART_DeInit(UART5);
USART_InitStructure.USART_BaudRate = 115200;      //波特率
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;      //一个数据字为8
  USART_InitStructure.USART_StopBits = USART_StopBits_1;                   //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(UART5, &USART_InitStructure);

  USART_Cmd(UART5, ENABLE);
}

//应用
/****************************************************************
// Summary: 发送一组字节  长度为定长
// Parameter: [in/USART_TypeDef]USARTx USART号
// [in/char*]pBuf 发送的数据
// [in/u8]len 发送的长度
//
// return: 发送的长度
****************************************************************/
u8 Usart_SendBufData(USART_TypeDef* USARTx, u8* pBuf, u8 len)
{
u8 i;

for(i = 0; i < len; i ++)
{
USART_SendData(USARTx, pBuf[i]);   //data->DR TXE=0  DR->移位寄存器 TXE = 1
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET); //完成 TC=1     
}     

return len;

#define  AD_RESP_DELAY_US        4000
#define  PACKET_INTERVAL_US     1000
/****************************************************************
// Summary: 接受数据       加上时间的判断 防止传输错误时一直处于接受状态  
// Parameter: [in/USART_TypeDef]USARTx USART号
// [in/char*]pBuf 接受的数据
// [in/u8]u8Len 接受的长度
//
// return: 实际接受的数据长度
****************************************************************/
u8 Usart_ReceBufData(USART_TypeDef* USARTx, u8* pBuf, u8 u8Len)
{
    u16  i, j, u16Delay, u16RecvLen;

    u16RecvLen = 0;
    u16Delay = AD_RESP_DELAY_US;
    for(i = 0; i < u8Len; i ++)
    {
for(j = 0; j < u16Delay; j ++)
{
if(USART_GetFlagStatus(USARTx, USART_FLAG_RXNE)) //RXNE=1 DR已有数据可以获取
{
pBuf[u16RecvLen++] = USART_ReceiveData(USARTx);
break;             //获取数据跳出时间延时获取
}
           Delay_us(1);
}
if(j >= u16Delay) //没有获取到数据
  break;
else
    u16Delay = PACKET_INTERVAL_US; //获取第一个数据后 包和包的数据间隔减短
}

    return u16RecvLen; //实际获取的数据个数
}  
[/code]

对于那些不定长数据的接受(对外的数据传输或者标定)。最好的是使用FIFO有超时判断。但stm32为省成本没有这个功能。要么使用中断模式
要么使用DMA+定时器来模拟FIFO模式。

这里接受数据我们采用的是中断模式,发送数据采用的是DMA模式
所谓DMA(直接存储器存取)用来提供在外设和存储器之间或者存储器和外设之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源。
[code]
//引脚配置
//配置USART1串口的引脚
GPIO_InitStructure.GPIO_Pin = USART1_TXD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIO_USART1, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = USART1_RXD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIO_USART1, &GPIO_InitStructure);
//中断优先级设置
//USART1串口的中断设置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;   //串口1
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure); 
//时钟配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
//功能配置
void USART1_Config(void)
{
USART_InitTypeDef USART_InitStructure;
USART_ClockInitTypeDef USART_ClockInitStructure;

USART_DeInit(USART1);
USART_InitStructure.USART_BaudRate = 9600; //波特率
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;   //一个数据字为8位
  USART_InitStructure.USART_StopBits = USART_StopBits_1; //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_ClockInitStructure.USART_Clock = USART_Clock_Disable; //时钟低电平活动
USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low; //下一个SCLK引脚时钟极性
USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge; //第二个时钟边沿进行捕获
USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable; //最后一位数据时钟脉冲不从sclk输出
USART_ClockInit(USART1, &USART_ClockInitStructure);  
// USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);                                            //这个在串口初始化中做了
  USART_ClearFlag(USART1,USART_FLAG_TC);
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);                                       //发送数据采用DMA模式
  USART_Cmd(USART1, ENABLE);  
}
//DMA配置
void DMA1_Config(void)
{
      DMA_InitTypeDef DMA_InitStructure;

     DMA_DeInit(DMA1_Channel4);
     DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART1->DR); //硬件地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)Usart_Send_Buffer; //内存地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //DMA传输方向 外设作为数据传送的目的地
    DMA_InitStructure.DMA_BufferSize = 64; //设置BUFFER缓存大小
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设递增模式 一个外设
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存递增模
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据字长 一个字节
    DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte; //内存数据字长 一个字节
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //DMA传输模式 正常模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA的优先级
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA的2个memory中的变量互相访问

    DMA_Init(DMA1_Channel4, &DMA_InitStructure);
      DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);
// DMA_Cmd(DMA1_Channel4, ENABLE);
}

//中断配置
//串口1DMA方式发送中断
void DMA1_Channel4_IRQHandler(void)
{
  DMA_ClearFlag(DMA1_FLAG_TC4); //清除标志位
DMA_Cmd(DMA1_Channel4, DISABLE); //关闭DMA
}

//设置串口1为中断模式
void USART1_IRQHandler(void)
{
if(USART_GetFlagStatus(USART1, USART_IT_RXNE) != RESET)
{
if(l_u8RecvLen < COMM_MAX_LEN)
UsartBuf[l_u8RecvLen ++] = USART_ReceiveData(USART1);
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
// while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
}
            //防止溢出 出现多包数据接受失败
if(USART_GetFlagStatus(USART1, USART_FLAG_ORE) == SET)
{
USART_ClearFlag(USART1, USART_FLAG_ORE);
UsartBuf[l_u8RecvLen ++] = USART_ReceiveData(USART1);
l_u8RecvLen2 ++;
}  
}
//在应用中
//中断接受使能设置 用于接受的初始化,设置了状态量来切换接受和发送的状态 
void UsartStartRecv(USART_TypeDef* USARTx)
{
      l_u8ComStat = COMM_STAT_RECV;   //开始接收数据
    l_u8BufStat = BUFF_EMPTY;   //设置缓存为空

    USART_ITConfig(USARTx, USART_IT_RXNE, ENABLE);             //可以产生接收中断 
}    

//中断发送使能设置  
void UsartStartSend(USART_TypeDef* USARTx)
{   
DMA_SetCurrDataCounter(DMA1_Channel4, l_u8SendMax);
DMA_Cmd(DMA1_Channel4,ENABLE);

while(DMA_GetITStatus(DMA1_IT_TC4) == RESET);
UsartStartRecv(USART1);         //发送完毕后开启接受
}

//与中断函数配套使用的    接收数据函数 放在滴答中 用于每次的接受检测 当返回1说明有数据接受到且接受完全
static u8 Usart_ReceINT(void)
{
    if(l_u8BufStat == BUFF_EMPTY)
    {
        if((l_u8RecvLen != 0) && (l_u8LastRecvLen == l_u8RecvLen))                  //有数据收到并且收全
      {
            l_u8BufStat = BUFF_RECV_FULL;       //收到全部数据  
         return 1;
        }    
        else
        {
            l_u8LastRecvLen = l_u8RecvLen;                                          //赋数据长度
            return 0;
        }
    }
    
    return 1;
}
[/code]

路过

雷人

握手

鲜花

鸡蛋

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 立即注册

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-5-3 22:08

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

返回顶部