STM32的串口编程实验理解分析

发布时间:2024-03-22  

USART—通用同步异步收发接收器,是一个串行通信设备,可以和外部设备进行灵活的全双工数据交换,有别于USART还有一个UART(在原来的基础上裁剪掉了同步通信功能(时钟同步)),串行通信一般是以帧格式传输数据,一帧一帧的传。


协议层:串口通信的一个数据包包含从起始信号开始,直到停止信号的结束 起始信号:一个逻辑0数据位表示。 停止信号:0.5,1,1.5或2个逻辑1的数据位表示。 0.5个停止位:智能卡模式下的接收数据时使用。 1个停止位:停止位的默认数值 1.5个停止位:智能卡模式下的手法数据和接收数据时使用 2个停止位:常规USART模式,单线模式以及调制解调器的模式。 有效数据的基本长度被约定为5,6,7,8. 奇偶检验(设置USART-CR1 的PS位) 偶检验:数据=00110101,里面数据1的个数位为偶数位,检验位置“0”,当数据检验和偶数相同的时候,证明没有出错,反之则错误 奇检验:数据 = 01110101,里面数据1的个数为奇检位,检验位置“1”,当数据检验和奇数相同,则证明没有出错,反之错误。 当然也会存在同时两个位一块出现错误,导致无法判断是否位奇偶检验的错误,但发生的概率很低。


下面是对代码的理解: 可以看出USART_RX_STA类似与一个16位的寄存器,前14位存储的是数据,后面两个分别检测0X0D和0X0A。 接下里分析:

void uart_init(u32 pclk2,u32 bound)

{

float temp;

u16 mantissa;

u16 fraction;

temp=(float)(pclk2*1000000)/(bound*16);//得到USARTDIV

mantissa=temp; //得到整数部分

fraction=(temp-mantissa)*16; //得到小数部分

mantissa《《=4;

mantissa+=fraction;

RCC-》APB2ENR|=1《《2; //使能PORTA口时钟

RCC-》APB2ENR|=1《《14; //使能串口时钟

GPIOA-》CRH&=0XFFFFF00F;//IO状态设置

GPIOA-》CRH|=0X000008B0;//IO状态设置

RCC-》APB2RSTR|=1《《14; //复位串口1

RCC-》APB2RSTR&=~(1《《14);//停止复位

//波特率设置

USART1-》BRR=mantissa; // 波特率设置

USART1-》CR1|=0X200C; //1位停止,无校验位。

#if EN_USART1_RX //如果使能了接收

//使能接收中断

USART1-》CR1|=1《《5; //接收缓冲区非空中断使能

MY_NVIC_Init(3,3,USART1_IRQn,2);//组2,最低优先级

#endif

} temp=(float)(pclk2*1000000)/(bound*16);这是一个计算公式,因为使能的是串口1,而串口1是在APB2ENR寄存器里面(其余串口均在寄存器APB1ENR里面),因为APB2的频率一般位72M,而APB1的频率一般位36M。 所以这里的pclk2为72M,而bound是你需要设置的波特率。

USARTX-BRR: 前四位为小数部分 ,后12位是整数部分,假设算出来的mantissa = 39.5,小数部分相当于把1分成了16份,所以相当于把0.5*16转化为二进制存入。

mantissa = temp的作用仅仅是:为了接下来将小数部分求出来

fraction=(temp-mantissa)*16; //得到小数部分

mantissa《《=4; 这两行代码是为将十进制的整数部分和小数部分,分别转化为16进制。然后存入到波特率寄存器里面。紧接着使能串口1和PORTA时钟(串口一对应的IO口是PA9,PA10,需要拿跳帽连接在一起)。 然后将IO口置零,然后分别进行设置成一个输入一个输出,

USART1-》CR1|=0X200C; 设置成使能串口8个字长1个停止位(USART_CR2中[13:12]默认为“0”)

MY_NVIC_Init(3,3,USART1_IRQn,2)

将其分在组2里面,此时的抢占优先级:响应优先级为 = 2:2,即(00-11)四种情况,而3:3的安排选择了组2优先级最小的一种情况。这样可以先执行上面的波特率赋值,以及串口使能等等操作,最后再进行这行代码运行。 接下来看下一部分:

u16 USART_RX_STA=0; //接收状态标记

void USART1_IRQHandler(void)

