ADS8688 STM32C8T6软件模拟SPI驱动程序编写教程

 一、ADS8688概述

二、前言

ADS8688功能强大,但是强大的功能往往也意味着复杂的驱动程序,相信看到这篇文章的读者朋友一定已经尝试移植了许多现成的驱动代码,最终的结果不是报错频出,就是读不懂代码不知道如何正确驱动ADS8688。这篇文章就将教会各位读者如何阅读ADS8688的芯片手册,并且从零开始写出一个自己的ADS8688驱动。首先声明,仅仅引出ADS8688芯片的管脚应该是不够的(笔者也不知道为什么,笔者是硬件白痴,笔者用过的ADS8688芯片和引出的管脚之间都有外围电路)。如果此时读者的手中仅仅只有一块ADS8688芯片,建议先去学习一下如何搭建外围电路。同时,读者应该具备写SPI通信代码,阅读芯片手册中SPI通信时序图的能力,为了节省篇幅,本文并没有涉及到SPI通信以及时序图阅读的讲解。

三、阅读芯片手册

1.状态总览

在芯片手册的36页有一张状态图,可以看到上电之后芯片默认处于IDLE状态,需要我们配置程序寄存器和命令寄存器来进入其他状态,接下来我们就要讲解程序寄存器和状态寄存器。我们要实现的采样功能就在状态图的右下角。

2.命令寄存器和程序寄存器

命令寄存器:命令寄存器用于选择通道采样模式(全自动:AUTO_RST或单通道:MAN_Ch_n),配置设备处于待机(STDBY)或下电(PWR_DN)模式,并复位(RST)程序寄存器到各自的寄存器
默认值。

命令寄存器的指令表。

程序寄存器:程序寄存器用于选择AUTO_RST模式的通道序列,选择SDO输出
格式,并配置单个通道的采样范围。

配置程序寄存器时,需要通过SDI端口向芯片输入16位数据,前七位是数据在寄存器中的地址,第八位决定是读数据还是写数据,后八位是写入对应地址的数据。

配置程序寄存器时序图

程序寄存器配置表

3.手动扫描模式和自动扫描模式

手动扫描模式:可以理解为单次、单通道扫描模式。通过书写命令寄存器中的(MAN_Ch_n),我们可以得到某一个特定通道的电压值。详情见命令寄存器指令表

自动扫描模式:可以理解为多通道同时进行模数转换,按顺序依次将使能的通道得到的电压数据传输给控制平台。未经过书写程序寄存器中AUTO SCAN SEQUENCING CONTROL来使通道掉电即为使能,也就是说向AUTO SCAN SEQUENCING CONTROL输入0x00即为使能所有通道。

两种模式都需要通过书写程序寄存器来确定输入电压范围。

四、代码

1.头文件

#ifndef _ADS8688_H_ 
#define _ADS8688_H_ 
#include "sys.h"

#define ADS_8688_DAISY_IN_H GPIO_SetBits(GPIOA,GPIO_Pin_5) 
#define ADS_8688_DAISY_IN_L GPIO_ResetBits(GPIOA,GPIO_Pin_5) 
#define ADS_8688_SCLK_H GPIO_SetBits(GPIOA,GPIO_Pin_4) 
#define ADS_8688_SCLK_L GPIO_ResetBits(GPIOA,GPIO_Pin_4) 
#define ADS_8688_nCS_H GPIO_SetBits(GPIOA,GPIO_Pin_7) /GPIO_SetBits///GPIO_ResetBits//GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);
#define ADS_8688_nCS_L GPIO_ResetBits(GPIOA,GPIO_Pin_7) 
#define ADS_8688_RST_PD_H GPIO_SetBits(GPIOA,GPIO_Pin_2) 
#define ADS_8688_RST_PD_L GPIO_ResetBits(GPIOA,GPIO_Pin_2) 
#define ADS_8688_SDI_H GPIO_SetBits(GPIOA,GPIO_Pin_3) 
#define ADS_8688_SDI_L GPIO_ResetBits(GPIOA,GPIO_Pin_3) 
#define ADS_8688_SDO GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6)//Command Register 
#define NO_OP 0X0000 
#define STDBY 0X8200 
#define PWR_DN 0X8300 
#define RST 0X8500 
#define AUTO_RST 0XA000 
#define MAN_Ch_1 0XC000 


