引言

嵌入式系统作为一种将计算能力集成到特定任务中的应用,已经渗透到了我们生活的方方面面。对于初学者来说,通过实际操作实验是理解和掌握嵌入式系统最佳途径之一。本文将详细介绍入门阶段必备的十大实验题,并提供详细的解题攻略。

实验一:LED闪烁控制

实验目的

  • 学习LED的基本连接方法。
  • 理解GPIO(通用输入输出)的概念。

实验步骤

  1. 硬件准备:准备一个LED灯和微控制器开发板。
  2. 软件准备:下载并安装对应的开发环境。
  3. 编写代码:编写控制LED闪烁的代码。
  4. 编译与烧录:编译代码并烧录到开发板上。
  5. 测试:观察LED是否按照预期闪烁。

代码示例(C语言)

#include <stdio.h>
#include "stm32f10x.h"

int main(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA时钟

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // LED连接到PA0
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    while(1)
    {
        GPIO_SetBits(GPIOA, GPIO_Pin_0); // 打开LED
        for(volatile int i = 0; i < 1000; i++); // 延时
        GPIO_ResetBits(GPIOA, GPIO_Pin_0); // 关闭LED
        for(volatile int i = 0; i < 1000; i++); // 延时
    }
}

实验二:按键扫描

实验目的

  • 学习按键的工作原理。
  • 掌握按键扫描算法。

实验步骤

  1. 硬件准备:准备一个按键和微控制器开发板。
  2. 软件准备:编写按键扫描的代码。
  3. 测试:通过按键改变显示状态。

代码示例(C语言)

#include <stdio.h>
#include "stm32f10x.h"

#define BUTTON_PIN GPIO_Pin_0
#define BUTTON_PORT GPIOA

int main(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA时钟

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = BUTTON_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
    GPIO_Init(BUTTON_PORT, &GPIO_InitStructure);

    while(1)
    {
        if(GPIO_ReadInputDataBit(BUTTON_PORT, BUTTON_PIN) == Bit_SET)
        {
            // 处理按键按下事件
        }
    }
}

实验三:串口通信

实验目的

  • 学习串口通信的原理。
  • 实现简单的数据收发。

实验步骤

  1. 硬件准备:准备一个具有串口功能的开发板。
  2. 软件准备:编写串口通信的代码。
  3. 测试:通过串口发送和接收数据。

代码示例(C语言)

#include <stdio.h>
#include "stm32f10x.h"

void USART_Config(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 使能USART1时钟

    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate = 9600;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStructure);

    USART_Cmd(USART1, ENABLE); // 使能USART1
}

int main(void)
{
    USART_Config();

    char data[] = "Hello, world!\r\n";
    while(1)
    {
        USART_SendData(USART1, data); // 发送数据
        while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); // 等待发送完成
    }
}

实验四:PWM控制电机转速

实验目的

  • 学习PWM(脉冲宽度调制)的工作原理。
  • 控制电机的转速。

实验步骤

  1. 硬件准备:准备一个电机和微控制器开发板。
  2. 软件准备:编写PWM控制电机的代码。
  3. 测试:通过改变PWM占空比控制电机转速。

代码示例(C语言)

#include <stdio.h>
#include "stm32f10x.h"

void TIM2_Config(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 使能TIM2时钟

    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_TimeBaseStructure.TIM_Period = 1000 - 1; // 自动重装载值
    TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; // 预分频器
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

    TIM_OCInitTypeDef TIM_OCInitStructure;
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = 500 - 1; // PWM占空比50%
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC1Init(TIM2, &TIM_OCInitStructure);

    TIM_Cmd(TIM2, ENABLE); // 使能TIM2
}

int main(void)
{
    TIM2_Config();

    while(1)
    {
        // 改变PWM占空比以改变电机转速
    }
}

实验五:ADC读取模拟信号

实验目的

  • 学习ADC(模数转换)的原理。
  • 读取模拟信号并进行处理。

实验步骤

  1. 硬件准备:准备一个具有ADC功能的开发板和一个传感器。
  2. 软件准备:编写ADC读取的代码。
  3. 测试:观察ADC读取的数据变化。

代码示例(C语言)

#include <stdio.h>
#include "stm32f10x.h"

void ADC_Config(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); // 使能ADC1时钟

    ADC_InitTypeDef ADC_InitStructure;
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel = 1;
    ADC_Init(ADC1, &ADC_InitStructure);

    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
    ADC_Cmd(ADC1, ENABLE);

    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1));
}

int main(void)
{
    ADC_Config();

    while(1)
    {
        ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 启动ADC转换
        while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); // 等待转换完成
        uint16_t adcValue = ADC_GetConversionValue(ADC1); // 读取转换值
        // 处理adcValue
    }
}

实验六:I2C通信

实验目的

  • 学习I2C通信的原理。
  • 实现与I2C设备的数据交换。

实验步骤

  1. 硬件准备:准备一个具有I2C功能的开发板和一个I2C设备。
  2. 软件准备:编写I2C通信的代码。
  3. 测试:通过I2C读写I2C设备的数据。

代码示例(C语言)

#include <stdio.h>
#include "stm32f10x.h"

void I2C_Config(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); // 使能I2C1时钟

    I2C_InitTypeDef I2C_InitStructure;
    I2C_InitStructure.I2C_ClockSpeed = 100000; // I2C速度为100KHz
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_OwnAddress1 = 0x00;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_Acknowledgemode = I2C_Acknowledgemode_Fast;
    I2C_Init(I2C1, &I2C_InitStructure);

    I2C_Cmd(I2C1, ENABLE);
}

int main(void)
{
    I2C_Config();

    while(1)
    {
        // 发送数据到I2C设备
        // 接收数据从I2C设备
    }
}

