资料参考:
【正点原子】STM32F103开发板资料(A盘);
注意:本文主要介绍 USART 异步串行模式下的收发器。
文章目录
https://www.keil.com/dd2/stmicroelectronics/stm32f103ze/
内核:ARM Cortex-M3,72MHz
内存:64kB RAM,512kB ROM
时钟和电源:2.00V—3.60V,72 MHz
通信 :SPI、I2C、UART、I2S、CAN、USART、USB、Device
定时器/计数器/PWM :8 × 16 位定时器
模拟:2通道 12位DAC,21通道 12位ADC
I/O 和封装: -40℃—85℃,144-QFP,144-BGA
串口通信是指外设和计算机间,通过数据信号线 、地线、控制线等,按位进行传输数据的一种通讯方式,如SPI通信、USART通信、EEPROM通信等。简单讲,串口通信实现了上位机(PC)与下位机(如STM32)之间的信息交互。
上位机(PC)通过串口调试助手等实现数据的接收和发送;
下位机(STM32)通过printf()、getchar()等函数实现字符或字符串的接收和发送。
处理器与外部设备通信有两种方式:
2.1 串行通信:数据按位顺序依次传输,如8个数据位依次传输,速度慢,但占用引脚资源少
按照数据传送方向,又分为:
2.2 并行通信:数据各个位同时传输,如8个数据位同时传输,占用引脚资源慢,但速度快。
串行通信按通信的方式可分为:

STM32F103系列提供5路串口,包含3个 USART 和2个 UART 。
串口的引脚如下图所示:

数据传输的格式/通信协议
串行通信一定要有适合的通信协议。
通信协议指通信双方之间为完成信息交互所必须遵守的一种规则和约定。比如两个人约定在何时交流、用中文还是英文交流、交流什么内容。
1.起始位
当未有数据发送时,数据线处于逻辑“1”状态;先发出一个逻辑“0”信号,表示开始传输字符。
2.数据位
紧随起始位之后,数据位表示真正要发送或接收的信息,位数一般有8位或9位
3.奇偶校验位
数据位末尾可以选择是否添加奇偶校验位,用于检测数据传输是否正确
4.停止位
代表信息传输结束的标志位,可以是1位,1.5位或2位。停止位的位数越多,数据传输的速率也越慢。
5.波特率设置
波特率表示每秒钟传输码元的个数,是衡量数据传输速率的指标,单位Baud。另外有个名词叫比特率,比特率表示每秒钟传输二进制位bit的个数,单位 bit/s。
比特(bit)就是指一位信息,当用二进制表示数据时,0是一位,1也是一位信息,它是固定不变的,一个比特就代表二进制下的一位。
通常描述码元,我们会说M进制的码元。比如八进制,我们知道八进制包含0~7共八种数据,而计算机是只识别0,1两种的,我们若是想将这八种数据发送给计算机,可以用3个比特为一组的形式来表示,即000,001,…,111共八组,因而一个八进制的码元就表示携带了3个比特,这时的比特率也就是波特率的3倍。那么,一个M进制的码元,就携带log2 M个比特。
如果还不明白可以看一下这篇文章:波特、码元与比特的关系
6.波特率的计算
波特率是由USART_BRR波特率寄存器控制的,TR/Tx的波特率 = 串口时钟 / BRR的值。
一般我们先确定波特率,通过计算得到BRR的值,并将其赋给USART_BRR寄存器。如串口时钟72M,选用波特率115200,BRR的值为72M÷115200=625(16进制0x0271),BRR寄存器的值就为0x0271。
串口设置的一般步骤可以总结为如下几个步骤:
1)串口时钟使能 GPIO 时钟使能
2)串口复位
3)GPIO 端口模式设置
4)串口参数初始化
5)开启中断 并且初始化 NVIC(如果需要开启中断才需要这个步骤)
6)使能串口
7)编写中断处理函数
以正点原子的实例-串口实验对串口时钟的使用步骤进行详细分析。
程序功能是STM32通过 串口1 和上位机(PC)对话,STM32收到上位机发送的数据(字符串)后,将数据原原本本的返回给上位机。采用USART1的全双工异步串行收发模式。
在例程的usart.c中,引入了一段使程序支持printf函数的代码,直接复制使用即可。
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif
GPIO口引脚复用,需要开启复用时钟;使用串口外设也要开启时钟
串口挂载、复用时钟在APB2下面的外设
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);
库函数中,串口的复位通过USART_DeInit()实现,选择待复位的串口USART1 进行设置即可。
USART_DeInit(USART1); // 复位串口 1
PA9是发送引脚,需要发送数字量上表示为0,1的高低电平,若使用开漏输出要建立外部电路,接上拉电阻。故采用推挽输出模式,直接输出0V、3.3V的电压。
PA10是接收引脚,需要接收高低电平,设置上拉会将引脚电平限制在高电平,下拉会限制在低电平,模拟输入则将信号传输到其他外设,只有浮空输入能实现高低电平的接收。
同时,STM32复用功能对端口的配置也有以下三条标准(详见参考手册P107 8.1.4 复用功能(AF)):
//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
库函数中,使用USART_Init()对串口进行初始化
void USART_Init(USART_TypeDef* USARTx, USART_I nitTypeDef* USART_InitStruct)
第一个参数:待初始化的串口标号
第二个参数:配置串口参数的结构体指针
typedef struct
{
u32 USART_BaudRate;
u16 USART_WordLength;
u16 USART_StopBits;
u16 USART_Parity;
u16 USART_HardwareFlowControl;
u16 USART_Mode;
u16 USART_Clock;
u16 USART_CPOL;
u16 USART_CPHA;
u16 USART_LastBit;
} USART_InitTypeDef;
以上是异步模式的配置参数,同步模式还需要配置7~10。
USART_Clock:时钟使能
USART_CPOL:时钟输出极性
USART_CPHA:时钟输出相位
USART_LastBit:最后一位数据的时钟脉冲输出方式
详见固件库使用手册P346 Table707