#define MAN_Ch_2 0XC400 
#define MAN_Ch_3 0XC800 
#define MAN_Ch_4 0XCC00 
#define MAN_Ch_5 0XD000 
#define MAN_Ch_6 0XD400 
#define MAN_Ch_7 0XD800 
#define MAN_Ch_8 0XDC00 
#define MAN_AUX 0XE000 
#define CH1 0x05 
#define CH2 0x06 
#define CH3 0x07 
#define CH4 0x08 
#define CH5 0x09 
#define CH6 0x0a 
#define CH7 0x0b 
#define CH8 0x0c 
void ADS8688_Init_Single(void); 
u16 get_ADS_ch1(void); 
void ADS8688_Init_Mult(void); 
double get_realdata(u16 x);
void ADS8688_GPIO_Init(void);
void get_ADS_allch(float *result); 
void Get_AUTO_RST_Mode_Data(uint16_t* outputdata, uint8_t chnum); 
void AUTO_RST_Mode(void); 
void ADS8688_SPI_WB(uint8_t com) ;
uint8_t ADS8688_SPI_RB(void) ;
void ADS8688_WriteCommandReg(uint16_t command);
void ADS8688_Write_Program_Register(uint8_t Addr,uint8_t data);
u8 ADS8688_READ_Program_Register(uint8_t Addr);
 void Enter_RESET_MODE(void);//软件复位模式,复位 program registers
 void Set_CH_Range_Select(uint8_t ch,uint8_t range); //设置各个通道的范围 
 u16 get_ADS_ch3(void);
#endif

养成一个良好的习惯,将用到的变量用通俗易懂的变量名在头文件中定义,能大大增加代码可读性和可移植性。

头文件中的所有宏定义、函数、变量都是芯片手册中的英文名,有不懂是什么意思的可以自行查询芯片手册。

2. .c文件

#include "ADS8688.H" 
#include "sys.h"
#include "delay.h"

extern u16 ADS_Results[8];

void ADS8688_SPI_WB(uint8_t com) 
{
	uint8_t com_temp=com,s;
	ADS_8688_nCS_L;
	for(s=0;s<8;s++)
	{
		if(com_temp&0x80)
		{
			ADS_8688_SDI_H; 
		}
		else 
		{ 
			ADS_8688_SDI_L;
		}
	ADS_8688_SCLK_H;
	com_temp<<=1; 
	ADS_8688_SCLK_L; 
	} 
}


uint8_t ADS8688_SPI_RB(void) 
{ 
	uint8_t Rdata=0,s; 
	ADS_8688_nCS_L;
	for(s=0;s<8;s++) 
	{ 
		Rdata<<=1;
		ADS_8688_SCLK_H;
		if(ADS_8688_SDO) 
		{ 
			Rdata|=0x01;
		}
		else 
		{ 
			Rdata&=0xFE;
		}
	ADS_8688_SCLK_L; 
	}
	return Rdata;
}


void ADS8688_WriteCommandReg(uint16_t command)//写ADS8688命令寄存器 
{ 
	ADS_8688_nCS_L; 
	ADS8688_SPI_WB(command>>8 & 0XFF);
	ADS8688_SPI_WB(command & 0XFF);
	ADS_8688_nCS_H;
}


void ADS8688_Write_Program_Register(uint8_t Addr,uint8_t data)//对程序寄存器的书写有地址和指令两部分,详情查看程序寄存器指令表
{ 
	ADS_8688_nCS_L;
	ADS8688_SPI_WB(Addr<<1| 0X01);
	ADS8688_SPI_WB(data);
	ADS_8688_nCS_H; 
}


u8 ADS8688_READ_Program_Register(uint8_t Addr)
{ 
	u8 data = 0;
	ADS_8688_nCS_L;
	ADS8688_SPI_WB(Addr<<1); 
	data = ADS8688_SPI_RB();
	data = ADS8688_SPI_RB(); 
	ADS_8688_nCS_H; 
	return data;
}


 void Enter_RESET_MODE(void)//软件复位模式,复位 program registers
{
 ADS8688_WriteCommandReg(RST);
}

void AUTO_RST_Mode(void)//自动扫描模式
{ 
	ADS8688_WriteCommandReg(AUTO_RST);
}

void MAN_Ch_n_Mode(uint16_t ch)//手动模式
{ 
	ADS8688_WriteCommandReg(ch);
}

void Set_CH_Range_Select(uint8_t ch,uint8_t range) //设置各个通道的电压范围 
{ 
	ADS8688_Write_Program_Register(ch,range);
}

