一、串口协议
I、实验目的
1) 了解串口协议和RS-232标准,以及RS232电平与TTL电平的区别
2)了解"USB/TTL转232"模块(以CH340芯片模块为例)的工作原理
II、串口概念
一、串行通信和并行通信的异同
-
串行通信:
- 定义:一次只传输一个比特,按照顺序逐位传输数据的通信方式。
- 优点:传输距离远、线路简单、成本低。
- 缺点:速度相对较慢。
-
并行通信:
- 定义:同时传输多个比特,每个比特占据一个通道,同时传输多个数据位的通信方式。
- 优点:传输速度快。
- 缺点:线路复杂、成本高、传输距离短。
-
相同点:
- 都是用于数据通信的方式。
- 都可以用于设备之间的数据传输。
-
不同点:
- 传输方式:串行通信逐位传输,而并行通信同时传输多位。
- 速度:并行通信速度较快,串行通信速度相对较慢。
- 线路复杂度:并行通信线路复杂,串行通信线路相对简单。
总的来说,串行通信适合长距离传输和对速度要求不高的场景,而并行通信适合对速度要求高的场景。在实际应用中,根据具体需求选择合适的通信方式。
二、单工通信、半双工通信及全双工通信的异同
-
单工通信:
- 定义:数据只能单向传输,不能同时发送和接收数据。
- 特点:类似广播,一方发送,另一方接收。
- 例子:广播电台、电视信号传输。
-
半双工通信:
- 定义:数据可以双向传输,但不能同时进行发送和接收,需在发送和接收之间切换。
- 特点:通信双方可以交替发送和接收数据。
- 例子:对讲机、无线电。
-
全双工通信:
- 定义:数据可以双向同时传输,通信双方可以同时发送和接收数据。
- 特点:通信双方可以同时进行发送和接收,实现真正的双向通信。
- 例子:电话通信、互联网通信。
-
异同点:
- 共同点:都是用于数据通信的方式。
- 不同点:
- 传输方向:单工只能单向传输,半双工和全双工可以双向传输。
- 同时性:全双工可以同时发送和接收数据,而单工和半双工不能同时进行发送和接收。
- 效率:全双工通信效率最高,单工通信效率最低。
在选择通信方式时,根据实际需求和成本考虑,选择适合的单工、半双工或全双工通信方式。
三、同步通信和异步通信的异同
-
同步通信:
- 定义:通信双方需要时钟信号进行同步,数据传输在时钟信号的控制下进行。
- 特点:通信双方需要严格的时序同步。
- 优点:数据传输稳定、可靠。
- 缺点:对时钟同步要求高、复杂度较高。
-
异步通信:
- 定义:通信双方不需要共享时钟信号,通过起始位和停止位来同步数据传输。
- 特点:通信双方不需要严格的时序同步。
- 优点:灵活、简单。
- 缺点:数据传输稳定性相对较差。
-
异同点:
- 共同点:都是用于数据通信的方式。
- 不同点:
- 时序要求:同步通信需要严格的时序同步,异步通信不需要严格的时序同步。
- 时钟信号:同步通信需要共享时钟信号,异步通信不需要共享时钟信号。
- 稳定性:同步通信稳定性高,异步通信稳定性相对较差。
在选择通信方式时,根据实际需求和系统复杂度考虑,选择适合的同步或异步通信方式。异步通信适用于简单的通信场景,而同步通信适用于对稳定性要求较高的场景。
1)串口协议和RS-232标准
串口协议是一种用于数据通信的协议,RS-232是一种串行通信标准,定义了信号线的连接方式和电气特性。RS-232标准规定了信号传输的电压范围、波特率、数据位、校验位和停止位等参数。
2)RS232电平与TTL电平的区别
RS232电平一般为±12V,逻辑“1”为负电平,逻辑“0”为正电平;TTL电平一般为0-5V,逻辑“1”为高电平(5V),逻辑“0”为低电平(0V)。在RS232与TTL之间需要进行电平转换。
3)USB/TTL转232模块的工作原理
USB/TTL转232模块一般使用CH340芯片,通过USB接口与计算机连接,将TTL电平转换为RS232电平,实现串口通信。CH340芯片负责USB转串口的功能,将数据从USB接收后转换为串口信号发送出去。
4)STM32标准库开发与UART通信程序
- 使用标准库点亮LED灯或实现流水灯效果。
- 通过标准库实现UART串口通信,设置波特率为9600,无校验位,1位停止位,向上位机发送数据并接收指定字符控制LED灯的点亮和熄灭。
- 实验代码:
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启A口的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;//用的GPIO A的0号引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_0);
GPIO_ResetBits(GPIOA,GPIO_Pin_0);//配置低电平,若只需要点亮LED灯就只执行此条命令。
while(1)
{
GPIO_Write(GPIOA,~0x0001);//0000 0000 0000 0001 PA0 0000 0000 0000 0010 PA2
Delay_ms(500);
GPIO_Write(GPIOA,~0x0002);//0000 0000 0000 0010
Delay_ms(500);
GPIO_Write(GPIOA,~0x0004);//0000 0000 0000 0100
Delay_ms(500);
GPIO_Write(GPIOA,~0x0008);//0000 0000 0000 1000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0010);//0000 0000 0001 0000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0020);//0000 0000 0010 0000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0040);//0000 0000 0100 0000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0080);//0000 0000 1000 0000
Delay_ms(500);
GPIO_Write(GPIOB,~0x0001);//0000 0000 0000 0001 PA0 0000 0000 0000 0010 PA2
Delay_ms(500);
}
}
闪烁
二、标准库的查询
I、设置波特率
初始化
void Usart2_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //使能串口2时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //选中串口默认输出管脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //定义输出最大速率
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//定义管脚9的模式
GPIO_Init(GPIOA, &GPIO_InitStructure); //调用函数,把结构体参数输入进行初
始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //A端口
USART_InitStructure.USART_BaudRate = 115200; //速率
115200bps
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(USART2, &USART_InitStructure); //将以上赋完值的结构体带入库函数USART_Init
进行初始化
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
USART_Cmd(USART2, ENABLE);//开启USART2,注意与上面RCC_APB2PeriphClockCmd()设置的区
别
}
II、修改波特率
void USART_Config(uint32_t baud)
{
USART_InitTypeDef USART_InitStructure;
USART_Cmd(USART2, DISABLE);
USART_InitStructure.USART_BaudRate =baud;
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(USART2, &USART_InitStructure);
USART_Cmd(USART2, ENABLE);
}
III、串口发送“hello windows!”
#include "sys.h"
#include "usart.h"
#include "delay.h"
int main(void)
{
u16 t; u16 len; u16 times=0;
Stm32_Clock_Init(9); //系统时钟设置
delay_init(72); //延时初始化
uart_init(72,115200); //串口初始化为115200
while(1)
{
if(USART_RX_STA&0x8000)
{
len=USART_RX_STA&0x3FFF;//得到此次接收到的数据长度
printf("\r\n Hello Windows! \r\n\r\n");
for(t=0;t<len;t++)
{
USART1->DR=USART_RX_BUF[t];
while((USART1->SR&0X40)==0);//等待发送结束
}
printf("\r\n\r\n");//插入换行
USART_RX_STA=0;
}else
{
times++;
if(times%200==0)printf("Hello Windows!\r\n");
delay_ms(10);
}
}
}
实验结果:
三、点亮熄灭LED灯
要求:STM32以查询方式接收上位机(win10)串口发来的数据,如果接收到“Y”则点亮链接到stm32上的一个LED灯;接收到“N”则熄灭LED灯。
代码:
串口函数
#include "stm32f10x.h" // Device header
#include <stdio.h>
#include <stdarg.h>
/**
* 函 数:串口初始化
* 参 数:无
* 返 回 值:无
*/
void Serial_Init(void)
{
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //开启USART1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
/*GPIOA_9初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA9引脚初始化为复用推挽输出
//GPIOA_10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//选择A6为亮灯口
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/*USART初始化*/
USART_InitTypeDef USART_InitStructure; //定义结构体变量
USART_InitStructure.USART_BaudRate = 9600; //波特率
USART_InitStructure.USART_HardwareFlowControl =USART_HardwareFlowControl_None; //硬件流控制,不需要
USART_InitStructure.USART_Mode = USART_Mode_Tx|USART_Mode_Rx; //模式,选择收发模式
USART_InitStructure.USART_Parity = USART_Parity_No; //奇偶校验,不需要
USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位,选择1位
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长,选择8位
USART_Init(USART1, &USART_InitStructure); //将结构体变量交给USART_Init,配置USART1
/*USART使能*/
USART_Cmd(USART1, ENABLE); //使能USART1,串口开始运行
}
/**
* 函 数:串口发送一个字节
* 参 数:Byte 要发送的一个字节
* 返 回 值:无
*/
void Serial_SendByte(uint8_t Byte)
{
USART_SendData(USART1, Byte); //将字节数据写入数据寄存器,写入后USART自动生成时序波形
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); //等待发送完成
/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}
/**
* 函 数:串口发送一个数组
* 参 数:Array 要发送数组的首地址
* 参 数:Length 要发送数组的长度
* 返 回 值:无
*/
void Serial_SendArray(uint8_t *Array,uint16_t Length)//发送数组
{
uint16_t i;
for(i=0;i<Length;i++)
{
Serial_SendByte(Array[i]);
}
}
/**
* 函 数:串口发送一个字符串
* 参 数:String 要发送字符串的首地址
* 返 回 值:无
*/
void Serial_SendString(char *string)
{
uint8_t i;
for(i=0;string[i]!='\0';i++)
{
Serial_SendByte(string[i]);
}
}
/**
* 函 数:次方函数(内部使用)
* 返 回 值:返回值等于X的Y次方
*/
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1; //设置结果初值为1
while (Y --) //执行Y次
{
Result *= X; //将X累乘到结果
}
return Result;
}
/**
* 函 数:串口发送数字
* 参 数:Number 要发送的数字,范围:0~4294967295
* 参 数:Length 要发送数字的长度,范围:0~10
* 返 回 值:无
*/
void Serial_SendNumber(uint32_t Number,uint8_t Length)
{
uint8_t i;
for(i=0;i<Length;i++)
{
Serial_SendByte(Number/Serial_Pow(10,Length-i-1)%10+'0');//由十进制的高位到低位依次发送
//加上'0'就转换为ascll码的类型,从ascll码中字符0的位置开始算,也可以改为0x30.
}
}
/**
* 函 数:使用printf需要重定向的底层函数
* 参 数:保持原始格式即可,无需变动
* 返 回 值:保持原始格式即可,无需变动
*/ //将printf的底层重定向到自己的发送字节函数
//什么是重定向?重定向是指将fputc里面的输出指向目标设备。因printf函数调用了fputc,
//而fputc输出有默认指向的目标,且不同库中的fputc输出指向不同,所以需要重写fputc
int fputc(int ch,FILE *f) //printf重定向,为串口
{
Serial_SendByte(ch);
return ch;
}
/**
* 函 数:自己封装的prinf函数
* 参 数:format 格式化字符串
* 参 数:... 可变的参数列表
* 返 回 值:无
*/
void Serial_Printf(char *format, ...)
{
char string[100];
va_list arg; //参数列表变量
va_start(arg,format); //
vsprintf(string,format,arg);
va_end(arg);//释放参数表
Serial_SendString(string);//发送string
}
主函数
#include "stm32f10x.h" // Device header
#include "Serial.h"
//操作IO口的三个步骤
//1、使用RCC开启GPIO时钟
//2、使用GPIO_Init函数初始化GPIO
//3、使用输出或输入函数控制GPIO口
uint8_t KeyNum;
uint8_t RxData;
int main()
{
OLED_Init();
Serial_Init();
GPIO_WriteBit(GPIOA,GPIO_Pin_6,Bit_SET);//将A6口初始化为电平
//GPIO_SetBits(GPIOA,GPIO_Pin_6);另一种初始化函数
while(1)
{
if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==SET)//若接收寄存器数据转到RDR中,则RXNE标志位置一,标志位自动清零
{
RxData=USART_ReceiveData(USART1);
if(RxData=='Y')
{GPIO_ResetBits(GPIOA,GPIO_Pin_6);}
if(RxData=='N')
{GPIO_SetBits(GPIOA,GPIO_Pin_6);}
}
}
}
NY