Atmel 工作室中的 SPI 初始化

Posted

技术标签:

【中文标题】Atmel 工作室中的 SPI 初始化【英文标题】:SPI initializing in Atmel studio 【发布时间】:2018-09-22 12:14:23 【问题描述】:

我正在尝试使用我的 Micro OLED Sparkfun 显示器(这是数据表 enter link description here),但我遇到了代码问题。也许是代码中的问题,因为我第一次在 Atmel 使用 SPI,所以如果有人这么好,请检查一下,给我一些建议,我会非常感激。

编辑:这是显示线的更改代码和图片

#define F_CPU 16000000
 #include <avr/io.h>
 #include <util/delay.h>
 #define SCK DDB5
 #define SDIN DDB3
 #define D_C DDB0
 #define CS DDB2
 #define RST DDB1

 void SPI_MasterInit(void)
 
     /* Set MOSI and SCK output, all others input */
     DDRB = (1<<SCK)|(1<<SDIN)|(1<<D_C)|(1<<CS)|(1<<RST);
     /* Enable SPI, Master, set clock rate fck/16 */
     PORTB |= (1<<CS);
     PORTB |= (1<<D_C);
     SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0)|(0<<DORD);
 
 void SPI_MasterTransmit_Command(int cCommand)
 
     PORTB &= ~(1<<CS);
     PORTB &= ~(1<<D_C);
     _delay_ms(200);
     /* Start transmission */
     SPDR = cCommand;
     /* set dc and cs low to send command */
     /* Wait for transmission complete */
     while(!(SPSR & (1<<SPIF)));
     /* set dc cs high again */
     PORTB |= (1<<CS);
     PORTB |= (1<<D_C);


 

 void Display_ON(void)
 
     PORTB &= ~(1<<RST);
     _delay_ms(50);
     PORTB |= (1<<RST);
     _delay_ms(50);
     PORTB &= ~(1<<RST);
     _delay_ms(50);
     SPI_MasterTransmit_Command(0xAF);
     _delay_ms(200);
 

 int main(void)
 
     SPI_MasterInit();
     Display_ON();
     SPI_MasterTransmit_Command(0xA5);

 

【问题讨论】:

显示控制器如何与板子连接?什么是PB0? PB1? CS# 在哪里连接以及在哪里控制?您的代码中没有在命令之前拉下某些东西并在之后将其拉起的地方。 我建议您在断言片选之前更改 C/D。不要认为这是你的问题,但你不想在那里比赛。 如果您在芯片中使用 spi 外设,最好有一个示波器。它们现在便宜得多,大约 250 美元,您可以获得一个非常适合此应用程序的 4 通道。如果没有得到一些 LED 将它们放在 MCU 的输出上,请尝试尽可能慢地为 MCU 计时,看看您是否可以直观地看到 LED 上的状态变化。如果没有得到另一个单片机并用它来轮询线路,把它变成一个逻辑分析仪。假设芯片供应商的文档有问题,即使文档中只有一个错误,它也可能出现在您正在处理的外围设备中...... 忽略“模式”数字查看从机的数据表,在示波器或逻辑分析仪上确保数据在正确的半时钟边​​沿上更改状态,以便从机在正确的时钟边沿上对其进行采样时钟边沿。反之亦然,尽管使用这样的显示,您通常不需要回读任何内容。 移除第二个 PORTB &= ~(1Display_ON(),否则显示将保持在重置状态 【参考方案1】:

首先。当您在主模式下使用 AVR ATmega 上的 SPI 时,SS 引脚虽然不被 SPI 模块直接使用,但应该始终 配置为输出,否则引脚上的高电平会将 SPI 模块切换到从模式。换句话说,如果您使用例如 ATmega328(P),则应将 PB2 配置为输出(应设置 DDRB 中的 DD2 位)。最好将 SSD1306 的 CS# 连接到此引脚。