void Get_AUTO_RST_Mode_Data(uint16_t* outputdata, uint8_t chnum) //保存自动扫描中各个通道的数据
{ 
	
	u8 i=0,datal=0,datah=0;
	u16 data=0; 
	for (i=0; i<chnum; i++) 
	{ 
		ADS_8688_nCS_L; ADS8688_SPI_WB(0X00); 
		ADS8688_SPI_WB(0X00); 
		datah = ADS8688_SPI_RB();;
		datal = ADS8688_SPI_RB();;
		ADS_8688_nCS_H;
		data = datah<<8 | datal; //高位在前,低位在后 
		*(outputdata+i) = data; 
	} 
}


uint16_t Get_MAN_Ch_n_Mode_Data(void) //得到手动扫描的数据
{ 
	u8 datah=0,datal=0;
	ADS_8688_nCS_L; 
	ADS8688_SPI_WB(0X00);
	ADS8688_SPI_WB(0X00);
	datah = ADS8688_SPI_RB();
	datal = ADS8688_SPI_RB();
	ADS_8688_nCS_H; 
	return (datah<<8 | datal);
}


void ADS8688_GPIO_Init(void)
{ 
	GPIO_InitTypeDef GPIO_Initure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_Initure.GPIO_Pin=GPIO_Pin_7; 
	GPIO_Initure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出模式  
	GPIO_Initure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_Initure); 
	GPIO_Initure.GPIO_Pin=GPIO_Pin_2;
	GPIO_Init(GPIOA,&GPIO_Initure);
	GPIO_Initure.GPIO_Pin=GPIO_Pin_3; 
	GPIO_Init(GPIOA,&GPIO_Initure);
	GPIO_Initure.GPIO_Pin=GPIO_Pin_4;
	GPIO_Init(GPIOA,&GPIO_Initure);
	GPIO_Initure.GPIO_Pin=GPIO_Pin_5; 
	GPIO_Init(GPIOA,&GPIO_Initure);
	GPIO_Initure.GPIO_Pin=GPIO_Pin_6;
	GPIO_Initure.GPIO_Mode=GPIO_Mode_IPD;
	GPIO_Init(GPIOA,&GPIO_Initure);
}

//初始化通道1,默认正负10V范围 
void ADS8688_Init_Single() 
{ 
	ADS8688_GPIO_Init();
	ADS_8688_RST_PD_L;
	Delay_us(2);
	ADS_8688_RST_PD_H;
	ADS_8688_DAISY_IN_L;
	Enter_RESET_MODE();
	ADS8688_Write_Program_Register(0X01,0XFF);
	ADS8688_READ_Program_Register(0X01);
	Delay_us(2);
	ADS8688_Write_Program_Register(0x02,0x00);//设置power dowm的通道
	ADS8688_Write_Program_Register(0x01,0xff);//使能所有通道 
	Set_CH_Range_Select(CH1,0x00);//设置通道1的输?范围:+-2.5*Vref
	Set_CH_Range_Select(CH3,0x00);	
	// //0x00 -> +-2.5*ref 
	// //0x01 -> +-1.25*ref 
	// //0x02 -> +-0.625*ref
	// //0x03 -> +2.5*ref 
	// //0x04 -> +1.25*ref 
	 
	Delay_ms(300);
 }



 
u16 get_ADS_ch1(void) 
{ 
	MAN_Ch_n_Mode(MAN_Ch_1);//需要手动采集哪个通道就将函数的变量改为那个通道
	return Get_MAN_Ch_n_Mode_Data();//读取通道1数据,具体通道数由函数 MAN_Ch_n_Mode()所决定的 
}

u16 get_ADS_ch3(void) 
{ 
	MAN_Ch_n_Mode(MAN_Ch_3);
	return Get_MAN_Ch_n_Mode_Data();
}


double get_realdata(u16 x)//采集到的数据和真实电压之间的关系
{
	double y;
	y=x-32768;
	if(y<0) y = y * 0.0001907+0.02;
	else y = y * 0.0001907+0.02;
	return y;
}

 五、结语