实验七:SPI通信

实验目的

  • 学习SPI通信的原理。
  • 实现与SPI设备的数据交换。

实验步骤

  1. 硬件准备:准备一个具有SPI功能的开发板和一个SPI设备。
  2. 软件准备:编写SPI通信的代码。
  3. 测试:通过SPI读写SPI设备的数据。

代码示例(C语言)

#include <stdio.h>
#include "stm32f10x.h"

void SPI_Config(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_SPI1, ENABLE); // 使能GPIOB和SPI1时钟

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5; // MOSI、MISO、SCK
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; // NSS
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    SPI_InitTypeDef SPI_InitStructure;
    SPI_InitStructure.SPI_Direction = SPI_Direction_Mosi | SPI_Direction_Miso | SPI_Direction_Sck;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_Init(SPI1, &SPI_InitStructure);

    SPI_Cmd(SPI1, ENABLE);
}

int main(void)
{
    SPI_Config();

    while(1)
    {
        // 发送数据到SPI设备
        // 接收数据从SPI设备
    }
}

实验八:CAN通信

实验目的

  • 学习CAN通信的原理。
  • 实现与CAN设备的数据交换。

实验步骤

  1. 硬件准备:准备一个具有CAN功能的开发板和一个CAN设备。
  2. 软件准备:编写CAN通信的代码。
  3. 测试:通过CAN读写CAN设备的数据。

代码示例(C语言)

#include <stdio.h>
#include "stm32f10x.h"

void CAN_Config(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); // 使能CAN1时钟

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13; // CAN_TX、CAN_RX
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    CAN_InitTypeDef CAN_InitStructure;
    CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
    CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
    CAN_InitStructure.CAN_BS1 = CAN_BS1_3tq;
    CAN_InitStructure.CAN_BS2 = CAN_BS2_4tq;
    CAN_InitStructure.CAN_Prescaler = 6; // 分频系数
    CAN_InitStructure.CAN_TTCM = DISABLE;
    CAN_InitStructure.CAN_ABOM = DISABLE;
    CAN_InitStructure.CAN_AWUM = DISABLE;
    CAN_InitStructure.CAN_NART = DISABLE;
    CAN_InitStructure.CAN_RFLM = DISABLE;
    CAN_InitStructure.CAN_TEOFN = DISABLE;
    CAN_InitStructure.CAN_Tequence = DISABLE;
    CAN_InitStructure.CAN_BRP = 6;
    CAN_Init(CAN1, &CAN_InitStructure);

    CAN_FilterInitTypeDef CAN_FilterInitStructure;
    CAN_FilterInitStructure.CAN_FilterNumber = 0;
    CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdList;
    CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_16bit;
    CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
    CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
    CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
    CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
    CAN_FilterInit(&CAN_FilterInitStructure);

    CAN_Cmd(CAN1, ENABLE);
}

int main(void)
{
    CAN_Config();

    while(1)
    {
        // 发送数据到CAN设备
        // 接收数据从CAN设备
    }
}

实验九:RTC实时时钟

实验目的

  • 学习RTC(实时时钟)的原理。
  • 使用RTC获取系统时间。

实验步骤

  1. 硬件准备:准备一个具有RTC功能的开发板。
  2. 软件准备:编写RTC读取时间的代码。
  3. 测试:观察RTC显示的系统时间。

代码示例(C语言)

#include <stdio.h>
#include "stm32f10x.h"

void RTC_Config(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_RTC, ENABLE); // 使能RTC时钟

    RTC_TimeTypeDef RTC_TimeStructure;
    RTC_TimeStructure.RTC_Hours = 0; // 时
    RTC_TimeStructure.RTC_Minutes = 0; // 分
    RTC_TimeStructure.RTC_Seconds = 0; // 秒
    RTC_TimeStructure.RTC_H12 = RTC_H12_AM; // 12小时制
    RTC_SetTime(&RTC_TimeStructure);

    RTC_DateTypeDef RTC_DateStructure;
    RTC_DateStructure.RTC_WeekDay = 0x00; // 星期
    RTC_DateStructure.RTC_Month = 0x01; // 月份
    RTC_DateStructure.RTC_Date = 0x01; // 日期
    RTC_DateStructure.RTC_Year = 0x20; // 年份
    RTC_SetDate(RTC_Format_BCD, &RTC_DateStructure);
}

int main(void)
{
    RTC_Config();

    while(1)
    {
        // 读取RTC时间
    }
}

实验十:看门狗定时器

实验目的

  • 学习看门狗定时器的工作原理。
  • 使用看门狗定时器防止系统死锁。

实验步骤

  1. 硬件准备:准备一个具有看门狗定时器的开发板。
  2. 软件准备:编写看门狗定时器的代码。
  3. 测试:观察系统在遇到死锁时是否能够被看门狗定时器恢复。

代码示例(C语言)

#include <stdio.h>
#include "stm32f10x.h"

void IWDG_Config(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_IWDG, ENABLE); // 使能看门狗定时器

    IWDG_Init(4095); // 设置看门狗定时器计数为4095
}

int main(void)
{
    IWDG_Config();

    while(1)
    {
        // 执行任务
        IWDG_ReloadCounter(); // 重新装载看门狗计数器
        if(/* 出现错误 */) break; // 出现错误,停止看门狗定时器
    }
}

总结

通过以上十大实验,初学者可以系统地学习和掌握嵌入式系统的基础知识和实践技能。每个实验都是嵌入式系统开发中不可或缺的一部分,熟练掌握这些实验有助于未来在嵌入式领域的发展。希望本文的详细讲解能对您的学习有所帮助。