红叶何时落水
什么是串口呢?简单来说它是一种通信协议;
串口是一个泛称,UART、TTL、RS232、RS485都遵循类似的通信时序协议,因此都被通称为串口。
而对于单片机来说,串口通信遵守TTL电平标准
高电平 1 2.4V~5V
低电平 0 0V~0.5V
它可以实现两个设备之间的通信,一般来说,它是全双工通信,需要接三根线。
便可以实现双向通信
既然他是一种通信协议,那么它有哪些协议呢?
1.波特率
我们知道在二进制中只有01两种状态,那么我们的发送设备发送一串二进制数00010110,之后,
接受设备接收到了这一段波形,那么该如何将其中的信息解析出来呢?
在串口中,波特率就是用来约束两者之间的协议之一。比如说,我发送了一段0和1构成的波形,这段波形我希望他有8个二进制的信息,那么我让每一个二进制信息都保持一秒钟,然后在发生变化。也就是说,00010110是一段时长为8秒的波形。前三秒为低电平,代表了000,4秒为高电平,代表了1,后面类似。
我希望这段波形中有八个信息。那么如果我想让别人知道其中的信息,那么我就得告诉别人我这段信息中,每一秒代表一个信息。那么别人在获取我这段时长为8秒的波形后,就会一秒一秒的数八次,依次找出每个二进制信息。
那么,每一秒有一个信息,这就是波特率。1秒一个信息,波特率为1;1秒9600个信息,波特率为9600;
2.起始信号与终止信号
那么,我们如果想要连续发好多个信号,我们不能把所有信息连到一起发送出去,就像我们写英语作文,不能把所有单词的字母挨得特别紧,一点空也不留。Iloveyou 与 I love you; 效果上来看后一个更好。而起始信号和终止信号就像单词之间的空格。
我们规定起始信号就是一个下降沿,即由高电平下降到低电平,后面的八位数据就是一个单词。八位数据写完后,我们将电平变为高电平,告诉别人这个单词结束了。这个高电平可由0.5、1、1.5或2个逻辑1的数据位表示,只要双方约定一致即可。
3.数据大小
通常我们规定你一次只能发送八个二进制数据,别人也只数八个,多出来的就不数了。
4.数据校验
我们如何保证我们将这段信息发送出去后,信息在传输的路上没有发生变化呢?
由于我们的信息中只有1和0两种状态,那么这就意味着1的个数可以是0~8,不是奇数就是偶数。
那么,我们可以选择奇校验 当这八个数据里由0或2或4...个1时,我们设置校验位为1,那么这九个数据里就有奇数个1。如果八个数据里1的个数本来就为奇数个,那么,校验位设置为0.保证数据里1的个数为奇数。接受者数一数1的的个数,就大概知道数据有没有被破坏。
偶校验同理。当然,我们也可以选择不校验。(反正没啥用)
知道了这些,我们便可以利用单片机IO口来模拟一个usart
打字打不动了,直接放代码吧。写一点注释算了
#include "stm32f10x.h" // Device header
#include "Timer.h"
#include "Delay.h"
extern u8 data;
extern u8 re_data;
int flag3 = 0;
int flag2 = 0;
int flag = 0;
void GPIO_init() {//两个IO口初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
PA0 = 1;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;//PA1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //
GPIO_Init(GPIOA, &GPIO_InitStructure);//
PA1 = 1;
}
void Timer_Init(u32 psc, u32 arr)//定时器2初始化,注意定时器2的主频为72M,原因去看时钟树
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = arr - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = psc - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//通用定时器没有这个功能,高级定时器可以实现,2的64次方计数
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;//这里的名字加上Handler,就是中断函数名
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
GPIO_init();
TIM_Cmd(TIM2, ENABLE);//使能定时器
}
void send_data() {
if(flag && flag < 10) {
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
PA0 = (data >> (flag - 1)) & 0x01;//发送八位数据
flag++;
} else if(!flag) {
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
PA0 = 0;//制造起始位
flag++;
} else {
PA0 = 1;//制造结束位
flag = 0;
TIM_Cmd(TIM2, DISABLE);
TIM_ITConfig(TIM2,TIM_IT_Update,DISABLE);
Delay_ms(1);//这里要加一个延时,否者会连续发送两个字节
EXTI->IMR |= EXTI_Line1;//开启外部中断
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
void recive_data() {
int i = 0;
int counter = 0;
if(flag2 && flag2 < 17) {
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
for(i = 0; i < 9; i++)
counter += GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);//获取引脚口状态
re_data = (!(flag2 % 2) && flag2 < 16) ? ((counter > 3) ? (re_data|0x80)>>1 : re_data >> 1) : (flag2 == 16 && counter > 3) ? (re_data|0x80) : re_data;//通过位操作,串行八位数据
flag2++;
} else if(!flag2) {
re_data = 0;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
for(i = 0; i < 3; i++) {
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)) {//判断起始位
TIM_Cmd(TIM2, DISABLE);
EXTI->IMR |= EXTI_Line1;
break;
}
}
if(!GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1))
flag2++;
} else {
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
flag2 = 0;
flag3 = 1;
PA1 = 1;
TIM2->ARR = 750; //寄存器改变定时器频率
}
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
if(!flag3)
recive_data();
else
send_data();
}
}
void EXTIX_Init(void) {
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//ÍⲿÖжϣ¬ÐèҪʹÄÜAFIOʱÖÓ
//GPIOA.1
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource1);
EXTI_InitStructure.EXTI_Line=EXTI_Line1;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void EXTI1_IRQHandler(void)
{
TIM2->ARR = 375;
flag3 = 0;
flag2 = 0;
TIM_Cmd(TIM2, ENABLE);//使能定时器
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//开启定时器中断
EXTI->IMR &= ~(EXTI_Line1);//寄存器关闭外部中断
EXTI_ClearITPendingBit(EXTI_Line1);
}
注意的点
寄存器操作改变引脚口状态,三种不同方式的优缺点
定时器主频
中断的开启与关闭
数据的|与&以及<<操作
寄存器改变定时器频率
定时器重复技术计数功能