重学STM32---(十)之CAN通信(二)
前言
CAN协议是非常难的,但是在stm32中却是简单的,只需要我们配置寄存器即可,,,即使这样,我在学习的时候也遇到了许多困难
程序编写
1、开时钟,不用说
2、设置GPIO口,,CAN_TX复用推挽输出,CAN_RX上拉或浮空输入
3、CAN初始化了,分三步
- 一般设置
	/*对CAN初始化*/
    CAN1->MCR |= 1 << 15;          //软复位
    CAN1->MCR &= ~(1 << 1);    //退出睡眠模式  (郁闷,忘退出睡眠模式了,折腾了半天)
    CAN1->MCR |= 1 << 0;           //设置初始化模式 必须
    while(!(CAN1->MSR & 1 << 0));   //等待确认初始化
    CAN1->MCR |= 0 << 7;           //禁止时间触发模式
    CAN1->MCR |= 0 << 6;           //禁止自动离线
    CAN1->MCR |= 0 << 5;            //禁止自动唤醒
    CAN1->MCR |= 1 << 4;            //报文只传送一次,禁止自动重传
    CAN1->MCR |= 0 << 2;            //优先级由报文的标识符决定
- 时序设置(时序也在初始化模式中)
/* 设置CAN位时序*/
    CAN1->BTR &= 0x00000000;        //
    CAN1->BTR |= mode << 30;        //正常模式(0)或环回模式(1)可见CAN_BTR寄存器
    CAN1->BTR |= sjw << 24;            //设置sjw
    CAN1->BTR |= ts2 << 20;            //
    CAN1->BTR |= ts1 << 16;            //
    CAN1->BTR |= brp << 0;
    CAN1->MCR &= ~(1 << 0);            //退出初始化
    while(CAN1->MSR & 1 << 0);        //确认退出初始化
- 滤波器设置
/* 设置过滤器组 */
    CAN1->FMR |= 1 << 0;            //过滤器组设置在初始化模式(记得必须设置初始化模式)
    CAN1->FA1R &= ~(1 << 0);        //过滤器禁用
    CAN1->FM1R &= ~(1<< 0);   //过滤器组工作在标识符屏蔽位模式
    CAN1->FS1R |= 1<<0;         //过滤器组位宽为32位
    CAN1->FFA1R &= ~(1 << 0);  //关联到fifo0
    CAN1->sFilterRegister[0].FR1 = 0X00000000;     //全都为0 ,什么数据都接受
    CAN1->sFilterRegister[0].FR2 = 0X00000000;
    CAN1->FA1R |= 1 << 0;        //激活过滤器
    //退出滤波器初始化模式  (记得要退出,否则发送成功却接受不到,不要问我为什么这么清楚。。。)
    CAN1->FMR &= 0 << 0;
4、发送消息函数和得到发送状态函数
 见下写程序
  u8 CAN_TX_Msg(u8 ide, u32 id, u8 rtr, u8 len, u8 *data)
  u8 CAN_TX_Statt(u8 mbox)
