在STM8S板上执行SPI通信的教程

通信网络

647人已加入

描述

  在本教程中,我们将了解 使用 8x8 Led 矩阵显示模块 作为 SPI 设备在 STM8S103F3P6 板上实现串行并行接口 (SPI) 通信。我们将使用 4 个 GPIO 引脚来执行 SPI 通信,如下图所示。那么,让我们看看在 STM8S 板上执行 SPI 通信需要哪些组件。您可以查看我们 关于 STM8S 微控制器的教程,其中我们讨论了如何设置 Cosmic C 编译器,并且我们已经使用 STM8S103F3P6 开发板介绍了 PWM、ADC、UART 和 I2C。

微控制器

  所需组件

  我们将需要以下组件在 STM8S 上使用 MLX90614 I2C 传感器执行 I2C 通信。

  STM8S103F3P6开发板

  ST-Link V2 编程器

  8x8 MAX7219 LED 矩阵显示模块。

  连接线

  使用 SPI 将点阵显示模块与 STM8S 连接的电路图

  下图为 MAX719 点阵显示模块与 STM8S103F3P6 开发板的连接图。我已将模块的 DIN 引脚与 STM8S103F3P6 板的 SDA (PB5) 和 SCL(PB4) 引脚连接起来。我还为传感器和 LCD 提供了 5V 电源。请注意,我们需要使用 USB 连接器为 STM8S103F3P6 板供电,以便板为传感器和 LCD 提供适当的 5V 电源。

微控制器

  STM8S103F3P6 上的 SPI

  在开始使用 STM8S 上的 SPI 通信之前,您需要确保您对 SPI 通信的工作原理有基本的了解。到目前为止,我们已经介绍了与不同类型微控制器的SPI 通信。所以,我不打算讨论 SPI 通信的理论部分。您需要从我们的 STM8S 教程系列的 GITHUB 存储库中下载完整的代码文件。转到可以在下载的存储库中找到的“T8_SPI_Communication_on_STM8S_using_Cosmic_C_Compiler”文件夹。此文件夹包含两个子文件夹,即。 “inc”和“src”。 我们将使用 Cosmic C 编译器和 SPL 库。我希望您已经阅读了我们关于 STM8S 的第一个教程,我们在其中讨论了如何设置项目工作区。完成项目工作区的设置后,在下图中我用红色圆圈标记的“包含文件”文件夹下应该有以下头文件。在STM8S的第一个教程中,我们已经讨论了如何添加包含文件(可以在“ inc ”文件夹中找到)和源文件(可以在“ src ”文件夹中找到)。

微控制器

  现在,让我们看看库里面有什么。我创建了两个重要的库来简化 STM8S 上的 SPI 通信。即“ stm8s103_spi.h ”和“ stm8s_max72xx.h ”。您可能想知道图片中的其他头文件。可以参考《STM8S 标准外设库》手册。现在,让我们进入编码部分。

  在stm8s103_spi.h头文件里面:

在 stm8s103_spi.h 文件的开头,我们包含了“ STM8S.h ”头文件。“ STM8S.h ”文件包含“ stm8s_spi.h ”头文件的定义和STM8S开发板的板配置。SPI 通信的预定义函数可以在“ stm8s_spi.h ”文件中找到。我们不讨论stm8s_spi.h头文件中的每个函数,而是讨论“stm8s103_spi.h”头文件中使用的重要函数。在“stm8s103_spi.h”中,头文件包含三个用于SPI通信的函数。让我们一一讨论每个功能。

 

void delay_ms(int ms) //函数定义
{
整数 i =0 ;
诠释 j = 0;
对于 (i=0; i<=ms; i++)
{
for (j=0; j<120; j++) // Nop = Fosc/4
_asm("nop"); //不执行任何操作
}
}

 

上面提到的函数“delay_ms()”用于在任务中提供以毫秒为单位的延迟。您可以在delay_ms()函数中找到两个嵌套的 for 循环,其中包含另一个函数“_asm(“nop”)”。_asm(“nop”)可用于指示微控制器不执行任何操作。此delay_ms()函数可以将一个参数作为整数,以毫秒为单位表示延迟。

 

