文章目录
- [1. 实验目的](#1. 实验目的)
- [2. 硬件清单](#2. 硬件清单)
- [3. 硬件接线](#3. 硬件接线)
- [4. 构思环节](#4. 构思环节)
-
- [1. 框架搭建](#1. 框架搭建)
- [2. CAN内容](#2. CAN内容)
- [3. 主函数](#3. 主函数)
- [5. 出现的问题](#5. 出现的问题)
- [6. 代码展示](#6. 代码展示)
- [7. 实验结果](#7. 实验结果)
1. 实验目的
- 使用环回模式实现自发自收;
- 两个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。