5、接收函数
// 参数要用指针,开始我ide没用指针,在其他函数调用这个函数ide的值是不确定的
void CAN_RX_Msg(u8 *ide, u32* id, u8* rtr, u8* len, u8 *data, u8 fifox)
{
    *ide = (CAN1->sFIFOMailBox[fifox].RIR & 1<<2)>>2;
    if(*ide == 0)
    {
        *id = CAN1->sFIFOMailBox[fifox].RIR >> 21;
    }
    else
    {
        *id = CAN1->sFIFOMailBox[fifox].RIR >> 3;
    }
    *rtr = (CAN1->sFIFOMailBox[fifox].RIR & 1<<1)>>1;
    *len = CAN1->sFIFOMailBox[fifox].RDTR & 0x0f;
        //接收数据
    data[0]=CAN1->sFIFOMailBox[fifox].RDLR&0XFF;
    data[1]=(CAN1->sFIFOMailBox[fifox].RDLR>>8)&0XFF;
    data[2]=(CAN1->sFIFOMailBox[fifox].RDLR>>16)&0XFF;
    data[3]=(CAN1->sFIFOMailBox[fifox].RDLR>>24)&0XFF;
    data[4]=CAN1->sFIFOMailBox[fifox].RDHR&0XFF;
    data[5]=(CAN1->sFIFOMailBox[fifox].RDHR>>8)&0XFF;
    data[6]=(CAN1->sFIFOMailBox[fifox].RDHR>>16)&0XFF;
    data[7]=(CAN1->sFIFOMailBox[fifox].RDHR>>24)&0XFF;
    if(fifox==0)CAN1->RF0R|=0X20;//释放FIFO0邮箱
    else if(fifox==1)CAN1->RF1R|=0X20;//释放FIFO1邮箱     
}
主代码
只有一块开发板,只能测试回环模式
main.c
主函数
u8 txBuff[8] ;
int main()
{
    u8 key,time = 0;
    u8 i;
    u8 data = 0;
    u8 reBuff[8];
    Stm32_Clock_Init(9);    //系统时钟设置
    uart_init(72,115200);    //串口初始化为115200
    delay_init(72);    //延时初始化
    LED_Init();    //初始化与LED连接的硬件接口
    LCD_Init();
    KEY_Init();
    LCD_ShowString(10,10,100,16,16,"CAN回环模式实验");
    LCD_ShowString(10,30,100,16,16,"KEY1:发送数据");
    CAN_Init(1,8,9,4,1);
    while(1)
    {
        key = KEY_Scan();     //查询按键
        if(key == KEY1)    //按键1发送数据
        {
            for(i = 0; i < 8; i++)
            txBuff[i] = data++;
            CAN_Send_Msg((u8*)txBuff, 8);
            // delay_ms(10);
            if(CAN_Receive_Msg(reBuff))
            {
                for(i = 0; i < 8; i++)
                {
                    printf ("CAN发送数据为:%d\r\n\r\n", reBuff[i]);
                    txBuff[i] = 0;
                    // LCD_ShowxNum(10 + i*10, 100,reBuff[i], 3, 16, 0x80);
                }
            }
        }
       time++;
       delay_ms(100);
       if(time>10)
       {
           LED0 = !LED0 ;
           time = 0;
       }
    }
}
can.c
#include "can.h"
//#include "stm32f10x.h"
#include "sys.h"
#define CAN_INT_ENABLE    0
void CAN_Init(u8 sjw, u8 ts2, u8 ts1,u16 brp, u8 mode)
{
    sjw -= 1;
    ts1 -= 1;
    ts2 -= 1;
    brp -= 1;
    /*开启CAN_TX与CAN_RX的时钟PB*/
    RCC->APB2ENR |= 1 << 3;
    /*开启CAN时钟*/
    RCC->APB1ENR |= 1 << 25;
    /*设置PB11上拉输入,PB12推挽输出*/
    GPIOB->CRH &= ~(0xff << 12);
    GPIOB->CRH |= 0xB3 << 12;
    GPIOB->ODR |= 1<<11;
    /*对CAN初始化*/
    CAN1->MCR |= 1 << 15;          //软复位
    CAN1->MCR &= ~(1 << 1);
    CAN1->MCR |= 1 << 0;           //设置初始化模式
    while(!(CAN1->MSR & 1 << 0));   //等待确认初始化
    CAN1->MCR |= 0 << 7;           //禁止时间触发模式
    CAN1->MCR |= 0 << 6;           //禁止自动离线
    CAN1->MCR |= 0 << 5;            //禁止自动唤醒
    CAN1->MCR |= 1 << 4;            //报文只传送一次,禁止自动重传
    CAN1->MCR |= 0 << 2;            //优先级由报文的标识符决定
    /* 设置CAN位时序*/
    CAN1->BTR &= 0x00000000;        //
    CAN1->BTR |= mode << 30;        //正常模式或环回模式
    CAN1->BTR |= sjw << 24;            //设置sjw
    CAN1->BTR |= ts2 << 20;            //
    CAN1->BTR |= ts1 << 16;            //
    CAN1->BTR |= brp << 0;
    CAN1->MCR &= ~(1 << 0);            //退出初始化
    while(CAN1->MSR & 1 << 0);        //确认退出初始化
    /* 设置过滤器组 */
    CAN1->FMR |= 1 << 0;            //过滤器组设置在初始化模式
    CAN1->FA1R &= ~(1 << 0);        //过滤器禁用
    CAN1->FM1R &= ~(1<< 0);   //过滤器组工作在标识符屏蔽位模式
    CAN1->FS1R |= 1<<0;         //过滤器组位宽为32位
    CAN1->FFA1R &= ~(1 << 0);  //关联到fifo0
    CAN1->sFilterRegister[0].FR1 = 0X00000000;     //全都为0 ,什么数据都接受
    CAN1->sFilterRegister[0].FR2 = 0X00000000;
    CAN1->FA1R |= 1 << 0;        //激活过滤器
    CAN1->FMR &= 0 << 0;         //退出滤波器初始化模式
#if CAN_INT_ENABLE
    CAN1->IER |= 1 << 1;   //FIFO0消息挂号中断使能
    MY_NVIC_Init(2,2,USB_LP_CAN1_RX0_IRQn,2);
#endif
}
// ide 0:标准帧  1:扩展帧
// id  标识id
// rtr  0:数据帧  1:远程帧
// len 数据长度
// *data 指向要发送数据的指针
u8 CAN_TX_Msg(u8 ide, u32 id, u8 rtr, u8 len, u8 *data)
{
    u8 mbox;
    if(CAN1->TSR & 1<<26) mbox = 0;   //邮箱0为空
    if(CAN1->TSR & 1<<27) mbox = 1;
    if(CAN1->TSR & 1<<28) mbox = 2;
    else return 0xff;
    /*为标准id时*/
    if(ide == 0)
    {
        id = id & 0x07ff;          //取11位
        id <<= 21;
    }
    /*为扩展ID时*/
    if(ide == 1)
    {
        id <<= 3;
    }
    CAN1->sTxMailBox[mbox].TIR = 0;
    CAN1->sTxMailBox[mbox].TIR |= ide << 2;        //
    CAN1->sTxMailBox[mbox].TIR |= id ;             //
    CAN1->sTxMailBox[mbox].TIR &= ~(1<<1);         //
    CAN1->sTxMailBox[mbox].TDTR |= len&0xf;        //
    CAN1->sTxMailBox[mbox].TDLR =((u32)data[0]) | ((u32)data[1]<<8) | ((u32)data[2]<<16) | ((u32)data[3]<<24);
    CAN1->sTxMailBox[mbox].TDHR =((u32)data[4]) | ((u32)data[5]<<8) | ((u32)data[6]<<16) | ((u32)data[7]<<24);
    CAN1->sTxMailBox[mbox].TIR |= 1 << 0;
    return     mbox ;
}
//判断发送是成功
//state = 7成功
u8 CAN_TX_Statt(u8 mbox)
{
    u8 state;
    switch(mbox)
    {
        case 0:
            state |= (CAN1->TSR & 1<<0) | (CAN1->TSR & 1<<1) |((CAN1->TSR & 1<<26)>>24);
            break ;
        case 1:
            state |= (CAN1->TSR & 1<<8)>>8;
            state |= (CAN1->TSR & 1<<9)>>9;
            state |= (CAN1->TSR & 1<<27)>>25;
            break ;
        case 2:
            state |= (CAN1->TSR & 1<<16)>>16;
            state |= (CAN1->TSR & 1<<17)>>17;
            state |= (CAN1->TSR & 1<<28)>>26;
            break ;
        default :
            state = 0x05;
    }
    return state ;
}
//得到在FIFO0/FIFO1中接收到的报文个数.
//fifox:0/1.FIFO编号;
//返回值:FIFO0/FIFO1中的报文个数.
u8 CAN_Msg_Pend(u8 fifox)
{
    if(fifox==0)return CAN1->RF0R&0x03;
    else if(fifox==1)return CAN1->RF1R&0x03;
    else return 0;
}
// 参数同上
void CAN_RX_Msg(u8 *ide, u32* id, u8* rtr, u8* len, u8 *data, u8 fifox)
{
    *ide = (CAN1->sFIFOMailBox[fifox].RIR & 1<<2)>>2;
    if(*ide == 0)
    {
        *id = CAN1->sFIFOMailBox[fifox].RIR >> 21;
    }
    else
    {
        *id = CAN1->sFIFOMailBox[fifox].RIR >> 3;
    }
    *rtr = (CAN1->sFIFOMailBox[fifox].RIR & 1<<1)>>1;
    *len = CAN1->sFIFOMailBox[fifox].RDTR & 0x0f;
        //接收数据
    data[0]=CAN1->sFIFOMailBox[fifox].RDLR&0XFF;
    data[1]=(CAN1->sFIFOMailBox[fifox].RDLR>>8)&0XFF;
    data[2]=(CAN1->sFIFOMailBox[fifox].RDLR>>16)&0XFF;
    data[3]=(CAN1->sFIFOMailBox[fifox].RDLR>>24)&0XFF;
    data[4]=CAN1->sFIFOMailBox[fifox].RDHR&0XFF;
    data[5]=(CAN1->sFIFOMailBox[fifox].RDHR>>8)&0XFF;
    data[6]=(CAN1->sFIFOMailBox[fifox].RDHR>>16)&0XFF;
    data[7]=(CAN1->sFIFOMailBox[fifox].RDHR>>24)&0XFF;
      if(fifox==0)CAN1->RF0R|=0X20;//释放FIFO0邮箱
    else if(fifox==1)CAN1->RF1R|=0X20;//释放FIFO1邮箱     
}
//
u8 CAN_Send_Msg(u8* data, u8 len)
{
    u8 mbox;
    u16 i = 0;
    mbox = CAN_TX_Msg(0,0X12,0,len,data);
    while((CAN_TX_Statt(mbox) != 7) && (i < 0xfff)) i++;
    return 0;
}
//返回0 接收失败
u8 CAN_Receive_Msg(u8* data)
{
    u32 id;
    u8 ide = 0,rtr = 0,len = 0;
    while(!CAN_Msg_Pend(0));
    CAN_RX_Msg(&ide,&id,&rtr,&len,data,0);
    if( id!=0x12 || ide!=0 || rtr!=0) return 0;
    else return len;
}
测试
已在本地测试OK。
关注公众号"小败日记",搬砖过程遇到的问题,大家一起探讨,资源共享