无效SPI_setup(无效)
{
     SPI_DeInit();
     SPI_Init(SPI_FIRSTBIT_MSB,
              SPI_BAUDRATEPRESCALER_2,
              SPI_MODE_MASTER,
              SPI_CLOCKPOLARITY_HIGH,
              SPI_CLOCKPHASE_1EDGE,
              SPI_DATADIRECTION_1LINE_TX,
              SPI_NSS_SOFT,
              0x00);
     SPI_Cmd(启用);
}

 

  上面提到的下一个函数“ SPI_setup(void)”是一个不可返回的函数,可以用来启动SPI通信。该函数对STM8S上的SPI通信有重要作用。在讨论这个函数之前,让我告诉你这个函数可以在stm8s_spi.h头文件中找到。您可以简单地右键单击每个功能,然后您需要单击“转到定义”选项,该选项可以在您按右键单击该功能后在弹出窗口中找到。你可以参考下图。

微控制器

  SPI_DeInit ()函数可用于停止板上任何先前启动的 SPI 通信。SPI_Init ()函数用于启动 Board 和 Slave 之间的 SPI 通信。这个初始化函数有一些参数需要处理。您可以按照与上述相同的方法转到每个参数的定义。我们应该感谢“ stm8s_spi.h ”头文件的创建者,因为他们在注释中提到了文件中的每个细节。我想通过阅读这些注释,您将很容易理解这些函数中使用的每个参数。我们可以使用SPI_Cmd()函数启用或禁用 SPI 外设。

 

void SPI_write(unsigned char slave_address, unsigned char value)
{
    而(SPI_GetFlagStatus(SPI_FLAG_BSY));
    GPIO_WriteLow(CS_port,CS_pin);              
    SPI_SendData(slave_address);
    而(!SPI_GetFlagStatus(SPI_FLAG_TXE));       
    SPI_SendData(值);
    而(!SPI_GetFlagStatus(SPI_FLAG_TXE));               
    GPIO_WriteHigh(CS_port,CS_pin);
}

 

SPi_write ()函数可用于将数据写入目标寄存器。首先,我们需要检查 SPI 状态寄存器是否空闲。我们可以在while循环下使用SPI_GetFlagStatus(SPI_FLAG_BSY)函数来检查SPI通信的状态。“ GPIO_WriteLow(ChipSelect_port, ChipSelect_pin)”函数用于向使用头文件开头的“ ChipSelect_port”和“ChipSelect_pin”定义的片选引脚发送“0”信号。然后“SPI_SendData(slave_address)”用于发送“slave_address”所在的从地址参数包含从设备的目标寄存器的地址。然后我们需要等到发送缓冲区清空。然后我们将使用“SPI_SendData(value)”发送值。然后我们需要再次检查发送缓冲区的状态,我们需要等到它为空。现在,我们可以使用“GPIO_WriteHigh(ChipSelect_port, ChipSelect_pin)”将芯片选择引脚设置为高电平。

在 STM8S 上执行 SPI 时可能会遇到一些错误。我已经提到了我在执行此操作时遇到的错误。IE

“while(!SPI_GetFlagStatus(SPI_FLAG_TXE))”循环永远不会中断。这意味着发送缓冲区不为空。您可以使用“ SPI_SendData() ”函数检查您发送的地址位。或者您可以检查您的接线设置,如果所有电线都正确连接。

在stm8s_max72xx.h头文件里面:

“ stm8s_max7xx.h”头文件有一些功能,可在使用 SPI 通信将 8x8 MAX72xx Led 矩阵显示板与 STM8S 连接时使用。在这个文件的开头,我已经为设备寄存器单独定义了一些宏。这些地址可以从MAX72xx IC的数据表中找到。

 

#define decode_mode_reg 0x09
#define intensity_reg 0x0A
#define scan_limit_reg 0x0B
#define shutdown_reg 0x0C
#define display_test_reg 0x0F
#define shutdown_cmd 0x00
#define run_cmd 0x01
#define no_test_cmd 0x00
#define test_cmd 0x01

 

那么“alphabets[26]”就是存储 26 个字母的 char 数组。“ alpha_char[26][8]”是一个二维 (2D) 数组,其中包含 8x8 Led 矩阵的每个字母表的八个 8 位地址。例如,让我们查看“ alpha_char”的第 0个索引,我们有 8 个 8 位十六进制数据。此十六进制数据表示 8x8 矩阵格式中的字母“A”。

 

