低容量STM8 Modbus协议移植与裁剪

发布者:Lianai最新更新时间:2019-11-29 来源: eefocus关键字:低容量  STM8  Modbus协议  移植与裁剪 手机看文章 扫描二维码
随时随地手机看文章

1.freeModbus开源包的下载


一般STM8用的开发环境是IAR,所以这里我们就讲在IAR下移植FreeModbus, 

下载freemodbus-v1.5.0,官方下载地址http://www.freemodbus.org/找到Download 

这里写图片描述

点击freemodbus-v1.5.zip即可下载。 

这里写图片描述

2.freeModbus开源包的简介


打开文件夹的目录如下 

这里写图片描述

然后我们打开主要的文件夹modbus 

这里写图片描述

我们可以看到有ascii、functions、include、rtu、tcp以及mb.c源文件 

Ascii Modbus ascii通信方式相关文件夹, 

Rtu Modbus Rtu 通信方式和CRC校验相关文件夹, 

Tcp Modbus Tcp通信方式相关文件夹, 

Functions Modbus 功能函数相关, 

Include Modbus 主要头文件都在这里, 

Mb.c Modbus 最主要的头文件 ,包含初始化函数,poll函数等等 

这些都是移植不需要修改的。涉及到Modbus的协议层,属于硬件无关层,所以都抽象出来,给下面提够统一的软接口就行了。 

下面我们看看需要移植的部分,这部分在demo里面有很多平台的实例 

这里写图片描述

我们打开看看,包含一些比较常见的平台,比如Atmel公司的ARM处理器,AVR单片机 

TI的Msp430单片机,以及Liunx,Windows操作系统下的平台等等,但是没有我们想要的STM8处理器的接口。 

这里写图片描述

但我们可以依葫芦画瓢,移植到其他平台,这里我们打开AVR下面目录看看,主要看port文件夹下面的文件,这些都是我们要移植的,包括串口驱动相关的portserial.c,定时器驱动相关的porttimer.c以及总中断相关的port.c(这个目录没有,我们添加进去)等等。 

这里写图片描述

3.导入工程


分析完FreeModbus文件夹后,我们开始移植工作 

首先在IAR里面建立工程,将Modbus文件夹拷贝到工程下面目录,将port文件夹拷贝到Modbus下面。如下图所示 

这里写图片描述

Port在modbus文件夹下面目录 

这里写图片描述

然后在工程中建立新的Modbus组 

这里写图片描述

然后在工程的options菜单下面添加头文件路径,其中PROJDIRPROJDIR为当前工程目录 

 这里写图片描述 

在上述步骤完成后,我们开始改写硬件驱动,主要分三大块


3.1硬件设备驱动移植


3.1.1串口分析与移植


打开portserial.c对下面函数进行功能完善 

Void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ); 

BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity ); 

BOOL xMBPortSerialPutByte( CHAR ucByte ); 

BOOL xMBPortSerialGetByte( CHAR * pucByte ); 

以及串口接收中断服务函数,串口发送完成中断服务函数。 

首先是Void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable );这个函数主要是串口发送和接收的中断使能。函数的第一个参数为接收使能布尔型参数,第二个为发送使能布尔型参数。当xRxEnable为真时,打开串口接收中断,为假时,关闭串口接收中断;同样,发送也是一样。为了节约使用低容量处理器有限的存储空间,我们接下来的单片机相关资源的操作都采用寄存器的方式,那么我们先看STM8寄存器手册, 

这里写图片描述 
这里写图片描述 

可以很清楚发现UART1_CR2寄存器的第5位和第6位是控制串口接收中断和串口的发送完成中断,这里我们只需要将此两位置高和置低即可实现相应的使能和禁止功能。所以这里我们的代码


void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ) //控制串口的收发中断

{

    if(xRxEnable==TRUE)

    {

            UART1_CR2|=(1<<5);

    }

    else

    {

            UART1_CR2&=~(1<<5);

    }


    if(xTxEnable==TRUE)

    {

            UART1_CR2|=(1<<6);

    }

    else

    {

           UART1_CR2&=~(1<<6);

    }

}


然后就是BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )函数的实现。 

