串口通讯
通讯原理
- 传送方式: 并行串行
串行: USART, I2C, SPI只有一两根数据线
并行: SDIO, FSMC
- 通信的方向
全双工: 同时收发数据USART, SPI
半双工: 不可同时收发数据, 分时收发数据SPI
单工: 任何时候都是固定一个方向传输
- 同步方式
同步: 有时钟信号, 效率比较高, 对时钟允许误差比较小
异步: 没有时钟信号, 加上辅助标志符
- 速率
比特率bit/s, 每秒二进制数的位数
波特率: 每秒传输的码元个数
0V-3.3V一个二进制位为一个码元
用多种不同的电压代表不同的信息, 码元和位数就不同了
串口
通信协议
物理层
- RS232
-15V是1, +15V是0, 主要用于工业设备直接通讯, 转换芯片一般有SP3232, MAX3232
- TTL
芯片里面出来的都是TTL电平
- USB转串口
主要芯片有CH340, PL2303, CP2102, FT232
协议层
起始位是一个逻辑0
结束位是0.5、1、1.5或2个逻辑1表示
有效数据: 经常被约定为5, 6, 7, 8位长度
校验位:
- 奇校验, 加上校验位之后1的个数为奇数
- 偶校验, 加上校验位之后1的个数为偶数
- 无校验
- 零校验
- 一校验
STM的实现
实际使用
- 发送
UE, TE打开发送时钟和通道
数据放到TDR, 之后传到发送移位寄存器, 最后发送到TX
之后TXE位为1表示发送数据寄存器数据空了, 这时候不能写
之后TC寄存器表示发送移位寄存器空了
- 接收
首先使能寄存器, UE,RE
之后数据进来, 进入移位寄存器, 设置RXNE寄存器为1
可以从RDR读取
是使用有5根线
- TX: 数据发送
- RX: 数据接收
- SCLK: 时钟, 可选
- nRTX: 请求发送, 可选, n=>低电平有效, 硬件控制
- nCTX: 允许发送
- 其他引脚: 红外编码模块
注: 实际在使用的时候注意时钟
寄存器
- USART_SR: 状态寄存器
- USART_BRR: 波特比率寄存器, 设置分频因子
fck是串口的时钟, 根据总线的不同变化
- USART_DR: 数据寄存器
包含了发送或接收的数据。由于它是由两个寄存器组成的,一个给发送用(TDR),一个给接收 用(RDR),该寄存器兼具读和写的功能。TDR寄存器提供了内部总线和输出移位寄存器之间的 并行接口(参见图248)。RDR寄存器提供了输入移位寄存器和内部总线之间的并行接口。 当使能校验位(USART_CR1中PCE位被置位)进行发送时,写到MSB的值(根据数据的长度不 同,MSB是第7位或者第8位)会被后来的校验位该取代。 当使能校验位进行接收时,读到的MSB位是接收到的校验位。
一个地址对应两个实际的物理地址,九位有效
- USART_CR1: 控制寄存器
M位(12): 控制数据的长度, 1: 一个起始位, 9个数据位, n个停止位, 0: 八个数据位
可以通过读取状态寄存器知道实际的问题
打开串口, 实际控制的是时钟
控制发送接收使能
其他各种中断
- USART_CR2: 控制寄存器
- USART_CR3: 控制硬件
- USART_GTPR: 保护时间, 预分频寄存器
固件库实现
typedef struct
{
uint32_t USART_BaudRate; /*!< This member configures the USART communication baud rate.
设置波特率 */
uint16_t USART_WordLength; /*!< Specifies the number of data bits transmitted or received in a frame.
This parameter can be a value of @ref USART_Word_Length设置数据长度 */
uint16_t USART_StopBits; /*!< Specifies the number of stop bits transmitted.
This parameter can be a value of @ref USART_Stop_Bits 设置停止位长度*/
uint16_t USART_Parity; /*!< Specifies the parity mode.
This parameter can be a value of @ref USART_Parity
@note When parity is enabled, the computed parity is inserted
at the MSB position of the transmitted data (9th bit when
the word length is set to 9 data bits; 8th bit when the
word length is set to 8 data bits). 设置校验, 奇校验, 偶校验*/
uint16_t USART_Mode; /*!< Specifies wether the Receive or Transmit mode is enabled or disabled.
This parameter can be a value of @ref USART_Mode 设置模式, 接收还是发送*/
uint16_t USART_HardwareFlowControl; /*!< Specifies wether the hardware flow control mode is enabled
or disabled.
This parameter can be a value of @ref USART_Hardware_Flow_Control
硬件流控制器, 是否使用后面的两根线 */
} USART_InitTypeDef;
typedef struct
{
//设置时钟
uint16_t USART_Clock; /*!< Specifies whether the USART clock is enabled or disabled.
This parameter can be a value of @ref USART_Clock 设置使能不使能*/
uint16_t USART_CPOL; /*!< Specifies the steady state value of the serial clock.
This parameter can be a value of @ref USART_Clock_Polarity 极性, 空闲时候电平*/
uint16_t USART_CPHA; /*!< Specifies the clock transition on which the bit capture is made.
This parameter can be a value of @ref USART_Clock_Phase 相位, 读取的边沿*/
//上面的两种相互组合有四种不同的效果
uint16_t USART_LastBit; /*!< Specifies whether the clock pulse corresponding to the last transmitted
data bit (MSB) has to be output on the SCLK pin in synchronous mode.
This parameter can be a value of @ref USART_Last_Bit 最后一位的时钟是否输出*/
} USART_ClockInitTypeDef;
实际实现
- 初始化GPIO引脚, RX设置为复用推挽输出, TX设置为浮空输入
- 之后初始化串口, 初始化之后使能
- 初始化NVIC
- 初始化中断, 如果读取寄存器不为空的时候就产生中断
- 在传递的时候判断标志位, TXE是数据移入移位寄存器, TC是移位寄存器为空的时候
void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
//初始化时钟
DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
//设置GPIO
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
//配置波特率
USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
//数据长度
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
//停止位
USART_InitStructure.USART_StopBits = USART_StopBits_1;
//校验位
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
//初始化串口
USART_Init(DEBUG_USARTx, &USART_InitStructure);
//初始化NVIC
NVIC_Configuration();
//初始化中断,在接受的时候产生中断
USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
//使能串口
USART_Cmd(DEBUG_USARTx, ENABLE);
}
void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data)
{
USART_SendData(pUSARTx, data);
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
void Usart_sendHalfWord(USART_TypeDef* pUSARTx, uint16_t data)
{
uint8_t temp_h, temp_l;
temp_h = (data & 0xff00)>>8;
temp_l = data & 0xff;
USART_SendData(pUSARTx, temp_h);
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
USART_SendData(pUSARTx, temp_l);
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
void Usart_sendString(USART_TypeDef* pUSARTx, uint8_t* data, uint8_t num)
{
int i;
for(i=0 ; i<num ; i++)
{
Usart_SendByte(pUSARTx, data[i]);
}
//这里设置为TC为发送完成的时候循环结束
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);
}
注: 不能在循环过程中判断USART_FLOG_TC, 因为在初始化的时候TC为1, 所以这时候不会进入循环, 直接发送第二字节的内容, 会覆盖第一字节导致出错
初始化printf函数
重写fputc函数
//重定义C库函数
int fputc(int ch, FILE *f)
{
USART_SendData(DEBUG_USARTx, (uint8_t)ch);
while(USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);
return ch;
}
int fgetc(FILE *f)
{
while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);
return (int)USART_ReceiveData(DEBUG_USARTx);
}
在使用后一个函数的时候最好不初始化中断, 或者重写中断函数, 否则会卡住