前述
作为一个初学STM32的人来说, 花了一些业余时间搞项目挺累的. 我是机械专业毕业的, 工作一段时间发现机电一体化的时代, 不懂点电气电子真的很难立足. 于是下决心学习单片机. 在某宝上买了杜洋STM32的教材, 收益颇丰.
元器件介绍
绝对值编码器AS5050A
AMS公司有许多种绝对值编码器, 分辨率, 输出信号, 精度等等都不同. 这次买的是一个AMS官网售卖的5050套件.这是一个SPI输出。
具体原理看一下官网上都有的, 不多赘述.上图.
因为这次是控制两个编码器同时显示角度值在液晶屏上,所以需要按照datasheet标注的样式接线。
STM32F103C8T6杜洋开发板连上两个角度传感器之后的样子
SPI通信的时序是发送函数自带一个返回值, 在这里的时序是一连发送n个读角度寄存器指令, 途径每一个编码器芯片后最后总传递给MCU.如图:
接下来开始上代码:
main.c
#include <string.h>
#include <stdio.h>
#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "touch_key.h"
#include "relay.h"
#include "oled0561.h"
#include "AMS_encoder.h"
#include "spi.h"
extern u16 angle,value;
extern u8 alarmhi1,alarmlo1,alarmhi2,alarmlo2;
int main (void){//主程序
delay_ms(100); //上电时等待其他器件就绪
RCC_Configuration(); //系统时钟初始化
I2C_Configuration();//I2C初始化
OLED0561_Init(); //OLED初始化
SPI2_Init();//SPI接口初始化
while(1){
spiReadData();
OLED_DISPLAY_8x16_BUFFER(0," AS5050 absolute "); //显示字符串
OLED_DISPLAY_8x16_BUFFER(2," Angle1:"); //显示字符串
OLED_DISPLAY_8x16_BUFFER(4," Angle2:"); //显示字符串
OLED_DISPLAY_8x16(2,8*8,angle1/1000+0x30);//
OLED_DISPLAY_8x16(2,9*8,angle1%1000/100+0x30);//
OLED_DISPLAY_8x16(2,10*8,angle1%1000%100/10+0x30);//
OLED_DISPLAY_8x16(2,11*8,0x2E);//
OLED_DISPLAY_8x16(2,12*8,angle1%1000%100%10+0x30);//
OLED_DISPLAY_8x16(4,8*8,angle2/1000+0x30);//
OLED_DISPLAY_8x16(4,9*8,angle2%1000/100+0x30);//
OLED_DISPLAY_8x16(4,10*8,angle2%1000%100/10+0x30);//
OLED_DISPLAY_8x16(4,11*8,0x2E);//
OLED_DISPLAY_8x16(4,12*8,angle2%1000%100%10+0x30);//
if(alarmhi1==1){OLED_DISPLAY_8x16_BUFFER(6," alarmhi1"); }//显示1号磁场强度异常,过高
if(alarmhi2==1){OLED_DISPLAY_8x16_BUFFER(6," alarmhi2"); }//显示2号磁场强度异常,过低
if(alarmlo1==1){OLED_DISPLAY_8x16_BUFFER(6," alarmlo1"); }//显示1号磁场强度异常,过高
if(alarmlo2==1){OLED_DISPLAY_8x16_BUFFER(6," alarmlo2"); }//显示2号磁场强度异常,过低
}
}
spi.c
#include "spi.h"
void SPI2_Init(void){ //SPI2初始化
SPI_InitTypeDef SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);//使能SPI_2时钟
GPIO_InitStructure.GPIO_Pin = SPI2_MISO; //SPI2的MISO(PB14)为浮空输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(SPI2PORT,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = SPI2_MOSI | SPI2_SCK; //SPI2的MOSI(PB15)和SCLK(PB13)为复用推免输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(SPI2PORT,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = SPI2_NSS; //SPI2的NSS(PB12)为推免输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(SPI2PORT,&GPIO_InitStructure);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//双线输入输出全双工模式
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//设置为SPI的主机模式(SCK主动产生时钟)
SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;//SPI数据大小:发送16位帧数据结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;//空闲状态时SCK的状态,High为高电平,Low为低电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//时钟相位,1表示在SCK的奇数沿边采样,2表示偶数沿边采样
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS由软件控件片选
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;//时钟的预分频值(0~256)
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //MSB高位在前
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC较验和的多项式
SPI_Init(SPI2,&SPI_InitStructure); //初始化SPI2的配置项
SPI_Cmd(SPI2,ENABLE); //使能SPI2
}
//SPI2数据发+收程序(主要用于发送)
u16 SPI2_SendByte(u16 Byte){ //通过SPI2口发送1个数据,同时接收1个数据
while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE) == RESET); //如果发送寄存器数据没有发送完,循环等待
SPI_I2S_SendData(SPI2,Byte); //往发送寄存器写入要发送的数据
while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE) == RESET); //如果接受寄存器没有收到数据,循环
return SPI_I2S_ReceiveData(SPI2);
}
/*
应用的方法:
SPI_SendByte(a); //发送一个字节数据,a是一个变量或者数值
*/
spi.h
#ifndef __SPI_H
#define __SPI_H
#include "sys.h"
#define SPI2PORT GPIOB //定义IO接口
#define SPI2_MOSI GPIO_Pin_15 //定义IO接口
#define SPI2_MISO GPIO_Pin_14 //定义IO接口
#define SPI2_SCK GPIO_Pin_13 //定义IO接口
#define SPI2_NSS GPIO_Pin_12 //定义IO接口
void SPI2_Init(void);
u16 SPI2_SendByte(u16 Byte);
#endif
AMS_encoder.c
#include "AMS_encoder.h"
#include "spi.h"
u8 alarmhi1,alarmlo1,alarmhi2,alarmlo2;
u16 dat,angle1,value1,angle2,value2,a1,a2,agcreg1,agcreg2;
void spiReadData(void)
{
GPIO_SetBits(SPI2PORT,SPI2_NSS);
/* Send READ AGC command. Received data is thrown away: this data comes from the precedent command (unknown)*/
dat = SPI_CMD_READ | SPI_REG_AGC;//读AGC寄存器
dat |= spiCalcEvenParity(dat);//加奇偶校验
GPIO_ResetBits(SPI2PORT,SPI2_NSS); /* SPI片选有效 */
SPI2_SendByte(dat);//发送读取AGC指令
SPI2_SendByte(dat);//发送读取AGC指令
GPIO_SetBits(SPI2PORT,SPI2_NSS);
delay_us(1);
/* Send READ ANGLE command. Received data is the AGC value, from the precedent command */
dat = SPI_CMD_READ | SPI_REG_DATA;//读角度寄存器
dat |= spiCalcEvenParity(dat);//加奇偶校验
GPIO_ResetBits(SPI2PORT,SPI2_NSS); /* SPI片选有效 */
agcreg1=SPI2_SendByte(dat);//发送读取角度指令
agcreg2=SPI2_SendByte(dat);//发送读取角度指令
GPIO_SetBits(SPI2PORT,SPI2_NSS);
delay_us(1);
/* Send NOP command. Received data is the ANGLE value, from the precedent command */
dat = 0x0000; // NOP command.
GPIO_ResetBits(SPI2PORT,SPI2_NSS); /* SPI片选有效 */
a1=SPI2_SendByte(dat);//接收角度指令
a2=SPI2_SendByte(dat);//接收角度指令
GPIO_SetBits(SPI2PORT,SPI2_NSS);
delay_us(1);
value1 = a1 & 0xfff; // Angle value (0..4095 for AS5055)
angle1 = (value1 * 3600) / 4095; // Angle value in degree (0..359.9°)
value2 = a2 & 0xfff; // Angle value (0..4095 for AS5055)
angle2 = (value2 * 3600) / 4095; // Angle value in degree (0..359.9°)
alarmhi1=(a1>>12)&0x1;
alarmhi2=(a2>>13)&0x1;
alarmlo1=(a1>>12)&0x1;
alarmlo2=(a2>>13)&0x1;
}
AMS_encoder.h
#ifndef __AMS_encoder_H
#define __AMS_encoder_H
#include "sys.h"
#include "delay.h"
#include "spi.h"
#define SPI_CMD_READ 0x8000 /*!< flag indicating read attempt when using SPI interface */
#define SPI_REG_DATA 0x7ffe /*!< data register when using SPI */
#define SPI_REG_AGC 0x7ff0 /*!< agc register when using SPI */
#define SPI_REG_CLRERR 0x6700 /*!< clear error register when using SPI */
static u8 spiCalcEvenParity(u8 value);
extern u16 angle1,value1,angle2,value2;
extern u8 alarmhi1,alarmlo1,alarmhi2,alarmlo2;
#endif
希望能看懂,第一次发文章,如有错误,望多多指教。
未完待续。。。