看函数的参数,第一个是端口,第二个是波特率,第三个是数据位,第四个是校验方式 

因为本实验移植采用的STM8103F3C6,只带一个Uart串口,所以第一个参数就不用去实现了。 

第二个参数是波特率,我们可以看单片机的寄存器手册 

这里写图片描述 
这里写图片描述

波特率的设置是通过时钟与波特率分频的比值决定的。通过查看寄存器,很容易发现,波特率比率由两个8位寄存器构成,但是寄存器的排列方式不是按照一般数据存储方式。UART_BRR1为波特率分频系数的4至11位数据,UART_BRR2的低四位为波特率分频系数的第四位,而UART_BRR2的高四位为波特率分频系数的12至15位。所以这里我们需要进行转换 

UART1_BRR2 = div & 0x0f; 

UART1_BRR2 |= ((div & 0xf000) >> 8); 

UART1_BRR1 = ((div & 0x0ff0) >> 4); 

Div为波特率分频系数 

那么ulBaudRate = f/Div;(f为系统的时钟)。 

接着我们分析第三个参数,数据位,如下图,UART_CR1寄存器第4位,当为0时,是一个起始位,8个 

这里写图片描述

数据位,n个停止位,这里的停止位需要通过UART_CR3寄存器设定;当为1时,是一个起始位,9个数据位,一个停止位。然后我们去分析UART_CR3寄存器 

这里写图片描述 

通过查看寄存器,发现停止位有1,1.5,2等3种情况。一般应用下,我们都是用的一个停止位,故这里可以直接保留默认值就行了。所以代码可以


if(ucDataBits == 8)

{

   UART1_CR1&=~(1<<4);

}else

{

   UART1_CR1|=(1<<4);

}


最后我们看看串口奇偶校验参数实现,查看寄存器UART1_CR1 

这里写图片描述

第1位 PS 奇偶检验选择 当为0时,偶检验;当为1时奇校验。 

第2位 奇偶校验使能位 当为0时,禁用校验;当为1时使能校验。 

这些代码之前注意参数eMBParity eParity,位枚举型变量,看看定义 

这里写图片描述 

所以代码为


switch(eParity)

{

    case MB_PAR_NONE:

        UART1_CR1&=~(1<<2);

        break;

    case MB_PAR_ODD: 

        UART1_CR1|=(1<<2);

        UART1_CR1|=(1<<1);

        break;

    case MB_PAR_EVEN:

        UART1_CR1|=(1<<2);

        UART1_CR1&=~(1<<1);

        break;

    default:break;

}


至此串口的主要功能基本实现差不多,还有BOOL xMBPortSerialPutByte( CHAR ucByte ); 

BOOL xMBPortSerialGetByte( CHAR * pucByte )主要是数据的写入和读取,也就是直接读取和写入UART_DR就行了。


BOOL xMBPortSerialPutByte( CHAR ucByte )

{

    while((UART1_SR & 0x80)==0x00);

      UART1_DR=ucByte;

    return TRUE;

}


// 串口收

BOOL xMBPortSerialGetByte( CHAR * pucByte )

{


    *pucByte = UART1_DR;

    return TRUE;

}


3.1.2 中断分析与移植


除此之外,我们还要对两个中断服务函数进行处理。


/* Create an interrupt handler for the transmit buffer empty interrupt

 * (or an equivalent) for your target processor. This function should then

 * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that

 * a new character can be sent. The protocol stack will then call 

 * xMBPortSerialPutByte( ) to send the character.

 */

 void prvvUARTTxReadyISR( void )

{

    pxMBFrameCBTransmitterEmpty(  );

}


/* Create an interrupt handler for the receive interrupt for your target

 * processor. This function should then call pxMBFrameCBByteReceived( ). The

 * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the

 * character.

 */

void prvvUARTRxISR( void )

{

     pxMBFrameCBByteReceived(  );

}


//将收到的数据再发送出去

#pragma vector= UART1_R_RXNE_vector

__interrupt void UART1_R_RXNE_IRQHandler(void)