第二。 连接 SSD1306 时,应连接 4 条线(参见datasheet 的第 17 页): 1) SCLK - 串行时钟 - 将其连接到 SPI 端口的 SCK 引脚(例如,用于 ATmega328 的 PB5) 2)SDAT - 串行数据 - 将其连接到 MOSI 引脚(PB3) 3) CS# - 片选 - 将其连接到 SS 引脚 (PB2) 4) D/#C - 数据/命令选择 - 将其连接到任何空闲引脚(我想您已将其连接到 PB0) 模块也可能有RESET输入,我想你已经把它连接到PB1了。

因此,向 SSD1306 发送数据或命令字节应如下所示:

PORTB &= ~(1 << CS); // pulling the CS# line low
PORTB |= (1 << D_C); // high for DATA
or
PORTB &= ~(1 << D_C); // low for COMMAND
SPDR = cData; // transmit the byte
while(!(SPSR & (1<<SPIF)))  // wait the transmission to complete
PORTB |= (1 << CS); // pull the CS# line back high

CS# 可以通过几个命令保持在低电平,而不会上升。它有助于确定命令的边缘。

第三。 命令 0xAF 将打开显示器,但它可能不会显示任何内容,因为其中没有数据,因此您可能看不到任何区别。 尝试在此之后发送命令 0xA5,它将点亮所有像素 - 仅用于测试目的。

UPD 此外,您还必须打开 DC-DC 电荷泵(参见应用说明,第 62 页,datasheet 末尾)。它由两个命令字节 0x8D 0x14 的序列完成。在打开显示器之前(即在 0xAF 之前)发送它

【讨论】:

【参考方案2】:

很遗憾,您没有具体说明您正在使用的 µC。我只是使用 ATMEGA32 作为参考。希望您的 AVR 具有相同的配置。

ATMEGA32 在端口 B 上也有一个 SPI 接口。您依赖 DDRB 寄存器的复位值(为零)。为您的引脚配置显式设置所有所需的值会很好。从 ATMEGA 的数据表来看,您似乎没有正确初始化引脚。只需将 SPI 接口的 PB7 和 PB5 设置为输出即可。在您的初始化中,您将 PB4、PB6 和 PB7 设置为输出。你混合了 MISOMOSI

这里是我的初始化例程:

/* 
SPI pin configuration:
PB7: SCK         Output
PB6: MISO        Input
PB5: MOSI        Output
PB4: Chip select Output
*/
void SPI_MasterInit(void)
  DDRB = 0x00;
  DDRB |= ((1 << DDB2) | (1 << DDB5) | (1 << DDB7));
  PORTB |= (1<<PB2);
  /* Enable SPI, Master, set clock rate fck/16 */
  SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);

您还需要检查以下内容:

显示器是否支持您选择的 SPI 频率? IDLE 期间 SCK 的状态应该是什么? 应该何时对 SPI 数据进行采样?在 SCK 的上升沿或下降沿期间。 CS 是否按照 OLED 显示器数据表中的时序图设置?

如果您处理了所有这些事情,请回来提供简短的反馈。我希望这个答案能给你一些指导方针。

【讨论】:

嗨,我用的是atmega328p,我对代码做了一些小升级,但是突然显示仍然不想工作pastebin.com/zG3JpwWv 在您的新代码中,您将SDIN 设置为输出(应该是输入),并在初始化后拉动CSdown,这是您不应该这样做的。 CS 上的低电平激活显示器的 SPI 接口。如果你想开始通信,你应该只将它设置为低,例如当您开始传输数据时。仅将其配置为输出。

以上是关于Atmel 工作室中的 SPI 初始化的主要内容,如果未能解决你的问题,请参考以下文章

Atmel AVR 汇编器时间戳计数器

SAM4S 初始化步骤

stm32的spi低温下初始化失败

ESP32-IDF 02-4 外设-SPI

Linux SPI总线和设备驱动架构之二:SPI通用接口层

Linux SPI初始化及接口函数代码细究