HC32F103BCB使用SPI获取AS5040编码器数据

news2024/10/7 14:33:41

1.AS5040介绍

2.硬件电路

硬件上使用SSI通信方式连接。

3.配置硬件SPI

查看手册,AS5040时序

可以看到在空闲阶段不发生数据传输的时候时钟(CLK)和数据(DO)都保持高电位(tCLKFE阶段),在第一个脉冲的下降沿触发编码器载入发送数据,然后每一个时钟脉冲的上升沿编码器送出数据,数据的高位在前,低位在后,共发送10个数据(对应其分辨率10BIT),后面是6BIT校验数据;当传送完所有的位数以后时钟回到高电平,数据也对应回到高电平,直到下一个传输周期到达。

谢谢此文章作者的分享

stm32使用SPI获取磁性单圈绝对值编码器数据 - 知乎​​​​​​​

代码操作:

/*
*********************************************************************************************************
*
*	模块名称 : BSP_AS5040.C
*	文件名称 : 编码器驱动模块
*	版    本 : V0.1
*	说    明 :

在空闲阶段不发生数据传输的时候时钟(CLK)和数据(DO)都保持高电位(tCLKFE阶段),
在第一个脉冲的下降沿触发编码器载入发送数据
然后每一个时钟脉冲的上升沿编码器送出数据,数据的高位在前,低位在后,共发送10个数据(对应其分辨率10BIT)
当传送完所有的位数以后时钟回到高电平,数据也对应回到高电平,直到下一个传输周期到达。

对比四种模式,只有当CPOL配置成“0”和CPHA配置成“1”的时候 才符合每一个时钟脉冲的上升沿编码器送出数据的情况。

使用SPI获取AS5040磁性单圈绝对值编码器数据。 10bit+6bit
*********************************************************************************************************
*/
#include "bsp.h"

/* GPIO口对应配置 */

//绝对值编码器  CSN引脚     A4  黄色 29				--SPI1_NSS
#define ABSCSN_PORT         GPIOA
#define ABSCSN_RCC	        RCC_APB2Periph_GPIOA
#define ABSCSN_PIN          GPIO_Pin_4

//绝对值编码器  CLK引脚     A5  绿色 30				---SPI1_SCK
#define ABSCLK_PORT         GPIOA
#define ABSCLK_RCC	        RCC_APB2Periph_GPIOA
#define ABSCLK_PIN          GPIO_Pin_5

//绝对值编码器  DO引脚     A6  蓝色 31				---SPI1_MISO
#define ABSDO_PORT         GPIOA
#define ABSDO_RCC	         RCC_APB2Periph_GPIOA
#define ABSDO_PIN          GPIO_Pin_6

//时钟线高低电平
#define ABSCLK_H            GPIO_SetBits(ABSCLK_PORT,ABSCLK_PIN)
#define ABSCLK_L            GPIO_ResetBits(ABSCLK_PORT,ABSCLK_PIN)

//片选高低电平
#define ABSCSN_H            GPIO_SetBits(ABSCSN_PORT,ABSCSN_PIN)
#define ABSCSN_L            GPIO_ResetBits(ABSCSN_PORT,ABSCSN_PIN)

//数据线读
#define READABS_DO          GPIO_ReadInputDataBit(ABSDO_PORT,ABSDO_PIN)

//函数声明
uint16_t SPI1_ReadWriteByte(uint8_t TxData);

