tag 标签: uart

相关帖子
相关博文
  • 热度 3
    2022-5-20 07:14
    718 次阅读|
    2 个评论
    UART介绍 简介 通用异步收发器(Universal Asynchronous Receiver/Transmitter),既UART 时序 根据时序图可以了解到: 1. uart在空闲的时候是高电平 2. 当突变为低电平或者有一个下降沿,则是告知接收方uart要传数据了 3. 这里实现为8bit数据传输,当数据传输完成,在1或1.5或2个时钟周期内将传输线拉高,表示停止传输 UART之RX实现 状态转移图 源代码 状态转移实现 always @(*) begin case (state) IDLE:begin if(rx_pin==1'b0) nextstate <= START; else nextstate <= IDLE; end START:begin if(cycle_cnt == CYCLE_CNT_MAX-1)//当一个BIT周期后,接收数据 nextstate <= REC_BYTE; else nextstate <= START; end REC_BYTE:begin if(cycle_cnt == CYCLE_CNT_MAX-1&&bit_cnt==4'd7)//8位数据接收完成,跳转到检测停止位 nextstate <= STOP; else nextstate <= REC_BYTE; end STOP:begin if(cycle_cnt == CYCLE_CNT_MAX/2-1)//半个bit周期,检测到停止位,将接收的数据发送到其他模块 nextstate <= DATA; else nextstate <= STOP; end DATA:begin //一个时钟周期后模块进入空闲态 nextstate <= IDLE; end endcase end 时序描述 //周期计数,既系统时钟内每个BIT所需要的周期数,通过波特率可计算出最大计数值 always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) cycle_cnt <= 16'd0; else if(state == REC_BYTE && (cycle_cnt == CYCLE_CNT_MAX-1||nextstate != state))//只需要在开始接收数据时开始计数,所以前提条件是状态在REC_BYTE,而且如果计数值达到最大值或者在状态跳转的时候都需要将计数值清零 cycle_cnt <= 16'd0; else cycle_cnt <= cycle_cnt + 16'd1; end //位计数,以确认接收的位数 always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) bit_cnt <= 4'd0; else if(state == REC_BYTE) //仅在接收数据状态时进行位计数 if(cycle_cnt == CYCLE_CNT_MAX-1) bit_cnt <= bit_cnt + 4'd1; else bit_cnt <= bit_cnt; else bit_cnt <= 4'd0; end //数据接收 always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) rx_data_r <= 8'd0; else if(state == REC_BYTE && cycle_cnt == CYCLE_CNT_MAX/2-1) rx_data_r <= tx_pin; else rx_data_r <= rx_data_r; end //将数据传输给其他模块 always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) rx_data <= 8'd0; else if(state == STOP || nextstate != state) rx_data <= rx_data_r; end //接收完成标志 always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) rx_done <= 1'b0; else if(state == STOP ) rx_done <= 1'b1; else rx_done <= 1'b0; end UART之TX实现 实现TX就不用三段式状态机这么麻烦了,直接用序列机完全就可以了 模块使能或者说发送请求 always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) tx_en <= 1'b0; else if(tx_req_pos == 1'b1) tx_en <= 1'b1; else if(bit_cnt == 4'd11) tx_en <= 1'b0; else tx_en <= tx_en; end 周期计数、位计数 //周期计数,既系统时钟内每个BIT所需要的周期数,通过波特率可计算出最大计数值 always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) cycle_cnt <= 16'd0; else if(tx_en) if(cycle_cnt == CYCLE_CNT_MAX-1)//当模块使能时,开始计数,计数到最大值再从零开始 cycle_cnt <= 16'd0; else cycle_cnt <= cycle_cnt + 16'd1; else cycle_cnt <= 16'd0; end //位计数 always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) bit_cnt <= 4'd0; else if(cycle_cnt == 16'd1) //如果计数到最大值bit_cnt累加的话,那么数据发送需要多等一个bit周期 bit_cnt <= bit_cnt + 4'd1; else if(bit_cnt == 4'd11) bit_cnt <= 4'd0; else bit_cnt <= bit_cnt; end 发送数据 always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) tx_pin_r <= 1'b1; else case(bit_cnt) 0:tx_pin_r <= 1'b1; //这里需要避免bit_cnt=0的时候发送起始位,因为当复位的时候bit_cnt的值是零的,会在复位时就已经发出了起始位,而导致接收端的误判和发送的时序紊乱 1:tx_pin_r <= START_BIT; 2:tx_pin_r <= send_data ; 3:tx_pin_r <= send_data ; 4:tx_pin_r <= send_data ; 5:tx_pin_r <= send_data ; 6:tx_pin_r <= send_data ; 7:tx_pin_r <= send_data ; 8:tx_pin_r <= send_data ; 9:tx_pin_r <= send_data ; 10:tx_pin_r <= STOP_BIT; default: tx_pin_r <= 1'b1; end
  • 2022-5-13 14:00
    0 个评论
    智慧灯杆是集照明、视频监控、交通管理、环境监测、通信等多功能于一体的新型信息基础设施,是构建新型智慧城市的重要载体。 5G 的普及,为智慧灯杆发展创造了新机遇,作为新一代城市信息基础设施的智慧灯杆,与“新基建”中的不少领域相关,比如 5G 基站、新能源汽车充电桩和车联网等。 智慧灯杆作为智慧城市的一个重要组成部分和重要入口,是智慧城市信息化建设天然的搭载平台。可以说在智慧城市建设中, 5G 是不可或缺的主角,而智慧杆将如同末梢神经元般存在。 智慧灯杆物联网领域技术复杂,涉及网络通信、云计算、电力、人工智能等专业种类, 内含通信、视频监控、照明、环境监测、交通、信息发布、能源、运维等多个子系统。 产品设计需考虑各系统的兼容、协同,灯杆的弹性扩展以及产品服务的升级、回退、扩容等等,整体复杂度高,专业要求高。 国内某交通信号系统供应商,计划研发一款集路灯照明控制、 LED 显示、 WiFi 热点、视频监控、广告屏、环境监测、紧急呼叫、汽车充电桩、 4G/5G 微型基站等多种功能于一体的智慧灯杆产品。 智慧灯杆是多种设备设施和技术的综合体,从功能硬件方面看,通信杆、路灯、交通监控、安防监控、环境监测 、路侧广告牌等多种主要功能设施于一身。 如此多的功能设施需要互联互通,其核心是通信控制设备,因此作为主控的核心板要求算力高,性能强,且需支持多路网口和 UART ,支持 5G 和 OpenWrt 系统。 综合评估后,飞凌推荐用户选用 FET1046A-C 核心板进行产品开发。 该核心板有丰富的接口资源,包括原生 8 路网口( 最大支持 2 路 10GbE 接口), 4 路 UART , 3 路 USB3.0 等,基于该款核心板开发的通信控制设备通过光纤接口,可以实现 4G/5G 微基站和无线高速 WiFi 覆盖。 RJ45 网口方便智慧灯杆快速接入 LED 信息发布屏、高清摄像头、 WiFi 等杆载设备。 RS485 可用于环境传感器、环境监测单元、单灯控制器等设备串口通信。 FET1046A-C 核心板搭载 4 核 ARM Cortex-A72 处理器, 1.8GHz 主频, 2GB DDR4 RAM ,数据吞吐量最高可达 2.1GT/s ,高达 45000 CoreMark 分值的性能,满足执行多种通信形式、通信速率和网络协议的需求,支持对不同智慧灯杆通信方式的选用,可实现智慧灯杆系统的一站式搭建。
  • 热度 16
    2021-3-21 21:57
    1362 次阅读|
    0 个评论
    FreeModbus 从站设计( 8 ) - 用 HAL 库函数理清 Modbus 的数据收发流程 关键词: FreeModbus STM32F103 CubeMX HAL 库 1. 基本框图 如图 1 所示, HAL 库的函数中,与 Freemodbus 协议栈相关的,主要是定时器和串口的操作部分。孔丙火(微信公众号:孔丙火)认为,可以这样简单描述:在协议栈完成初始化后,就将串口( RS485 )设置为接收状态,等待主站的数据,当接收到主站的一个字节的数据后,开启定时器,在 3.5 个字符周期内如果接收到了第二个字节的数据,则将定时器清零重新开始计时,若果 3.5 个字符周期内没有接收到新的字节,则认为一帧数据接收完毕,开始处理数据,并相应地发送回复数据,回复数据也是逐个字节进行发送的。 图 1 2. 接口函数 2.1 vMBPortSerialEnable() 这个函数定义在 portserial.c 中,所有的调用均在 mbrtu.c 和 mbascii.c 中,由于这里我们只实现 RTU ,因此,只关注 mbrtu.c 中调用。在 mbrtu.c 中,共有 5 处调用此函数,分别在 eMBRTUStop() 、 eMBRTUStop() 、 eMBRTUSend() 、 xMBRTUTransmitFSM() 中,这个函数的作用,就是使能或禁止串口的接收或发送中断,状态的转换是根据协议栈的状态机来进行的,孔丙火(微信公众号:孔丙火)认为,看一看 Modbus 协议文本中的状态机图,有助于深入理解协议的运行。基于以上理解, vMBPortSerialEnable() 函数的代码如下: 图2 HAL_UART_Receive_IT(&huart2,&ucUsrUart2Rxbuf,1) ,这个函数是 HAL 库函数,孔丙火(微信公众号:孔丙火)认为,可以这样理解,以中断方式通过串口 2 接收一个字节的数据,存在变量 ucUsrUart2Rxbuf 中,这里取得是 ucUsrUart2Rxbuf 的地址指针。 __HAL_UART_DISABLE_IT(&huart2,UART_IT_RXNE) 这个函数是禁止接收中断。在这个函数下面,写了这样一个语句: huart2.RxState = HAL_UART_STATE_READY; 同样的,在发送部分有: huart2.gState = HAL_UART_STATE_READY; 下面简单阐述一下作用。 最初开始弄 FreeModbus 移植的时候,在网上查阅了一些资料,有的是基于 HAL 库的,有的不是,有的是交叉使用,由于学习了一段时间的 HAL 库,感觉其还是比较易用的,孔丙火(微信公众号:孔丙火)就想着能不能完全用 HAL 库的函数来实现的 FreeModbus 移植,于是就有后来的实践。 加这样一个语句,是为了解决在实际调试过程中碰到的问题。刚开始调试的时候发现,单片机作为从站,只能接收一帧数据,第 2 帧以后的数据,不再有反应。由于是调用 HAL_UART_Receive_IT(&huart2,&ucUsrUart2Rxbuf,1); 来使能接收非空中断,调用 __HAL_UART_DISABLE_IT(&huart2,UART_IT_RXNE); 来禁用接收非空中断,那就要从 HAL_UART_Receive_IT() 这个函数开始说起,在以前的文章中,孔丙火(微信公众号:孔丙火)说过,这个函数本质上是一个配置函数,配置好接收缓冲区的指针和一次接收的字节数,把这个函数贴出来看一下: 图 3 可以看出,这个函数的主要功能就是设置接收缓存的指针和字节数,然后使能接收非空中断,但在一开始有一个条件判断, RxState ,这其实是对串口接收状态的一个管理,当串口中断方式接收已经被配置过了、接收过程还没有完成的时候,是不能再次配置的,防止接收数据出错。 在这个函数中,配置完了以后,就将 RxState 的状态置为了 HAL_UART_STATE_BUSY_RX ,这个状态是在串口中断处理函数中完成数据接收后,重新设为 HAL_UART_STATE_READY 。在本文的程序中,当从站接收数据完成后(定时器动作,表示一个完整的帧接收完成),协议栈需要将接收非空中断禁掉,但这之前串口仍处于接收状态(调用过 HAL_UART_Receive_IT(&huart2,&ucUsrUart2Rxbuf,1) ),因此 RxState 的状态是 HAL_UART_STATE_BUSY_RX ,这样在下次转为接收时,无法 HAL_UART_Receive_IT ()来进行配置,就是由于上面提到的图 3 红框中的条件判断。因此,在发现此问题后,加了这个语句,确保后续能正常接收,并且这样可以不用修改 HAL 库本身的函数。发送部分是同样的道理。 下一篇精彩继续。 文章在公众号( 孔丙火 )同步推出,欢迎查看更多系列文章。 单片机、 PLC 、嵌入式软硬件的设计经验分享,秉承“点点滴滴皆智慧”的理念,以实际项目为单元阐述知识点,一起分享,共同交流。
  • 热度 4
    2021-3-20 13:07
    1294 次阅读|
    0 个评论
    STM32F103、FreeModbus从站设计(7)-如何让RTU的定时器正常工作起来 关键词:Modbus FreeModbus STM32F103C8T6 CubeMX 移植 1.基本原理 在CubeMX工程配置中,已经将定时器2(TIM2)的时钟周期(可以理解为心跳一下)设为50us,Counter Period(产生中断)暂时设为了35,也就是说50us×35=1750us产生一次中断,这个时间就是判断RTU中帧间隔的标准。但在Freemodbus协议栈(遵循Modbus国标)中,这个时间不是固定的,在波特率小于19200bps时,需要具体计算这个时间,当波特率大于或等于19200bps时,这个时间固定为1750us,如图1所示。因此,孔丙火(微信公众号:孔丙火)认为,把它设为固定值是不方便的,当波特率修改的时候,还要单独修改此参数,可以用一个变量来设置此参数,变量的值随波特率而改变,这也有利于后期通信参数修改的程序。 图1 2.代码修改 在mbrtu.c中,有一个eMBRTUInit()函数,在协议栈初始化的时候被调用,在图2中可以看出,t3.5的计算方法。计算完了以后,调用xMBPortTimersInit()这个函数,这个函数在porttimer.c中,如果参数是写死的,CubeMX本身已经生成了定时器的初始化函数,这个函数是不用写的,直接返回True就行了。孔丙火(微信公众号:孔丙火)这里需要根据这个计算结果修改通信参数,因此要用一下这个函数。 图2 在modbus_app.c中定义一个全局变量uint16_t usUsrTimeOutCount;//modbus定时器计数周期(50us的个数),在xMBPortTimersInit()中,将usTimerT35_50us的值赋给usUsrTimeOutCount,然后在函数MX_TIM2_Init()中用这个值对TIM2进行初始化。xMBPortTimersInit()的代码如图3所示。MX_TIM2_Init()修改后的代码如图4所示。孔丙火(微信公众号:孔丙火)提醒,这样修改之后,在main.c中MX_TIM2_Init()这个函数的调用必须在eMBInit( MB_RTU, ucUsrSlaveAddress, 1, ulUsrBaudRate, eUsrParity );之后。在main.c中的初始化阶段的调用关系如图5所示。 图3 图4 图5 文章在公众号( 孔丙火 )同步推出,欢迎查看更多系列文章。 单片机、ARM、现场总线、PLC、嵌入式软硬件的设计经验分享,秉承“点点滴滴皆智慧”的理念,以实际项目为单元阐述知识点,一起分享,共同交流。
  • 热度 4
    2021-3-18 21:34
    1477 次阅读|
    1 个评论
    STM32F103、FreeModbus从站设计(6)-让串口和Modbus初始化的参数同步起来
    FreeModbus从站设计(6)-让串口和Modbus初始化的参数同步起来 关键词:Modbus FreeModbus STM32F103C8T6 CubeMX 移植 1.基本原理 在这一篇文章中,孔丙火(微信公众号:孔丙火)主要介绍协议栈初始化,以及与串口相关的代码修改。串口的初始化函数是CubeMx自动生成的,其波特率等参数最好与freemodbus协议栈初始化参数保持一致,这样方便程序的维护和功能扩展,例如,当需要修改通信的波特率的时候,只需要修改一处就可以了,不用先修改串口的初始化参数,再修改协议栈的参数,这是一个实战例程与纯教程的区别。 本文的总体思路:创建一些全局变量,用于存储通信参数,串口、定时器、Freemodbus协议栈的初始化,都采用这些变量,需要更改的时候,只需改变这些变量的值即可。另外,这些通信参数后续计划存储在flash里面,定时器的参数会随波特率的不同而改变,这些在后续的文章中会阐述。 2.代码修改 2.1关于assert的说明 在Freemodbus的协议栈中,很多地方使用了assert,因此,在Cube生成工程的时候,孔丙火(微信公众号:孔丙火)认为,最好使能enable assert选项,如果没有使能,编译的时候可能出错,这个时候可以勾掉MicroLIB,因为MicroLIB不支持assert。 2.2宏的修改 (1)MB_RTU_ENABLED、MB_ASCII_ENABLED、MB_TCP_ENABLED 这里我们实现的是RTU,只需将MB_RTU_ENABLED定义为1即可。孔丙火(微信公众号:孔丙火)顺便说一句,在基于串口的Modbus中,RTU是必选项,ASCII是可选项,也就是说RTU是必须要实现的。 (2)ENTER_CRITICAL_SECTION( )和EXIT_CRITICAL_SECTION( ) 这两个函数是进入和退出关键进程,这里定义为__disable_irq()和__enable_irq(),后边两个函数是ARM内核函数,作用是禁止中断和使能中断。 #define ENTER_CRITICAL_SECTION( ) __disable_irq() #define EXIT_CRITICAL_SECTION( ) __enable_irq() 2.3协议栈初始化 在main.c文件中的while(1)之前,调用eMBInit( )完成协议栈的初始化,然后调用eMBEnable( )使能协议栈。eMBInit( )的定义如下:eMBErrorCode eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity ),共5个参数,eMode模式:RTU或ASCII,ucSlaveAddress从站地址,ucPort单片机的串口号,ulBaudRate波特率,eParity校验方式,我们这里串口参数是在Cube生产的函数里初始化的,而且串口接收或发送的数据HAL库函数已经处理好了,因此,孔丙火(微信公众号:孔丙火)认为,有用的参数只有eMode,ucSlaveAddress,ulBaudRate,其余两个设错了,其实也没影响(仅限于本文的实现方法)。 在modbus_app.c中定义如下全局变量: uint8_t ucUsrSlaveAddress;//从站地址 uint32_t ulUsrBaudRate;//波特率 eMBParity eUsrParity;//校验方式 uint8_t ucUsrStopBits;//停止位 新建modbus_app.h,在其中进行extern定义,一般在其他c文件中使用。 最终,在main.c中做如下调用: eMBInit( MB_RTU, ucUsrSlaveAddress, 1, ulUsrBaudRate, eUsrParity ); 2.4串口参数初始化 在MX_USART2_UART_Init()中修改,此函数是Cube自动生成的,在usart.c中。 最终函数如下: huart2.Instance = USART2; huart2.Init.BaudRate = ulUsrBaudRate; if(eUsrParity == MB_PAR_NONE)huart2.Init.WordLength = UART_WORDLENGTH_8B; else huart2.Init.WordLength = UART_WORDLENGTH_9B; if(ucUsrStopBits == 1)huart2.Init.StopBits = UART_STOPBITS_1; else if(ucUsrStopBits == 2)huart2.Init.StopBits = UART_STOPBITS_2; if(eUsrParity == MB_PAR_NONE)huart2.Init.Parity = UART_PARITY_NONE; else if(eUsrParity == MB_PAR_EVEN)huart2.Init.Parity = UART_PARITY_EVEN; else if(eUsrParity == MB_PAR_ODD)huart2.Init.Parity = UART_PARITY_ODD; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart2) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); } 波特率使用了全局变量,单片机的数据位个数和停止位根据全局变量确定。 3.总结 这种方法让让串口和Modbus初始化的参数同步起来了,也方便通信参数的在线修改,更接近实战。 ———————————————— 文章在微信公众号同步推出: 孔丙火 ,关注欣赏更多系列文章。-----单片机、ARM、现场总线、PLC、嵌入式软硬件的设计经验分享,秉承“点点滴滴皆智慧”的理念,以实际项目为单元阐述知识点,一起分享,共同交流。
相关资源