对串口1 初始化范例:
//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
如果要使用中断服务,便要对中断优先级进行配置,如果只有一个中断服务,随意配置顺序即可。
//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_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
在移植代码或自己编写的过程中,都不能忘记开启串口功能
USART_Cmd(USART1, ENABLE); //使能串口1
其中,SYSTEM_SUPPORT_OS 的作用是实现STM32和操作系统共用STM32内部SysTick定时器,使二者拥有相同的时钟周期。
中断服务过程:
USART_GetITStatus():用于获取接收状态,即是否开启接收
USART_ReceiveData():获得当前接收到的数据,数据长度8位
USART_RX_STA:接收的状态标志,数据长度16位,第14位、15位对接收结束的判断,第0~13位是接收数据的长度。
USART_RX_BUF[]:数据存储栈,将接受到的数据依次存储
当 USART_GetITStatus读取到起始位时触发更新请求,代表数据开始接收。用USART_ReceiveData()读取当前接收的数据,如果不是0x0d,则将数据保存到BUF中,如果连续接收到了0x0d(回车),0x0a(换行)表示数据接收结束,STA的第15位置1,在主程序main()中执行相关操作并等待开启下一次的数据接收(当STA=0时表示数据可以被接收存储),详细流程图如下。

扩展:回车、换行的区别
回车 \r 本义是光标重新回到本行开头,
换行 \n 本义是光标往下一行(不一定到下一行行首)
void USART1_IRQHandler(void)//串口1中断服务程序
{
u8 Res;
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntEnter(); //进入中断服务函数
#endif
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;//接收错误,重新开始
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;//接收数据错误,重新开始接收
}
}
}
}
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntExit();
#endif
}
我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我正在使用ruby1.9解析以下带有MacRoman字符的csv文件#encoding:ISO-8859-1#csv_parse.csvName,main-dialogue"Marceu","Giveittohimóhe,hiswife."我做了以下解析。require'csv'input_string=File.read("../csv_parse.rb").force_encoding("ISO-8859-1").encode("UTF-8")#=>"Name,main-dialogue\r\n\"Marceu\",\"Giveittohim\x97he,hiswife.\"\
我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
简而言之错误:NOTE:Gem::SourceIndex#add_specisdeprecated,useSpecification.add_spec.Itwillberemovedonorafter2011-11-01.Gem::SourceIndex#add_speccalledfrom/opt/local/lib/ruby/site_ruby/1.8/rubygems/source_index.rb:91./opt/local/lib/ruby/gems/1.8/gems/rails-2.3.8/lib/rails/gem_dependency.rb:275:in`==':und
文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,
MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO
在VMware16.2.4安装Ubuntu一、安装VMware1.打开VMwareWorkstationPro官网,点击即可进入。2.进入后向下滑动找到Workstation16ProforWindows,点击立即下载。3.下载完成,文件大小615MB,如下图:4.鼠标右击,以管理员身份运行。5.点击下一步6.勾选条款,点击下一步7.先勾选,再点击下一步8.去掉勾选,点击下一步9.点击下一步10.点击安装11.点击许可证12.在百度上搜索VM16许可证,复制填入,然后点击输入即可,亲测有效。13.点击完成14.重启系统,点击是15.双击VMwareWorkstationPro图标,进入虚拟机主
我正在使用ruby2.1.0我有一个json文件。例如:test.json{"item":[{"apple":1},{"banana":2}]}用YAML.load加载这个文件安全吗?YAML.load(File.read('test.json'))我正在尝试加载一个json或yaml格式的文件。 最佳答案 YAML可以加载JSONYAML.load('{"something":"test","other":4}')=>{"something"=>"test","other"=>4}JSON将无法加载YAML。JSON.load("
我想用Nokogiri解析HTML页面。页面的一部分有一个表,它没有使用任何特定的ID。是否可以提取如下内容:Today,3,455,34Today,1,1300,3664Today,10,100000,3444,Yesterday,3454,5656,3Yesterday,3545,1000,10Yesterday,3411,36223,15来自这个HTML:TodayYesterdayQntySizeLengthLengthSizeQnty345534345456563113003664354510001010100000344434113622315