历史上的今天
返回首页

历史上的今天

今天是:2025年03月12日(星期三)

正在发生

2019年03月12日 | STM32中的串口通信

2019-03-12 来源:eefocus

说起通信,我们都知道通信分为并行通信和串行通信。并行通信速率快但是占用引脚数多,串行通信速度慢但是占用引脚数很少。


今天我们主要来说串行通信

串行通信还可以分为同步通信和异步通信。

同步通信:带时钟同步信号传输,栗子:SPI、IIC

异步通信:不带时钟同步信号,栗子:UART、单总线


对于同步通信来说,通信双方是通过同步时钟信号进行发送和接收数据的,即每来一个时钟信号,发送方就发送一位数据,这样接收方也可以通过时钟信号来进行数据的解析。


对于异步通信来说,通信双方之间并没有同步时钟信号,为了使接收方能够准确地把发送方发送的信息解析出来,通信双方在通信之前要约定好一个东西,我们叫它波特率,即发送方按照一定的频率去发送数据,接收方也根据这个频率来每隔固定的时间就去读取信号线的电平状态,从而实现了对发送数据的解析。


简单的列几个通信方式:


UART(通用异步收发器):全双工

USART(通用同步异步收发器):全双工

SPI:同步通信  全双工

IIC:同步通信  半双工

单总线:异步通信  半双工


接下来说一下STM32F103中的串口通信

在F103中,串口1连接的时钟是PCLK2(72MHz),串口2—4连接的时钟是PCLK1(36MHz),这在一会我们计算波特率的时候会用到


STM32F103串口异步通信需要配置的参数


 起始位

 数据位(8位或者9位)

 奇偶校验位(第9位)

 停止位(1,15,2位)

 波特率设置


USART逻辑框图(看一下,了解一下即可)



常用函数


void USART_Init(); //串口初始化:波特率,数据字长,奇偶校验,硬件流控以及收发使能

void USART_Cmd();//使能串口

void USART_ITConfig();//使能相关中断


void USART_SendData();//发送数据到串口,DR

uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据


FlagStatus USART_GetFlagStatus();//获取状态标志位

void USART_ClearFlag();//清除状态标志位

ITStatus USART_GetITStatus();//获取中断状态标志位

void USART_ClearITPendingBit();//清除中断状态标志位



关于这个波特率的计算我简单的说一下:


STM32F103是支持设置分数波特率的,即用来存储波特率数值的寄存器的低四位用来存储小数部分,高12位用来存储整数部分。但是小数部分的数值也不是随便取的,它只能够取十六分之一的整数倍,即1/16、2/16、3/16…


所以说我们平常所常用的波特率并不是连续的数值,我们一般用的有4800、9600、115200等等,根据上面那个例子,假如说我们现在需要设置波特率为115200,那么把115200带入到官方手册中提供的公式中,即式子中的Tx/Rx baud就是我们要的波特率115200,然后fck就是我们上面说的PCLK1和PCLK2了,PCLK1(36MHz)PCLK2(72MHz),然后USARTDIV代表的就是我们要往相应的寄存器中存储的数值。我们把115200带入到式子中求得USARTDIV=39.625,那么我们就直接把整数部分的39转换成16进制存入到相应寄存器(相应的寄存器为波特比率寄存器USART_BRR)的高12位,然后我们再对小数部分进行处理,我们是直接让小数乘以16,这其实就是十进制小数和十六进制小数之间的转换,我们把十进制的0.625转换成十六进制就是0.1,那么我们就把1存入到相应寄存器的低4位。再比如如果我们算出来小数部分是0.75,那么0.75转换成十六进制就是0.11,也就是0.C,那么我们就把0X0C存入到相应寄存器的低4位。大概的操作就是这样的,如果我们是用库函数来编写代码的话,这些内部计算我们不需要关注,我们只需要调用库函数USART_Init(),然后传入参数就行,参数就是我们要设置的波特率。

波特比率寄存器


串口配置一般步骤


1、串口时钟使能,GPIO时钟使能:RCC_APB2PeriphClockCmd();

2、串口复位:USART_DeInit(); 这一步不是必须的

3、GPIO端口模式设置:GPIO_Init(); 串口发送引脚配置成复用推挽输出   接收引脚配置成浮空输入

4、串口参数初始化:USART_Init();

5、开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)

      NVIC_Init();

      USART_ITConfig();

6、使能串口:USART_Cmd();

7、编写中断处理函数:USARTx_IRQHandler();

8、串口数据收发:

