#单片机通信数据接收解析方法

前阵子一朋友使用单片机与某外设进行通信时,外设返回的是一堆格式如下的数据:

AA AA 04 80 02 00 02 7B AA AA 04 80 02 00 08 75 AA AA 04 80 02 00 9B E2 AA AA 04 80 02 00 F6 87 AA AA 04 80 02 00 EC 91

其中 AA AA 04 80 02 是数据校验头,后面三位是有效数据,问我怎么从外设不断返回的数据中取出有效的数据。

对于这种问题最容易想到的就是使用一个标志位用于标志当前正解析到一帧数据的第几位,然后判断当前接收的数据是否与校验数据一致,如果一致则将标志位加一,否则将标志位置0重新判断,使用这种方法解析数据的代码如下:

if(flag == 0)
{
 if(tempData == 0xAA)
  flag++;
 else
  flag = 0;
}
else if(flag == 1)
{
 if(tempData == 0xAA)
  flag++;
 else
  flag = 0;
}
else if(flag == 2)
{
 if(tempData == 0x04)
  flag++;
 else
  flag = 0;
}
else if(flag == 3)
{
 if(tempData == 0x80)
  flag++;
 else
  flag = 0;
}
else if(flag == 4)
{
 if(tempData == 0x02)
  flag++;
 else
  flag = 0;
}
else if(flag == 5 || flag == 6 || flag == 7)
{
 data[flag-5] = tempData;
 flag = (flag == 7) ? 0 : flag+1;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.

使用上述方法是最容易想到的也是最简单的方法了,百度了一下基本上也都是使用类似的方法进行数据解析,但是使用这种方法有如下几个缺点:

1、 大量使用了判断,容易导致出现逻辑混乱。

2、 代码重复率高,抽象程度低。从上述代码可以看到一大堆代码仅仅是判断的数据不同,其他代码都完全一致。

3、 代码可复用性差。写好的代码无法用在其他类似的外设上,如果有多个外设就需要编写多份类似的代码。

4、 可扩展性低。如果外设还有一个数据校验尾需要校验或者数据校验头发生改变,就需要再次写多个判断重新用于校验,无法在原有的代码上进行扩展。

5、 容易出现误判  。

对此,这里提出了一种新的解决方案,可以通用与所有类似的数据解析,原理如下:

使用一个固定容量的队列用来缓存接收到的数据,队列容量等于一帧数据的大小,每来一个数据就将数据往队列里面加,当完整接收到一帧数据时此时队列中的全部数据也就是一帧完整的数据,因此只需要判断队列是否是数据校验头,队列尾是否是数据校验尾就可以得知当前是否已经接收到了一帧完整的数据,然后在将数据从队列中取出即可。原理图如下:

每来一个数据就往队列里面加:

51c嵌入式~IO合集1_解析器

当接收到一帧完整数据时队列头和数据校验头重合:

51c嵌入式~IO合集1_数据_02

此时只需要从队列中取出有效数据即可。

如果有数据尾校验,仅仅只需要添加一个校验尾即可,如下图所示:

51c嵌入式~IO合集1_数据校验_03

好,分析结束,开始编码。

首先需要一个队列,为了保证通用性,队列底层使用类似于双向链表的实现(当然也可以使用数组实现),需要封装的结构有队列容量、队列大小、队头节点和队尾节点,需要实现的操作有队列初始化、数据入队、数据出队、清空队列和释放队列,具体代码如下:

/* queue.h */
 
#ifndef _QUEUE_H_
#define _QUEUE_H_
 
#ifndef NULL
#define NULL ((void *)0)
#endif
 
typedef unsigned char uint8;
 
/* 队列节点 */
typedef struct Node
{
 uint8 data;
 struct Node *pre_node;
 struct Node *next_node;
} Node;
 
/* 队列结构 */
typedef struct Queue
{
 uint8 capacity;     // 队列总容量
 uint8 size;         // 当前队列大小
 Node *front;        // 队列头节点
 Node *back;         // 队列尾节点
} Queue;
 
/* 初始化一个队列 */
Queue *init_queue(uint8 _capacity);
/* 数据入队 */
uint8 en_queue(Queue *_queue, uint8 _data);
/* 数据出队 */
uint8 de_queue(Queue *_queue);
/* 清空队列 */
void clear_queue(Queue *_queue);
/* 释放队列 */
void release_queue(Queue *_queue);
 
#endif
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
/* queue.c */
 
#include <stdlib.h>
#include "parser.h"
 
/**
 * 初始化一个队列
 *
 * @_capacity: 队列总容量
 */
Queue *init_queue(uint8 _capacity)
{
 Queue *queue = (Queue *)malloc(sizeof(Queue));
 queue->capacity = _capacity;
 queue->size = 0;
 return queue;
}
 
/**
 * 数据入队
 *
 * @_queue: 队列
 * @_data: 数据
 **/
uint8 en_queue(Queue *_queue, uint8 _data)
{
 if(_queue->size < _queue->capacity)
 {
  Node *node = (Node *)malloc(sizeof(Node));
  node->data = _data;
  node->next_node = NULL;
 
        if(_queue->size == 0)
        {
            node->pre_node = NULL;
            _queue->back = node;
            _queue->front = _queue->back;
        }
        else
        {
            node->pre_node = _queue->back;
 
            _queue->back->next_node = node;
            _queue->back = _queue->back->next_node;
        }
  _queue->size++;
 }
 else
 {
  Node *temp_node = _queue->front->next_node;
  _queue->front->pre_node = _queue->back;
  _queue->back->next_node = _queue->front;
  _queue->back = _queue->back->next_node;
  _queue->back->data = _data;
  _queue->back->next_node = NULL;
  _queue->front = temp_node;
 }
 return _queue->size-1;
}
 
/**
 * 数据出队
 *
 * @_queue: 队列
 *
 * @return: 出队的数据
 */
uint8 de_queue(Queue *_queue)
{
    uint8 old_data = 0;
 
    if(_queue->size > 0)
    {
        old_data = _queue->front->data;
        if(_queue->size == 1)
        {
            free(_queue->front);
            _queue->front = NULL;
            _queue->back = NULL;
        }
        else
        {
            _queue->front = _queue->front->next_node;
            free(_queue->front->pre_node);
            _queue->front->pre_node = NULL;
        }
        _queue->size--;
    }
    return old_data;
}
 
/**
 * 清空队列
 *
 * @_queue: 队列
 */
void clear_queue(Queue *_queue)
{
    while(_queue->size > 0)
    {
        de_queue(_queue);
    }
}
 
/**
 * 释放队列
 *
 * @_queue: 队列
 */
void release_queue(Queue *_queue)
{
    clear_queue(_queue);
    free(_queue);
    _queue = NULL;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.

其次是解析器,需要封装的结构有解析数据队列、数据校验头、数据校验尾、解析结果以及指向解析结果的指针,需要实现的操作有解析器初始化、添加数据解析、获取解析结果、重置解析器和释放解析器,具体代码如下:

/* parser.h */
 
#ifndef _PARSER_H_
#define _PARSER_H_
 
#include "queue.h"
 
typedef enum
{
    RESULT_FALSE,
    RESULT_TRUE
} ParserResult;
 
/* 解析器结构 */
typedef struct DataParser
{
    Queue *parser_queue;   // 数据解析队列
    Node *resule_pointer;   // 解析结果数据指针
    uint8 *data_header;    // 数据校验头指针
    uint8 header_size;    // 数据校验头大小
    uint8 *data_footer;    // 数据校验尾指针
    uint8 footer_size;    // 数据校验尾大小
    uint8 result_size;    // 解析数据大小
    ParserResult parserResult;  // 解析结果
} DataParser;
 
/* 初始化一个解析器 */
DataParser *parser_init(uint8 *_data_header, uint8 _header_size, uint8 *_data_footer, uint8 _foot_size, uint8 _data_frame_size);
/* 将数据添加到解析器中进行解析 */
ParserResult parser_put_data(DataParser *_parser, uint8 _data);
/* 解析成功后从解析器中取出解析结果 */
int parser_get_data(DataParser *_parser, uint8 _index);
/* 重置解析器 */
void parser_reset(DataParser *_parser);
/* 释放解析器 */
void parser_release(DataParser *_parser);
 
#endif
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.


/* parser.c */
 
#include <stdlib.h>
#include "parser.h"
 
/**
 * 初始化一个解析器
 *
 * @_data_header: 数据头指针
 * @_header_size: 数据头大小
 * @_data_footer: 数据尾指针
 * @_foot_size: 数据尾大小
 * @_data_frame_size: 一帧完整数据的大小
 *
 * @return: 解析器
 */
DataParser *parser_init(uint8 *_data_header, uint8 _header_size, uint8 *_data_footer, uint8 _foot_size, uint8 _data_frame_size)
{
    if((_header_size+_foot_size) > _data_frame_size || (_header_size+_foot_size) == 0)
        return NULL;
 
    DataParser *parser = (DataParser *)malloc(sizeof(DataParser));
    parser->parser_queue = init_queue(_data_frame_size);
    parser->resule_pointer = NULL;
    parser->data_header = _data_header;
    parser->header_size = _header_size;
 parser->data_footer = _data_footer;
 parser->footer_size = _foot_size;
    parser->result_size = _data_frame_size - parser->header_size - parser->footer_size;
    parser->parserResult = RESULT_FALSE;
 
    while(_data_frame_size-- > 0)
    {
        en_queue(parser->parser_queue, 0);
    }
 
    return parser;
}
 
/**
 * 将数据添加到解析器中进行解析
 *
 * @_parser: 解析器
 * @_data: 要解析的数据
 *
 * @return: 当前解析结果,返回 RESULT_TRUE 代表成功解析出一帧数据
 */
ParserResult parser_put_data(DataParser *_parser, uint8 _data)
{
    uint8 i;
    Node *node;
 
 if(_parser == NULL)
  return RESULT_FALSE;
 
    en_queue(_parser->parser_queue, _data);
 
 /* 校验数据尾 */
 node = _parser->parser_queue->back;
 for(i = _parser->footer_size; i > 0; i--)
 {
  if(node->data != _parser->data_footer[i-1])
            goto DATA_FRAME_FALSE;
        node = node->pre_node;
 }
 
 /* 校验数据头 */
    node = _parser->parser_queue->front;
    for(i = 0; i < _parser->header_size; i++)
    {
        if(node->data != _parser->data_header[i])
            goto DATA_FRAME_FALSE;
        node = node->next_node;
    }
 
    if(_parser->resule_pointer == NULL && _parser->result_size > 0)
        _parser->resule_pointer = node;
    if(_parser->parserResult != RESULT_TRUE)
     _parser->parserResult = RESULT_TRUE;
    return _parser->parserResult;
 
DATA_FRAME_FALSE:
    if(_parser->resule_pointer != NULL)
        _parser->resule_pointer = NULL;
    if(_parser->parserResult != RESULT_FALSE)
        _parser->parserResult = RESULT_FALSE;
    return _parser->parserResult;
 
}
 
/**
 * 解析成功后从解析器中取出解析结果
 *
 * @_parser: 解析器
 * @_index: 解析结果集合中的第 _index 个数据
 *
 * @return: 获取解析成功的数据,返回 -1 代表数据获取失败
 */
int parser_get_data(DataParser *_parser, uint8 _index)
{
    Node *node;
    if(_parser == NULL
 || _parser->parserResult != RESULT_TRUE
    || _index >= _parser->result_size
    || _parser->resule_pointer == NULL)
        return -1;
    node = _parser->resule_pointer;
    while(_index > 0)
    {
        node = node->next_node;
        _index--;
    }
    return node->data;
}
 
/**
 * 重置解析器
 *
 * @_parser: 解析器
 */
void parser_reset(DataParser *_parser)
{
 uint8 _data_frame_size;
 
 if(_parser == NULL)
  return;
 
 _data_frame_size = _parser->parser_queue->size;
 while(_data_frame_size-- > 0)
    {
        en_queue(_parser->parser_queue, 0);
    }
    _parser->resule_pointer = NULL;
    _parser->parserResult = RESULT_FALSE;
}
 
/**
 * 释放解析器
 *
 * @_parser: 解析器
 */
void parser_release(DataParser *_parser)
{
 if(_parser == NULL)
  return;
    release_queue(_parser->parser_queue);
    free(_parser);
    _parser = NULL;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.

接下来编写测试代码测试一下:

/* main.c */
 
#include <stdio.h>
#include "parser.h"
 
int main()
{
    uint8 i;
    // 数据头
    uint8 data_header[] = {0xAA, 0xAA, 0x04, 0x80, 0x02};
    // 要解析的数据,测试用
    uint8 data[] = {
        0xAA, 0xAA, 0x04, 0x80, 0x02, 0x00, 0x02, 0x7B, 0xAA, 0xAA, 0x04, 0x80,
        0x02, 0x00, 0x08, 0x75, 0xAA, 0xAA, 0x04, 0x80, 0x02, 0x00, 0x9B, 0xE2,
        0xAA, 0xAA, 0x04, 0x80, 0x02, 0x00, 0xF6, 0x87, 0xAA, 0xAA, 0x04, 0x80,
        0x02, 0x00, 0xEC, 0x91, 0xAA, 0xAA, 0x04, 0x80, 0x02, 0x01, 0x15, 0x67,
        0xAA, 0xAA, 0x04, 0x80, 0x02, 0x01, 0x49, 0x33, 0xAA, 0xAA, 0x04, 0x80,
        0x02, 0x00, 0xE7, 0x96, 0xAA, 0xAA, 0x04, 0x80, 0x02, 0x00, 0x68, 0x15,
        0xAA, 0xAA, 0x04, 0x80, 0x02, 0x00, 0x3C, 0x41, 0xAA, 0xAA, 0x04, 0x80,
        0x02, 0x00, 0x66, 0x17, 0xAA, 0xAA, 0x04, 0x80, 0x02, 0x00, 0xA5, 0xD8,
        0xAA, 0xAA, 0x04, 0x80, 0x02, 0x01, 0x26, 0x56, 0xAA, 0xAA, 0x04, 0x80,
        0x02, 0x01, 0x73, 0x09, 0xAA, 0xAA, 0x04, 0x80, 0x02, 0x01, 0x64, 0x18,
        0xAA, 0xAA, 0x04, 0x80, 0x02, 0x01, 0x8B, 0xF1, 0xAA, 0xAA, 0x04, 0x80,
        0x02, 0x01, 0xC6, 0xB6, 0xAA, 0xAA, 0x04, 0x80, 0x02, 0x01, 0x7B, 0x01,
        0xAA, 0xAA, 0x04, 0x80, 0x02, 0x00, 0xCB, 0xB2, 0xAA, 0xAA, 0x04, 0x80,
        0x02, 0x00, 0x2C, 0x51, 0xAA, 0xAA, 0x04, 0x80, 0x02, 0xFF, 0xE5, 0x99
    };
 
    /**
     * 初始化一个解析器
     * 第一个参数是数据头
     * 第二个参数是数据头长度
     * 第三个参数是数据尾指针
     * 第四个参数是数据尾大小
     * 第五个参数是一整帧数据的大小
     */
    DataParser *data_parser = parser_init(data_header, sizeof(data_header), NULL, 0, 8);
 
    // 将要解析的数据逐个取出,添加到解析器中
    for(i = 0; i < sizeof(data); i++)
    {
        // 解析数据,返回 RESULT_TRUE 代表成功解析出一组数据
        if(parser_put_data(data_parser, data[i]) == RESULT_TRUE)
        {
            printf("成功解析出一帧数据...\n");
 
            /* 一位一位取出解析后的数据 */
            printf("第一个数据是:0x%x\n", parser_get_data(data_parser, 0));
            printf("第二个数据是:0x%x\n", parser_get_data(data_parser, 1));
            printf("第三个数据是:0x%x\n\n\n", parser_get_data(data_parser, 2));
        }
    }
 
    // 当不再需要解析器时,应该把解析器释放掉,回收内存,避免造成内存泄漏
    parser_release(data_parser);
 
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.

测试结果如下:

51c嵌入式~IO合集1_解析器_04

从上面可以看出,解析的结果与目标一致。

github地址:

 https://github.com/528787067/DataFrameParser



#STM32F103~CAN收发通信

CAN通信

    CAN 是Controller Area Network 的缩写(以下称为CAN),该通信使用的是ISO11898标准,该标准的物理层特征如下图所示。

51c嵌入式~IO合集1_解析器_05

 CAN协议是通过以下5种类型的帧进行的:

  • 数据帧
  • 摇控帧
  • 错误帧
  • 过载帧
  • 帧间隔

    另外,数据帧和遥控帧有标准格式和扩展格式两种格式。标准格式有11 个位的标识符(ID),扩展格式有29 个位的ID。

    大部分系统使用的都是数据帧 ,我这里使用的也是数据帧。
    数据帧一般由7个段构成,即:
(1) 帧起始。表示数据帧开始的段。
(2) 仲裁段。表示该帧优先级的段。
(3) 控制段。表示数据的字节数及保留位的段。
(4) 数据段。数据的内容,一帧可发送0~8个字节的数据。
(5) CRC段。检查帧的传输错误的段。
(6) ACK段。表示确认正常接收的段。
(7) 帧结束。表示数据帧结束的段。

    明确了数据帧概念,还需要理解一下过滤器的作用。

    STM32的标识符屏蔽滤波目的是减少了CPU处理CAN通信的开销。STM32的过滤器组最多有28个(互联型),但是STM32F103ZET6只有14个(增强型),每个滤波器组x由2个32为寄存器,CAN_FxR1和CAN_FxR2组成。
    STM32每个过滤器组的位宽都可以独立配置,以满足应用程序的不同需求。根据位宽的不同,每个过滤器组可提供:

  • 1个32位过滤器,包括:STDID[10:0]、EXTID[17:0]、IDE和RTR位
  • 2个16位过滤器,包括:STDID[10:0]、IDE、RTR和EXTID[17:15]位

    此外过滤器可配置为,屏蔽位模式和标识符列表模式。

    在屏蔽位模式下,标识符寄存器和屏蔽寄存器一起,指定报文标识符的任何一位,应该按照“必须匹配”或“不用关心”处理。
    而在标识符列表模式下,屏蔽寄存器也被当作标识符寄存器用。因此,不是采用一个标识符加一个屏蔽位的方式,而是使用2个标识符寄存器。接收报文标识符的每一位都必须跟过滤器标识符相同。相关文章:
 CAN总线详解

    一般也都是使用标识符列表模式,这里使用的也是标识符列表模式。滤波过程举例如下:

51c嵌入式~IO合集1_解析器_06

    在程序中就是:

//要过滤的ID高位 
CAN_FilterInitStructure.CAN_FilterIdHigh=0X00;  
//要过滤的ID低位                 
CAN_FilterInitStructure.CAN_FilterIdLow= (((u32)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF; 
//过滤器屏蔽标识符的高16位值
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0xFFFF;   
//过滤器屏蔽标识符的低16位值         
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0xFFFF;
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
这里的CAN_FilterId和CAN_FilterMaskId是配合使用的,意思是CAN_FilterId指出需要屏蔽ID的什么内容,什么格式;CAN_FilterMaskId是指CAN_FilterId的每一位是否需要过滤,若CAN_FilterMaskId在某位上是1的话,ID对应位上的数值就必须和CAN_FilterId该位上的一样,保持一致,反之则是“不关心”。
  • 1.

上述程序的设置的含义就是:只接收来自0x1314的数据,屏蔽其他ID的数据。

程序思路

    这里准备做一个主机与从机的通信,主要用扩展标识符ExtId来区分,分配的标识符是:
主机:0x1314
从机:0x1311

    主机负责接收所有从机的数据,不需要过滤,用扩展标识符ExtId来区分不同从机的数据;主机还可以向不同从机发送信息。而从机则只接收来自主机的数据,同样用扩展标识符ExtId来区分是否是发向自己的数据;同时,也能够向主机发送信息。

相关代码

    代码也是非常简单的,这里贴出了主机和从机的can.c和can.h两个文件。

从机相关代码

can.c文件:

#include "can.h"




/* 在中断处理函数中返回 */
//__IO uint32_t ret = 0;




//接收数据缓冲器
u8 RxBuf[5];
u8 Rx_flag=0;




void CAN1_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure; 
    NVIC_InitTypeDef NVIC_InitStructure;
    CAN_InitTypeDef        CAN_InitStructure;
    CAN_FilterInitTypeDef  CAN_FilterInitStructure;




    /* 复用功能和GPIOB端口时钟使能*/    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE);                                                                      




    /* CAN1 模块时钟使能 */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); 




    /* Configure CAN pin: RX */  // PB8
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;       //上拉输入
    GPIO_Init(GPIOB, &GPIO_InitStructure);




    /* Configure CAN pin: TX */   // PB9
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;     //复用推挽输出
    GPIO_Init(GPIOB, &GPIO_InitStructure);




    //#define GPIO_Remap_CAN    GPIO_Remap1_CAN1 本实验没有用到重映射I/O
    GPIO_PinRemapConfig(GPIO_Remap1_CAN1, ENABLE);




    //CAN_NVIC_Configuration(); //CAN中断初始化   
    /* Configure the NVIC Preemption Priority Bits */  
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);




    #ifdef  VECT_TAB_RAM  
      /* Set the Vector Table base location at 0x20000000 */ 
      NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); 
    #else  /* VECT_TAB_FLASH  */
      /* Set the Vector Table base location at 0x08000000 */ 
      NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);   
    #endif




    /* enabling interrupt */
    NVIC_InitStructure.NVIC_IRQChannel=USB_LP_CAN1_RX0_IRQn;;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);




    //CAN_INIT();//CA初始化N模块 
    /* CAN register init */
    CAN_DeInit(CAN1);                       //将外设CAN的全部寄存器重设为缺省值
    CAN_StructInit(&CAN_InitStructure);     //把CAN_InitStruct中的每一个参数按缺省值填入




    /* CAN cell init */
    CAN_InitStructure.CAN_TTCM=DISABLE;         //没有使能时间触发模式
    CAN_InitStructure.CAN_ABOM=DISABLE;         //没有使能自动离线管理
    CAN_InitStructure.CAN_AWUM=DISABLE;         //没有使能自动唤醒模式
    CAN_InitStructure.CAN_NART=DISABLE;         //没有使能非自动重传模式
    CAN_InitStructure.CAN_RFLM=DISABLE;         //没有使能接收FIFO锁定模式
    CAN_InitStructure.CAN_TXFP=DISABLE;         //没有使能发送FIFO优先级
    CAN_InitStructure.CAN_Mode=CAN_Mode_Normal; //CAN设置为正常模式
    CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;      //重新同步跳跃宽度1个时间单位
    CAN_InitStructure.CAN_BS1=CAN_BS1_3tq;      //时间段1为3个时间单位
    CAN_InitStructure.CAN_BS2=CAN_BS2_2tq;      //时间段2为2个时间单位
    CAN_InitStructure.CAN_Prescaler=60;         //时间单位长度为60 
    CAN_Init(CAN1,&CAN_InitStructure);          //波特率为:72M/2/60(1+3+2)=0.1 即波特率为100KBPs




    // CAN filter init 过滤器,注意,只接收主机发过来的数据,屏蔽其他数据
    CAN_FilterInitStructure.CAN_FilterNumber=1;                     //指定过滤器为1
    CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;   //指定过滤器为标识符屏蔽位模式
    CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;  //过滤器位宽为32位




    //CAN_FilterInitStructure.CAN_FilterIdHigh= (((u32)0x1314<<3)&0xFFFF0000)>>16;  
    CAN_FilterInitStructure.CAN_FilterIdHigh=0X00;                  //要过滤的ID高位 
    CAN_FilterInitStructure.CAN_FilterIdLow= (((u32)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF; //要过滤的ID低位 




    CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0xFFFF;            //过滤器屏蔽标识符的高16位值
    CAN_FilterInitStructure.CAN_FilterMaskIdLow=0xFFFF;             //过滤器屏蔽标识符的低16位值
    CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0;     //设定了指向过滤器的FIFO为0
    CAN_FilterInitStructure.CAN_FilterActivatinotallow=ENABLE;            //使能过滤器
    CAN_FilterInit(&CAN_FilterInitStructure);                       //按上面的参数初始化过滤器




    /* CAN FIFO0 message pending interrupt enable */ 
    CAN_ITConfig(CAN1,CAN_IT_FMP0, ENABLE);                         //使能FIFO0消息挂号中断
}




/* 发送两个字节的数据*/
u8 CAN_SetMsg(u8 Data1,u8 Data2)
{ 
    u8 mbox;
    u16 i=0; 
    CanTxMsg TxMessage;  




    TxMessage.StdId=0x0000;     //标准标识符为0x00
    TxMessage.ExtId=0x1311;     //扩展标识符0x1311,可以更改该标识符以示区分不同从机
    TxMessage.IDE=CAN_ID_EXT;   //使用扩展标识符
    TxMessage.RTR=CAN_RTR_DATA; //为数据帧
    TxMessage.DLC=2;            //消息的数据长度为2个字节
    TxMessage.Data[0]=Data1;    //第一个字节数据
    TxMessage.Data[1]=Data2;    //第二个字节数据 




    //发送数据
    mbox= CAN_Transmit(CAN1, &TxMessage);  
    while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))
        i++;    //等待发送结束
    if(i>=0XFFF)
        return 0;
    return 1;
}
u8 CAN_GetMsg(u8 *msg1,u8 *msg2)
{
    if(Rx_flag == 1)//发现数据
    {
        *msg1=RxBuf[0];
        *msg2=RxBuf[1];
        Rx_flag=0;//数据已经取走,可以更新数据
        return 1;
    }else
        return 0;
}
/* USB中断和CAN接收中断服务程序,USB跟CAN公用I/O,这里只用到CAN的中断。 */
void USB_LP_CAN1_RX0_IRQHandler(void)
{




  CanRxMsg RxMessage;




  RxMessage.StdId=0x00;
  RxMessage.ExtId=0x00;
  RxMessage.IDE=0;
  RxMessage.DLC=0;
  RxMessage.FMI=0;
  RxMessage.Data[0]=0x00;
  RxMessage.Data[1]=0x00;    




  CAN_Receive(CAN1,CAN_FIFO0, &RxMessage); //接收FIFO0中的数据  




  if(Rx_flag == 0)//数据已取走或者缓冲器为空
    {
        RxBuf[0]=RxMessage.Data[0];
        RxBuf[1]=RxMessage.Data[1];
        Rx_flag=1;//数据已经备好,等待取走
    }




}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
  • 221.
  • 222.
  • 223.
  • 224.
  • 225.