void AbsEncoder_Init(void)
{
	
	/*GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(ABSCLK_RCC| ABSCSN_RCC | ABSDO_RCC|RCC_APB2Periph_AFIO,ENABLE);
	
	//GPIO_DeInit(GPIOA);	
	//时钟线 初始化     推挽输出
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Pin = ABSCLK_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(ABSCLK_PORT,&GPIO_InitStructure);


  //片选线 初始化  推挽输出
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Pin = ABSCSN_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(ABSCSN_PORT,&GPIO_InitStructure);

  //数据线 初始化  上拉输入
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
  GPIO_InitStructure.GPIO_Pin = ABSDO_PIN;
  GPIO_Init(ABSDO_PORT,&GPIO_InitStructure);

  //先把时钟和片选拉高
  ABSCLK_H;
  ABSCSN_H;
	*/
	GPIO_InitTypeDef GPIO_InitStructure;
	SPI_InitTypeDef  SPI_InitStructure;
	/* 使能GPIO 时钟 */
	RCC_APB2PeriphClockCmd(ABSCSN_RCC, ENABLE);
	/* 配置片选口线为推挽输出模式 */
	ABSCSN_H;		/* 片选置高,不选中 */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = ABSCSN_PIN;
	GPIO_Init(ABSCSN_PORT, &GPIO_InitStructure);
	/*SPI配置*/
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1, ENABLE );	
	//
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	//
 	GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6);
	//
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;					//设置SPI工作模式:设置为主SPI
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;		//设置SPI的数据大小:SPI发送接收16位帧结构
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;					//选择了串行时钟的稳态:低电平
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;				//数据捕获于第二个时钟沿
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		//NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;		//定义波特率预分频的值:波特率预分频值为256
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
	SPI_InitStructure.SPI_CRCPolynomial = 7;	//CRC值计算的多项式
	//
	SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
 
	SPI_Cmd(SPI1, ENABLE); //使能SPI外设
	
	//SPI1_ReadWriteByte(0xff);//启动传输		
	/**/
}
//SPIx 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
uint16_t SPI1_ReadWriteByte(uint8_t TxData)
{		
		u8 retry=0;				 	
		while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
		{
			retry++;
			if(retry>200)return 0;
		}			  
		SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个数据
		retry=0;

		while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)//检查指定的SPI标志位设置与否:接受缓存非空标志位
		{
			retry++;
			if(retry>200)return 0;
		}	  						    
		return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据					    
}
/*
*********************************************************************************************************
*	函 数 名: 
*	功能说明: 
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/

uint16_t SPIDataBuff;	//储存SPI采集绝对编码器的数据

void Torque_Read(void)
{
	ABSCSN_L;
	SPIDataBuff = SPI1_ReadWriteByte(0x00);	//SPI采集绝对值,数据只有10位
	SPIDataBuff = SPIDataBuff >> 6;		 			//将采到的值移6位,对应10Bit
	ABSCSN_H;
}

/*
*********************************************************************************************************
*	模块名称 : 
*	文件名称 : bsp_encoder.h
*	版    本 : V1.0
*	说    明 :  
*	修改记录 :
*		版本号  日期        作者     说明
*********************************************************************************************************
*/
#ifndef _BSP_ENCODER_H
#define _BSP_ENCODER_H


//声明
extern uint16_t SPIDataBuff;	

void Torque_Read(void);
uint16_t SPI1_ReadWriteByte(uint8_t TxData);
void AbsEncoder_Init(void);


#endif

主函数读取

int main()
{
  SYS_Configuration();
  RCC_config();
  bsp_Init();
  /**/
  AbsEncoder_Init();	/* AS5040磁编码器 */
  /**/
  bsp_StartAutoTimer(1, 300);	/* 定时器1周期 300毫秒 */
  //
  while (1)
    {
      bsp_Idle();		/* 空闲时执行的函数,比如喂狗. 在bsp.c中 */
      if (bsp_CheckTimer(1))	/* 定时到 */
        {
			Torque_Read();	
         }

    }
}

抓取时序:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1686776.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Unity Shader入门精要 第9章】更复杂的光照(四)

1. 透明度测试物体的阴影 对于物体有片元丢弃的情况,比如透明度测试或者后边会讲到的消融效果,使用默认的 ShadowCaster Pass 会产生问题,这是因为该Pass在生成阴影映射纹理时,没有考虑被丢弃的片元,而是使用完整的模…

FTP文件传输议

FTP是一种文件传输协议:用来上传和下载,实现远程共享文件,和统一管理文件 工作原理:用于互联网上的控制文件的双向传输是一个应用程序。工作在TCP/IP协议簇的,其传输协议是TCP协议提高文件传输的共享性和可靠性&#…

阅读笔记——《AFLNeTrans:状态间关系感知的网络协议模糊测试》

【参考文献】洪玄泉,贾鹏,刘嘉勇.AFLNeTrans:状态间关系感知的网络协议模糊测试[J].信息网络安全,2024,24(01):121-132.【注】本文仅为作者个人学习笔记,如有冒犯,请联系作者删除。 目录 摘要 1、引言 2、背景及动机 2.1、网络协议实现程…

正点原子LWIP学习笔记(二)MAC简介

MAC简介 一、MAC简介(了解)二级目录三级目录 二、ST的ETH框架(了解)三、SMI站管理接口(熟悉)四、介质接口MII、RMII(熟悉) 一、MAC简介(了解) STM32 的 MAC …

Ubuntu24.04设置静态IP地址

Ubuntu24.04设置静态IP地址 前言:vm17.5的动态IP问题 第一个是设置的静态IP我们可以看到是forever,第二个则是动态IP则是一天的时间。 如果我们不设置静态IP的话,那么可能在本地测试项目的时候,第二天发现一些服务不能用了&#…

13.js对象

定义 一种复杂数据类型,是无序的(不保留键的插入顺序),以键值对({key:value})形式存放的数据集合 对象的创建 (1)字面量创建 var 对象名{ } (2)内部构造函数创建 v…

VirtualBox安装ubuntu22.04记录

一,VirtualBox 软件安装 虚拟机(Virtual Machine)指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统。在实体计算机中能够完成的工作在虚拟机中都能够实现。 常见的虚拟机软件主要有两款 VMware 和 VirtualBox 。VMwar…

