继续创造,加速成长!这是我参加「日新计划 10 月更文应战」的第11天,点击检查活动详情

仍是一个FreeRTOS的比方,这次不是裸机工程转的,没有大部分仿制的代码,
所以会把进程会记载详细一点,这应该也是博文中 FreeRTOS 最终一个比方了
平台: STM32L051C8T6   
	   欧姆龙 D6T 红外测温传感器 I2C 协议 
	   设备作为485从机 

产品的功用便是经过红外测温传感器守时丈量温度保存,设备经过RS484接口,运用ModbusRTU协议进行传输,设备作为从机接纳主机的查询上报温度等其他数据。

详细的理论分析请参阅博文 MODBUS RTU 485 部分:MODBUS RTU 485 协议扼要阐明

一、STM32CubeMX 创立工程

第一步创立工程,这个曾经博文中就提到过很屡次了,真实不会去看一下我FreeRTOS记载(九、一个裸机工程转FreeRTOS的实例)中的阐明,里边的 2.1 根本结构搭建 有所有相关的博文地址。

这儿我就简略放一个进程,阐明一下我的根本设置:

1.1 芯片根本设置

  1. GPIO口,按键,LED,一个I2C传感器:

    FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

  2. 体系时钟源,运用外部高速时钟:

    FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

  3. 根底时钟源和调试方法,不勾选 Debug Serial Wire 烧录一次就会锁死,不能正常烧录,需求手动操作:

    FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

  4. 体系时钟频率,运用的STM32L051 ,最大支撑32MHz,这儿就选32MHz:

    FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

  5. 守时器设置,依据自己的运用习气,两个守时器,一个守时器做根本逻辑时间管理,一个用于按键驱动的守时器:

    FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

  6. 串口设置,2个串口,一个用于打印的调试串口1,一个用于485通讯的串口:

    FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

1.2 FreeRTOS根本设置

  1. 使命和音讯行列的创立,要考虑到设备的主要功用有哪些,传感器读取,485通讯,按键用来做一些观察测验(按键其实能够不用,可是考虑也能够经过按键来设置485的地址,这个想法再看):

    FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

  2. 翻开使命运转情况查询功用,确保施行观察使命运转状况,这个东西了解了今后或许等体系稳定今后能够去掉:

    FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

    FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

  3. 然后就能够生成代码了,用什么环境选什么,我用的是 gcc 环境,这儿选用的是 Makefile ,这儿最终我还有个小操作,把堆空间改小了一点:

    FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

二、根本结构代码增加

2.1 一些习气的typedef 和 宏界说

依据个人习气,把数据类型的称号命名头文件参加工程,在main.h文件中包括,这样在写代码的时分一些就能够依据个人习气运用数据类型了:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

接下来对LED灯,和I2C引脚进行需求的宏界说:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

2.2 各外设相关代码

2.2.1 守时器(逻辑处理和按键驱动计时)

界说两个全局变量,别离核算 守时器2 和 守时器 21的次数:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

关于守时器21,直接计数就能够,后边在按键驱动中运用Timer21_count的值:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

关于守时器2,在stm32l0xx_it.c文件中,进入守时器中止就处理,由于这儿或许需求在中止中发送FreeRTOS使命告诉或许音讯等:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

2.2.2 串口(调试串口和485通讯串口)

首要打印串口运用USART1,printf重界说,然后就能直接运用printf了:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

接下来485通讯串口 USRAT2,咱们需求运用 FreeRTOS 音讯行列来发送通讯音讯,需求在中止处理函数中操作:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

上面是把串口接纳到的音讯放入音讯行列,咱们在 freertos.c文件中,把对应的接纳音讯行列的部分结构搭建好,一起自己要建立一个缓存用来保存音讯行列的数据:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

StartModbusTask使命中:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

完结上面的结构,别忘了翻开串口接纳中止,可是要注意,由于运用了音讯行列接纳,翻开中止需求等FreeRTOS 初始化完了今后,否者会出现问题!

如下图,咱们在MX_FREERTOS_Init函数中结尾处,翻开串口2 接纳中止:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