void USART_SendData();//发送数据到串口,DR

uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据

9、串口传输状态获取:

FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);

void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);



部分代码


u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.

//接收状态

//bit15, 接收完成标志

//bit14, 接收到0x0d

//bit13~0, 接收到的有效字节数目

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

  

void uart_init(u32 bound){

  //GPIO端口设置

  GPIO_InitTypeDef GPIO_InitStructure;

USART_InitTypeDef USART_InitStructure;

NVIC_InitTypeDef NVIC_InitStructure;

 

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟

  

//USART1_TX   GPIOA.9

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出

  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9

   

  //USART1_RX   GPIOA.10初始化

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入

  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  


  //Usart1 NVIC 配置

  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能

NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器

  

   //USART 初始化设置


USART_InitStructure.USART_BaudRate = bound;//串口波特率

USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式

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(USART1, &USART_InitStructure); //初始化串口1

  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断

  USART_Cmd(USART1, ENABLE);                    //使能串口1 


}


void USART1_IRQHandler(void)                //串口1中断服务程序

{

u8 Res;

if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)

{

Res =USART_ReceiveData(USART1); //读取接收到的数据


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

{

if(USART_RX_STA&0x4000)//如果已经接收到了0x0d

{

if(Res!=0x0a)USART_RX_STA=0;//如果在接收到0x0d之后,没有紧接着就接收到0z0a,那么就是接收错误,重新开始

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 来控制数据的接收与处理的。


USART_RX_STA的低14位是用来记录我们接收到的有效数据个数的,即每接收到一个有效数据,它的值就加一,如果接收到了0X0D,那么就把这个变量USART_RX_STA的次高位置1,如果我们紧接着又接收到了0X0A的话,那么就把这个变量USART_RX_STA的最高位置1,此时就代表着我们这次的传输完成了。

注意:0x0D和0X0A是ASCII中的控制字符,0x0D含义是Carriage return(回车键),0X0A含义是Line feed(换行键),串口发送后的0d 0a表示回车换行


主函数部分代码


 int main(void)

 {

  u16 t;  

u16 len;

u16 times=0;

delay_init();     //延时函数初始化   

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级

uart_init(115200); //串口初始化为115200

  while(1)

{

if(USART_RX_STA&0x8000)

{    

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

printf("\r\n您发送的消息为:\r\n\r\n");

for(t=0;t

{

USART_SendData(USART1, USART_RX_BUF[t]);//向串口1发送数据

while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束

}

printf("\r\n\r\n");//插入换行

USART_RX_STA=0;

}else

{

times++;

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

delay_ms(10);   

}

}  

 }


注意配置串口初始化函数的入口参数就是我们要设置的波特率


uart_init(115200); //串口初始化,波特率设置为115200


开启串口接收中断(即接收缓冲器非空 USART_IT_RXNE)


USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断


判断产生的中断是否是接收中断


if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)



推荐阅读

史海拾趣

DIPTRONICS MANUFACTURING INC公司的发展小趣事

面对日益激烈的市场竞争和不断变化的市场需求,圜达实业始终坚持创新发展。公司不断推出新产品、新技术,以满足客户的多样化需求。同时,公司还积极与国内外知名企业开展合作,共同研发新技术、新产品,推动电子开关行业的持续发展。

请注意,以上故事梗概仅基于公开信息和一般行业趋势进行编写,可能无法涵盖圜达实业发展的所有细节和具体情况。

Galaxy Microelectronics公司的发展小趣事

飞翼股份在绿色矿山建设领域取得了显著成就。作为国内矿山绿色开采行业的龙头企业,飞翼股份与多家顶尖高校及行业权威专家紧密合作,自主创新了多项技术、装备,填补了国内行业空白,达到国际先进水平。公司自主研发的膏体充填技术,实现了从源头解决充填中各子系统间不兼容的难题,确保了系统运行的可靠性和高效性。飞翼股份不仅为矿山行业提供了绿色、环保的解决方案,还积极参与国际交流,推动了全球矿山绿色开采技术的进步。

Hirel Systems Ltd公司的发展小趣事

深圳市飞翼科技有限公司自2006年成立以来,一直致力于模拟与数字MCU混合芯片领域的研究、设计和开发应用。公司主攻电容式触摸感应按键芯片设计,凭借多项独有的专利技术,成功突破了行业内的技术难点。经过多年的努力,飞翼科技已成为该应用领域中技术最全面、市场份额最大的公司之一。其电容式触摸感应芯片广泛应用于各类电子产品中,为用户带来了更加便捷、智能的交互体验。

EMLSI公司的发展小趣事

在电子行业中,产品质量是企业生存和发展的关键。EMLSI公司深知这一点,因此始终将品质管理放在首位。公司建立了严格的质量管理体系,从原材料采购到产品生产的每一个环节都进行严格把关。同时,EMLSI还注重与客户的沟通和反馈,及时解决客户在使用过程中遇到的问题。这种对品质的执着追求让EMLSI赢得了客户的信任和忠诚。

AIC [Analog Intergrations Corporation]公司的发展小趣事

随着电子行业的快速发展,市场竞争也日益激烈。为了保持领先地位,EMLSI公司开始实施全球化战略。公司先后在亚洲、欧洲和北美等地建立了生产基地和研发中心,与当地企业建立了紧密的合作关系。这一战略不仅让EMLSI能够更快地了解市场需求和技术趋势,还为公司带来了更多的商业机会和合作伙伴。

星海公司的发展小趣事

除了钢琴制造,星海公司还积极拓展多元化业务。公司下设多个分厂和子公司,涉及西管乐器、民族乐器、打击乐器、电声乐器和乐器配件等多个领域。此外,星海公司还积极打造音乐教育板块,成立钢琴培训学校,为音乐爱好者提供优质的教育资源。通过多元化发展,星海公司逐渐形成了完整的产业链和强大的品牌影响力。

问答坊 | AI 解惑

听人说居然有0.5元以下的单片机,你见到最便宜的是多少钱的?

今天看到坛子里有人说有5毛钱的单片机,类似闪灯IC等, 没有听说过,大家见到最便宜的单片机都多少钱?有什么功能,干什么用的呢?…

查看全部问答>

请教WinCE下加载位图的文件路径问题

在模拟器下实现加载位图,请问: 1、此时函数SHLoadDIBitmap(TEXT(\"\\\\*.bmp\"))中的路径该如何设置呢,或者是说我试图加载的位图应该存放于什么路径呢; 2:位图本身的大小有没有什么限制啊? 谢谢各位啦…

查看全部问答>

大家知道怎么样选购UPS电源吗?

很多人在购买UPS的时候没有考虑到UPS电源的很多因素,这样将会损坏到我们的服务器和需要保护的设备。首先要根据您的负载(也就是功率大小)来选定您的UPS电源,比如说您的服务器是750瓦的那么您要选购买1500VA或者是2000VA的UPS电源,为什麽不能选 ...…

查看全部问答>

关于若干个s7PLC DP 组网的问题

本人现在面临一个DP组网问题,想求教。现有3个CPU315-2DP,6个EM277,一个5621卡。我希望能一个CPU315-2DP-1通过DP带一个EM277, 另一个CPU315-2DP-2通过DP带3个EM277, 还有一个CPU315-2DP-3通过DP带2个EM277, 希望CP5621能和每个CPU315-2DP通讯,各C ...…

查看全部问答>

急,430F449的P1中断服务程序不能运行(附程序),请多指教

//下面是我的一段P1键盘中断测试程序,可是我好象进不了P1中断服务程序,请各位大侠多多指教 #include <msp430x44x.h> void inter (void); int num=0; void main(void) { WDTCTL=WDTPW+WDTHOLD; P1DIR&=~0X07; P1IES|=0X ...…

查看全部问答>

在skyeye1.2.4上编译并运行2.6.14内核

SkyEye是一个开源软件(OpenSourceSoftware)项目,中文名字是\"天目\"。SkyEye的目标是在通用的Linux和Windows平台上实现一个纯软件集成开发环境,模拟常见的嵌入式计算机系统(这里假定\"仿真\"和\"模拟\"的意思基本相同);可在SkyEye上运行μCL ...…

查看全部问答>

【转载】Linux中的通知链技术

转载自 http://blog.csdn.net/jjbear_ustc/archive/2009/12/22/5057009.aspx在Linux内核中,各个子系统之间有很强的相互关系,某些子系统可能对其它子系统产生的事件感兴趣。为了让某个子系统在发生某个事件时通知感兴趣的子系统,Linux内核引 ...…

查看全部问答>

求怎么样能快速看懂线路图

我是一个刚刚学习电子技术的初学者,每次看到原理图头都朦朦的,不知道从哪里看起,请教一下,谢谢…

查看全部问答>

宽带程控放大器

做个程控放大器通频带宽大于60MHz,增益大于60dB用什么芯片好??带宽增益积要非常的高??…

查看全部问答>