const char 字母[26]= "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const uint8_t alpha_char[26][8] = {{0x0, 0xfc, 0xfe, 0x27, 0x27, 0xfe, 0xfc, 0x0},
{0x0, 0xfe, 0xfe, 0x92, 0x92, 0xfe, 0x6c, 0x0},
{0x0, 0x7e, 0xff, 0xc3, 0xc3, 0xe7, 0x66, 0x0},
{0x0, 0xff, 0xff, 0xc3, 0xc3, 0xff, 0x7e, 0x0},
{0x0, 0xfe, 0xfe, 0x92, 0xba, 0x82, 0xc6, 0x0},
{0x82, 0xfe, 0xfe, 0x92, 0x3a, 0x2, 0x6, 0x0},
{0x0, 0x7e, 0xff, 0xc3, 0xd3, 0xf7, 0x76, 0x0},
{0x0, 0xfe, 0xfe, 0x30, 0x30, 0xfe, 0xfe, 0x0},
{0x0, 0xc6, 0xc6, 0xfe, 0xfe, 0xc6, 0xc6, 0x0},
{0x0, 0x30, 0x70, 0x63, 0x63, 0x7f, 0x3f, 0x3},
{0x0, 0xff, 0xff, 0x18, 0x3c, 0x6e, 0xc7, 0x0},
{0x0, 0x81, 0xff, 0xff, 0x81, 0x80, 0xe0, 0x0},
{0x0, 0xfe, 0xfe, 0x1c, 0x38, 0x1c, 0xfe, 0xfe},
{0x4e, 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6},
{0x4f, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38},
{0x50, 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0},
{0x51, 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c},
{0x0, 0xff, 0xff, 0x33, 0x33, 0xff, 0xee, 0xc0},
{0x0, 0xce, 0xdf, 0xdb, 0xdb, 0xfb, 0x73, 0x0},
{0x0, 0x7, 0x83, 0xff, 0xff, 0x83, 0x7, 0x0},
{0x0, 0x7f, 0xff, 0xc0, 0xc0, 0xff, 0x7f, 0x0},
{0x56, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30},
{0x57, 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6},
{0x58, 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6},
{0x59, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78},
{0x7f, 0x7f, 0x61, 0x31, 0x98, 0x8c, 0xfe, 0xfe}
};

 

然后我们有一个“ string_len()”函数来获取字符串的长度。我创建了一个MAX7219_init()函数来将 MAX7219 LED 矩阵初始化为从设备。在这个函数中,你可以看到我已经在“ GPIO_MODE_OUT_PP_HIGH_FAST ”模式下初始化了“ChipSelect_port”和“ChipSelect_pin”。我们需要按照数据表使用该寄存器命令来初始化 MAX7219。我调用了“SPI_write()”函数将数据写入我在头文件顶部定义的电阻器中。

 

无效 MAX7219_init(无效)
{
    GPIO_Init(ChipSelect_port, ChipSelect_pin, GPIO_MODE_OUT_PP_HIGH_FAST);
    SPI_write(shutdown_reg, run_cmd);                
    SPI_write(decode_mode_reg, 0x00);
    SPI_write(scan_limit_reg, 0x07);
    SPI_write(intensity_reg, 0x04);
    SPI_write(display_test_reg, test_cmd);
    延迟毫秒(10);    
    SPI_write(display_test_reg, no_test_cmd);
}

 

下面提到的“display_clear(void)”函数用于清除 LED 矩阵。我在“zeros_clr[8]”数组中使用了 8 个 8 位 0 。然后我在 for 循环中使用该数组的每一位将 8x8 LED 矩阵显示器的每个 LED 设置为'0'或'LOW'。

 

无效显示清除(无效){
                    无符号字符 zeros_clr[8] = {0x00、0x00、0x00、0x00、0x00、0x00、0x00、0x00};
 无符号字符 j = 0x00;               
                for(j = 0; j < sizeof(zeros_clr); j++)
                {                                              
                                                SPI_write((1 + j), zeros_clr [j]);
                                                延迟毫秒(100);
                }
}

 

“ display_char(int alphabet_sequence)”可用于在 LED 矩阵显示板上显示字符。我们需要提供“alpha_char[][]”数组的字母索引。在函数内部,我们有“SPI_write()”。这次我们提供了 Led 矩阵的行和列的寄存器值,以将数据写入特定的 LED。

 