{


      if(UART1_SR&(1<<3))

      {

       // UART1_SR&=~(1<<3);

      }

      else

      {

        prvvUARTRxISR();//接受中断


        //UART1_SR&=~(1<<5);

      }


      return;

}


//将收到的数据再发送出去

#pragma vector= UART1_T_TC_vector

__interrupt void UART1_T_TC_IRQHandler(void)

{


      prvvUARTTxReadyISR();//发送完成中断

      //UART1_SR&=~(1<<6);


      return;

}


3.1.3 定时器分析与移植


下面就开始定时器的移植,定时器的寄存器功能设置和上面讲解的串口寄存器设置类似,所以下面只适当注释下代码,相信大家很容易看懂。定时器的移植主要是对porttimer.c中 

BOOL xMBPortTimersInit( USHORT usTim1Timerout50us ); 

void vMBPortTimersEnable( ); 

void vMBPortTimersDisable( ); 

prvvTIMERExpiredISR( );函数的回调 

xMBPortTimersInit( USHORT usTim1Timerout50us );主要是50us时基,用于产生和判断1.5-3.5个字符时间,作为产生和判断数据帧的结束标准。那么,产生50us时基我们可以采用定时器分频做到,首先1/50us转换成频率是20kHz,即定时器的计数器频率为20kHz时,计数器每增一或减一,所需要时间就是50us.所以我们分频系数为8MHz/20KHz = 400;而 USHORT usTim1Timerout50us参数为50us的个数,


    /* Set the Prescaler value */

    TIM1_PSCRH = (unsigned char)((period >> 8)&0xff);

    TIM1_PSCRL = (unsigned char)(period&0xff);


    usTim1Timerout50us = usTim1Timerout50us-1;


    TIM1_ARRH = (unsigned char)((usTim1Timerout50us >> 8)&0xff);

    TIM1_ARRL = (unsigned char)(usTim1Timerout50us&0xff);


    /* Set the Repetition Counter value */

    TIM1_RCR = 0;

    /* Set the ARPE Bit */

    TIM1_CR1 |= MASK_TIM1_CR1_ARPE;

    /* Enable the Interrupt Upmode sources */

    TIM1_IER |= 0x01;

    /* set or Reset the CEN Bit */

    TIM1_CR1 |= MASK_TIM1_CR1_CEN;



 那么 xMBPortTimersInit( USHORT  usTim1Timerout50us )函数实现为:


    unsigned int period = 400-1;//分频系数

    TIM1_PSCRH = (unsigned char)((period >> 8)&0xff);

    TIM1_PSCRL = (unsigned char)(period&0xff);


    usTim1Timerout50us = usTim1Timerout50us-1;


    TIM1_ARRH = (unsigned char)((usTim1Timerout50us >> 8)&0xff);

    TIM1_ARRL = (unsigned char)(usTim1Timerout50us&0xff);




    TIM1_RCR = 0;

    TIM1_CR1 |= MASK_TIM1_CR1_ARPE;

    TIM1_IER |= 0x01;//使能中断

    TIM1_CR1 |= MASK_TIM1_CR1_CEN;//使能计数器


然后就是实现vMBPortTimersEnable( ),vMBPortTimersDisable( );打开和关闭时钟


void vMBPortTimersEnable() //打开时钟

{

        TIM1_SR1 &= ~(1<<0);//清除标志

        TIM1_IER |= (1<<0);//使能中断

        TIM1_CNTRH = 0x00;

        TIM1_CNTRL = 0x00;  //计数器清零

        TIM1_CR1 |= MASK_TIM1_CR1_CEN;//使能定时器

}


void vMBPortTimersDisable() //关闭时钟

{

      TIM1_CR1 &= ~MASK_TIM1_CR1_CEN;//关闭定时器

      TIM1_CNTRH = 0x00;

      TIM1_CNTRL = 0x00;  //计数器清零

      TIM1_IER &= ~(1<<0);//关闭中断

      TIM1_SR1 &= ~(1<<0);//清除标志

}


以及在中断服务函数中添加,溢出中断中调用prvvTIMERExpiredISR( )函数进行协议处理


#pragma vector=TIM1_OVR_UIF_vector

