在我的项目中,我使用Master SPI通信从外部ADC获取模拟数据。我的MCU是STM32F746ZGTX。我的系统需要实时工作,所以我使用了SPI DMA接收和发送功能。
我在不使用DMA的情况下,通过SPI轮询正确读取所有外部ADC数据。在SPI轮询中,我首先向外部ADC发送控制字节,在此期间,程序在while(SPI_Ready)
循环中等待,然后开始接收所有ADC数据。这个场景非常有效。
但我不想在每次ADC读取时(SPI准备就绪)循环中等待。因为它会影响我的实时计算。这就是为什么我把我的功能切换到DMA。
我的新算法如下:
HAL_SPI_Transmit_DMA()
函数向外部ADC发送read命令。HAL_SPI_TxCpltCallback
函数中,触发HAL_SPI_Receive_DMA()
HAL_SPI_RxCpltCallback
功能中,缓冲接收到的ADC数据,并使芯片选择引脚高以终止通信。当我使用此算法时,我总是在ADC缓冲区中获取0xFF值。似乎即使ADC不发送原始数据,由于触发接收DMA,我的MCU也会发送时钟并将所有逻辑高电平信号作为接收数据进行检测。
我在下面分享我的代码。如果你有任何关于我错在哪里的建议,请分享你的观点。
/* SPI1 init function */
static void MX_SPI1_Init(void)
{
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
}
}
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(hspi->Instance==SPI1)
{
/* USER CODE BEGIN SPI1_MspInit 0 */
/* USER CODE END SPI1_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_SPI1_CLK_ENABLE();
/**SPI1 GPIO Configuration
PA5 ------> SPI1_SCK
PA6 ------> SPI1_MISO
PB5 ------> SPI1_MOSI
*/
GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* SPI1 DMA Init */
/* SPI1_RX Init */
hdma_spi1_rx.Instance = DMA2_Stream0;
hdma_spi1_rx.Init.Channel = DMA_CHANNEL_3;
hdma_spi1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_spi1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi1_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_spi1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_spi1_rx.Init.Mode = DMA_NORMAL;
hdma_spi1_rx.Init.Priority = DMA_PRIORITY_LOW;
hdma_spi1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_spi1_rx) != HAL_OK)
{
}
__HAL_LINKDMA(hspi,hdmarx,hdma_spi1_rx);
/* SPI1_TX Init */
hdma_spi1_tx.Instance = DMA2_Stream3;
hdma_spi1_tx.Init.Channel = DMA_CHANNEL_3;
hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_spi1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi1_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_spi1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_spi1_tx.Init.Mode = DMA_NORMAL;
hdma_spi1_tx.Init.Priority = DMA_PRIORITY_LOW;
hdma_spi1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_spi1_tx) != HAL_OK)
{
}
__HAL_LINKDMA(hspi,hdmatx,hdma_spi1_tx);
/* SPI1 interrupt Init */
HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(SPI1_IRQn);
/* USER CODE BEGIN SPI1_MspInit 1 */
/* USER CODE END SPI1_MspInit 1 */
/* USER CODE BEGIN SPI1_MspInit 1 */
/* USER CODE END SPI1_MspInit 1 */
}
}
void HAL_SPI_MspDeInit(SPI_HandleTypeDef* hspi)
{
if(hspi->Instance==SPI1)
{
/* USER CODE BEGIN SPI1_MspDeInit 0 */
/* USER CODE END SPI1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_SPI1_CLK_DISABLE();
/**SPI1 GPIO Configuration
PA5 ------> SPI1_SCK
PA6 ------> SPI1_MISO
PB5 ------> SPI1_MOSI
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_5|GPIO_PIN_6);
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_5);
/* USER CODE BEGIN SPI1_MspDeInit 1 */
/* Peripheral DMA DeInit*/
HAL_DMA_DeInit(hspi->hdmarx);
HAL_DMA_DeInit(hspi->hdmatx);
/* Peripheral interrupt Deinit*/
HAL_NVIC_DisableIRQ(SPI2_IRQn);
/* USER CODE END SPI1_MspDeInit 1 */
}
}
/* External Interrupt for data ready output of ADC */
void EXTI15_10_IRQHandler(void)
{
/* USER CODE BEGIN EXTI15_10_IRQn 0 */
/* USER CODE END EXTI15_10_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_15);
/* USER CODE BEGIN EXTI15_10_IRQn 1 */
adc_selectADC(); /* Make Chip Select pin low */
HAL_SPI_Transmit_DMA (&hspi1, &controlByte, 1);
}
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
if (hspi->Instance == hspi1.Instance)
{
/* Transmit is completed */
/* Trigger receive DMA to get raw data from external ADC */
HAL_SPI_Receive_DMA (&hspi1, (uint8_t*)adcRecBuffer, 24);
}
}
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
if (hspi->Instance == hspi1.Instance)
{
/* Receive is completed */
adc_deselectADC(); /* Make Chip Select pin high */
}
}
***Working Algorithm without using DMA or Interrupt:***
/* External Interrupt for data ready output of ADC */
void EXTI15_10_IRQHandler(void)
{
/* USER CODE BEGIN EXTI15_10_IRQn 0 */
/* USER CODE END EXTI15_10_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_15);
/* USER CODE BEGIN EXTI15_10_IRQn 1 */
adc_selectADC(); /* Make Chip Select pin low */
while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
HAL_SPI_Transmit(&hspi1, &controlByte, 1,1);
while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
HAL_SPI_Receive(&hspi1, (uint8_t*)adcRecBuffer, 24, 1);
while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
adc_deselectADC(); /* Make Chip Select pin high*/
}
SPI通常是全双工的,这意味着“读取”实际上是主机生成时钟和发送零。在STM HAL实现中,“receive”函数只发送读缓冲区中的数据。可能是零,可能是垃圾,ADC将其解释为一些命令并进入某种不良状态。
尝试先用命令ID(“控制字节”)接收2个25字节的缓冲区,然后在TX缓冲区中接收24个零字节。作为响应,您应该得到大小为25的RX缓冲区,其中第一个字节可以被丢弃。这样,您只需处理RXCplt中断,即释放ADC CS引脚。
垃圾回收 我们对生产中花了很多时间来调整垃圾回收。垃圾回收的关注点与Java大致相似,尽管一些惯用的Scala代码比起惯用的Java代码会容易产生更多(短暂的)垃圾——函数式风格的副产品。Hotspot的分代垃圾收集通常使这不成问题,因为短暂的(short-lived)垃圾在大多情形下会被有效的释放掉。 在谈GC调优话题前,先看看这个Attila的报告,它阐述了我们在GC方面的一些经验。 Scal
对于开发者来说,JavaScript 的内存管理是自动的、无形的。我们创建的原始值、对象、函数……这一切都会占用内存。 当我们不再需要某个东西时会发生什么?JavaScript 引擎如何发现它并清理它? 可达性(Reachability) JavaScript 中主要的内存管理概念是 可达性。 简而言之,“可达”值是那些以某种方式可访问或可用的值。它们一定是存储在内存中的。 这里列出固有的可达值的
Kubernetes 垃圾收集器的角色是删除指定的对象,这些对象曾经有但以后不再拥有 Owner 了。 注意:垃圾收集是 beta 特性,在 Kubernetes 1.4 及以上版本默认启用。 Owner 和 Dependent 一些 Kubernetes 对象是其它一些的 Owner。例如,一个 ReplicaSet 是一组 Pod 的 Owner。具有 Owner 的对象被称为是 Owner
垃圾收集,引用计数,显式分配 和所有的现代语言一样,OCaml提供垃圾收集器,所以你不用像C/C++一样显式地分配和释放内存。 JWZ在他的文章 "Java sucks" rant(Java蛋疼(怒)!): 第一个好家伙是Java没有 free()。其他的都没有所谓了。这几乎掩盖了所有的缺点,不管有多糟糕, 这个有点让后续文档基本都没有意义了,但是...(译注:但是啥大家自己看吧) OCaml的垃
我在STM32F4发现板上安装了STM32F4407VGT6控制器。我尝试使用SPI DMA从AD7683 ADC读取数据,但DMA接收缓冲区始终为空(全部为零)。在轮询模式下,一切正常,但我必须读取一个16位样本值作为3x 8位SPI值,并使用位移位。这也许就是问题所在。我的采样频率为48 kHz,在每个周期内必须读取三个spi值以获得一个ADC样本。 AD7683时序图见第5页的数据表。 引脚
主要内容:垃圾回收器函数,实例Lua 采用了自动内存管理。 这意味着你不用操心新创建的对象需要的内存如何分配出来, 也不用考虑在对象不再被使用后怎样释放它们所占用的内存。 Lua 运行了一个垃圾收集器来收集所有死对象 (即在 Lua 中不可能再访问到的对象)来完成自动内存管理的工作。 Lua 中所有用到的内存,如:字符串、表、用户数据、函数、线程、 内部结构等,都服从自动管理。 Lua 实现了一个增量标记-扫描收集器。 它使用