一:项目介绍
在基础部分,简单的介绍了整体项目的基本思路,说明了使用的控制系统的主控、所使用的压力、位移采集芯片,这里简单说明一下在本次活动中所使用到的厂家的芯片,及其具体的设计思路,在调试时遇到的问题和解决的办法。
在本次项目是基于STM32H7设计压力采集及其电机控制。实现的功能有两路压力、两路位移的采集,计算功能;通过
STM32串口实现与工业串口屏显示实时数据;采集环境内的温湿度和伺服(步进)电机控制的功能;数据存储的功能;
二:项目原理图介绍:
2.1系统框架介绍
在本次活动中,我使用的时EDA软件进行电路设计和PCB的制版,使用的本次FastBond3活动中主要使用的芯片厂商:ST公司的主控芯片STM32H753,TI公司的ADS8341E(SSOP-16封装)、TLE2072IDR(SOIC-8封装)
ADI公司的AD620BRZ-R7(SOIC-8封装)、AD780BRZ(SOIC-8封装)
系统框架图如下:
如框图所示:电源电路使用DC12对系统进行供电,使用XL4005芯片对供电电压进行降压至DC5V,分别为ATMS1117-3V3供电及其串口工业屏供电,以保证系统的显示稳定工作。
同时DC12V为了保证运算放大器TLE2072稳定工作,这里使用LM317对供电电压进行降压至DC9V,而TL2072在工作的时候需要负电压供电,为了使电路供电不复杂,这里使用两个TP7660H的电荷泵芯片提供DC -9V,以保证TLE2072、AD620的稳定工作。同时为了使模拟的芯片的模拟部分供电与数字供电电压分开,以避免模、数之间的干扰,这里使用线性稳压器78M05为整个系统提供模拟电压5V,以保证ADS8341的稳定。
基准电压采用AD780BRZ-2.5V基准电压芯片,以保证AD8341的外部参考电压的稳定。
3D效果如下:
项目中主要芯片介绍:
ADS8341是一款由Burr Brown(现为德州仪器TI的一部分)推出的16位模数转换器(ADC)具有高性能和低功耗的特点。
具有高精度、低功耗、多通道输入、串行接口、高速转换的特点;作为一款高性能、低功耗的16位模数转换器,在嵌入式系统、数据采集系统以及精密测量仪器等领域具有广泛的应用前景。
AD620:高精低失调电压和低失调漂移、低噪声、低功耗、易于设置增益、宽电源电压范围、低输入偏置电流的特点,该芯片是一款低成本、高精度仪表放大器,其特性和应用广泛。
AD780:具有高精度、低温飘、低噪声与低静态电流;AD780的噪声水平非常低,高频时的宽带噪声谱密度为100nV/√Hz,有助于降低系统噪声 其静态电流最大值为1mA,有助于降低功耗。
模拟部分电路原理图如下:
主控电路及其IO口配置电路如下:
PCB效果图如下:
三:成品制作过程及成品展示
供电板制作过程图片:
压力采样板制作图片:
实物实测图片如下:
四:软件部分:
4.1 主要使用说明
主要代码:
ADS8341采样底层驱动代码:
/**
* 函数功能: 往串行ADS8341读取写入一个字节数据
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
uint8_t ADS8341_WriteReadByte(uint8_t byte)
{
uint8_t d_read,d_send = byte;
if(HAL_SPI_TransmitReceive(&hspi3,&d_send,&d_read,1,1000) != HAL_OK)
d_read = 0xFF;
return d_read;
}
/**
* 函数功能: 得到ADS8341采样数据
* 输入参数: uCH: 通道号
* 返 回 值: 采样数据
* 说 明: 无
*/
int Get_SampleData(uint8_t uCH, uint16_t *ReadSample)
{
uint16_t uOvertimes;
uint8_t iREADH;
uint8_t iREADM;
uint8_t iREADL;
uint16_t iTempData;
ADS8341_CS_LOW;
switch(uCH)
{
case 0: ADS8341_WriteReadByte(0x97);
break;
case 1: ADS8341_WriteReadByte(0xD7);
break;
case 2: ADS8341_WriteReadByte(0xA7);
break;
case 3: ADS8341_WriteReadByte(0xE7);
break;
default:
break;
}
uOvertimes = 0;
while(HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_15) == 0)
{
uOvertimes++;
if(uOvertimes > 100)
{
ADS8341_CS_HIGH;
return 0;
}
}
iREADH = ADS8341_WriteReadByte(0);
iREADM = ADS8341_WriteReadByte(0);
iREADL = ADS8341_WriteReadByte(0);
ADS8341_CS_HIGH;
iTempData = iREADH;
iTempData = (iTempData<<8) | iREADM;
iTempData = (iTempData<<1) | (iREADL>>7);
*ReadSample = iTempData;
return 1;
}
存储芯片:AT45DB041部分底层驱动代码:
**
* 函数功能: 往串行Flash读取写入一个字节数据
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
uint8_t Flash_WriteReadByte(uint8_t byte)
{
uint8_t d_read,d_send = byte;
if(HAL_SPI_TransmitReceive(&hspi1,&d_send,&d_read,1,2000) != HAL_OK)
d_read = 0xFF;
return d_read;
}
/**
* 函数功能: 等待FLASH操作完成
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
void SPI_FLASH_WaitForWriteEnd(void)
{
uint8_t FLASH_Status = 0;
/* Select the FLASH: Chip Select low */
Flash_CS_LOW;
/* Send "Read Status Register" instruction */
Flash_WriteReadByte(STATUS_REGISTER);
/* Loop as long as the memory is busy with a write cycle */
do
{
/* Send a dummy byte to generate the clock needed by the FLASH
and put the value of the status register in FLASH_Status variable */
FLASH_Status = Flash_WriteReadByte(0xA5);
}
while ((FLASH_Status & WIP_Flag) == RESET); /* Write in progress */
/* Deselect the FLASH: Chip Select high */
Flash_CS_HIGH;
}
/**
* 函数功能: FLASH整页擦除
* 输入参数: PageAddr -要擦除的页地址
* 返 回 值: 无
* 说 明: 无
*/
void SPI_FLASH_PageErase(uint32_t PageAddr)
{
/* Wait the end of Flash writing */
SPI_FLASH_WaitForWriteEnd();
/* Select the FLASH: Chip Select low */
Flash_CS_LOW;
/* Send "Flash to Buffer" instruction */
Flash_WriteReadByte(PAGE_ERASE);
/* Send WriteAddr high nibble address byte to write to */
Flash_WriteReadByte((PageAddr ) >> 7);
/* Send WriteAddr medium nibble address byte to write to */
Flash_WriteReadByte((PageAddr ) << 1);
/* Send WriteAddr low nibble address byte to write to */
Flash_WriteReadByte(Dummy_Byte);
/* Deselect the FLASH: Chip Select high */
Flash_CS_HIGH;
}
/**
* 函数功能: FLASH块擦除
* 输入参数: BulkAddr -要擦除的块地址
* 返 回 值: 无
* 说 明: 无
*/
void SPI_FLASH_BulkErase(uint32_t BulkAddr)
{
/* Wait the end of Flash writing */
SPI_FLASH_WaitForWriteEnd();
/* Select the FLASH: Chip Select low */
Flash_CS_LOW;
/* Send "BulkErase" instruction */
Flash_WriteReadByte(BULK_ERASE);
/* Send WriteAddr high nibble address byte to write to */
Flash_WriteReadByte((BulkAddr ) >> 4);
/* Send WriteAddr medium nibble address byte to write to */
Flash_WriteReadByte((BulkAddr ) << 4);
/* Send WriteAddr low nibble address byte to write to */
Flash_WriteReadByte(Dummy_Byte);
/* Deselect the FLASH: Chip Select high */
Flash_CS_HIGH;
}
/**
* 函数功能: 读指定页得主存数据到Buffer1
* 输入参数: PageAddr -页地址
* 返 回 值: 无
* 说 明: 无
*/
void SPI_FLASH_PageToBuffer1(uint32_t PageAddr)
{
/* Wait the end of Flash writing */
SPI_FLASH_WaitForWriteEnd();
/* Select the FLASH: Chip Select low */
Flash_CS_LOW;
/* Send "Flash to Buffer" instruction */
Flash_WriteReadByte(PAGE_TO_B1_XFER);
/* Send WriteAddr high nibble address byte to write to */
Flash_WriteReadByte((PageAddr ) >> 7);
/* Send WriteAddr medium nibble address byte to write to */
Flash_WriteReadByte((PageAddr ) << 1);
/* Send WriteAddr low nibble address byte to write to */
Flash_WriteReadByte(Dummy_Byte);
/* Deselect the FLASH: Chip Select high */
Flash_CS_HIGH;
}
工业串口屏底层代码:
/**
* 函数功能: 串口2的DMA发送
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
void UART2_TX_DMA_Send(uint8_t *buffer, uint16_t length)
{
HAL_UART_Transmit(&huart2,buffer,length,1000);
}
/***********************************************************************************************
* @brief void Add_Write_Data1(unsigned int ucAdd,int uiDATA)
* @param 向指定地址发送两字节数据
* @retval 无
* @author 聪聪哥哥
* @version V1.1.0
* @date 1-8-2024
*************************************************************************************************/
void Add_Write_Data1(unsigned int ucAdd,int uiDATA)
{
SendBuffer2[0] = 0x5A;
SendBuffer2[1] = 0xA5;
SendBuffer2[2] = 0x05;
SendBuffer2[3] = 0x82;
SendBuffer2[4] = ucAdd>>8;
SendBuffer2[5] = ucAdd;
SendBuffer2[6] = uiDATA>>8;
SendBuffer2[7] = uiDATA;
HAL_UART_Transmit(&huart2,&SendBuffer2[0],8,10);
}
/**
* 函数功能: 串口2从DWIN屏接收数据
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
void DWIN_Rec_Deal(void)
{
uint16_t RecAdd;
uint16_t KeyValue;
if(usart2_Flag == 1)
{
usart2_Flag = 0 ;
if((Usart2_DEAL_RX_Buf[0] == 0x5A)&&(Usart2_DEAL_RX_Buf[1] == 0xA5))
{
switch(Usart2_DEAL_RX_Buf[3])
{
case 0x83: RecAdd = (Usart2_DEAL_RX_Buf[4]<<8) + Usart2_DEAL_RX_Buf[5];
switch(RecAdd)
{
case 0: KeyValue = (Usart2_DEAL_RX_Buf[7]<<8) + Usart2_DEAL_RX_Buf[8];
DWIN_Key_Deal(KeyValue);
break;
default :
break ;
}
}
}
}
}
压力、位移数据处理部分代码如下:
/**
* @brief 通过采样值计算出真实值
* @param chNum: 通道号
* @retval 无
*/
void CalCulateOutData(uint8_t chNum)
{
uint8_t i, P2, P1;
uint32_t iSum;
__IO uint32_t maxtem,mintem;
//1、计算平均值
iSum = maxtem = mintem = SampleValue[chNum].SampleValue[0];
for(i = 1; i < 10; i++)
{
iSum += SampleValue[chNum].SampleValue[i];
if(maxtem <= SampleValue[chNum].SampleValue[i]) maxtem = SampleValue[chNum].SampleValue[i];
if(mintem >= SampleValue[chNum].SampleValue[i]) mintem = SampleValue[chNum].SampleValue[i];
}
SampleValue[chNum].AVESampleValue = (iSum-maxtem-mintem)/(10-2);
//2、计算输入电压值
//3、根据量程和倍率计算实际输出值
for(i = 0; i < 8; i++)
{
if(SampleValue[chNum].AVESampleValue < CalSet[chNum].BDPoint[i])
{
P2 = i;
break;
}
}
if(i == 0)
{
SampleValue[chNum].FactValue = ( SampleValue[chNum].AVESampleValue - CalSet[chNum].BDPoint[0])/SampleValue[chNum].SlopeValue[0];
}
else if(i == 8)
{
SampleValue[chNum].FactValue = CalSet[chNum].iEEMaxScale + ( SampleValue[chNum].AVESampleValue - CalSet[chNum].BDPoint[7])/SampleValue[chNum].SlopeValue[7];
}
else
{
P1 = 0;
for(i = P2; i > 0; i--)
{
if((SampleValue[chNum].AVESampleValue > CalSet[chNum].BDPoint[i-1]) && (CalSet[chNum].BDPoint[i-1] != 0))
{
P1 = i - 1;
break;
}
}
SampleValue[chNum].FactValue = (CalSet[chNum].CalPoint[P1]) + (SampleValue[chNum].AVESampleValue - CalSet[chNum].BDPoint[P1])/SampleValue[chNum].SlopeValue[P1];
}
}
五:心得体会
最后感谢硬禾学堂举办的活动,在本次活动中,我尝试使用STM32cube 软件生成工程底层驱动,在活动的开始,我接触硬件设计的时候,查找了几种信号采集的方案,最后才确定使用ADS8341采集压力、位移的数据;使用ST官方的cube软件学习到了STM32h7单片机的串口、定时器、IIC、SPI通讯相关知识。学习起来也是很方便。
在硬件测试中也是遇到了很多的难题,比如AD620放大倍数设计不合适、TL2072芯片作为运算放大器接入压力传感器后,瞬间电压拉低的情况,ADS8341采样过程中出现数据飘动严重问题等等,不过在克服困难之后,成功的喜悦也是溢于言表的。
最后再次感谢本次活动的硬禾学堂和各位赞助商。