《嵌入式系统原理及应用综合设计》
1.设计题目
基于STM32单片机的交通灯控制器的设计与实现
2.设计内容
设计选题基于单片机的交通灯控制器的设计与实现,每班学生原则上3或4人一组,每组有分工协作地完成下述任务:
1、单片机模块设计与实现
完成主体功能
2、指示灯模块设计与实现
完成红绿灯跳转功能
3、倒计时显示模块设计与实现
完成倒计时显示功能
4、数码管显示模块设计与实现
完成数码管显示功
3.设计目的
    通过本实践教学环节,一方面使学生对局部电路测试、应用程序开发以及硬软件联调有一个全面的,系统的训练;另一方面,能提高学生团队意识,提高学生组织分工、协作协同能力。培养学生分析解决实际问题的能力,提高学生的创新意识及动手能力,为毕业实习、设计,以及毕业后从事嵌入式系统应用开发打下基础,积累初步的经验。
4.设计材料和工具(实习用到的工具、软件等)
STM32F103实验及其周边电路
Proteus仿真软件、Keil 5编程软件。
5.设计详细说明
(一)总体设计思路及算法描述
1)概述
本交通灯控制系统利用单片机STM32F103C6作为核心元件,实现了通过信号灯对路面状况的智能控制。从一定程度上解决了交通路口堵塞、车辆停车等待时间不合理、急车强通等问题。系统具有结构简单、可靠性高、成本低、实时性好、安装维护方便等优点,有广泛的应用前景。
2)软件设计
软件流程图
1、功能需求
①实现红绿黄LED灯转换功能;
②能够实现倒计时显示功能;
③实现不同情况下倒计时数值切换;
系统功能图如下:
图1软件结构图
本设计研究的是基于STM32F103C6单片机的交通灯智能控制系统。根据交通控制系统的设计原理,阐述了硬件和软件方面开发的整个过程。主控系统采用STM32F103C6单片机作为控制器,控制通行倒计时及右拐、右拐、直行,占用端口少,耗电也最小。有各种成熟电路可供选用,使此方案可靠稳定。该设计可直接在I/O口上接按键开关,精简并优化了电路。结合实际情况,显示界面采用LED提示和数码管计时的方法,满足了倒计时的时间显示输出和状态灯提示信息输出的要求,减少系统的复杂度。
3)硬件设计
图 2 系统原理框图
3系统总体电路图
其中:
1、单片机模块
2、指示灯模块
3、倒计时显示模块
4、数码管显示模块
任务分配:
        耿相东:单片机模块
        葛从雨:指示灯模块
        杨洋:倒计时显示模块
        李玉坤:数码管显示模块
(二)单片机模块
方案选择
方案一:利用PLC实现对交通灯控制,其可靠性高,抗干扰能力强。对于交通灯这种特殊装置,其可靠性是至关重要的,因为交通灯控制系统中途若发生什么意外,其后果是不堪设想。而且PLC系统的设计、制造工作量小,维护方便,体积小、重量轻、能耗低,还可以进行智能化控制以更有效、合理地控制交通。但是使用PLC控制也有缺点,因为PLC比起其它控制系统,其价格较贵,一般一台小型的PLC价格最便宜的也在二三千元以上。 
    方案二:运用单片机对交通灯系统进行实现,使用51单片机为主控核心,通过软件来控制过往车辆的正常运作。同时它也具有如下的优点: 