void display_char (int alphabet_sequence)
{
                  无符号整数 i;
                                for(i=0; i<8; i++){
                SPI_write((i+1), alpha_char[alphabet_sequence][i]);
                                                             延迟毫秒(100);                              
                                }
}

 

“ display_string()”可用于显示字符串。这是一个简单的程序,我使用字符比较来将输入字符串的每个字符与“字母”的每个字符进行比较。我为输入字符串中的每个字符记录了“pos”变量中“ alphabets ”的索引,并将该索引传递给“display_char()”。

 

void display_string (const char string[]){                               
    无符号字符 j,pos;                              
    int input_string_length = string_len(string);
                                int alphabets_length = string_len(字母);                               
                for(j=0;j

 

在 main.c 文件中:

现在,我们有了要讨论的main.c文件。在main.c文件中,我们有三个函数。即main()、clock_setup()和GPIO_setup()。在clock_setup()函数中,我使用了CLK_DeInit()函数来停止微控制器内部任何先前启动的时钟。您可以轻松获得此处使用的每个函数和参数的详细说明。您需要使用我之前讨论过的“转到定义”方法。在GPIO_setup() 中,我使用GPIO_DeInit()函数去初始化端口 C。然后我初始化了端口 C 的 Pin 5 和 Pin 6 。 通过使用GPIO_Init()函数。

 

无效时钟设置(无效)
{
     CLK_DeInit();              
     CLK_HSICmd(启用);
     而(CLK_GetFlagStatus(CLK_FLAG_HSIRDY) == FALSE);
     CLK_ClockSwitchCmd(启用);
     CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);
     CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);               
     CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSI,
     禁用,CLK_CURRENTCLOCKSTATE_ENABLE);         
     CLK_PeripheralClockConfig(CLK_PERIPHERAL_SPI, ENABLE);
}
无效 GPIO_setup(无效)
{
     GPIO_DeInit(GPIOC);
     GPIO_Init(GPIOC, ((GPIO_Pin_TypeDef)GPIO_PIN_5 | GPIO_PIN_6),
               GPIO_MODE_OUT_PP_HIGH_FAST);
}

 

在main () 函数中,我已经调用了我们目前讨论的四个函数。这些函数可以以相同的顺序用于在 STM8S 上建立 SPI 通信。然后我们有“display_clear()”函数在启动时清除显示。然后我用参数“0”调用“ display_char() ”来显示字母“A”。在“while()”循环中,我使用“display_string()”和“display_clear()”函数来显示字符串。就我而言,我使用“ CIRCUITDIGEST ”作为字符串显示在 Led 矩阵显示器上。它将每隔 2 秒显示一次该字符串。

  至此,我们终于在STM8S103F3P6开发板上完成了SPI通信。

    #include “STM8S.h”

  #include “stm8s103_SPI.h”

  #include “stm8s_max72xx.h”

  void clock_setup(void);

  无效 GPIO_setup(无效);

  void main()

  {

  const char input_string2[] = “CIRCUITDIGEST”;

  时钟设置();

  GPIO_setup();

  SPI_setup();

  MAX7219_init();

  显示清除();//清除显示

  delay_ms(1000);

  显示字符(0);// 显示字母“A”

  delay_ms(4000);

  while(TRUE)

  {

  display_clear(); //清除显示

  display_string(input_string2);

  };

  }

  void clock_setup(void)

  {

  CLK_DeInit();

  CLK_HSICmd(启用);

  而(CLK_GetFlagStatus(CLK_FLAG_HSIRDY) == FALSE);

  CLK_ClockSwitchCmd(启用);

  CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);

  CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);

  CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO,CLK_SOURCE_HSI,

  禁用,CLK_CURRENTCLOCKSTATE_ENABLE);

  CLK_PeripheralClockConfig(CLK_PERIPHERAL_SPI, ENABLE);

  }

  无效 GPIO_setup(void)

  {

  GPIO_DeInit(GPIOC);

  GPIO_Init(GPIOC, ((GPIO_Pin_TypeDef)GPIO_PIN_5 | GPIO_PIN_6),

  GPIO_MODE_OUT_PP_HIGH_FAST);

  }

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分