can.h文件

#ifndef __CAN_H
#define __CAN_H




#include "sys.h"




void CAN1_Init(void);
u8 CAN_SetMsg(u8 Data1,u8 Data2);
u8 CAN_GetMsg(u8 *msg1,u8 *msg2);




#endif /* __CAN_H */
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
主机相关代码

    这里主机代码大部分是和从机类似的,就只贴出不同的地方了。
can.c文件:

#include "can.h"




/* 在中断处理函数中返回 */
//__IO uint32_t ret = 0;




void CAN1_Init(void)
{
    ......//以上与从机部分相同




    //CAN filter init 过滤器,已经设置为任意,可以通过ExtId标识符区分从机代号
    CAN_FilterInitStructure.CAN_FilterNumber=1;                     //指定过滤器为1
    CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;   //指定过滤器为标识符屏蔽位模式
    CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;  //过滤器位宽为32位
    CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;                //过滤器标识符的高16位值
    CAN_FilterInitStructure.CAN_FilterIdLow=CAN_ID_EXT|CAN_RTR_DATA;//过滤器标识符的低16位值
    CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;            //过滤器屏蔽标识符的高16位值
    CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;             //过滤器屏蔽标识符的低16位值
    CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0;     //设定了指向过滤器的FIFO为0
    CAN_FilterInitStructure.CAN_FilterActivatinotallow=ENABLE;            //使能过滤器
    CAN_FilterInit(&CAN_FilterInitStructure);                       //按上面的参数初始化过滤器




    /* CAN FIFO0 message pending interrupt enable */ 
    CAN_ITConfig(CAN1,CAN_IT_FMP0, ENABLE);                         //使能FIFO0消息挂号中断
}