(1)单片机体积小巧、使用灵活、成本低,易于真正产品化。组装各种智能式控制设备和仪器,能做到机电仪一体化。 
(2)面向控制。能有针对性地解决各种从简单到复杂的各类控制任务,因而能获得最佳的性能价格比。 
(3)抗干扰能力强,适应温度范围宽,在各种恶劣的环境下都能可靠的工作。这是其它微机集中无法比拟的。 
(4)可以方便的实现多机、分布式的集散控制,使整个控制系统的效率大大地提高。 
(5)单片机应用产品的研制周期短,所开发出来的样机就是以后批量生产的产品,可以避免不必要的二次开发过程。 
综上所述,比较5种交通灯控制装置的优点与缺点,根据交通灯所工作的特殊环境,体积小巧、使用灵活、成本低,易于真正产品化面向控制抗干扰能力强,适应温度范围宽可以方便的实现多机、分布式的集散控制便等特点,现在拟采用单片机来控制交通灯。
本设计研究的是基于STM32F103C6单片机的交通灯智能控制系统。根据交通控制系统的设计原理,阐述了硬件和软件方面开发的整个过程。主控系统采用STM32F103C6单片机作为控制器,控制通行倒计时及右拐、右拐、直行,占用端口少,耗电也最小。,有各种成熟电路可供选用,使此方案可靠稳定。该设计可直接在I/O口上接按键开关,精简并优化了电路。结合实际情况,显示界面采用LED提示和数码管计时的方法,满足了倒计时的时间显示输出和状态灯提示信息输出的要求,减少系统的复杂度。
设计原理
开机上电便处于正常运行状态,南北方向通行9秒后变为东西方向通行12秒,如此循环一次,使得东西方向和南北方向交替通行。 
显示系统则显示到下一次改变通行方向所剩的时间,利于司机调整车辆状况。每到通行方向转换时,正在通行的方向绿灯熄灭,变为黄灯常亮,提醒司机注意通行方向的改变,避免不必要的危险。 
延时方法可以有两种,一种是利用内部定时器才生溢出中断来确定1秒的时间,另一种是采用软延时的方法。本程序的倒计时采用软件延时。
系统硬件设计
主控芯片采用STM32F103C6单片机,其管脚图下图所示。 
 STM32F103C6引脚图
STM32F103C6是STM32系列单片机的典型产品,STM32单片机有很多个系列,其中包括基本型、USB基本型、增强型以及互联型几大系列,这写系列的STM32单片机都是具有性能高、功耗低、成本低等特点。其内部结构图如图 3.2所示:
STM32内部结构图
本课题采用的是STM32F103C6单片机芯片,这是是一款ARM M3内核的增强型微控制器,这款内核的工作频率是能够达到72MHz的,它拥有着128K字节的闪存和极其丰富的外设,如GPIO口,串口,定时器,中断,数模转换,实时时钟,看门狗,SPI,IIC,CAN总线等部分组成。STM32F103系列单片机的性能在同一个类别的产品中是最高的,它能够在-40°C -85°C温度下正常地进行工作,工作的电压范围为2V-3.6V,具有低功耗的节能工作模式,闪存存储器的容量为64K字节。
系统硬件结构图
电源电路:本课题电源由USB接口提供5V直流电。
复位电路:按键SW1接入高电平到STM32内部NRST引脚,NRST上脉冲只要持续大于300ns的即可触发单片机复位。
晶振电路:外接8M晶振,通过倍频产生最高72MHz工作频率。
下载电路:本课题选用SWD下载电路,只需要引出SWDIO、SWCLK、VCC3.3V、GND,在MDK下载选项中选择SWD即可下载程序到单片机上。
(三)调试说明
1.调试电路要分模块进行,分顺序的进行。
2.每连接一个模块都要进行检查,看是否有没有连接错误,能不能出结果。
3.电路连接好后,无确显示的时候,可进行通电调试过程中检查电源、底线有没有接好,有没有接错的问题,还是没有现象的话在通电状态下用外用表测量电路中有些节点的点位,是不是和理论的有出入。
4.如果实在还是有问题,那么就要去检查一下自己的电路的原理徒有没有问如果说电路原理图有大的错误,那么再怎么搞都是不会出结果的!
图5.2 软件预览页面
图5.2 交通控制系统运行(南北通行)
图5.3 交通控制系统运行(南北等待)
图5.4 交通控制系统运行(东西通行)
图5.5 交通控制系统运行(东西等待)
图5.6 交通控制系统运行(紧急情况,四方向黄灯闪)
6.程序代码(只描述核心代码)
main.c
#include <stdlib.h>
#include <string.h>
#include "main.h"
#include "smg.h"
#include "ds18b20.h"
#include "beep.h"
#include "key.h"
#include "timer.h"
#include "delay.h"
#define  SPEED              101    //因电脑配置不同,导致仿真速度有区别,如果需要实时倒计时趋于1秒,修改这个值
#define  YELLOW_TIME          3      //黄灯时间(秒)
void SystemClock_Config(void);
uint8_t data_table[4] = {10,10,10,10}; //显示缓存数组
uint8_t bei_nan_time = 10; //南北方向默认时间(秒)
uint8_t xi_dong_time = 15; //东西方向默认时间(秒)
uint8_t temp_bei = 10; //南北倒计时,显示临时缓存变量
uint8_t temp_xi  = 15 + YELLOW_TIME;//东西倒计时,显示临时缓存变量
uint8_t mode = 1; /*mode =1,南北方向;mode =0,东西方向*/
uint8_t emg_mode = 0; /*emg_mode =0,正常;emg_mode =1,紧急停止,时间为0,所有方向亮红灯*/
uint8_t set_mode = 0; /*set_mode =0,不设置;set_mode =1,设置南北方向时间;set_mode =2,设置东西方向时间*/
uint16_t time_update = 101; //1秒计时变量
int main(void)
{   
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
  /* Configure the system clock */
  SystemClock_Config();
//    delay_init(10); //初始化延时函数
    TIM3_Init(1800-1, 18-1); // 10ms,数码管刷新用
   
    Key_Init(); //按键中断初始化
    Led_Init(); //指示灯的初始化
    SEG_Init(); //数码管引脚初始化
       
  while(1)
  {
        if(time_update > SPEED && set_mode == 0) //计时达到1秒且未进入设置模式
        {
            time_update = 0; //重置1秒钟倒计时时间
            if(mode) //mode=0,表示进入南北通行模式(南北绿灯+南北黄灯)
            {
                LED_NBR_OF();LED_NBY_OF();LED_NBG_ON(); //南北方向,亮绿灯
                LED_DXR_ON();LED_DXY_OF();LED_DXG_OF(); //东西方向,亮红灯
                if(temp_bei == 0) //南北方向,倒计时结束时
                {
                    if(temp_xi == 0) //多出来3秒的东西方向,也倒计时结束了
                    {
                        mode = 0; //进入东西方向绿灯模式
                        temp_xi  = xi_dong_time; //提前更新东西方向的倒计时时间
                        temp_bei = temp_xi + YELLOW_TIME; //更新南北方向的时间,为东西方向+3秒
                        LED_NBR_ON();LED_NBY_OF();LED_NBG_OF(); //南北方向,亮红灯
                        LED_DXR_OF();LED_DXY_OF();LED_DXG_ON(); //东西方向,亮绿灯
                    }
                    else //南北方向已经结束,但是东西方向多出来的3秒未结束(3秒黄灯时间)
                    {
                        temp_xi--; //剩余的3秒,倒计时-1秒
//                        temp_bei = temp_xi;//南北方向黄灯时间
                        LED_NBR_OF();LED_NBY_ON();LED_NBG_OF(); //南北亮黄灯
                        LED_DXR_ON();LED_DXY_OF();LED_DXG_OF();    //东西亮红灯                   
                    }
                }
                else
                {
                    temp_bei--; //南北方向。计时为-1秒           
                    temp_xi = temp_bei + YELLOW_TIME; //更新东西方向的倒计时时间
                }
            }
            else//mode=0,表示进入东西通行模式(东西绿灯+东西黄灯)
            {
                LED_NBR_ON();LED_NBY_OF();LED_NBG_OF(); //南北方向,亮红灯
                LED_DXR_OF();LED_DXY_OF();LED_DXG_ON(); //东西方向,亮绿灯
                if(temp_xi == 0) //东西方向,倒计时结束时
                {
                    if(temp_bei == 0) //多出来3秒的南北方向,也倒计时结束了
                    {
                        mode = 1; //进入南北方向绿灯模式
                        temp_bei  = bei_nan_time; //提前更新南北方向的倒计时时间
                        temp_xi = temp_bei + YELLOW_TIME; 嵌入式系统开发前景//更新东西方向的时间,为南北方向+3秒
                        LED_NBR_OF();LED_NBY_OF();LED_NBG_ON(); //南北方向,亮绿灯
                        LED_DXR_ON();LED_DXY_OF();LED_DXG_OF(); //东西方向,亮红灯
                    }
                    else //东西方向已经结束,但是南北方向多出来的3秒未结束(3秒黄灯时间)
                    {
                        temp_bei--; //南北方向,剩余的3秒,倒计时-1秒
//                        temp_xi = temp_bei;
                        LED_NBR_ON();LED_NBY_OF();LED_NBG_OF(); //南北亮红灯
                        LED_DXR_OF();LED_DXY_ON();LED_DXG_OF();    //东西亮黄灯   
                       
                    }
                }
                else
                {
                    temp_xi--; //东西方向。计时为-1秒   
                    temp_bei = temp_xi + YELLOW_TIME; //更新南北方向的倒计时时间
                }
            }                       
        }       
  }
}
//定时器3中断服务函数
uint8_t led_temp = 0;
void TIM3_IRQHandler(void)
{
    if(emg_mode) //判断是紧急模式
    {
        data_table[0] = 0; //倒计时全显示0000
        data_table[1] = 0;
        data_table[2] = 0;
        data_table[3] = 0;
        LED_NBR_ON();LED_NBY_OF();LED_NBG_OF(); //亮起所有红灯
        LED_DXR_ON();LED_DXY_OF();LED_DXG_OF();               
    }
    else //判断不是紧急模式
    {
        if(set_mode != 0) //是设置东西南北时间的模式
        {
            data_table[0] = bei_nan_time / 10; //将倒计时数据缓存到数码管显示数组
            data_table[1] = bei_nan_time % 10;
            data_table[2] = xi_dong_time  / 10;
            data_table[3] = xi_dong_time  % 10;           
            if(set_mode==1){
                LED_NBR_ON();LED_NBY_ON();LED_NBG_ON(); //开启南北方向指示灯,表示进入了南北时间设置模式
                LED_DXR_OF();LED_DXY_OF();LED_DXG_OF();
            }else if(set_mode==2){
                LED_NBR_OF();LED_NBY_OF();LED_NBG_OF();
                LED_DXR_ON();LED_DXY_ON();LED_DXG_ON();//开启东西方向指示灯,表示进入了东西时间设置模式
            }
           
        }
        else //不是设置东西南北时间的模式
        {
            time_update++; //累加时间10ms
            data_table[0] = temp_bei / 10; //将倒计时数据缓存到数码管显示数组
            data_table[1] = temp_bei % 10;
            data_table[2] = temp_xi  / 10;
            data_table[3] = temp_xi  % 10;
        }
    }
   
    switch(led_temp)
    {
        case 0:
            SEG_1(0);SEG_2(1);SEG_3(1);SEG_4(1); //打开第一个数码管
            Dsg_Display(data_table[0]); //将第一个数送至数码管
        break;
       
        case 1:
            SEG_1(1);SEG_2(0);SEG_3(1);SEG_4(1); //打开第二个数码管
            Dsg_Display(data_table[1]); //将第二个数送至数码管
        break;
        case 2:
            SEG_1(1);SEG_2(1);SEG_3(0);SEG_4(1); //打开第三个数码管
            Dsg_Display(data_table[2]); //将第三个数送至数码管
        break;
        case 3:
            SEG_1(1);SEG_2(1);SEG_3(1);SEG_4(0); //打开第四个数码管
            Dsg_Display(data_table[3]); //将第四个数送至数码管
        break;           
    }
        led_temp++;
   
       
        if(led_temp == 4)
            led_temp = 0;
       
    HAL_TIM_IRQHandler(&TIM3_Handler);
}
/**
  * @brief System Clock Configuration
  * @retval None
**/
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
  /** Initializes the CPU, AHB and APB busses clocks
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_LSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.LSIState = RCC_LSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
  }
   
   
  /** Initializes the CPU, AHB and APB busses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
  }
   
   
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_ADC;
  PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
  PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV2;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
  }
}
/********************************************************************************************/