__interrupt void TIM1_UPD_OVF_TRG_BRK_IRQHandler(void)

{

  prvvTIMERExpiredISR( );

  TIM1_SR1 &= ~(1<<0);//清除标志

}


最后还要实现 

void EXIT_CRITICAL_SECTION(void)//退出超临界 开总中断 

void ENTER_CRITICAL_SECTION(void)//进入超临界 关总中断


void ENTER_CRITICAL_SECTION(void)//进入超临界 关总中断

{

        asm("sim");

}


void EXIT_CRITICAL_SECTION(void)//退出超临界 开总中断

{

       asm("rim");

}


别忘了加入头文件#include “intrinsics.h”


到这里,整个Modbus的移植算是完成了。


3.2 api功能的实现


但在这里,没有读写寄存器,线圈的相关操作函数,我们可以根据自己的需求进行添加和裁剪。 

其中声明的函数:


eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs );

eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode );

eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode );

eMBErrorCode eMBRegDiscreteCB(UCHAR* pucRegBuffer,USHORT usAddress,USHORT usNDiscrete );


这里我们测试,就实现eMBRegHoldingCB保持寄存器的功能函数


eMBErrorCode

eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )

{

    eMBErrorCode    eStatus = MB_ENOERR;

    USHORT          iRegIndex;

    USHORT *        pusRegHoldingBuf;

    USHORT          REG_HOLDING_START;

    USHORT          REG_HOLDING_NREGS;

    USHORT          usRegHoldStart;


    pusRegHoldingBuf = usSRegHoldBuf;

[1] [2]
关键字:低容量  STM8  Modbus协议  移植与裁剪 引用地址:低容量STM8 Modbus协议移植与裁剪

上一篇:STM32设置为I2C从机
下一篇:STM8S003单片机串口接收与ADC设置冲突

推荐阅读最新更新时间:2024-11-02 16:22

STM8定时器TIM1-TIM6使用详解实验程序
STM8定时器概述 STM8S提供三种类型的 TIM定时器:高级控制型(TIM1)、通用型(TIM2/TIM3/TIM5)和基本型定时器(TIM4/TIM6)。它们虽有不同功能但都基于共同的架构。此共同的架构使得采用各个定时器来设计应用变得非常容易与方便(相同的寄存器映射,相同的基本功能)。 16位高级控制定时器(TIM1) ● 16位向上、向下、向上/下自动装载计数器 ● 允许在指定数目的计数器周期之后更新定时器寄存器的重复计数器 ● 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65535之间的任意数值 ● 同步电路,用于使用外部信号控制定时器以及定时器互联 (某些型号的芯片没有定时器互联功能) ●
[单片机]
<font color='red'>STM8</font>定时器TIM1-TIM6使用详解<font color='red'>与</font>实验程序
Modbus 通信协议在分布式控制系统中的应用
一、引 言   现代工业的迅速发展,不断促进着自控技术及设备创新的日新月异。当前,DCS、IPC、PLC及智能仪表已广泛应用到工厂现场生产控制系统当中,并发展到由上述设备相互协同、共同面向整个生产过程的分布式工业自动控制系统。在此系统中,现场通信技术堪称关键。但由于开始没有统一的通信协议标准,各厂商自控产品通信协议各自为政,通信网络各成体系,造成不同厂家的自控设备网络连接困难甚至不能连接,给分布式控制系统的灵活应用造成了不便。一些公司为适应市场,纷纷将各自的协议标准公开化,可无偿使用。经过多年发展,一些通信协议如Modicon公司的Modbus通信协议因其兼容性、易用性的优势,在工业领域得到了广泛应用,已成为一种通用的工业通信
[嵌入式]
STM8-TIMER实现系统嘀嗒计时器
STM8中没有系统嘀嗒计时器,所以用timer实现系统嘀嗒计时器的功能,每1ms产生一个中断。本次使用的是高速内部时钟,时钟频率是2Mhz 1. timer.c #include timer.h void systic_init(void) // TIMER2_CH1 - PB0 { CLK_PeripheralClockConfig(CLK_Peripheral_TIM2, ENABLE); // 使能时钟 TIM2_DeInit(); // 恢复寄存器到默认值 TIM2_TimeBaseInit(TIM2_Prescaler_2, TIM2_CounterMode_Up, 1000);
[单片机]
瑞杰凯推出MBUS-MODBUS协议转换器
北京瑞杰凯自动化技术有限公司隆重推出了MBUS-MODBUS协议转换器,解决了MBUS设备与PLC设备、管理软件、触摸屏等设备之间的通讯问题。使MBUS接口仪表与抄表主站或控制器连接成为可能。 MBUS-MODBUS 瑞杰凯拥有优秀的团队,可根据现场完全定制,为客户量身打造最适合的产品。 MBUS-MODBUS协议转换器特点及其优势:  220V交流电与24V直流电 两种电压选择,完成适应各种现场的需要;  技术隔离:电源、MBUS、485三端隔离,提供安全的工作环境;  MODBUS RTU从设备波特率2400-115200可配置,地址可配置,校验可配置,8位数据,1位停止,多元化适应各种需要;  MODB
[网络通信]
瑞杰凯推出MBUS-<font color='red'>MODBUS协议</font>转换器
stm8串口通信调试总结 (TTL)
一、硬件连接 一.GPIO及USART1初始化结构体变量定义 /* 调试串口Pin和配置 */ #define DEF_UBR_BAUDRATE 115200 #define DBG_UART USART1 #define DBG_UART_CLK CLK_Peripheral_USART1 #define DBG_UART_TX_PIN GPIOC, GPIO_Pin_2 #define DBG_UART_RX_PIN GPIOC, GPIO_Pin_3 二.串口时钟及GPIO端口时钟使能 USART1是挂在 CLK_Periphera
[单片机]
<font color='red'>stm8</font>串口通信调试总结 (TTL)
STM8 点亮第一个发光二极管
当 I/O 输出低电平时,LED 导通,LED被点亮;当 I/O 输出高点平时,LED 截止,LED 熄灭;当 I/O 周期性的交替输出高电平、低电平,LED就会闪烁 void InitLED(void) { PC_DDR|=0x08;//设置 PC3 为输出模式 PC_CR1|=0x08;//设置 PC3 为推挽输出 PC_CR2|=0x00;//设置 PC3 为 10MHz 快速输出 PE_DDR|=0x01;//设置 PE0 为输出模式 PE_CR1|=0x01;//设置 PE0 为推挽输出 PE_CR2|=0x00;//设置 PE0 为 10MHz 快速输出 PD_DDR|=0x08;//设置 PD3 为
[单片机]
STM8系列单片机时钟设置
为了降低功耗,时钟管理模块可以停止CPU、内存和其它独立外设的时钟。 时钟源有四种: 外部高速晶振(HSE 1-16MHZ) 内部高速RC振荡器(HSI 16MHZ) 外部低速晶振(LSE 32.768) 内部低速RC振荡器(LSI 38KHZ) 启动默认为内部2MHZ时钟(HIS/8) CSS时钟安全系统可通过软件使能,使能后,该系统在HSE停振时,可自动切换到HSI。 CCO 时钟输出功能 1、选择时钟源 void CLK_DeInit(void) 复位所有时钟寄存器 void CLK_HSICmd(FunctionalState NewState) 使能或禁能内部高速振荡器 void CLK_HSECo
[单片机]
STM8S学习笔记之四(STM8 time1)
这几天一直纠结于使用STM8库还是用底层寄存器写程序,用了用IAR和库的搭配,方便时肯定的,但是呢,鉴于我现在工作使用的就是STM32的单片机+STM32库,考虑再三,我还是用底层吧,这样两个就可以互补了,既不会太依赖于库,又对寄存器的操作有更深的了解。。只是都是相通的,是的,相通的。。 以下是从技术手册上粘贴的: TIM1由一个16位的自动装载计数器组成,它由一个可编程的预分频器驱动。 本章中使用i来代表1、2、3、4,分别对应于四个不同的捕获/比较通道。 高级控制定时器适用于许多不同的用途: ●基本的定时 ●测量输入信号的脉冲宽度(输入捕获) ●产生输出波形(输出比较
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved