在本教程中,我们将了解 使用 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);
}
评论
查看更多