USART 是Universal synchronous asynchronous receiver transmitter(通用同步和异步收发器)的简写,这里的同步和异步是根据是否同用一个信号时钟区分,如常见的串口通信,就是异步,SPI是同步。
一、串口的初始化
/*************************************************
uart1
配置串口一的发送和接收的GPIO口功能,以及中断
**************************************************/
void uart1_init(u32 pclk,u32 baud)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_1); //Tx
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1); //Rx
GPIO_PinAFConfig(GPIOA, GPIO_PinSource12, GPIO_AF_1); //RTS/CTS
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 |GPIO_Pin_10|GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//配置串口中断优先级
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_DeInit(USART1);
USART_InitStructure.USART_BaudRate = baud; //波特率
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_RTS_CTS; //硬件流控制
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_DECmd(USART1,ENABLE); //485硬件控制驱动使能
USART_DEPolarityConfig(USART1,USART_DEPolarity_High); //DE 触发后极性
USART_SetDEAssertionTime(USART1,4);
USART_SetDEDeassertionTime(USART1,4);
USART_OverSampling8Cmd(USART1,ENABLE); //设置过采样率为 8 默认为16
USART_Init(USART1, &USART_InitStructure);
//USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//使能串口空闲中断
//USART_ClearITPendingBit(USART1, USART_IT_IDLE);//清除串口空闲中断标志位
USART_SetReceiverTimeOut(USART1,2);//空闲2bit后出发中断
USART_ITConfig(USART1,USART_IT_RTO,ENABLE);//使能接收超时中断
USART_ReceiverTimeOutCmd(USART1, ENABLE);
USART_ClearITPendingBit(USART1, USART_IT_RTO);
USART_Cmd(USART1, ENABLE);//使能串口
DMA1_Config();
StaFlag.D_A_Pro = BUF_NO2;//首次进来让 dma使用数组2
StaFlag.Busy_Buf = BUF_NO2;
}DMA不定长接收,pingpong接收
所谓DMA不定长接收是 作为接收方的单片机不知道 主机发送的 通信命令字节长度,因此需要完全接收,然后再解析。一个命令通常包含十几~几十个甚至几百个字节,通常都会有相应的协议规定,命令头,命令长度,校验位,命令大小,命令的参数内容等等。通常人们会将一整条命令称为一个包或者一帧。
高速通信的情况下,用接收中断一字节一字节的接收,会占用大量CPU的时间,而且存在丢数据的可能,因此一般采用DMA进行接收。
DMA是什么不再赘述,只需要知道它是个“搬运工”,设置好要去那里“搬运”,“搬运”到那里,它就会不断的干活。
每当接收完成一个字节后,DMA控制模块,会将这个字节移动我们设置好的内存区域(自定义的数组),当整条命令接收完成后,总线会空闲一段时间,我们可以设置串口空闲中断或者接收超时中断,当总线不再传输数据是,就会进入中断,在中断中我们搞个标志位标志一下,然后在主函数中,去解析这条命令。
有时候主机发送实在是太快了,单片机处理到一半,又来数据了,如果只有一个接收数组,新的命令会同样会被搬运到 这个数组上,造成覆盖。此时解析命令就会出现混乱。为此需要进行pingpong接收,pingpong接收是指采用两个数组进行接收,,数组1接收完成一个包后,如果此时又有数据来了,新的数据将“搬运”到数组2,数组2接收完成一个包后,下一个包的数据将“搬运”到数组1,像来回的乒乓球。
下面为DMA接收的初始化:
extern u8 Getfinsh_BUF;
extern u8 Busy_Buf_flag; // 用于切换两个数组PINGPONG接收
u8 USART_RX_BUF1[USART_RX_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
u8 USART_RX_BUF2[USART_RX_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
u16 USART_RX_len=0; //接收状态标记(字节数)
// 用USART_RX_BUF2来做发送数组,
u8 USART1_TX_BUF[USART_TX_LEN]; //发送缓冲,最大USART_TX_LEN个字节.
/*************************************************
DMA1
用
**************************************************/
static DMA_InitTypeDef DMA_InitStructure;
void DMA1_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); //使能SYSCFG时钟,必须的
//和SPI1 DMA冲突,重映射
SYSCFG_DMAChannelRemapConfig(SYSCFG_DMARemap_USART1Tx, ENABLE);//将DMA串口发送从通道2重映射到通道4
SYSCFG_DMAChannelRemapConfig(SYSCFG_DMARemap_USART1Rx, ENABLE);//将DMA串口发送从通道3重映射到通道5
/*****DMA发送配置 Memory->Peripheral *****************/
DMA_DeInit(DMA1_Channel4); //将DMA1_Channel4信道寄存器初始化为默认的重置值。
DMA_InitStructure.DMA_BufferSize = USART_RX_LEN; //设置DMA1的Buffer缓冲区大小,DMA1_MEM_LEN_RX为自定义的变量
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //关闭内存到内存
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //正常模式
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //内存到外设
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; //优先级非常高
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址递增
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->TDR; //设置外设接收地址
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不变
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //设置内存数据长度以Byte为单位
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART_RX_BUF2; //设置内存发送地址,DMA_Tx为自定义变量
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //设置外设数据长度以Byte为单位
DMA_Init(DMA1_Channel4,&DMA_InitStructure);
DMA_ClearITPendingBit(DMA1_IT_TC4); //清除DMA_ch4传输完成中断标志
DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);//使能DMA1_CH4传输完成中断
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);//发送数组和接收数组2 共用一个
/*****DMA接收配置 Peripheral->Memory*****************/
DMA_DeInit(DMA1_Channel5);
DMA_InitStructure.DMA_BufferSize = USART_RX_LEN;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->RDR;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART_RX_BUF2;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_Init(DMA1_Channel5,&DMA_InitStructure);
DMA_ClearITPendingBit(DMA1_IT_TC5);
DMA_ITConfig(DMA1_Channel5,DMA_IT_TC,ENABLE);
USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
DMA_Cmd(DMA1_Channel5,ENABLE); //使能DMA通道3
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
NVIC_Init(&NVIC_InitStructure);
}串口一中断处理,pingpong切换
///*************************************************
// USART1_IRQHandler
// 串口2中断
//**************************************************/
void USART1_IRQHandler(void)
{
if(USART_GetFlagStatus(USART1, USART_FLAG_ORE) != RESET)
{
// USART_ReceiveData(USART1);
USART_ClearFlag(USART1, USART_FLAG_ORE);
}
if(USART_GetITStatus(USART1, USART_IT_RTO) != RESET) //超时中断
{
USART_ClearITPendingBit(USART1, USART_IT_RTO); //清除中断标志位
DMA1_Channel5->CCR &= (uint16_t)(~DMA_CCR_EN); //关DMA
USART_RX_len= USART_RX_LEN - DMA_GetCurrDataCounter(DMA1_Channel5); //获得传输的数据个数
if(USART_RX_len>7) //命令大小不可能小于 6
{
StaFlag.RX_Finsh_BUF = StaFlag.Busy_Buf; //标志接收完成的BUF
StaFlag.RX_Finsh =1;
}
if(StaFlag.Busy_Buf!=BUF_NO1||StaFlag.D_A_Pro==BUF_NO2) // 1号数组没有被使用,没有正在解析
{
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART_RX_BUF1;
DMA_Init(DMA1_Channel5, &DMA_InitStructure);
StaFlag.Busy_Buf=BUF_NO1;
// GPIOB->ODR|=1<<7;
}
else
{
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART_RX_BUF2;
DMA_Init(DMA1_Channel5, &DMA_InitStructure);
StaFlag.Busy_Buf=BUF_NO2;
// GPIOA->ODR|=1<<4;
}
}
DMA1_Channel5->CCR |= DMA_CCR_EN; //开DMA
}硬件收发控制
因为单片机出来的串口信号是TTL信号,传输距离较近,为了实现较远距离的通信,会将串口的TTL信号转成485或232,其中485采用的是差分信号,抗共模干扰能力强,无论是485芯片还是232芯片,都有一个引脚用于控制通信的方向(接收 or 发送),单片机出来TX引脚和RX引脚,还需要额外一个RTS/CTS引脚去控制,STM32F0 的串口中有对应的功能,调用函数启用即可。
USART_DECmd(USART1,ENABLE); //485硬件控制驱动使能
USART_DEPolarityConfig(USART1,USART_DEPolarity_High); //DE 触发后极性
USART_SetDEAssertionTime(USART1,4); //
USART_SetDEDeassertionTime(USART1,4);过采样率
48MHZ主频下,USART波特率最大为3MHZ ,因为USART默认配置成16位采样率,可以通过下面函数来进行设置。
USART_OverSampling8Cmd(USART1,ENABLE); //设置过采样率为 8 默认为16 主频48M,过采样率为8情况下,USART波特率最大为6MHZ