Linux 学习记录56(ARM篇)
一、总线概念
1. 总线
总线是完成传输的一种媒介,总线可以分为系统总线、数据总线和地址总线
系统总线是连接计算机的主要组成部分,它负责传输数据和控制信息。数据总线用于传输数据,而地址总线用于指定数据的存储位置。
2. 串行总线
串行总线同一时刻,只可以收发一位数据,并且串行总线只有一根数据线
串行总线是一种数据传输方式,其中数据位按照顺序一个接一个地通过单一的导线传输。与并行总线相比,串行总线只使用一条导线来传输数据,这样可以减少物理连接的数量和复杂性。
串行总线具有一些优点。首先,它可以在较长的距离上进行数据传输,因为信号只需通过一条导线传送,而不需要同时在多条导线上进行传输。其次,串行总线可以节省空间,因为只需要一根导线和相应的接口电路。此外,串行总线还可以提供较高的数据传输速率和更高的可靠性。
3. 并行总线
并行总线同一时刻,只可以收发多位数据,并且并行总线有多根数据线
并行总线是计算机内部用于传输数据的一种技术,它可以同时传输多个位元,使得数据传输速度更快。
其原理是将数据分成多个位元,每个位元通过不同的线路传输,这些线路同时工作,从而实现数据的并行传输。在计算机内部,不同的设备(如CPU、内存、硬盘等)可以通过并行总线进行数据传输。
并行总线的宽度越大,支持的数据传输速度也越快。同时,由于并行总线可以同时传输多个位元
4. 单工/半双工/全双工
单工、半双工和全双工是用于描述数据传输的不同方式。
- 单工、半双工和全双工是用于描述数据传输的不同方式。
- 半双工(Half Duplex):半双工传输允许通信双方进行双向数据传输,但不能同时进行。通信双方可以轮流发送和接收数据,但在某个时刻只能有一个角色处于发送状态,另一个角色处于接收状态。对于半双工通信,必须有一种机制来控制发送和接收的切换。典型的例子是对讲机或以太网的半双工模式。
- 全双工(Full Duplex):全双工传输允许通信双方同时进行双向数据传输,可以同时发送和接收数据。通信双方可以独立地发送和接收数据,无需进行切换。典型的例子是电话通信或计算机之间的网络通信。在全双工通信中,通常需要使用不同的频率或信道来实现同时传输和接收的功能。
5. 同步
通信双方共用一个时钟信号,根据时钟信号的变化,完成数据收发
同步通信是指在发送方和接收方之间建立一种同步机制,发送方在发送数据时必须等待接收方发送确认信号后才能继续发送数据。这种方式的优点是通信稳定可靠,数据传输过程中不会出现丢包或重复发送等问题,适用于一些对通信可靠性要求较高的场景。但缺点是通信效率相对较低,因为发送方需要等待接收方的反馈信号
6. 异步
通信双方各自有自己独立时钟信号,根据时钟信号的变化,完成数据收发
通过双方保持相同的时钟频率
异步通信是指发送方和接收方之间没有建立同步机制,发送方可以随时发送数据,而接收方则随时接收数据。这种方式的优点是通信效率高,因为发送方和接收方之间不需要等待,数据传输速度相对较快。但缺点是通信稳定性相对较差,数据传输过程中可能会出现丢包或重复发送等问题,适用于一些对通信效率要求较高但对通信稳定性要求不太高的场景
二、串口(UART)
1. 串口配置信息
- 波特率
串口的波特率是指每秒钟传输的比特数。它用于衡量串口通信的速度。常见的串口波特率包括9600、115200等。波特率越高,传输速度越快,但也需要更高的硬件支持。在进行串口通信时,发送端和接收端的波特率必须一致,否则数据传输可能会出错。
- 停止位
串口的停止位(Stop Bit)是指在每个数据字节传输完之后,发送端发送的一个额外的位,用于标识数据传输的结束。停止位的作用是为了提供数据传输的稳定性和同步性。
常见的停止位有1位、1.5位和2位。其中,1位停止位是最常用的配置。在1位停止位的情况下,发送端在每个数据字节传输完之后会发送一个逻辑高电平的停止位,用于告知接收端数据传输结束。这样接收端就可以根据停止位的信号来判断数据的边界。
在串口通信中,发送端和接收端必须统一设置停止位的配置,否则可能导致数据传输出错。一般情况下,1位停止位已经足够满足大部分的串口通信需求。
- 数据位
- 校验位
串口的校验位(Parity Bit)是一种用于检测和纠正数据传输错误的机制。校验位位于数据位之后,用于验证数据的准确性。
校验位可以有以下几种配置:
- 奇校验(Odd Parity):校验位被设置为确保数据位中"1"的个数为奇数。
- 偶校验(Even Parity):校验位被设置为确保数据位中"1"的个数为偶数。
- 无校验(No Parity):不使用校验位进行数据验证。
发送端在发送每个数据字节之前会计算校验位,并将其添加到数据位之后。接收端在接收数据时会对接收到的数据位进行校验,验证数据的准确性。如果校验失败,接收端会丢弃或纠正错误的数据。
校验位可以提高数据传输的可靠性,但也会占用额外的传输时间和带宽。在实际应用中,是否使用校验位取决于通信的可靠性要求和硬件限制。
2. 串口通信协议(异步串行全双工总线)
空闲态:串口没有进行数据传输时,总线处于空闲状态
起始信号:串口数据传输时,开始的标志
数据位:串口数据传输(进行数据的收发),先发送低位,在发送高位,串口配置信息常用配置8位数据位
校验位:校验数据传输受否准备
奇校验:数据位和校验位1的个数为奇数
假设当前发送的数据为0x55(0101 0101) 校验位:1
偶校验:数据位和校验位1的个数为偶数
假设当前发送的数据为0x55(0101 0101) 校验位:0
停止信号:1)串口数据传输时,结束的标志 2)校验时钟信号
校验时钟的作用:
串口数据传输时,采用的是异步通信方式,双方都有自己独立的时钟源,需要设置双方的时钟频率保持一致,
但是在数据传输时,会存在误差,越往后,数据的误差越大,导致收发数据不准确,所以在停止信号 需要校
验时钟频率
3. 框图分析
4. 使能串口
5. GPIO的复用模式
GPIO的复用寄存器有高低位两个
复用寄存器的配置
通过查询手册可以得知,例如PB2引脚对应的串口4复用为AF8,PG11引脚对应的串口4复用为AF6,
6. RCC时钟
文章来源:https://www.toymoban.com/news/detail-607139.html
7. UART寄存器
文章来源地址https://www.toymoban.com/news/detail-607139.html
封装UART配置及收发
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
#define __IO volatile
#define Enbale 1
#define Disnbale 0
#define RCC_MP_AHB4_ENSETR (*(volatile unsigned int*)0x50000A28)
#define RCC_MP_APB1_ENSETR (*(volatile unsigned int*)0x50000A00)
typedef struct
{
__IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */
__IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */
__IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */
__IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */
__IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */
__IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */
__IO uint16_t BSRRL; /*!< GPIO port bit set/reset low register, Address offset: 0x18 */
__IO uint16_t BSRRH; /*!< GPIO port bit set/reset high register, Address offset: 0x1A */
__IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */
__IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */
}GPIO_TypeDef;
#define GPIOA ((GPIO_TypeDef *) 0x50002000)
#define GPIOB ((GPIO_TypeDef *) 0x50003000)
#define GPIOC ((GPIO_TypeDef *) 0x50004000)
#define GPIOD ((GPIO_TypeDef *) 0x50005000)
#define GPIOE ((GPIO_TypeDef *) 0x50006000)
#define GPIOF ((GPIO_TypeDef *) 0x50007000)
#define GPIOG ((GPIO_TypeDef *) 0x50008000)
#define GPIOH ((GPIO_TypeDef *) 0x50009000)
#define GPIOI ((GPIO_TypeDef *) 0x5000A000)
#define GPIOJ ((GPIO_TypeDef *) 0x5000B000)
#define GPIOK ((GPIO_TypeDef *) 0x5000C000)
/*GPIO模式*/
typedef enum
{
GPIO_Mode_IN = 0, //输入
GPIO_Mode_OUT, //输出
GPIO_Mode_AF, //复用
GPIO_Mode_AN //模拟
}GPIOMode_TypeDef;
/*GPIO引脚速度*/
typedef enum
{
GPIO_Low_Speed = 0, //低速
GPIO_Medium_Speed, //中速
GPIO_Fast_Speed, //快速
GPIO_High_Speed //高速
}GPIOSpeed_TypeDef;
/*GPIO输出模式*/
typedef enum
{
GPIO_OType_PP = 0, //推挽
GPIO_OType_OD, //开漏
}GPIOOType_TypeDef;
/*GPIO上下拉设置*/
typedef enum
{
GPIO_PuPd_NOPULL = 0, //推挽
GPIO_PuPd_UP, //上拉
GPIO_PuPd_DOWN, //下拉
}GPIOPuPd_TypeDef;
typedef struct
{
unsigned int GPIO_Pin; // Pin脚
GPIOMode_TypeDef GPIO_Mode; //模式
GPIOSpeed_TypeDef GPIO_Speed; //速度
GPIOOType_TypeDef GPIO_OType; //输出模式
GPIOPuPd_TypeDef GPIO_PuPd; //上下拉
}GPIO_InitTypeDef;
typedef enum
{
AF0 = 0,
AF1,
AF2,
AF3,
AF4,
AF5,
AF6,
AF7,
AF8,
AF9,
AF10,
AF11,
AF12,
AF13,
AF14,
AF15,
}GPIO_AF_TypeDef;
typedef enum
{
GPIO_PinSource0 = ((uint8_t)0x00),
GPIO_PinSource1,
GPIO_PinSource2,
GPIO_PinSource3,
GPIO_PinSource4,
GPIO_PinSource5,
GPIO_PinSource6,
GPIO_PinSource7,
GPIO_PinSource8,
GPIO_PinSource9,
GPIO_PinSource10,
GPIO_PinSource11,
GPIO_PinSource12,
GPIO_PinSource13,
GPIO_PinSource14,
GPIO_PinSource15,
}GPIO_PinSource;
void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx,uint16_t GPIO_PinSource,uint8_t GPIO_AF)
{
if(GPIO_PinSource <= 7)
GPIOx->AFR[0] |= GPIO_AF << GPIO_PinSource*4;
else
GPIOx->AFR[1] |= GPIO_AF << GPIO_PinSource*4;
}
void GPIO_Init(GPIO_TypeDef* GPIOx,GPIO_InitTypeDef* GPIO_init)
{
/*初始化GPIOE模式*/
GPIOx->MODER &= ~(0x3 << (GPIO_init->GPIO_Pin*2));
GPIOx->MODER |= GPIO_init->GPIO_Mode << (GPIO_init->GPIO_Pin*2);
/*初始化GPIOE输出模式*/
GPIOx->OTYPER &= ~(GPIO_init->GPIO_OType << GPIO_init->GPIO_Pin);
/*初始化GPIOE速度*/
GPIOx->OSPEEDR &= ~(0x3 << (GPIO_init->GPIO_Pin*2));
GPIOx->OSPEEDR |= GPIO_init->GPIO_Speed << (GPIO_init->GPIO_Pin*2);
/*初始化GPIOE上下拉设置*/
GPIOx->PUPDR &= ~(0x3 << (GPIO_init->GPIO_Pin*2));
GPIOx->PUPDR |= GPIO_init->GPIO_PuPd << (GPIO_init->GPIO_Pin*2);
}
typedef struct {
__IO uint32_t CR1;
__IO uint32_t CR2;
__IO uint32_t CR3;
__IO uint32_t BRR;
__IO uint32_t GTPR;
__IO uint32_t RTOR;
__IO uint32_t RQR;
__IO uint32_t ISR;
__IO uint32_t ICR;
__IO uint32_t RDR;
__IO uint32_t TDR;
__IO uint32_t PRESC;
}USART_TypeDef;
#define USART4 ((USART_TypeDef*) 0x40010000)
typedef enum
{//数据位
USART_WordLength_8b = 0,
USART_WordLength_9b,
USART_WordLength_7b,
}USART_WordLength;
typedef enum
{//数据停止位
USART_StopBits_1 = 0,
USART_StopBits_0_5,
USART_StopBits_2,
USART_StopBits_1_5,
}USART_StopBits;
typedef enum
{//串口功能
USART_Mode_Rx = 0x01,
USART_Mode_Tx,
}USART_Mode;
typedef enum
{//串口校验位
USART_Parity_No = 0x00,
USART_Parity_Even,
}USART_Parity;
typedef enum
{//串口采样速度
USART_SamplingRate_16 = 0x00,
USART_SamplingRate_8,
}USART_SamplingRate;
#define USART_Baud(x) (64000000/x) //计算波特率
typedef struct
{
uint32_t USART_BaudRate;//波特率
uint16_t USART_WordLength;//数据位
uint16_t USART_StopBits;//停止位
uint16_t USART_Parity;//校验位
uint16_t USART_Mode;//模式
uint16_t USART_SamplingRate;//采样速度设置位
} USART_InitTypeDef;
/*使能串口-UE*/
void USART_cmd(USART_TypeDef* UARTx,uint8_t cmd)
{
UARTx->CR1 &= ~0x1;//清空发送接收使能标志位
UARTx->CR1 |= cmd;//设置发送接收使能标志位
}
void USART_Init(USART_TypeDef* UARTx,USART_InitTypeDef* UART_init)
{
UARTx->CR1 &= ~(0x3 << 2);//清空发送接收使能标志位
UARTx->CR1 |= UART_init->USART_Mode << 2;//设置发送接收使能标志位
UARTx->CR1 &= ~(0x1 << 10);//清空校验设置位
UARTx->CR1 |= UART_init->USART_Parity << 10;//设置校验设置位
UARTx->CR1 &= ~(0x1 << 15);//清空采样速度设置位
UARTx->CR1 |= UART_init->USART_SamplingRate << 15;//设置采样速度设置位
UARTx->CR1 &= ~(0x1 << 12);//清空数据位设置
UARTx->CR1 &= ~(0x1 << 28);//清空数据位设置
UARTx->CR1 |= (UART_init->USART_WordLength&0x01) << 12;//设置数据位
UARTx->CR1 |= (UART_init->USART_WordLength&0x02) << 28;//设置数据位
UARTx->CR2 &= ~(0x03 << 12);//清空停止位设置
UARTx->CR2 |= UART_init->USART_StopBits << 12;/
UARTx->BRR = USART_Baud(UART_init->USART_BaudRate);//设置波特率
}
/*串口数据发送*/
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
{
uint32_t TXE = USARTx->ISR & (0x1<<7);
while (1)
{//等待发送数据寄存器清空
TXE = USARTx->ISR & (0x1<<7);
if(TXE)
{//发送数据
USARTx->TDR = Data;
break;
}
}
}
/*串口数据接收*/
uint16_t USART_ReceiveData(USART_TypeDef* USARTx)
{
uint16_t buf = (uint16_t)(USARTx->RDR);
return buf;
}
/*LED控制*/
#define ledE_on(x) GPIOE->ODR |= 0x1 << x
#define ledE_off(x) GPIOE->ODR &= ~(0x1 << x)
#define ledF_on(x) GPIOF->ODR |= 0x1 << x
#define ledF_off(x) GPIOF->ODR &= ~(0x1 << x)
void delay_ms(int ms)
{
int i,j;
for(i = 0; i < ms;i++)
for (j = 0; j < 1800; j++);
}
int main()
{
delay_ms(50);
RCC_MP_AHB4_ENSETR |= (0x1 << 4);//使能GPIO B-G时钟
RCC_MP_AHB4_ENSETR |= (0x1 << 1);//使能GPIO B-G时钟
RCC_MP_AHB4_ENSETR |= (0x1 << 6);//使能GPIO B-G时钟
RCC_MP_APB1_ENSETR |= (0x1 << 16);//使能uart4时钟
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = 2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//输入模式
GPIO_InitStructure.GPIO_Speed = GPIO_Fast_Speed;//低速
GPIO_PinAFConfig(GPIOB,GPIO_PinSource2,AF8);//GPIOB2复用为USART4的RX
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
GPIO_InitStructure.GPIO_Pin = 11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Fast_Speed;
GPIO_PinAFConfig(GPIOG,GPIO_PinSource11,AF6);//GPIOG11复用为USART4的TX
GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化GPIO
GPIO_InitStructure.GPIO_Pin = 10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输入模式
GPIO_InitStructure.GPIO_Speed = GPIO_Low_Speed;//低速
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
//串口1对应引脚复用映射
GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIO
/*UART4初始化*/
USART_InitTypeDef USART_InitStructure;
/*串口初始化*/
USART_InitStructure.USART_BaudRate = 115200;//设置波特率为115200
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式
USART_InitStructure.USART_Parity = USART_Parity_No;//无校验位
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_SamplingRate = USART_SamplingRate_16;//16采样
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8个数据位
USART_Init(USART4,&USART_InitStructure);//初始化串口
USART_cmd(USART4,Enbale);//使能串口
uint16_t buf[256];
uint16_t cnt;
USART_SendData(USART4,'\n');//发送一个换行
while (1)
{
if(USART4 ->ISR & (0x1<<5))
{//如果接收到数据
ledE_on(10);//如果接收到数据led点亮
buf[cnt] = (uint16_t)(USART4->RDR);//从读取数据
USART_SendData(USART4,*(buf+cnt));//当接收到数据再发出
if(*(buf+cnt)=='\n')//当监测到回车换行全部再输出一次
{
for(uint16_t i=0; i<cnt+1; i++)
{//全部输出
USART_SendData(USART4,*(buf+i)+1);
}
USART_SendData(USART4,'\r');
USART_SendData(USART4,'\n');
USART_SendData(USART4,cnt+0x30);
ledE_off(10);
cnt=0;
}
else
{
cnt++;
}
}
}
return 0;
}
到了这里,关于Linux 学习记录56(ARM篇)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!