{

u8 res;

#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.

OSIntEnter();

#endif

if(USART1-》SR&(1《《5)) //接收到数据

{

res=USART1-》DR;

if((USART_RX_STA&0x8000)==0)//接收未完成

{

if(USART_RX_STA&0x4000)//接收到了0x0d

{

if(res!=0x0a)USART_RX_STA=0;//接收错误,重新开始

else USART_RX_STA|=0x8000; //接收完成了

}

else //还没收到0X0D

{

if(res==0x0d)USART_RX_STA|=0x4000;

else

{

USART_RX_BUF[USART_RX_STA&0x3fff]=res;

USART_RX_STA++;

if(USART_RX_STA》(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收

}

}

}

} 起始阶段: USART_RX_STA=0,对接受状态的标记。 先通过状态寄存器SR的RXNE是否为1,是1则接收到了数据,反之则没有。紧接这定义一个res变量来接收从数据寄存器的一个字节,然后此时USART_RX_STA为0,与0X8000进行&运算,结果为0,则未接受到,接着继续进行判断,0X4000进行与运算,看是否为0,也是判断是否接受道路0X0D,如果没有接受到,则将这个res变量存放在数组里面,此时的USART_RX_STA为 0 与0X3fff进行&运算,大家算算会发现,因为他的前14位是数据位,所以你会发现第一个变量就会存放在BUF[0]里面,大概逻辑是这样的: 所以每个字节都会被存放到具体的数组位上 。 if(USART_RX_STA》(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收 当数组越界的时候,则会重新开始。 接下来就会一直循环,当数据位存满后,接下来res里面接受的就是0X0D,先和上面一样判断USART_RX_STA是否接受到了0X0A和0X0D。 接着执行:

if(res==0x0d)USART_RX_STA|=0x4000; 将USART_RX_STA的第十五位变为1,,接下来进行下一次循环,这一次res接受到的值为0X0A, 然后进行判断进入到

if(USART_RX_STA&0x4000)//接收到了0x0d

{

if(res!=0x0a)USART_RX_STA=0;//接收错误,重新开始

else USART_RX_STA|=0x8000; //接收完成了

} 所以执行USART_RX_STA|=0x8000,使得USART_RX_STA的第十六位变为1。 接下来看主函数部分:

int main(void)

{

u8 t;

u8 len;

u16 times=0;

Stm32_Clock_Init(9); //系统时钟设置

delay_init(72); //延时初始化

uart_init(72,9600); //串口初始化为9600

LED_Init(); //初始化与LED连接的硬件接口

while(1)

{

if(USART_RX_STA&0x8000)

{

len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度

printf(“

您发送的消息为:

”);

for(t=0;t《len;t++)

{

USART1-》DR=USART_RX_BUF[t];

while((USART1-》SR&0X40)==0);//等待发送结束

}

printf(“

”);//插入换行

USART_RX_STA=0;

}else

{

times++;

if(times%5000==0)

{

printf(“

ALIENTEK MiniSTM32开发板 串口实验

”);

printf(“正点原子@ALIENTEK

”);

}

if(times%200==0)printf(“请输入数据,以回车键结束

”);

if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行。

delay_ms(10);

} }

} if(USART_RX_STA&0x8000) 判断是否接收到了0X0A len=USART_RX_STA&0x3fff;举个简单的例子此时USART_RX_STA为1100000000000011和0X3fff进行&运算,得到的结果是3,自然就表示了当前数组的大小。 最后阶段,重点理解以下两行代码:

USART1-》DR=USART_RX_BUF[t];

while((USART1-》SR&0X40)==0);//等待发送结束 分析如下:将每个组内的信息存入到数据寄存器,此时数据寄存器将数据给TDR,发送信息的时候,是一位一位发送的,每一数据帧都有起始位,数据位,以及停止位,当检测到数据寄存器的细信息发送完了(完全给了TDR),此时状态寄存器的TXE便变为1,当检测到TXE为1后,TC也会变为1(系统自动进行)。所以第二行才会检测这个状态寄存器的第6位是否为1来判断是否发送成功了这个字节。 由此推出,直接判断TXE也可以判断发送是否完成 所以代码可以改为:

for(t=0;t《len;t++)

{

USART1-》DR=USART_RX_BUF[t];

while((USART1-》SR&0X80)==0);//等待发送结束


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>