争议湖北消费金融2023年业绩,营收下滑or财务内控重大缺陷?

近日,湖北消费金融股份有限公司(下称“湖北消费金融”)披露了2023年度相关信息,包括股权结构、关联方、董事会、分支机构、资产负债情况等信息。 据介绍,湖北消费金融的注册资本为10.058亿元,法定代表人为…

linux---线程控制

线程和进程 以前我们要同时跑多个程序,可以通过fork()多个子进程,然后通过系统函数进行程序的替换,但是创建进程代价大,不仅要拷贝一份父进程的地址空间,页表,文件表述符表等。但是线程不需要因为是进程的…

使用JavaScript日历小部件和DHTMLX Gantt的应用场景(三)

DHTMLX Suite UI 组件库允许您更快地构建跨平台、跨浏览器 Web 和移动应用程序。它包括一组丰富的即用式 HTML5 组件,这些组件可以轻松组合到单个应用程序界面中。 DHTMLX Gantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表,可满足项目管理应用…

【调试笔记-20240520-Linux-在 WSL2 / Ubuntu 20.04 中编译 QEMU 可运行的 OVMF 固件】

调试笔记-系列文章目录 调试笔记-20240520-Linux-在 WSL2 / Ubuntu 20.04 中编译 QEMU 可运行的 OVMF 固件 文章目录 调试笔记-系列文章目录调试笔记-20240520-Linux-在 WSL2 / Ubuntu 20.04 中编译 QEMU 可运行的 OVMF 固件 前言一、调试环境操作系统:Windows 10 …

作业-day-240523

思维导图 知识点问答 1、IO多路复用的原理 1、创建一个检测文件描述符的容器 fd_set fds; 2、将需要检测的文件描述符放入容器中 FD_SET(文件描述符,&fds); 3、通过一个阻塞函数阻塞等待容器中是否有事件产生,如果有一个或多个事件产生&#xff0c…

C++设计模式|结构型 适配器模式

1.什么是适配器模式? 可以将⼀个类的接⼝转换成客户希望的另⼀个接⼝,主要⽬的是 充当两个不同接⼝之间的桥梁,使得原本接⼝不兼容的类能够⼀起⼯作。 2. 适配器模式的组成 (1)接口类,给客户端调用&…

软件设计师-上午题-计算题汇总

一、存储系统 - 存储容量计算(字节编址、位编址、芯片个数) 内存地址是16进制 内存地址编址的单位是Byte,1K1024B 1B 8 bit 1.计算存储单元个数 存储单元个数 末地址 - 首地址 1 eg. 按字节编址,地址从 A4000H 到 CBFFFH&…

汽车IVI中控开发入门及进阶(十八):显示技术之Frame Buffer帧缓冲器

Frame Buffer帧缓冲器(帧缓冲器,有时是帧存储器)是随机存取存储器(RAM)的一部分,包含驱动视频显示器的位图。它是一个内存缓冲区,包含表示完整视频帧中所有像素的数据。现代视频卡的核心包含帧缓冲电路。该电路将内存中的位图转换为可以在计算机监视器上显示的视频信号。…

Redis未授权访问漏洞复现 CNVD-2019-21763 CNVD-2015-07557

CNVD-2019-21763 漏洞描述 Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。 由于在Reids 4.x及以上版本中新增了模块功能,攻击者可通过外部拓展,在Redis中实现一个新…

redis集群不允许操作多个key解决方案、redis key负载均衡方案

前提 在cluster redis 中进行同一个命令处理不同的key会报错:CROSSSLOT Keys in request dont hash to the same slot,例如: 此示例使用sdiff 命令对pool_1与pool_2进行diff操作。 那么我们在业务场景中就需要将集群redis中的不同key进行操作,我们该如何处理呢? 本次的…

多线程(八)

一、wait和notify 等待 通知 机制 和join的用途类似,多个线程之间随机调度,引入 wait notify 就是为了能够从应用层面上,干预到多个不同线程代码的执行顺序.( 这里说的干预,不是影响系统的线程调度策略 内核里的线程调度,仍然是无序的. 相当于是在应用程序…

探秘QT 5软件开发:从零到实战的旅程

新书上架~👇全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我👆,收藏下次不迷路┗|`O′|┛ 嗷~~ 目录 一、QT 5软件开发的启航 二、GUI框架比较与QT 5的选择 三、QT 5环境搭建与软件设计实战 四…

GPT-SoVITS语音克隆部署与使用

GPT-SoVITS是一款强大的少量样本语音转换与语音合成开源工具。当前,GPT-SoVITS实现了如下几个方面的功能: 由参考音频的情感、音色、语速控制合成音频的情感、音色、语速可以少量语音微调训练,也可不训练直接推理可以跨语种生成,…