STM32学习笔记【36.CAN收发实验】

文章目录

  • [1. 实验目的](#1. 实验目的)
  • [2. 硬件清单](#2. 硬件清单)
  • [3. 硬件接线](#3. 硬件接线)
  • [4. 构思环节](#4. 构思环节)
    • [1. 框架搭建](#1. 框架搭建)
    • [2. CAN内容](#2. CAN内容)
    • [3. 主函数](#3. 主函数)
  • [5. 出现的问题](#5. 出现的问题)
  • [6. 代码展示](#6. 代码展示)
  • [7. 实验结果](#7. 实验结果)

1. 实验目的

  1. 使用环回模式实现自发自收;
  2. 两个CAN设备实现收发。

2. 硬件清单

  • TJA1050(2个)
  • 上官二号(2个)
  • ST-Link
  • USB转TTL

本实验上官二号一个也可以实现

3. 硬件接线

4. 构思环节

1. 框架搭建

在串口打印功能实验基础上做,建立CAN收发实验,BSP文件夹中有按键key文件夹、can文件夹,并加载相关.c和.h文件。

加入CAN的驱动文件

c 复制代码
void can_init(void)
{
}

void HAL_CAN_MspInit(句柄)
{
}

void can_send_data(uint32_t id, uint8_t *buf, uint8_t len)
{
}

// uint8_t *buf接收数据用指针接收
void can_recvive_data(uint8_t *buf)
{
}

2. CAN内容

c 复制代码
void can_init(void)
{
    // 参数配置
    对象是CAN1
    工作模式:回环模式
    预分频器:4
    TS1 = 9Tq(8+1)
    TS2 = 8Tq(7+1)
    SyncJumpWidth = CAN_SJW_1TQ

    其它6个为DISABLE
    HAL_CAN_Init(句柄);
    
    // 过滤器配置
    // 参数配置
    工作模式:掩码模式
    配置开关位:32位
    FilterIdLow = 0;
    MaskIdHigh = 0;
    MaskIdLow = 0;
    
    FilterBack = 0;
    FiletrFIFOAssignment = CAN_FILTER_FIFO0;
    FilterActivation = ENABLE;
    SlaveStartFilterBank = 14;
    HAL_CAN_ConfigFiletr(句柄);
    
    // can开始工作
    HAL_CAN_Start(句柄);
}

void HAL_CAN_MspInit(句柄)
{
    // 打开CAN1时钟
    // 打开GPIO时钟
    // 对GPIO初始化,看手册
    TX引脚PA12
    复用推挽输出
    上拉
    高速
    HAL_GPIO_Init();

    RX引脚11
    复用输入
    
}

// 发送函数
void can_send_data(uint32_t id, uint8_t *buf, uint8_t len)
{
    // 定义邮箱
    uint32_t tx_mail = CAN_TX_MAILBOX0;
    // 数据帧参数配置
    ExtId = id; // 拓展帧
    DLC = len;
    IDE = CAN_ID_EXT;
    RTR = 数据帧
    HAL_CAN_AddTxMessage(句柄,数据帧,buf, 指定邮箱);
    
    // 等待数据发完
   while(HAL_CAN_GetMailboxesFreeLevel(句柄) != 3); // 三个邮箱都空,数据就发完了
   
   // 打印发的buf数据内容
   uint8_t i = 0;
   printf("发送数据:\r\n");
   for(i = 0; i < len; i++)
      printf("%X", buf[i]);
   printf("\r\n");
}

// 接收函数
// uint8_t *buf接收数据用指针接收
uint8_t can_recvive_data(uint8_t *buf)
{
      // 定义数据帧首位
      // 先看邮箱是否有数据
     if(HAL_CAN_GetRxFifoFillLevel(can句柄, CAN_RX_FIFO0) == 0)
     return 0;
      HAL_CAN_GetRxMessage(can句柄, CAN_RX_FIFO0, 数据头句柄, buf);
   
   uint8_t i = 0;
   printf("接收数据:\r\n");
   for(i = 0; i < rx_header.DLC; i++)
      printf("%X", buf[i]);
   printf("\r\n");
   
   return rx_header.DLC;
}

3. 主函数

main.c

c 复制代码
// 发送数据数组
uint8_t data_send[8] = {
   0x00, 0x11, 0x22, 0x33,
   0x44, 0x55, 0x66, 0x77,
   0x88
};
// 接收数据数组
uint8_t data_recv[8];

can_init();
key_init();

uint8_t i = 0;
// 按下按键1,发送数据
while(1)
{
if(key_scan() == 1)
{
    for(i = 0; i < 8; i++)
         data_send[i]++;
         
    can_send_data(0x12345678, 指定发送的数据data_send, len 8);
}

can_receive_data(data_recv);
}

如何接到总线上?

将工作模式从回环模式改为NORMAL,再编译烧录。

TJ1050板子一定要5V供电,3.3V无法正常工作。

5. 出现的问题

问题:

解决:

can_handle.Init.TimeSeg1 = CAN_BS1_9TQ;

can_handle.Init.TimeSeg2 = CAN_BS2_8TQ;

6. 代码展示

CAN总线代码

can.c

c 复制代码
#include "can.h"
#include "stdio.h"

CAN_HandleTypeDef can_handle = {0};
void can_init(void)
{
    can_handle.Instance = CAN1;
    can_handle.Init.Mode = CAN_MODE_LOOPBACK;
    can_handle.Init.Prescaler = 4;
    can_handle.Init.TimeSeg1 = CAN_BS1_9TQ;
    can_handle.Init.TimeSeg2 = CAN_BS2_8TQ;
    can_handle.Init.SyncJumpWidth = CAN_SJW_1TQ;
    
    can_handle.Init.AutoBusOff = DISABLE;
    can_handle.Init.AutoRetransmission = DISABLE;
    can_handle.Init.AutoWakeUp = DISABLE;
    can_handle.Init.ReceiveFifoLocked = DISABLE;
    can_handle.Init.TimeTriggeredMode = DISABLE;
    can_handle.Init.TransmitFifoPriority = DISABLE;
    HAL_CAN_Init(&can_handle);
    
    // 配置过滤器
    CAN_FilterTypeDef filter_config = {0};
    filter_config.FilterMode = CAN_FILTERMODE_IDMASK;      // 掩码模式
    filter_config.FilterScale = CAN_FILTERSCALE_32BIT;    // 32位扩展帧
    filter_config.FilterIdLow = 0;                         // 期望ID
    // 过滤器的开关,用来指定FilterId中哪些位必须精准匹配,
    //置1必须匹配
    filter_config.FilterMaskIdHigh = 0;                    // 期望的掩码值,
    filter_config.FilterMaskIdLow = 0;                     // 掩码值

    filter_config.FilterBank = 0;                          // 过滤器组编号
    filter_config.FilterFIFOAssignment = CAN_FILTER_FIFO0; // FIFO分配
    filter_config.FilterActivation = CAN_FILTER_ENABLE;    // 过滤器激活
    HAL_CAN_ConfigFilter(&can_handle, &filter_config);
    
    // 开始工作
    HAL_CAN_Start(&can_handle);

}

void HAL_CAN_MspInit(CAN_HandleTypeDef *hcan)
{
    GPIO_InitTypeDef gpio_initstruct;

    // 打开can和gpio时钟
    __HAL_RCC_CAN1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();                                // 使能GPIOB时钟
    
    // 调用GPIO初始化函数
    gpio_initstruct.Pin = GPIO_PIN_12;               
    gpio_initstruct.Mode = GPIO_MODE_AF_PP;      
    gpio_initstruct.Pull = GPIO_PULLUP;              
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;    
    HAL_GPIO_Init(GPIOA, &gpio_initstruct);
    
    gpio_initstruct.Pin = GPIO_PIN_11;
    gpio_initstruct.Mode = GPIO_MODE_AF_INPUT;
    HAL_GPIO_Init(GPIOA, &gpio_initstruct);
    
}

// 发送数据
void can_send_data(uint32_t id, uint8_t *buf, uint8_t len)
{
    // 定义邮箱
    uint32_t tx_mail = CAN_TX_MAILBOX0;
    CAN_TxHeaderTypeDef pheader_handle = {0};
    pheader_handle.ExtId = id;
    pheader_handle.DLC = len;
    pheader_handle.IDE = CAN_ID_EXT;
    pheader_handle.RTR = CAN_RTR_DATA;
    HAL_CAN_AddTxMessage(&can_handle, &pheader_handle, buf, &tx_mail); // 发送
    
    // 等待数据发完,三个邮箱都空,表示数据发完
    while(HAL_CAN_GetTxMailboxesFreeLevel(&can_handle) != 3);
    
    // 打印数据内容
    uint8_t i = 0;
    printf("发送数据:\r\n");
    for(i = 0; i < len; i++)
        printf("%X ", buf[i]);
    printf("\r\n");
}

// 接收数据
uint8_t can_recv_data(uint8_t *buf)
{
    CAN_RxHeaderTypeDef recv_header = {0};
    // 先看邮箱是否有数据
    if(HAL_CAN_GetRxFifoFillLevel(&can_handle, CAN_RX_FIFO0) == 0)
        return 0;
    HAL_CAN_GetRxMessage(&can_handle, CAN_RX_FIFO0, &recv_header, buf); // 接收
    
    uint8_t i = 0;
    printf("接收数据:\r\n");
    for(i = 0; i < recv_header.DLC; i++)
        printf("%X ", buf[i]);
    printf("\r\n");
    
    return recv_header.DLC;
}

can.h

c 复制代码
#ifndef __CAN_H__
#define __CAN_H__

#include "sys.h"

void can_init(void);
void can_send_data(uint32_t id, uint8_t *buf, uint8_t len);
uint8_t can_recv_data(uint8_t *buf);

#endif

按键代码

key.c

c 复制代码
#include "key.h"
#include "delay.h"

// 初始化GPIO
void key_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    
    // 打开时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();                                // 使能GPIOB时钟
    
    // 调用GPIO初始化函数
    gpio_initstruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;               // KEY对应的引脚
    gpio_initstruct.Mode = GPIO_MODE_INPUT;                      // 浮空输入
    gpio_initstruct.Pull = GPIO_PULLUP;                          // 上拉
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;                // 高速
    HAL_GPIO_Init(GPIOA, &gpio_initstruct);
    
}

// 按键扫描
uint8_t key_scan(void)
{
    // 检测按键是否按下
    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
    {
        // 消抖
        delay_ms(10);
        // 再次检测按键是否被按下
        if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
        {
            // 如果是按下状态,等待按键松开
            while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);
            // 返回按键值
            return 1;
        }
    }
    
    // 检测按键是否按下
    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)
    {
        // 消抖
        delay_ms(10);
        // 再次检测按键是否被按下
        if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)
        {
            // 如果是按下状态,等待按键松开
            while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);
            // 返回按键值
            return 2;
        }
    }
    // 返回默认值
    return 0;
}

key.h

c 复制代码
 #ifndef _KEY_H
#define _KEY_H

#include "sys.h"

void key_init(void);
uint8_t key_scan(void);

#endif

主函数

main.c

c 复制代码
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "key.h"
#include "can.h"


uint8_t send_data[8] = {
    0x11, 0x22, 0x33,0x44, 
    0x55, 0x66, 0x77,0x88
};

uint8_t recv_data[8];

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    led_init();
    uart1_init(115200);
    key_init();
    can_init();
    printf("hello world! \r\n");
    
    uint8_t i = 0;
    
    while(1)
    {
        // 如果按下按键1,发送数据
        if(key_scan() == 1)
        {
            for(i = 0; i < 8; i++)
                  send_data[i]++;
            
            can_send_data(0x12345678, send_data, 8);
        }
        can_recv_data(recv_data);
    }
}

7. 实验结果

先按下复位键,再按下按键1,发送数据与接收数据内容相同。

再按下按键1,与前面的数据相比,全部+1。

相关推荐
渣渣灰飞4 小时前
MySQL 系统学习 第五阶段:企业级 MySQL 实战开发 第一章:企业数据库设计规范
数据库·学习·mysql
疯狂打码的少年4 小时前
【软件工程】需求工程:需求规格说明与需求验证
笔记·软件工程
吃着火锅x唱着歌5 小时前
Effective C++ 学习笔记 条款33 避免遮掩继承来的名称
c++·笔记·学习
学计算机的计算基5 小时前
LeetCode 图论四题精讲:BFS、拓扑排序、Trie 树的模板与优化
java·笔记·算法
浩瀚地学5 小时前
【面试算法笔记】0202-链表-基本功能实现
java·经验分享·笔记·算法·面试
你想知道什么?5 小时前
Matplotlib学习笔记
笔记·学习·matplotlib
m0_626535205 小时前
qwen2.5vl学习
学习
小鱼儿电子6 小时前
58-基于物联网的老年人医嘱联动健康监测装置
stm32·物联网·腾讯云·健康手环
AOwhisky6 小时前
Linux(CentOS)系统管理入门笔记:第二期核心知识点自测与详解
linux·笔记·centos
铅笔侠_小龙虾6 小时前
Rust 学习(8)-切片类型
学习·rust·c#