0. обзор
На этот раз выборку АЦП необходимо выполнить с помощью микросхемы TL2518 компании TI. Чип представляет собой интерфейс SPI, имеет восемь каналов, и все они могут быть настроены как AIN для выборки.
1.ТЛА2158
Прежде всего, в целях экономии места следует отобразить некоторые функции чипа, здесь показаны только наиболее важные функции, а подробное описание чипа следует посмотреть в его руководстве.
1.1.1 чтение регистра
Хотя чип является интерфейсом SPI, формат кадра данных не полностью соответствует стандартному формату SPI, поэтому при настройке SPI хоста CS должен выбрать программное управление.![[实例] Чип АЦП с интерфейсом SPI 全旅行纹没面重使 — — на основе чипа HAL库和TL2518 — Chanlin — blogger园 [实例] Чип АЦП с интерфейсом SPI 全旅行纹没面重使 — — на основе чипа HAL库和TL2518 — Chanlin — blogger园](https://img2024.cnblogs.com/blog/3287659/202605/3287659-20260511223410678-2076575214.png)

Выше указано время чтения, ниже — выбранная мной конфигурация SPI, это пример с определенной платы разработки. Что касается частоты синхронизации SPI, рекомендуется выбрать более высокую частоту, поскольку интерфейс SPI чипа TL2518 может принимать SPI_CLK с частотой 30 МГц быстрее всего.
void SPI2_Init(u32 datasize)
{
SPI2_Handler.Instance=SPI2; //SPI2
SPI2_Handler.Init.Mode=SPI_MODE_MASTER; //设置SPI工作模式,设置为主模式
SPI2_Handler.Init.Direction=SPI_DIRECTION_2LINES; //设置SPI单向或者双向的数据模式:SPI设置为双线模式
SPI2_Handler.Init.DataSize=datasize; //设置SPI的数据大小:寄存器读写时8bit;读数据时16bit
SPI2_Handler.Init.CLKPolarity=SPI_POLARITY_LOW; //串行同步时钟的空闲状态为高电平
SPI2_Handler.Init.CLKPhase=SPI_PHASE_1EDGE; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
SPI2_Handler.Init.NSS=SPI_NSS_SOFT; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
SPI2_Handler.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256;//定义波特率预分频的值:波特率预分频值为256
SPI2_Handler.Init.FirstBit=SPI_FIRSTBIT_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
SPI2_Handler.Init.TIMode=SPI_TIMODE_DISABLE; //关闭TI模式
SPI2_Handler.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;//关闭硬件CRC校验
SPI2_Handler.Init.CRCPolynomial=7; //CRC值计算的多项式
HAL_SPI_Init(&SPI2_Handler);//初始化
__HAL_SPI_ENABLE(&SPI2_Handler); //使能SPI2
// SPI2_ReadWriteByte(0Xff); //启动传输
}
//SPI5底层驱动,时钟使能,引脚配置
//此函数会被HAL_SPI_Init()调用
//hspi:SPI句柄
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOB_CLK_ENABLE(); //使能GPIOB时钟
__HAL_RCC_SPI2_CLK_ENABLE(); //使能SPI2时钟
//PB13,14,15
GPIO_Initure.Pin=GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH; //快速
HAL_GPIO_Init(GPIOB,&GPIO_Initure);
}
void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)
{
assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性
__HAL_SPI_DISABLE(&SPI2_Handler); //关闭SPI
SPI2_Handler.Instance->CR1&=0XFFC7; //位3-5清零,用来设置波特率
SPI2_Handler.Instance->CR1|=SPI_BaudRatePrescaler;//设置SPI速度
__HAL_SPI_ENABLE(&SPI2_Handler); //使能SPI
}
1.1.2 формат кадра данных
Разрешение АЦП TL2518 составляет 12 бит, а это значит, что недостаточно читать каждый раз только один байт данных, необходимо читать его по полуслову, но четыре цифры, которые выйдут, не будут потрачены впустую, потому что обратное чтение происходит в режиме ID APPEND, и идентификатор канала, отбираемого в конце каждого кадра данных, прикрепляется. В этом 16-битном кадре данных включается передискретизация чипа, что снижает общую частоту дискретизации, но может улучшить разрешение одиночной выборки.
Используя режим ID APPEND, мы можем гарантировать правильность каждого кадра данных без включения CRC, а также можем гарантировать правильность каждого кадра данных. Ниже показано, как настроен мой регистр чипа. A2528.h может быть скопирован с помощью файла A2528.h. Для этого необходимо использовать SPI или ADC.
/*************************************************
* 写入一串字符
*
* @param void
* @return void
* @author Chanlin
**************************************************/
static void TLA_WriteBytes(uint8_t bytes[],uint32_t size){
TLA_CS = 0;
while(size -- > 0){
// printf("byte:%x\t",*bytes);
TLA_SPIReadWriteByte(*(bytes++));
// bytes++;
}
TLA_CS = 1;
// printf("\r\n");
}
/*************************************************
* 完成一次寄存器写入操作
*
* @param void
* @return void
* @author Chanlin
**************************************************/
static void TLA_WriteReg(Reg addr,Data data){
// 先简单实现一下
uint8_t bytes[3]; // 设置spi frame {WR_REG,addr,data}
bytes[0] = WR_REG;
bytes[1] = addr;
bytes[2] = data;
TLA_WriteBytes(bytes,3);
// delay_us(2);
}
/*************************************************
* 完成一次寄存器读取操作
*
* @param void
* @return void
* @author Chanlin
**************************************************/
static void TLA_ReadReg(Reg addr,Data* data){
// 先简单实现一下
uint8_t bytes[3]; // 读取数据帧 {RD_REG,addr,DUMMY};
bytes[0] = RD_REG;
bytes[1] = addr;
bytes[2] = DUMMY;
// 写入读取帧
TLA_WriteBytes(bytes,3);
// 读出数据
TLA_CS = 0;
*data=TLA_SPIReadWriteByte(DUMMY);
TLA_CS = 1;
// 解码完成后,读回数据
// *data=TLA_SPIReadWriteByte(DUMMY);
}
// 以下是对寄存器的配置
// 读写检查
TLA_WriteReg(GENERAL_CFG,0x01); // soft reset
delay_ms(20); // wait for the reset completing
TLA_ReadReg(GENERAL_CFG,&data); // soft reset
printf("GENERAL_CFG:%x\r\n",data);
TLA_ReadReg(OSR_CFG,&data); // soft reset
printf("OSR_CFG:%x\r\n",data);
TLA_ReadReg(SYSTEM_STATUS,&data);
printf("chip sys status:%x\r\n",data);
if(data != 0x81){
if(data == 0xc1) printf("chip sequence is ongoing\r\n");
else printf("Cannot access the chip\r\n");
}
// timing
// TLA_WriteReg(OPMODE_CFG,0x0); // 默认高速时钟源,如果你发现时钟不对或者想要修改
// pin
// TLA_WriteReg(PIN_CFG,0x00); // 全部设置为 AIN(默认)
// TLA_ReadReg(PIN_CFG,&data);
// printf("PIN_CFG:%x\r\n",data);
// DATA
TLA_WriteReg(DATA_CFG,0x10); // 默认无debug,有ID APPEND,请检查此处时序设置是否正确
TLA_ReadReg(DATA_CFG,&data);
printf("DATA_CFG:%x\r\n",data);
// mode
TLA_WriteReg(AUTO_SEQ_CH_SEL,0xFF); // 默认通道全选
TLA_ReadReg(AUTO_SEQ_CH_SEL,&data);
printf("SEQ_CH:%x\r\n",data);
TLA_WriteReg(SEQUENCE_CFG,0x11); // 默认使用auto-sequence mode且打开
TLA_ReadReg(SEQUENCE_CFG,&data);
printf("SEQUENCE_CFG:%x\r\n",data);
TLA_CS =1;
//
TLA_ReadReg(PIN_CFG,&data);
printf("PIN_CFG:%x\r\n",data);
// 使用manual试下
// TLA_WriteReg(CHANNEL_SEL,1);
// ADC offset Calib
while(1){
TLA_ReadReg(GENERAL_CFG,&data);
// printf("ADC offset Calib:%x\r\n",data);
if((data >> 1 & 0x1) == 0 && (data >>2 &0x01) == 1) break;
// 非常重要的一点是,配完TLA2518的寄存器后,不要忘记把主机的SPI改成16bit的数据帧格式
__HAL_SPI_DISABLE(&SPI2_Handler);
SPI2_Handler.Init.DataSize = SPI_DATASIZE_16BIT;
HAL_SPI_Init(&SPI2_Handler);//初始化
__HAL_SPI_ENABLE(&SPI2_Handler);
SPI2_SetSpeed(SPI_BAUDRATEPRESCALER_2); //设置为42M时钟,高速模式
1.2.1 время выборки
Чип может выбирать тактовую частоту, но, как правило, он не будет работать при медленном источнике тактовой частоты, особенно при использовании в режиме АЦП, частота дискретизации превышает 1 МГц. Самая быстрая частота дискретизации чипа составляет 1 МГц, но, учитывая, что он имеет восемь каналов, если он полностью открыт, максимальная скорость для каждого канала составляет 125 кГц.
1.2.2 пример режима переключения каналов
TLA2518 обеспечивает три режима переключения каналов: ручной, «на лету» и режим автоматической последовательности, причем режим автоматической последовательности доступен только после введения.
При использовании этого режима вам нужно только записать канал, который вы хотите сэмплировать, в первый регистр, в конфигурации, показанной выше, у меня полностью открыты 8 каналов. Затем вам необходимо выполнить три условия, чтобы вся последовательность переместилась и прочитала нужные данные.
- 1. 电影CS 电影电影最新贝和新行活;
- 2. Управляйте SPI для генерации часов, если вы являетесь хостом;
- 3. Считайте данные из регистра SPI-DR в память, чтобы их можно было использовать;
Когда эти три условия объединены, легко подумать об использовании вывода CS управления ШИМ, использовании DMA, чтобы позволить SPI выполнять автоматическую передачу, и, наконец, реализовать всю синхронизацию.
2. понял
Несомненно, необходимые внешние ресурсы здесь включают: один канал таймера (который будет настроен как PWM), два DMA (один источник запуска для TIM_CH, один источник запуска для SPI_RX). Ниже приведена конфигурация TIM, а также пример конфигурации.
/*************************************************
*
*
* @param void
* @return void
* @author Chanlin
**************************************************/
void TIM3_PWM_Init(u16 arr,u16 psc)
{
TIM3_Handler.Instance=TIM3; //定时器3
TIM3_Handler.Init.Prescaler=psc; //定时器分频
TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;//向上计数模式
TIM3_Handler.Init.Period=arr; //自动重装载值
TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&TIM3_Handler); //初始化PWM
TIM3_CH4Handler.OCMode=TIM_OCMODE_PWM1; //模式选择PWM1
TIM3_CH4Handler.Pulse=arr/2; //设置比较值,此值用来确定占空比,默认比较值为自动重装载值的一半,即占空比为50%
TIM3_CH4Handler.OCPolarity=TIM_OCPOLARITY_HIGH; //输出比较极性为低
HAL_TIM_PWM_ConfigChannel(&TIM3_Handler,&TIM3_CH4Handler,TIM_CHANNEL_4);//配置TIM3通道2
SET_BIT(TIM3_Handler.Instance->DIER,TIM_DIER_CC4DE_Msk);
HAL_TIM_PWM_Start(&TIM3_Handler,TIM_CHANNEL_4);//开启PWM通道2
}
/*************************************************
*
*
* @param void
* @return void
* @author Chanlin
**************************************************/
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef GPIO_Initure;
if(htim->Instance==TIM3)
{
__HAL_RCC_TIM3_CLK_ENABLE(); //使能定时器3
// __HAL_AFIO_REMAP_TIM3_PARTIAL(); //TIM3通道引脚部分重映射使能
__HAL_RCC_GPIOB_CLK_ENABLE(); //开启GPIOB时钟
GPIO_Initure.Pin=GPIO_PIN_1; //PB1
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;//高速
HAL_GPIO_Init(GPIOB,&GPIO_Initure);
}
}
Ниже приведена конфигурация DMA, это действительно моя собственная запись.
/*************************************************
* 完成一次寄存器读取操作
*
* @param void
* @return void
* @author Chanlin
**************************************************/
static void ConfigDMA(){
__HAL_RCC_DMA1_CLK_ENABLE(); //DMA1时钟使能
__HAL_LINKDMA(&SPI2_Handler,hdmarx,SPIxDMA_Handler); //将DMA与SPI联系起来(发送DMA)
__HAL_LINKDMA(&SPI2_Handler,hdmatx,SPIxDMA_HandlerTX); //将DMA与SPI联系起来(发送DMA)
//Rx DMA配置
SPIxDMA_Handler.Instance=DMA1_Channel4; //通道选择
SPIxDMA_Handler.Init.Direction=DMA_PERIPH_TO_MEMORY; //存储器到外设
SPIxDMA_Handler.Init.PeriphInc=DMA_PINC_DISABLE; //外设非增量模式
SPIxDMA_Handler.Init.MemInc=DMA_MINC_ENABLE; //存储器增量模式
SPIxDMA_Handler.Init.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD; //外设数据长度:8位
SPIxDMA_Handler.Init.MemDataAlignment=DMA_MDATAALIGN_HALFWORD; //存储器数据长度:8位
SPIxDMA_Handler.Init.Mode=DMA_CIRCULAR; //外设循环模式
SPIxDMA_Handler.Init.Priority=DMA_PRIORITY_HIGH; //中等优先级
HAL_DMA_DeInit(&SPIxDMA_Handler);
HAL_DMA_Init(&SPIxDMA_Handler);
__HAL_DMA_ENABLE(&SPIxDMA_Handler);
// TX
SPIxDMA_HandlerTX.Instance=DMA1_Channel3; //通道选择
SPIxDMA_HandlerTX.Init.Direction=DMA_MEMORY_TO_PERIPH; //存储器到外设
SPIxDMA_HandlerTX.Init.PeriphInc=DMA_PINC_DISABLE; //外设非增量模式
SPIxDMA_HandlerTX.Init.MemInc=DMA_MINC_ENABLE; //存储器增量模式
SPIxDMA_HandlerTX.Init.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD; //外设数据长度:8位
SPIxDMA_HandlerTX.Init.MemDataAlignment=DMA_MDATAALIGN_HALFWORD; //存储器数据长度:8位
SPIxDMA_HandlerTX.Init.Mode=DMA_CIRCULAR; //外设循环模式
SPIxDMA_HandlerTX.Init.Priority=DMA_PRIORITY_MEDIUM; //中等优先级
HAL_DMA_DeInit(&SPIxDMA_HandlerTX);
HAL_DMA_Init(&SPIxDMA_HandlerTX);
__HAL_DMA_ENABLE(&SPIxDMA_HandlerTX);
if (HAL_SPI_TransmitReceive_DMA(&SPI2_Handler,
(uint8_t*)dummy_data, // 发送缓冲区
(uint8_t*)s_arrAINChannelVal, // 接收缓冲区
TLA2518_CHANNEL_MAX) != HAL_OK) {
// 启动失败处理
printf("SPI DMA start failed!\r\n");
}
}
После завершения этих настроек вам нужно только контролировать частоту ШИМ и коэффициент холостого хода на выходе TIM3 для завершения автоматической передачи. Учитывая характеристики тактовой частоты выборки чипа, рекомендуется установить коэффициент простоя менее 1 мкс за цикл, чтобы время низкого уровня CS каждого цикла составляло 50-100 нс, а высокий уровень был немного дольше, чем время простоя.