//接收数据缓冲器
u8 CAN_RX_BUF[CAN_RX_LEN]={0};     //接收缓冲,最大USART_REC_LEN个字节.
//接收标志位
u8 Rx_flag=0;
/* USB中断和CAN接收中断服务程序,USB跟CAN公用I/O,这里只用到CAN的中断。 */
void USB_LP_CAN1_RX0_IRQHandler(void)
{
    u8 i=0;
    CanRxMsg RxMessage;




    RxMessage.StdId=0x00;
    RxMessage.ExtId=0x00;
    RxMessage.IDE=0;
    RxMessage.DLC=0;
    RxMessage.FMI=0;




    CAN_Receive(CAN1,CAN_FIFO0, &RxMessage); //接收FIFO0中的数据  




    if(Rx_flag == 0)//数据已取走或者缓冲器为空
    {
        if((RxMessage.DLC) == 2)//是否收到2位字节数据
        {
             CAN_RX_BUF[0]=RxMessage.Data[0];
             CAN_RX_BUF[1]=RxMessage.Data[1];     
        }
    }




} 




/* 发送两个字节的数据*/
u8 CAN_SendMsg(u8* data1, u8* data2)
{ 
    u8 mbox;
    u16 i=0; 
    CanTxMsg TxMessage;  




    TxMessage.StdId=0x0000;     //标准标识符为0x00
    TxMessage.ExtId=0x1314;     //扩展标识符0x0000
    TxMessage.IDE=CAN_ID_EXT;   //使用扩展标识符
    TxMessage.RTR=CAN_RTR_DATA; //为数据帧
    TxMessage.DLC=2;            //消息的数据长度为2个字节
    TxMessage.Data[0]=Data1;    //第一个字节数据
    TxMessage.Data[1]=Data2;    //第二个字节数据 




    //发送数据
    mbox= CAN_Transmit(CAN1, &TxMessage);  
    while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))
        i++;    //等待发送结束
    if(i>=0XFFF)
        return 0;//发送失败
    return 1;//发送成功 
}
u8 CAN_GetMsg(u8 *msg1,u8 *msg2)
{
    if(Rx_flag == 1)//发现数据
    {
        *msg1=CAN_RX_BUF[0];
        *msg2=CAN_RX_BUF[1];
        Rx_flag=0;//数据已经取走,可以更新数据
        return 1;
    }else
        return 0;
}
void Clear_canBuffer(void)
{
    Rx_flag=0;//清楚接收标志位
    memset(CAN_RX_BUF, 0, sizeof(u8)*CAN_RX_LEN);//清空缓冲区
}
u8 Check_canRX(void)
{
    return (Rx_flag == 6);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.

can.h文件:

#ifndef __CAN_H
#define __CAN_H




#include "sys.h"
#include "string.h"




#define CAN_RX_LEN          30          //定义最大接收字节数 




extern u8  CAN_RX_BUF[CAN_RX_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 




void CAN1_Init(void);
u8 CAN_SendMsg(u8* data1, u8* data2);
u8 CAN_GetMsg(u8 *msg1,u8 *msg2);




#endif /* __CAN_H */
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.



whaosoft aiot  http://143ai.com