概述
基于STM32F103C8核心板的AD9834控制程序学习笔记
最近做项目需要产生正弦波用于调制解调,为了输出一定频率的正弦波,选择了AD9834芯片,这是一款输出频率高达37.5 MHz的DDS芯片,通过三线SPI进行通讯。为了控制该芯片,选用STM32F103C8核心板与该芯片进行SPI通信。
关于SPI通信原理和特点,这里就不再赘述,网上很多资料可以辅助我们学习。
本次使用的是STM32CubeMx进行配置和初始化,使用HAL库进行编程实现控制。
- 原理说明
AD9834芯片有2个频率寄存器,2个相位寄存器,1个控制寄存器需要写入数据从而实现控制。
频率寄存器:28位,该芯片输出信号的频率由
f = ∆Phase × fMCLK/228
计算得到,其中∆Phase值即为频率寄存器中的值。
控制寄存器:16位,每一位有不同的控制作用,详细请参考数据手册。
相位寄存器:输出信号会经过如下的相位偏移
2π/4096 × PHASEREG
其中PHASEREG值即为相位寄存器中的值。
由此可知,为了实现控制AD9834,我们需要通过SPI通信将数据写入这些寄存器。下面就是本文的重点,如何实现STM32F103C8和AD9834之间的硬件SPI通信。当然,模拟GPIO实现SPI通信也是完全可行的,网上也有很多相关程序,这里就不做讲解了。
- STM32CubeMx配置要点
在配置SPI时主要考虑 SPI工作模式、数据格式、MSB或LSB、时钟频率、CPOL、CPHA、NSS工作模式这几项。
配置选项 | Value |
---|---|
工作模式 | 全双工主机 |
数据格式 | 16BIT |
FIRST BIT | MSB |
时钟频率 | 16Mb(与系统时钟有关) |
CPOL | HIGH(空闲高电平) |
CPHA | 1EDGE(这里是下降沿) |
NSS | SOFTWARE(程序控制) |
以上参数的取值参考AD9834的数据手册,其中
CPOL在数据手册上给的是LOW即空闲时为低电平,但我在实际使用时发现需要设置为HIGH才能正常通信,这点还需要我深入学习其原因
另外NSS通过软件控制置即为在程序中控制一个额外的GPIO用于片选信号的传输。
值得注意的是,如果使用软件控制GPIO当作NSS信号,则最好为该GPIO配置一个下拉信号
这是由于我通过逻辑分析仪发现,如果软件控制NSS信号配置为不拉高也不拉底,则在通信过程中片选信号出现了随机跳变,目前我还没有发现这一干扰是如何产生的,因此为其配置GPIO Pull-down避免这一干扰的影响。这点也是后续我需要深入学东西的地方。
此外,阅读AD9834的数据手册发现FSELECT和PSELECT引脚在不使用时应该接CMOS高电平或低电平,因此最好额外配置GPIO输出管脚与之相连。
- 使用HAL的程序编写
配置完成之后就可以点击Generate Code 生成代码了。由于使用的是HAL库,并且需要传输的数据很少,因此使用其中的查询模式函数即可。本次只需要发送数据,因此只使用了HAL_SPI_Transmit函数。其定义如下。
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)
需要注意的是这个函数中的第二个参数,其定义为uint8_t,但是我们进行16bit通信时需要发送uint6_t的数据,这点困扰了我很久
但是我们详细看HAL库中源代码可以发现,实际上这个函数时可以使用的。这个函数定义中有:
/* Transmit data in 16 Bit mode */
if (hspi->Init.DataSize == SPI_DATASIZE_16BIT)
{
if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (initial_TxXferCount == 0x01U))
{
hspi->Instance->DR = *((uint16_t *)hspi->pTxBuffPtr);
hspi->pTxBuffPtr += sizeof(uint16_t);
hspi->TxXferCount--;
}
/* Transmit data in 16 Bit mode */
while (hspi->TxXferCount > 0U)
{
/* Wait until TXE flag is set to send data */
if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE))
{
hspi->Instance->DR = *((uint16_t *)hspi->pTxBuffPtr);
hspi->pTxBuffPtr += sizeof(uint16_t);
hspi->TxXferCount--;
}
else
{
/* Timeout management */
if ((((HAL_GetTick() - tickstart) >= Timeout) && (Timeout != HAL_MAX_DELAY)) || (Timeout == 0U))
{
errorcode = HAL_TIMEOUT;
goto error;
}
}
}
}
也就是说它实际上使用的存储单元是16位的,而C语言规定*a代表a中存储的地址对应的存储单元中的数据,即所定义的uint8_t是不影响后续数据使用的。
我们可以通过下面的函数实现一个16位数据的发送。
void SPI_Write_16Bits(uint16_t transdata)
{
HAL_GPIO_WritePin(SPI_CS_GPIO_Port,SPI_CS_Pin,GPIO_PIN_RESET);
uint16_t arrofdata[1];
arrofdata[0]=transdata;
HAL_SPI_Transmit(&hspi1, (uint8_t* )arrofdata, 1, HAL_MAX_DELAY);
HAL_GPIO_WritePin(SPI_CS_GPIO_Port,SPI_CS_Pin,GPIO_PIN_SET);
}
注意,HAL_GPIO_WritePin用于程序控制GPIO模拟片选信号,通信前拉低通信后拉高。HAL_SPI_Transmit中第三个参数是需要发送的数据个数。当其为n时,就会连续发送16n个时钟脉冲用于数据传输(数据格式16位的情况下)。
之后我们在main.c中写主体程序
/* USER CODE BEGIN 2 */
HAL_Delay(1000);//延迟1000ms
HAL_GPIO_WritePin(GPIOB,SPI_FS_Pin,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB,SPI_PS_Pin,GPIO_PIN_RESET);
SPI_Write_16Bits(0x2100);//RESET
SPI_Write_16Bits(0XC000);//相位寄存器写0
SPI_Write_16Bits(0x2100);//RESET
SPI_Write_16Bits(0x2000);//写控制位 连续写入
SPI_Write_16Bits(0x4937);//400kHz的后14位
SPI_Write_16Bits(0x4106);//400kHz的前14位
SPI_Write_16Bits(0x2800);
SPI_Write_16Bits(0x926E);//800kHz的后14位
SPI_Write_16Bits(0x820C);//800kHz的后14位
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_Delay(1000);
SPI_Write_16Bits(0x2808);//选择频率寄存器1
HAL_Delay(1000);
SPI_Write_16Bits(0x2008);//选择频率寄存器0
}
/* USER CODE END 3 */
- 总结
工欲善其事必先利其器。想要测试自己写的代码是否正确,一定要有相应的一期可以方便随时找出错误。本次学习用到了逻辑分析仪和示波器。其中逻辑分析仪可以方便的找出管脚输出电平的问题,帮助非常大。示波器用来验证结果,简洁可视。
他山之石可以攻玉。一定要多从网上学习相关知识,看看别人程序是怎么实现的,区别在哪里。网上大佬不计其数,分享的学习资料多如繁花,一定要善于查找资料,提高自学能力,才能达到自己想要的结果。
最后
以上就是能干红牛为你收集整理的外设SPI控制AD9834输出正弦波基于STM32F103C8核心板的AD9834控制程序学习笔记的全部内容,希望文章能够帮你解决外设SPI控制AD9834输出正弦波基于STM32F103C8核心板的AD9834控制程序学习笔记所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复