本文虽然只涉及到了实现ADS8688的基本功能,但笔者更希望能通过这篇文章告诉大家如何阅读芯片手册,读懂了芯片手册后能写出属于自己的驱动代码,或者至少懂得如何移植他人的代码,以此提高工作效率。笔者是在电赛时使用的这款芯片,如果读者现在处在电赛的准备阶段,那建议你去自己阅读一下芯片手册,尝试实现一下本文并没有提到的功能,以达到练习的目的,这样,即使电赛期间需要临时写其他芯片的驱动程序也可以游刃有余了。

  • 16
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编写STM32C8T6驱动OLED显示屏的程序,需要以下步骤: 1. 确认OLED显示屏型号和接口类型(一般为I2C或SPI接口)。 2. 在STM32C8T6上配置相应的GPIO口和SPI或I2C接口,以连接OLED显示屏。 3. 编写OLED显示屏初始化函数,包括OLED显示屏的配置寄存器设置和基本参数设置。 4. 编写OLED显示函数,将需要显示的内容写入OLED的显存中。 以下是一个简单的OLED显示屏初始化函数的示例代码: ```c #include "oled.h" // OLED显示屏头文件 // OLED初始化函数 void OLED_Init(void) { // 配置GPIO口和SPI接口 GPIO_InitTypeDef GPIO_InitStruct; SPI_InitTypeDef SPI_InitStruct; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7; // SCK和MOSI GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4; // CS GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOA, &GPIO_InitStruct); SPI_I2S_DeInit(SPI1); SPI_InitStruct.SPI_Direction = SPI_Direction_1Line_Tx; SPI_InitStruct.SPI_Mode = SPI_Mode_Master; SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; SPI_Init(SPI1, &SPI_InitStruct); SPI_Cmd(SPI1, ENABLE); GPIO_SetBits(GPIOA, GPIO_Pin_4); // OLED配置寄存器设置 OLED_WriteCmd(0xAE); // 关闭显示 OLED_WriteCmd(0xD5); // 设置时钟分频因子/振荡器频率 OLED_WriteCmd(0x80); // 设置分频因子(低4位)和振荡器频率(高4位) OLED_WriteCmd(0xA8); // 设置驱动路数 OLED_WriteCmd(0x3F); // 1/64duty OLED_WriteCmd(0xD3); // 设置显示偏移 OLED_WriteCmd(0x00); // 不偏移 OLED_WriteCmd(0x40); // 设置显示开始行 [5:0] OLED_WriteCmd(0x8D); // 电荷泵设置 OLED_WriteCmd(0x14); // 启用/禁用电荷泵(0x14开启) OLED_WriteCmd(0x20); // 内存地址模式 OLED_WriteCmd(0x02); // 水平地址模式 OLED_WriteCmd(0xA1); // 段反转 OLED_WriteCmd(0xC8); // COM扫描方向 OLED_WriteCmd(0xDA); // COM硬件引脚配置 OLED_WriteCmd(0x12); // 设置COM引脚配置 OLED_WriteCmd(0x81); // 对比度设置 OLED_WriteCmd(0xCF); // 1~255(默认0x7F) OLED_WriteCmd(0xD9); // 预充电周期 OLED_WriteCmd(0xF1); // Phase 1: 1 DCLK, Phase 2: 15 DCLKs OLED_WriteCmd(0xDB); // VCOMH电压倍率 OLED_WriteCmd(0x40); // VCOMH=0.82xVCC OLED_WriteCmd(0xA4); // 全局显示开启 OLED_WriteCmd(0xA6); // 设置显示方式:正常显示 OLED_WriteCmd(0xAF); // 开启显示 } // OLED写命令函数 void OLED_WriteCmd(uint8_t cmd) { GPIO_ResetBits(GPIOA, GPIO_Pin_4); // 置低CS SPI_I2S_SendData(SPI1, 0x00); // 发送命令标志 SPI_I2S_SendData(SPI1, cmd); // 发送命令 GPIO_SetBits(GPIOA, GPIO_Pin_4); // 置高CS } // OLED写数据函数 void OLED_WriteData(uint8_t data) { GPIO_ResetBits(GPIOA, GPIO_Pin_4); // 置低CS SPI_I2S_SendData(SPI1, 0x40); // 发送数据标志 SPI_I2S_SendData(SPI1, data); // 发送数据 GPIO_SetBits(GPIOA, GPIO_Pin_4); // 置高CS } ``` 在初始化函数中,首先配置GPIO口和SPI接口,然后设置OLED配置寄存器,最后开启OLED显示。 需要注意的是,OLED显示屏的具体配置寄存器设置与型号有关,需要参考OLED显示屏的数据手册进行设置。 在编写OLED显示函数时,需要将需要显示的内容写入OLED的显存中,具体实现方法也需要参考OLED显示屏的数据手册。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值