重学STM32---(十)之CAN通信(二)的更多相关文章
- 重学STM32---(十) ——CAN通信(二)
		CAN协议是非常难的,但是在stm32中却是简单的,只需要我们配置寄存器即可,,,即使这样,我在学习的时候也遇到了许多困难 1.开时钟,不用说 2.设置GPIO口,,CAN_TX复用推挽输出,CAN_ ... 
- 重学STM32---(九) ——CAN通信(一)
		一.CAN简介 1.CAN是什么? CAN 是 Controller Area Network的缩写(以下称为 CAN),是 ISO 国际标准化的串行通信协议. 2.CAN特点 (1) 多主控制 ( ... 
- 重学c#系列——c#运行原理(二)
		前言 c# 是怎么运行的呢?是否和java一样运行在像jvm的虚拟机上呢?其实差不多,但是更广泛. c# 运行环境不仅c#可以运行,符合.net framework 开发规范的都可以运行. c# 程序 ... 
- 重学c#系列——datetime 和 datetimeoffset[二十一]
		前言 简单介绍一下datetime和 datetimeoffset. 正文 了解一个国家的文化,就要了解一个国家的历史. 要了解datetimeoffset,那么很有必要了解一下datetime. 表 ... 
- 从头开始学JavaScript (十二)——Array类型
		原文:从头开始学JavaScript (十二)--Array类型 一.数组的创建 注:ECMAscript数组的每一项都可以保存任何类型的数据 1.1Array构造函数 var colors = ne ... 
- MINA、Netty、Twisted一起学(十二):HTTPS
		由于HTTPS协议是由HTTP协议加上SSL/TLS协议组合而成,在阅读本文前可以先阅读一下HTTP服务器和SSL/TLS两篇博文,本文中的代码也是由这两篇博文中的代码组合而成. HTTPS介绍 上一 ... 
- [老老实实学WCF] 第十篇 消息通信模式(下) 双工
		老老实实学WCF 第十篇 消息通信模式(下) 双工 在前一篇的学习中,我们了解了单向和请求/应答这两种消息通信模式.我们知道可以通过配置操作协定的IsOneWay属性来改变模式.在这一篇中我们来研究双 ... 
- 12天,这本《重学Java设计模式》PDF书籍下载量9k,新增粉丝1400人,Github上全球推荐榜!
		作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 
- 重学 Java 设计模式:实战适配器模式
		作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 擦屁屁纸80%的面积都是保护手的! 工作到3年左右很大一部分程序员都想提升自己的技术 ... 
随机推荐
- 跨 Docker 宿主机网络 overlay 类型
			跨 Docker 宿主机网络 overlay 类型 前言 a. 本文主要为 Docker的视频教程 笔记. b. 环境为 三台 CentOS 7.0 虚拟机 (Vmware Workstation 1 ... 
- UVA 1599 Ideal Path(双向bfs+字典序+非简单图的最短路+队列判重)
			https://vjudge.net/problem/UVA-1599 给一个n个点m条边(2<=n<=100000,1<=m<=200000)的无向图,每条边上都涂有一种颜色 ... 
- linux 上添加多个jdk
			1. 首先将你需要上传的jdk 上传并解压 2.你可以自定义解压的路径 3. alternatives --install /usr/bin/java java /usr/java/jdk1.7.0_ ... 
- 『Python』matplotlib常用图表
			这里简要介绍几种统计图形的绘制方法,其他更多图形可以去matplotlib找examples魔改 1. 柱状图 柱状图主要是应用在定性数据的可视化场景中,或是离散数据类型的分布展示.例如,一个本科班级 ... 
- nginx 配置文件(支持thnkphp3.2~5)
			server { listen 8080 ; server_name localhost; set $root /var/www/myweb; #listen 443 ssl; #ssl_certif ... 
- Windows10通过WSL编译jdk12
			Windows使用WSL编译OpenJDK 安装Ubuntu以及配置国内镜像 首选确保windows10已经安装了ubuntu 更换ubuntu20.04国内镜像,这里我选择的是阿里云镜像 sudo ... 
- Sentry 监控 - Snuba 数据中台架构简介(Kafka+Clickhouse)
			系列 1 分钟快速使用 Docker 上手最新版 Sentry-CLI - 创建版本 快速使用 Docker 上手 Sentry-CLI - 30 秒上手 Source Maps Sentry For ... 
- C#开发BIMFACE系列45 服务端API之创建离线数据包
			BIMFACE二次开发系列目录 [已更新最新开发文章,点击查看详细] BIMFACE的常规应用方式有公有云与私有化部署两种方式,并且浏览模型或者图纸需要使用ViewToken,ViewToke ... 
- jenkins容器内安装python3
			前言 很多小伙伴可能在考虑 jenkins 拉取了 github 上的代码后,发现还越少 python3 环境,那能怎么办呢? 咨询了一位运维朋友给我的答案是,将 python3 挂载到容器工作目录上 ... 
- 2020.5.16-ICPC Central Europe Regional Contest 2019
			A. ABB #include <bits/stdc++.h> using namespace std; #define PB push_back #define ZERO (1e-10) ... 
