1、串口协议
1. 串口232:可双向传输,全双工负逻辑电平
2. 串口358:可双向传输,4线全双工,2线单工
3. 串口485:可双向传输,4线全双工,2线单工
2、软件模拟串口
串口发送:
1、默认TX为高电平,延时
2、起始位TX写为低电平,延时
3、发送8位数据,小端在前(即发送的第1bit为8位数据最低位),延时
4、结束位Tx写为高电平,延时
注释:延时的时间需要精确计算 delay_time = 1/波特率,具体时间需要具体调试
串口接收
1、 初始化接收引脚为下降沿中断方式、
2、 配置定时器,定时的时间要根据波特率计算
3、当引脚检测到下降沿时,说明数据开始接收
4、每进入一次定时器中断服务函数,对引脚电平进行采样
5、当数据位,校验位,停止位接收完毕后,关闭定时器,等待下一次接收
3、串口接收处理的方式
1、中断接收,再分析处理(任意数据)
2、空闲中断接收+DMA(不定长数据)
3、进中断服务函数,关中断,队列接收数据,当寄存器无数据时,延时等待数据,若延时超时,开中断,退出中断服务函数(不定长数据)
软件模拟串口
#define OI_TXD PCout(13)
#define OI_RXD PBin(14)
#define BuadRate_9600 100
u8 len = 0; //接收计数
u8 USART_buf[11]; //接收缓冲区
enum{
COM_START_BIT,
COM_D0_BIT,
COM_D1_BIT,
COM_D2_BIT,
COM_D3_BIT,
COM_D4_BIT,
COM_D5_BIT,
COM_D6_BIT,
COM_D7_BIT,
COM_STOP_BIT,
};
u8 recvStat = COM_STOP_BIT;
u8 recvData = 0;
void IO_TXD(u8 Data)
{
u8 i = 0;
OI_TXD = 0;
delay_us(BuadRate_9600);
for(i = 0; i < 8; i++)
{
if(Data&0x01)
OI_TXD = 1;
else
OI_TXD = 0;
delay_us(BuadRate_9600);
Data = Data>>1;
}
OI_TXD = 1;
delay_us(BuadRate_9600);
}
void USART_Send(u8 *buf, u8 len)
{
u8 t;
for(t = 0; t < len; t++)
{
IO_TXD(buf[t]);
}
}
void IOConfig(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC, ENABLE); //使能PB,PC端口时钟
//SoftWare Serial TXD
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_SetBits(GPIOC,GPIO_Pin_13);
//SoftWare Serial RXD
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
EXTI_InitStruct.EXTI_Line = EXTI_Line14;
EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿触发中断
EXTI_InitStruct.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStruct);
NVIC_InitStructure.NVIC_IRQChannel= EXTI15_10_IRQn ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority =2;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void TIM4_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //时钟使能
//定时器TIM4初始化
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update);
TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
//中断优先级NVIC设置
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM4中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先占优先级1级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //从优先级1级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器
}
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
delay_init();
IOConfig();
TIM4_Int_Init(107, 71); //1M计数频率
while(1)
{
if(len > 10)
{
len = 0;
USART_Send(USART_buf,11);
}
}
}
void EXTI15_10_IRQHandler(void)
{
if(EXTI_GetFlagStatus(EXTI_Line14) != RESET)
{
if(OI_RXD == 0)
{
if(recvStat == COM_STOP_BIT)
{
recvStat = COM_START_BIT;
TIM_Cmd(TIM4, ENABLE);
}
}
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
void TIM4_IRQHandler(void)
{
if(TIM_GetFlagStatus(TIM4, TIM_FLAG_Update) != RESET)
{
TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update);
recvStat++;
if(recvStat == COM_STOP_BIT)
{
TIM_Cmd(TIM4, DISABLE);
USART_buf[len++] = recvData;
return;
}
if(OI_RXD)
{
recvData |= (1 << (recvStat - 1));
}else{
recvData &= ~(1 << (recvStat - 1));
}
}
}
空闲中断
void TS_USART3_Init(void)
{
USART_Config(3, 9600, USART_WordLength_8b, USART_Parity_No, 2, 0, ENABLE, DISABLE);
USART3_DMA();//空闲中断+DMA
USART3_485_En_Init();
USART3_485_OFF();
}
//主要是第二个函数:
void USART3_DMA(void)
{
DMA_InitTypeDef DMA_InitStructure;
USART_ITConfig(USART3,USART_IT_TC, DISABLE);//关闭发送中断
USART_ITConfig(USART3,USART_IT_RXNE,DISABLE);//关闭接收中断!
USART_ITConfig(USART3,USART_IT_IDLE,ENABLE); //开启总线空闲中断 !
USART_Cmd(USART3, ENABLE); // 使能 USART3
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA1时钟
DMA_DeInit(DMA1_Channel3); //初始化DMA1通道3 串口3接收通道
DMA_InitStructure.DMA_PeripheralBaseAddr = 0x40004804; //串口3接收数据的地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)GPS_Uart_Rcv_Buf; //数据存放的复制
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //数据传送方向 串口3作为数据源
DMA_InitStructure.DMA_BufferSize = GPS_Rev_Buf_Size; //接收数据个数
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外围设备 串口3 地址不递增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // DMA存储地址递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外围设备 串口3 数据宽度 8bits
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //DMA存储 数据宽度 8bits
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
//DMA_Mode_Normal(只传送一次), DMA_Mode_Circular (不停地传送)
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //串口3通道优先为中等
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //关闭内存到内存
DMA_Init(DMA1_Channel3, &DMA_InitStructure);
USART_DMACmd(USART3, USART_DMAReq_Rx, ENABLE); //使能串口3 DMA功能
DMA_Cmd(DMA1_Channel3, ENABLE);
}
//现在就配置好了,用的是DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
//也就是中断接收数据:过来0X12,0X34,0XFF这样一串,在一串全部过来以后会掉到中断去,并且这个串已经DMA自动搬运到数组中了!
//因为我是一次,下次DMA就无效了,所以服务函数需要再起启动
void USART3_IRQHandler(void)
{
u8 res,i;
if(USART_GetITStatus(USART3, USART_IT_IDLE) != RESET) //串口3 总线空闲
{
//USART_ClearITPendingBit(USART3, USART_IT_IDLE); //清除标记位
res = USART3->SR;//同上
res = USART3->DR; //清USART_IT_IDLE标志----------所谓的清空不是int a=0 而是一个数 a=->dr你拿走 它就空了
DMA_Cmd(DMA1_Channel3, DISABLE);//关闭DMA1通道
Iusart3_received_handle();//去分析数组吧!!
DMA_SetCurrDataCounter(DMA1_Channel3,GPS_Rev_Buf_Size); //接收数据个数
DMA_Cmd(DMA1_Channel3, ENABLE); //开启DMA1通道3
}