我们将使用 IAR Embedded Workbench for STM8 作为开发环境,这是ST官方推荐的、非常成熟的工具链,也会提及如何使用免费的 ST Visual Develop (STVD) + COSMIC编译器。

(图片来源网络,侵删)
第一部分:开发环境搭建
在写代码之前,我们需要准备好工具。
硬件
- 一块STM8S Discovery开发板:推荐使用
STM8S-Discovery(型号: STM8S-DISCOVERY),它板上自带ST-Link调试器,非常方便,STM8S105K4T6这款芯片。 - 一根Micro-USB数据线。
软件
- IAR Embedded Workbench for STM8:
- 访问 IAR官网 下载免费评估版(有代码大小限制,但对于学习和足够了)。
- 或者,购买正版许可证。
- ST Visual Develop (STVD) + COSMIC编译器:
- 访问 ST官网 下载STVD和免费的COSMIC CXSTM8编译器。
- 这是完全免费的组合,非常适合初学者。
安装与驱动
- 安装你选择的IDE(IAR或STVD)。
- STM8S Discovery板上的ST-Link调试器在首次连接时,需要安装USB驱动,Windows通常会自动安装,如果失败,可以从ST官网下载 "STSW-LINK009" 软件包,里面包含驱动。
第二部分:第一个程序 - LED闪烁 (Blinky)
这是所有嵌入式开发的 "Hello, World!",我们将让开发板上的LED灯一秒闪烁一次。
硬件分析
查看 STM8S-Discovery 的原理图,你会发现板载的LED(标记为LD1)连接在 PD4 引脚上,当PD4输出低电平时,LED点亮;输出高电平时,LED熄灭。
核心概念
- 时钟配置: STM8S默认使用内部高速时钟,但频率可能较低,为了获得更好的性能,我们通常需要配置时钟,例如使用内部16MHz时钟并分频。
- GPIO配置: 我们需要将PD4引脚配置为推挽输出模式。
- 延时函数: 为了控制闪烁频率,需要一个简单的延时函数。
C语言例程 (基于IAR EWSTM8)
下面是一个完整的Blinky例程。