2.2.3 按键

按键的话,我仍是运用曾经的那个驱动代码,就直接仿制过来,修改一下守时器源:

几个实用的按键驱动

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

增加了.c 文件记得在Makefile中也增加一下:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

三、各外设函数代码

3.1 485从机驱动(ModbusRTU协议)

详细的理论分析请参阅博文 MODBUS RTU 485 部分:[MODBUS RTU 485 协议扼要阐明](/post/715196…

先把CRC16校验的文件包括进来:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

然后写一下命令处理函数:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

最终实现一下Modbus_03_ack函数。

3.1.1 存放器地址的疑问

在写驱动代码的时分,遽然想到一个问题,存放器地址该怎样界说?

假如咱们就从 0x0000 开端,然后依次+1 的界说存放器,经过一个数组 test[n]是能够一一对应的,这个没问题。

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

可是看各厂家有些设备,地址不是连续的,而且有小的有大的,比方:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

上面这种情况总不能界说一个这么大的数组 test[0x0101]

(后续弥补阐明后来想想这个好像也不大,257,作为一款产品,肯定是厂家会确保产品地址在一定的范围内,然后能够用一个数组顺序对应存放器地址,即使有多个通道,也是会用不同的数组表示,在一定的范围内能够离散,可是差距太大的话就得分不同数组)

当然,即使这样,咱们仍旧能够依照如下对应:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

可是问题是,看到设备的阐明里边,从地址0000H 开端读取数据,读取7个长度的数据,0001H 到 0006H 的数据都会回来 0,这样子的话,单单用一个数组就不优点理了:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

暂时想不出来 一个数组怎样才干知道 他是否是连续的存放器地址呢?不确定是不是还有其他的设定?(答案应该是上面赤色部分)

当然,我考虑过运用 结构体,由于结构体的话,能够先针对的判别存放器的ID,然后再处理数据,比方:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

可是运用结构体数据处理相对来说 就没有数组那么简略,其实最主要的,关键仍是在于产品界说的存放区是否连续,假如连续的话,处理起来都没问题。

所以现在这个示例我仍是把地址设置为连续的,经过一个数组,人为的界说好对应关系。

这儿就直接上源码,把Modbus_rtu.c的源码都放上来:

#include "Modbus_rtu.h"
#include "stdio.h"
void Modbus_check()
{
    u16 crc;
    u16 receivecrc1;
    u16 receivecrc2;
    u8 sendbuff[5];
    crc = Checksum_CRC16(USART2_BUF,USART2_Data - 2);
    printf("crc is :0x%x\r\n",crc);
    /*No matter the high bits before or the low bits before*/
    receivecrc1 = (USART2_BUF[USART2_Data - 2]<<8) + USART2_BUF[USART2_Data - 1];
    receivecrc2 = (USART2_BUF[USART2_Data - 1]<<8) + USART2_BUF[USART2_Data - 2];
    // if((lrc == receivelrc2)||(lrc == receivelrc1)){
    //     if(USART2_BUF[0] == mymodbus_add){
    //这儿阐明一下,先判别地址,然后回来过错,假如先判别校验,假如出错了,那么总线上所有都一起回来就有问题了
    if(USART2_BUF[0] == mymodbus_add){        
        if((crc == receivecrc2)||(crc == receivecrc1)){
            switch (USART2_BUF[1]){
            case 3:
                Modbus_03_ack();
                break;
            case 6:
                Modbus_06_ack();
                break;           
            default:
                printf("An unsupported command!\r\n");//for test
                break;
            }
        }
        else{ //校验过错,回来异常
            sendbuff[0] = mymodbus_add;
            sendbuff[1] = 0x80 | USART2_BUF[1];
            sendbuff[2] = 0;
            crc = Checksum_CRC16(sendbuff,3);
            sendbuff[3] = (u8)(crc >> 8);
            sendbuff[4] = (u8)crc;
            Uart2_sendBuffer(sendbuff,5);
        }
    }   
}
void Modbus_03_ack(){
  u16   Register_add;  // 2,3
  u16   Register_len;  // 4,5
  u16   crc;
  u8    i;
  u8    j;
  Register_add = (USART2_BUF[2]<<8) + USART2_BUF[3]; //get add;
  Register_len = (USART2_BUF[4]<<8) + USART2_BUF[5]; //get len;
  u8 sendbuff[Register_len*2 + 5];
  /*
  假如读取的地址写错了,或许读取长度超过规定的长度
  回来过错
  */
  if(( 0x0010 <= Register_add)&&( Register_add <= 0x0014 )&&(Register_len < 6)){  
    i = 0;
    sendbuff[i++] = mymodbus_add;
    sendbuff[i++] = 0x03;
    sendbuff[i++] = Register_len<<1;
    switch(Register_add){
    case 0x0010:
        for(j=0;j<Register_len;j++){
            sendbuff[i++]= (u8)(Register_value[0+j]>>8);    //发送读取数据字节数的高位 
            sendbuff[i++]= (u8)Register_value[0+j];	  //发送读取数据字节数的低位 
        }
        break;
    case 0x0011:
        for(j=0;j<Register_len;j++){
            sendbuff[i++]= (u8)(Register_value[1+j]>>8);     
            sendbuff[i++]= (u8)Register_value[1+j];	   
        }
        break;           
    case 0x0012:
        for(j=0;j<Register_len;j++){
            sendbuff[i++]= (u8)(Register_value[2+j]>>8);     
            sendbuff[i++]= (u8)Register_value[2+j];	       
        }
        break;
    case 0x0013:
        for(j=0;j<Register_len;j++){
            sendbuff[i++]= (u8)(Register_value[3+j]>>8);    
            sendbuff[i++]= (u8)Register_value[3+j];	   
        }
        break; 
    case 0x0014:
        for(j=0;j<Register_len;j++){
            sendbuff[i++]= (u8)(Register_value[4+j]>>8);     
            sendbuff[i++]= (u8)Register_value[4+j];	       
        }
        break;
    default:break;  
    }
    crc = Checksum_CRC16(sendbuff,i);
    sendbuff[i++] = (u8)(crc >> 8);
    sendbuff[i++] = (u8)crc;
    Uart2_sendBuffer(sendbuff,i);
  }
  else{//地址不在规定回来或许长度太长,回来过错
    sendbuff[0] = mymodbus_add;
    sendbuff[1] = 0x80 | USART2_BUF[1];
    sendbuff[2] = 0;
    crc = Checksum_CRC16(sendbuff,3);
    sendbuff[3] = (u8)(crc >> 8);
    sendbuff[4] = (u8)crc;
    Uart2_sendBuffer(sendbuff,5);
  }
}
void Modbus_06_ack(){   
}

3.1.2 485 驱动测验

485接纳部分驱动完结,这儿需求测验一下,测验进程确实发现了问题,可是都是小问题,全体结构逻辑是正常的,在处理校验位的时分,数据位数处理有点粗心了(上面贴出的程序是现已修改正的):

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

把第一个存放器设置一个值:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

测验结果,一切正常:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

测完了仍是把 06 写单个存放器的函数给弥补一下,现在只敞开一个数据,便是设备ID的写入,直接上代码:

void Modbus_06_ack(){
  u16   Register_add;  // 
  u16   val;  // 
  u16   crc;
  u8    i;
  u8    sendbuff[8] = {0};
  if(USART2_Data < 9){     
    Register_add = (USART2_BUF[2]<<8) + USART2_BUF[3]; //get add;
    val = (USART2_BUF[4]<<8) + USART2_BUF[5]; //
    if(Register_add == 0x0013){
        mymodbus_add = val;
        Register_value[3] = mymodbus_add;
        i = 0;
        sendbuff[i++] = mymodbus_add;
        sendbuff[i++] = 0x06;
        sendbuff[i++] = (u8)(Register_add>>8);     
        sendbuff[i++] = (u8)Register_add;	
        sendbuff[i++] = (u8)(val>>8);     
        sendbuff[i++] = (u8)val;
        crc = Checksum_CRC16(sendbuff,i);
        sendbuff[i++] = (u8)(crc >> 8);
        sendbuff[i++] = (u8)crc;
        Uart2_sendBuffer(sendbuff,i);	 
    }
    else{//写地址不在规定范围
        sendbuff[0] = mymodbus_add;
        sendbuff[1] = 0x80 | USART2_BUF[1];
        sendbuff[2] = 3;
        crc = Checksum_CRC16(sendbuff,3);
        sendbuff[3] = (u8)(crc >> 8);
        sendbuff[4] = (u8)crc;
        Uart2_sendBuffer(sendbuff,5);
    }
  }
  else{//写地址不在规定范围
    sendbuff[0] = mymodbus_add;
    sendbuff[1] = 0x80 | USART2_BUF[1];
    sendbuff[2] = 1;
    crc = Checksum_CRC16(sendbuff,3);
    sendbuff[3] = (u8)(crc >> 8);
    sendbuff[4] = (u8)crc;
    Uart2_sendBuffer(sendbuff,5);
  }    
}

测验一下作用,如下图,看上去OK:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

想了一下,不对啊?

修改地址应该有个约束,从机的地址应该在: 1~ 247 (0XF7)

所以以代码里边还得加一个约束(按理来说应该是先判别地址,假如是写ID的存放器,才需求约束从机地址巨细,其他存放器,是能够写其他数值的,由于示例中只敞开ID存放器的写操作,所以这儿问题不大,可是在完善自己的从机代码的时分这儿得注意一下逻辑!!!):

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

3.2 D6T传感器驱动(I2C协议)

I2C的根底知识这儿就不多介绍了,网上很多,我那个总线协议记载里边也介绍了。

3.2.1 通用I2C驱动

I2C协议需求用到 us 延时,HAL库里边没有, FreeRTOS里边也没,这儿咱们仍是用曾经常用的自己写一个:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

咱们运用的是软件的I2C,所以要把通用的 I2C 驱动写一下,新建一个i2c.c和一个i2c.h文件作为软件 I2C的通用驱动,这儿直接上源码:

#include "i2c.h"
// ------------------------------------------------------------------
void i2c_init(void)  {
// the SDA and SCL pins are defined as input with pull up enabled
  // pins are initialized as inputs, ext. pull => SDA and SCL = high
}
// ------------------------------------------------------------------
// send start sequence (S)
void i2c_start(void)
{
   sda_high;
   delay_us(5);
   scl_high;
   delay_us(10);
   sda_low;
   delay_us(10);
   scl_low; //使SCL置低,准备发送或许承受数据
   delay_us(10);
}
// ------------------------------------------------------------------
// send stop sequence (P)
void i2c_stop(void)
{ 
   sda_low;
   delay_us(5);
   scl_low;
   delay_us(10);
   scl_high;
   delay_us(5);
   sda_high;
   delay_us(10); 
}
// ------------------------------------------------------------------
// returns the ACK or NACK
uint8 i2c_write(uint8 u8Data) 
{
	uint8 u8Bit;
	uint8 u8AckBit;
	// write 8 data bits
	u8Bit = 0x80;  //msb first  
	while(u8Bit) {
		if(u8Data&u8Bit) { 
			sda_high;
			delay_us(20);
		} 
		//& compare every bit
		else{ 
			sda_low;
			delay_us(20);
		}			
		scl_high;
		delay_us(30);
		u8Bit >>= 1; 
		//next bit
		scl_low;
		delay_us(30);
	}
	// read acknowledge (9th bit) 
	sda_high;                                               
	delay_us(10);		
	scl_high;
	delay_us(10);
	u8AckBit= sda_read;	//#define sda_read()  (sda_port & sda_pin)? 1 :0  ack on bus is low -> u8AckBit = 1   sda_port gpio0   sda_pin SCSEDIO0
	delay_us(10);
    scl_low;                                              
	delay_us(10);
    return u8AckBit;
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
u8 i2c_read_byte(unsigned char ack)
{
	unsigned char i,receive=0;
	// MYSDA_IN;//SDA设置为输入
   for(i=0;i<8;i++ )
	{
        scl_low;    //SCL为由低变高,在SCL高的时分去读 SDA的数据
        delay_us(10);
		scl_high;
        receive<<=1;  //第一次这儿仍是0,第2次开端每次接纳的数据做移动一位,从高位开端接纳
        if(sda_read)receive++;   //假如数据为1,++今后便是1,数据为0,不执行便是0; 
		  delay_us(10); 
   }					 
   if (!ack)
        IIC_NAck();//发送nACK
   else
        IIC_Ack(); //发送ACK   
   return receive;
}
// ------------------------------------------------------------------
// pass the ack/nack 
// returns the read data 
uint8 i2c_read(uint8 u8Ack)  
{
	uint8 u8Bit;
	uint8 u8Data;
	u8Bit = 0x80;       // msb first
	u8Data = 0;
	while(u8Bit){
		scl_high;
		delay_us(20);
		u8Bit >>= 1;    //next bit
		u8Data <<= 1;
		u8Data |= sda_read;         //(sda_port & sda_pin)? 1 :0      sda_port gpio0   sda_pin SCSEDIO0
		delay_us(20);
		scl_low;
		delay_us(50);
    }
	// 9th bit acknowledge
	if(u8Ack==I2C_ACK) {
		sda_low;
		delay_us(20);
	}     
	//I2C_ACK=0
	else {
		sda_high;
		delay_us(20);
	}
  	scl_high;
	delay_us(20);
	scl_low;
	delay_us(20);
	sda_high;
	delay_us(20);									
    return u8Data;
}
u8 i2c_wait_ack(void)
{
    u8 ucErrTime=0;
	delay_us(5);
    sda_high;delay_us(5);       //MCU DATA 置高,外面高便是高,外面低便是低
    scl_high; delay_us(5);       //CLK 高电平期间数据有用
    while(sda_read)             //低电平为有应对,高电平无应对    
    {
      ucErrTime++;
		if(ucErrTime>250)
		{
			i2c_stop();
			return 1;
		}  
    }
		delay_us(10);
    scl_low;
    return 0;
}
void IIC_Ack(void)
{
	scl_low;    //SCL为低,SDA为低,SCL为高,SDA为低,应对低电平有用,SCL为低,产生应对信号
	// MYSDA_OUT;
	sda_low;
	delay_us(10);
	scl_high;
	delay_us(10);
	scl_low;
	delay_us(10);
	sda_high;
}
void IIC_NAck(void)
{
	scl_low;     //SCL为低,SDA为高,SCL为高,SCL为低
	// MYSDA_OUT;
	sda_high;
	delay_us(10);
	scl_high;
	delay_us(10);
	scl_low;
}
//IIC发送一个字节
//回来从机有无应对
//1,有应对
//0,无应对			  
void i2c_send_byte(u8 txd)
{                        
   u8 t;   
	// MYSDA_OUT; 	    
   scl_low;  //拉低时钟开端数据传输   ,SCL为低,SDA变高或许变低(数据位),SCL变高,SCL变低,期间SDA为1既1,为0既0
   for(t=0;t<8;t++)  //一个字节8位,一位一位发送
    {              
     scl_low;
		if((txd&0x80)>>7)   //从最高位开端发送,假如是1,发送高电平
			sda_high;
		else
			sda_low;
		txd<<=1; 	      //SDA处理完毕,此刻能够将SCL拉高承受数据,拉高今后延时拉低
		delay_us(10);   //对TEA5767这三个延时都是有必要的
		scl_high;
		delay_us(10); 
		scl_low;	
		delay_us(5);
    }	 
}

3.2.2 I2C传感器数据读取

关于欧姆龙的这个传感器,主要的时序图如下:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

依据时序图,设计代码,源码如下(当然要依据自己的传感器类型进行细节修改):

void D6T_Measure()
{
	u8 D6Tbuff[20];
	u8 D6T_Data=0;
    // u16 tPEC;
  	i2c_start();
	i2c_send_byte(0X14); //地址,和读写指令
	i2c_wait_ack();
	delay_us(150);    //这儿有必要加
	i2c_send_byte(0X4C);
	i2c_wait_ack(); 
	delay_us(150);
	i2c_start();
	i2c_send_byte(0X15); //地址,读指令
	i2c_wait_ack();
	delay_us(120);
	// D6T44L_ReadLenByte(5);    //D6T-1A-02 只要5个数值
	u8 t;
  	D6T_Data=0;
	for(t=0;t<(5-1);t++)
	{
		D6Tbuff[D6T_Data++] = i2c_read_byte(1);
		delay_us(120);
	}
 	D6Tbuff[D6T_Data] = i2c_read_byte(0);
	delay_us(120);
	i2c_stop();
	//  tPTAT = 256 * D6Tbuff[1] + D6Tbuff[0];
	tP = 256 * D6Tbuff[3] + D6Tbuff[2];
}

然后在使命里边调用温度读取函数,做一个简略的测验:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

驱动OK,测验结果(结果是温度 * 10):

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

3.3 使命调整、使命通讯

驱动都现已设计完结,那么就到了最终的当地,使命间的通讯了,什么时分读取数据,读取的数据放入对应的 485 的存放器。

3.3.1 使命栈阐明

当然,不要忘了各个使命的栈空间调整,还要考虑到运用的STM32L051 单片机只要8K的 RAM,要注意内存的运用情况:

不看不知道,一看吓一跳 = =!:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

假如翻开了堆栈溢出钩子函数,估量一直在报溢出,测验一下:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

测验方法见博文:FreeRTOS记载(四、FreeRTOS使命堆栈溢出问题和临界区)

上面咱们看到,按键使命和 RTU接纳使命的 栈不行了,D6Tread 也不是很够,由于后期还需求做逻辑处理,也有调用关系,所以有必要对各使命的巨细进行一定的调整。

KeyTask

前面咱们说到,按钮现在仅仅为了测验,所以按钮把里边界说的数组改小一点,其实只要能够满意完全打印使命状况的巨细就行了,曾经设置500并没有细心的去核算。

使命巨细:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

使命运转:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

在后期的完善进程中,考虑到一个问题,作为485设备,假如拿到一个设备,并不知道他的 ID ,怎样办,所以正好能够经过按钮操作,使得设备康复默认ID。所以将按键使命加了一个长按操作:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

RTUreceived

串口接纳使命会溢出是开端没想打的,可是细心想想仍是不应该,看了一下,开端测验的时分加了一句 printf 函数,把printf后 RTUreceived 就正常不会溢出了:

加个printf就占用那么大空间?这个怎样理解呢,尽管知道运用 printf 会占用栈空间,可是这么大仍是有点惊讶的

使命巨细:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

使命运转:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

D6TRead

经过上文能够看到 D6TRead 只剩下 21 的栈空间,在该使命中,咱们调用了 I2C 的驱动,读取传感器数据,考虑到后边还或许进行数据核算操作,此使命估量得放大一点空间(为什么数据保存不能新建一个使命,由于内存空间不行,给FreeRTOS分配的总栈空间现已不行我再新建一个使命了,当然,实践使用,能够对keyTask使命进行减缩来交换其他使命更多的空间)。

当然,我仍是改大了一点:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

使命巨细:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

使命运转:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)

3.3.2 使命间通信

其实示例做到上面,全体上现已没什么大问题了,最主要的仍是示例仍是比较简略的,没有太多的使命,所以到这儿其实根本上大问题就没有了。余下的便是细节处理。

开始构思的时分,关于使命之间,什么时分读取传感器的数据,是想运用 TIM2 守时 ,在守时器中止中给D6TRead 使命告诉来实现,可是由于全体结构简略,这儿直接运用延时就能够达到作用= =!这儿就不再折腾了,由于详细的运用能够参阅博文,也是使命中读取传感器:

FreeRTOS记载(五、FreeRTOS使命告诉)

然后针对实践使用,或许需求对收集的温度进行一定的数据处理,直接依据需求修改即可。

最终全体测验一下,没有问题:

FreeRTOS记录(十、FreeRTOS实现带 I2C 通讯的 ModbusRTU 协议从机实例)