(图片来源网络,侵删)
#include "stm8s.h"
// 简单的软件延时函数
// 注意:这是一个粗略的延时,精确延时需要使用定时器
void Delay(uint32_t count)
{
for (; count > 0; count--);
}
void main(void)
{
// 1. 初始化时钟系统
// 使能内部高速时钟,并设置为16MHz
CLK_HSICmd(ENABLE); // 使能内部高速时钟
CLK_SYSCLKDiv(CLK_SYSCLKDiv_1); // 系统时钟不分频,为16MHz
// 2. 初始化GPIO
// 使能GPIOD端口时钟
CLK_PeripheralClockConfig(CLK_Peripheral_GPIO, ENABLE);
// 配置PD4引脚为推挽输出模式
// GPIO_Pin_4 对应 0x10
// GPIO_Mode_Out_PP_5mA 推挽输出,最大5mA驱动能力
GPIO_Init(GPIOD, GPIO_Pin_4, GPIO_Mode_Out_PP_5mA);
// 3. 主循环
while (1)
{
// 点亮LED (PD4输出低电平)
GPIO_WriteLow(GPIOD, GPIO_Pin_4);
Delay(50000); // 延时
// 熄灭LED (PD4输出高电平)
GPIO_WriteHigh(GPIOD, GPIO_Pin_4);
Delay(50000); // 延时
}
}
如何编译和下载
- 在IAR中创建一个新的 "STM8 C/C++ Project"。
- 将上面的代码复制到
main.c文件中。 - 编译项目(F7)。
- 连接STM8S Discovery板。
- 点击 "Download and Debug" (Ctrl+D)。
- 程序下载后,点击 "Go" (F5) 运行,你应该会看到板上的LED开始闪烁。
第三部分:基础外设例程
掌握了Blinky,我们就可以尝试更复杂的外设了。
例程1:按键输入
读取开发板上按键的状态(按键标记为 USER,连接在 PC5 引脚上)。
- 硬件分析: 按键PC5连接到GND,当按键未按下时,PC5被上拉电阻拉高;当按键按下时,PC5被拉到低电平。
- 软件配置: 将PC5配置为上拉输入模式。
#include "stm8s.h"
void main(void)
{
// 1. 初始化时钟
CLK_HSICmd(ENABLE);
CLK_SYSCLKDiv(CLK_SYSCLKDiv_1);
// 2. 初始化GPIO
// 使能GPIOC和GPIOD的时钟
CLK_PeripheralClockConfig(CLK_Peripheral_GPIO, ENABLE);
// 配置PC5为上拉输入
GPIO_Init(GPIOC, GPIO_Pin_5, GPIO_Mode_In_PU_No_IT); // 上拉输入,无中断
// 配置PD4为推挽输出,用于LED
GPIO_Init(GPIOD, GPIO_Pin_4, GPIO_Mode_Out_PP_5mA);
while (1)
{
// 读取PC5引脚状态
if (GPIO_ReadInputPin(GPIOC, GPIO_Pin_5) == RESET)
{
// 如果为低电平,说明按键按下
GPIO_WriteLow(GPIOD, GPIO_Pin_4); // 点亮LED
}
else
{
// 如果为高电平,说明按键未按下
GPIO_WriteHigh(GPIOD, GPIO_Pin_4); // 熄灭LED
}
}
}
例程2:定时器中断 - 精确延时
软件延时不精确且会阻塞CPU,使用定时器中断是更好的方式,我们将使用 TIM2 来产生一个1ms的定时中断,并利用它来精确控制LED闪烁。
- 核心概念:
- 配置TIM2,使其在1ms后产生中断。
- 编写TIM2的中断服务函数。
- 在主循环中,通过一个标志位来响应中断,实现非阻塞的延时。
#include "stm8s.h"
volatile uint16_t timer_counter = 0; // volatile关键字,防止编译器优化
// TIM2中断服务函数
// 在STM8标准外设库中,中断函数需要用 @interrupt 关键字声明
INTERRUPT(TIM2_UPD_OVF_IRQHandler, TIM2_UPD_OVF_VECTOR)
{
// 清除中断标志位,这一步非常重要!
TIM2_ClearFlag(TIM2_FLAG_Update);
timer_counter++; // 每次中断,计数器加1
}
void main(void)
{
// 1. 初始化时钟
CLK_HSICmd(ENABLE);
CLK_SYSCLKDiv(CLK_SYSCLKDiv_1);
// 2. 初始化GPIO
CLK_PeripheralClockConfig(CLK_Peripheral_GPIO, ENABLE);
GPIO_Init(GPIOD, GPIO_Pin_4, GPIO_Mode_Out_PP_5mA);
// 3. 初始化TIM2定时器
CLK_PeripheralClockConfig(CLK_Peripheral_TIM2, ENABLE); // 使能TIM2时钟
// 设置预分频器,使定时器时钟为 16MHz / 16 = 1MHz
TIM2_PrescalerConfig(TIM2_Prescaler_16);
// 设置自动重装载值,使定时器每 1000 个计数 (1/1MHz * 1000 = 1ms) 中断一次
TIM2_SetAutoreload(999);
// 使能更新中断
TIM2_ITConfig(TIM2_IT_Update, ENABLE);
// 使能TIM2
TIM2_Cmd(ENABLE);
// 4. 使能全局中断
enableInterrupts();
// 5. 主循环
while (1)
{
// 每500次中断,即500ms (0.5秒) 切换一次LED状态
if (timer_counter >= 500)
{
timer_counter = 0; // 复位计数器
// 翻转LED状态
if (GPIO_ReadOutputData(GPIOD) & GPIO_Pin_4)
{
GPIO_WriteLow(GPIOD, GPIO_Pin_4);
}
else
{
GPIO_WriteHigh(GPIOD, GPIO_Pin_4);
}
}
}
}
例程3:UART串口通信
通过UART向PC发送数据,是调试和通信的重要手段,我们将使用 USART1,其默认引脚为 PA9 (TX) 和 PA10 (RX)。
- 硬件准备: 需要一个USB-TTL转换器(如CH340、FT232)。
STM8S-Discovery的USART1_TX(PA9) 连接到 USB-TTL 的RX。STM8S-Discovery的USART1_RX(PA10) 连接到 USB-TTL 的TX。- 共同GND。
- 软件配置: 配置USART1为波特率9600,8位数据位,1位停止位,无校验位。
#include "stm8s.h"
// 发送一个字符
void UART_SendChar(char c)
{
// 等待发送数据寄存器为空
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
// 发送数据
USART_SendData8(USART1, c);
}
// 发送一个字符串
void UART_SendString(char *str)
{
while (*str)
{
UART_SendChar(*str++);
}
}
void main(void)
{
uint32_t counter = 0;
// 1. 初始化时钟
CLK_HSICmd(ENABLE);
CLK_SYSCLKDiv(CLK_SYSCLKDiv_1);
// 2. 初始化GPIO
// 使能GPIOA和USART1的时钟
CLK_PeripheralClockConfig(CLK_Peripheral_GPIOA, ENABLE);
CLK_PeripheralClockConfig(CLK_Peripheral_USART1, ENABLE);
// 配置PA9 (TX) 为复用功能推挽输出
GPIO_Init(GPIOA, GPIO_Pin_9, GPIO_Mode_Out_PP_High_Fast);
// 配置PA10 (RX) 为浮空输入
GPIO_Init(GPIOA, GPIO_Pin_10, GPIO_Mode_In_FL_No_IT);
// 3. 初始化USART1
USART_Init(USART1, 9600, USART_WordLength_8b, USART_StopBits_1, USART_Parity_No, USART_Mode_TxRx);
USART_Cmd(USART1, ENABLE);
// 4. 主循环
while (1)
{
UART_SendString("Hello from STM8S!\r\n");
UART_SendString("Counter: ");
// 将数字转换为字符串并发送 (这里简化处理,实际应用中应使用sprintf或itoa)
char buf[16];
itoa(counter, buf, 10); // itoa不是标准C,但很多编译器支持
UART_SendString(buf);
UART_SendString("\r\n");
counter++;
Delay(500000); // 粗略延时1秒
}
}
第四部分:重要资源与学习建议
- 数据手册: 每个芯片最重要的文档,必须仔细阅读,特别是关于引脚定义、电气特性、寄存器描述和时序图的部分。
- 参考手册: 描述了STM8S系列所有外设的功能和寄存器,当你想配置一个定时器、SPI或I2C时,这里就是你的字典。
- STM8S标准外设库: ST官方提供的C语言库,它将复杂的寄存器操作封装成了简单的函数(如
GPIO_Init,TIM2_PrescalerConfig)。强烈建议初学者使用,可以大大提高开发效率,避免直接操作寄存器时出错。 - 例程: ST官方提供了大量针对不同外设的例程,可以在ST官网上下载,这些例程是学习如何使用库函数的绝佳材料。
学习建议:
- 从GPIO开始: 熟悉如何配置和使用输入输出。
- 掌握定时器: 定时器是单片机的核心,学会配置和使用定时器中断是进阶的关键。
- 学习UART: 这是与外界交互最简单的方式,是调试利器。
- 逐步深入: 在掌握了GPIO、定时器、UART后,再尝试学习更复杂的外设,如 SPI (用于Flash、OLED屏)、I2C (用于传感器、EEPROM)、ADC (用于模拟信号采集)。
希望这份详细的例程能帮助你顺利入门STM8S的C语言编程!
