2019-07-09 13:50:35 pangyinglong 阅读数 107
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3419 人正在学习 去看看 朱有鹏

注:本文参考自https://blog.csdn.net/q2631837575/article/details/78776567

51在上电后,PSW的RS两个位默认为0,也即51默认使用工作寄存器组BANK 0,在默认状态下,对于普通的C语言函数,其传参、申请局部变量、导出函数的返回值等功能,keil将其翻译成汇编以后,肯定要使用R0~R7;对于51的中断服务函数,它没有形参,也不用返回值,但是一般肯定有局部变量,这时就需要用到R0~R7了;试想,在执行普通函数时,R0~R7已经被使用了,在执行普通函数时,一旦发生中断,而中断函数也需要使用R0~R7,那怎么办?我们最先想到的是,在执行中断服务函数前先把R0~R7入栈(像累加器A、状态PSW等也要入栈这个不用说大家也知道),在中断服务完成后把R0~R7出栈,然后就能恢复现场,回到普通函数中去了,但是这8个Rn不能直接入栈,PUSH R0这样的语句是不允许的,要想R0入栈只能用两句:MOV A R0; PUSH A;这样的后果是,每次工作寄存器入栈都需要2*8=16条汇编语句才能完成,再加上A、B、PSW等寄存器入栈等,相当于每次中断都要消耗大量的时间来出栈入栈,影响程序速度。如何解决这一问题呢?51提供了这样一种机制,切换工作寄存器组,过程如下:

    普通函数的执行过程中正在使用BANK0的R0~R7,执行过程中突然发生了中断,而中断函数也想使用R0~R7,在执行中断服务函数前,我们切换工作寄存器组,切换的具体方法就是直接修改PSW的RS两个比特位,而不必把BANK 0入栈,本文开头的例子中using 2,就是说,在进入外部中断0的服务函数前,先入栈CPU寄存器,再把工作寄存器组由0切换成2,在退出中断服务后,先由BANK2切换回BANK0,并弹出CPU寄存器,由于BANK0和BANK2处在不同的RAM空间,互相不干扰,切换回BANK0之后就把那个普通函数的现场给恢复了。


补图(过程图)

2019-11-06 10:20:49 weixin_41987706 阅读数 23
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3419 人正在学习 去看看 朱有鹏

夜光序言:

 

 

外貌决定有没有可能在一起,性格决定适不适合在一起,物质决定能不能稳定的在一起,信任决定能不能长久的在一起。

 

 

 

 

 

 

 

 

 

 

正文:

我们之前用单片机可能实现过定时的功能

比如说:让灯多少时间之后关闭


这一点就很厉害


 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;

namespace WindowsFormsApp3
{
    public partial class Form1 : Form
    {
        //device1 hy
        const byte DeviceOpen1 = 0x01;  //定义了一个常量,这个也是C语言通用的
        const byte DeviceClose1 = 0x81;  //夜光:开数据是01,关数据是81
        //device2 hy
        const byte DeviceOpen2 = 0x02;
        const byte DeviceClose2 = 0x82;

        //device3 hy
        const byte DeviceOpen3 = 0x03;
        const byte DeviceClose3 = 0x83;

        bool Button9Statue;

        //SerialPort Write Buffer
        byte[] SerialPortDataBuffer = new byte[1];

        public Form1()
        {
            InitializeComponent();   //夜光:窗口构造
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (serialPort1.IsOpen)   //串口打开就关闭
            {
                
                try
                {
                    serialPort1.Close();
                }
                catch                 //确保万无一失
                {
                    
                }
                ovalShape1.FillColor = Color.Gray;  //未打开,灰色
                button2.Text = "打开串口";
            }
            else
            {
                try
                {
                    serialPort1.PortName = comboBox1.Text;  //端口号
                    serialPort1.Open();
                    ovalShape1.FillColor = Color.Green;  //打开的就是绿色
                    button2.Text = "关闭串口";
                }
                catch
                {
                    MessageBox.Show("串口打开失败", "错误");
                }
            }




        }

        //加载的时候
        private void Form1_Load(object sender, EventArgs e)
        {
            ovalShape1.FillColor = Color.Gray;  //启动的时候,设置颜色
            SearchAndAddSerialToComboBox(serialPort1, comboBox1);
        }


        private void WriteByteToSerialPort(byte data)   //夜光:单字节写入串口
        {
            byte[] Buffer = new byte[2] { 0x00, data };  //夜光:定义数组
            if(serialPort1.IsOpen){         //传送数据的前提是端口已经打开
                try
                {
                    serialPort1.Write(Buffer, 0, 2);
                }                                           //写数据
                catch
                {
                    MessageBox.Show("串口数据发送出错,请检查", "错误");   //错误处理
                }
            }

        }




        private void SearchAndAddSerialToComboBox(SerialPort MyPort, ComboBox MyBox)
        {                                           //将可用端口号添加到ComboBox
            string[] MyString = new string[20];      //最多容纳20个,太多会影响效率
            string Buffer;          //缓存
            MyBox.Items.Clear();                  //清空ComboBox内容
            for (int i = 1;i < 20; i++)           //循环
            {
                try                                             //核心原理是依赖try catch完成遍历
                {
                    Buffer = "COM" + i.ToString();
                    MyPort.PortName = Buffer;
                    MyPort.Open();                     //如果失败,后面的代码不会执行
                    MyString[i - 1] = Buffer;
                    MyBox.Items.Add(Buffer);    //打开成功,添加至下俩列表
                    MyPort.Close();             //关闭
                }
                catch
                {

                }
                MyBox.Text = MyString[0];
            }



        }

        private void button3_Click(object sender, EventArgs e)
        {
            int i = 0;
            try
            {
                i = Convert.ToInt32(textBox1.Text.Substring(0, 2));  //先处理两位数
            }
            catch
            {
                try
                {
                    i = Convert.ToInt32(textBox1.Text.Substring(0, 1));//处理一位数
                }
                catch
                {
                    MessageBox.Show("请输入正确的数字");  //错误提示
                    return;          //退出函数
                }
            }
            if (serialPort1.IsOpen)
            {
                if(i == 0)
                {
                    return;
                }
                else
                {
                    timer1.Interval = i * 1000;
                    timer1.Start();
                    button3.Enabled = false;
                }
            }

            /*WriteByteToSerialPort(DeviceOpen1);          //器件1开*/
        }

        private void button4_Click(object sender, EventArgs e)
        {
            //夜光:这个是定时器关的
            try
            {
                timer1.Stop();
            }
            catch
            {

            }
            button3.Enabled = true;
            WriteByteToSerialPort(DeviceClose1);          //器件1关
        }

        private void button6_Click(object sender, EventArgs e)
        {
            WriteByteToSerialPort(DeviceOpen2);
        }

        private void button5_Click(object sender, EventArgs e)
        {
            WriteByteToSerialPort(DeviceClose2);
        }

        private void button8_Click(object sender, EventArgs e)
        {
            WriteByteToSerialPort(DeviceOpen3);
        }

        private void button7_Click(object sender, EventArgs e)
        {
            WriteByteToSerialPort(DeviceClose3);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            SearchAndAddSerialToComboBox(serialPort1, comboBox1);
        }

        private void button9_Click(object sender, EventArgs e)
        {
          


                                      //避免定时器浪费时间和用户
            if (serialPort1.IsOpen)   //串口打开就关闭  
            {

                try
                {
                    serialPort1.Close();
                }
                catch                 //确保万无一失
                {

                }
                ovalShape1.FillColor = Color.Gray;  //未打开,灰色
                button9.BackgroundImage = Properties.Resources.Image2;
                /*                button2.Text = "打开串口";*/
            }
            else
            {
                try
                {
                    serialPort1.PortName = comboBox1.Text;  //端口号
                    serialPort1.Open();
                    button9.BackgroundImage = Properties.Resources._09f9d033140dcdff688d0e67dd1b3e20;
                    ovalShape1.FillColor = Color.Green;  //打开的就是绿色
/*                    button2.Text = "关闭串口";*/
                }
                catch
                {
                    MessageBox.Show("串口打开失败", "错误");
                }
            }
        }

        private void button9_MouseHover(object sender, EventArgs e)
        {
            button9.BackgroundImage = Properties.Resources._09f9d033140dcdff688d0e67dd1b3e20;
        }

        private void button9_MouseLeave(object sender, EventArgs e)
        {
            if (Button9Statue)  //定义一个布尔类型的全局变量Button9Statue
            {
                button9.BackgroundImage = Properties.Resources.Image2;
            }
            else
            {
                button9.BackgroundImage = Properties.Resources.Image3;
            }
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            button3.Enabled = true;
            timer1.Stop();
            WriteByteToSerialPort(DeviceClose1);
        }
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2007-05-22 15:59:00 jk1983 阅读数 916
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3419 人正在学习 去看看 朱有鹏

单片机软件,工作环境keil C51

选用最常见的华邦MCU,带ISP在线可编程,只需要一个232的串口,就可以降低一个仿真器的成本,按如下步骤进行的话,大多数功能都可以通用。

包含有:Main.c I2C.c KeyHandle.c MCUint.c EEPROM.c  GlobalVarible.c Remote.c

Main.c

void main()using 0
{
 delay20ms();delay20ms();delay20ms();delay20ms();delay20ms();//减少硬件复位影响
 Mcuint();
 while(1){
         KeyCodeHandle();        //主要检查电源按键
  if(IoGotoISP==0){                 //按键进入ISP
   Goto_ISP();
  }
  if(KeyCodeCurrent==keyfactory)FactoryFlag=1;//可以设置工厂模式
  if(!PowerSwitchFlag)continue;                            //电源按下执行
  PowerSwitchFlag=0;                                           //电源标志位复位
  PowerOn_LED//open KEY BORAD LED         亮灯
   //check EEPROM and Get EEPROM data    //EEPROM数据保存
  EEPRomInt(); 
     //ini brighness IC
  TSL2550Ini();                         //为后面举例I2C,这是一颗亮度传感器
  PowerSwitchFlag=1;        
  while(1)
  { 
   KeyCodeHandle();
   AudioHandle();
         HotKeyChannelHandle();
   AutoAFC(); 
   if( (EnableDisplayDealyFlag)&&(EnableDisplayDealyTimer==0) ){
    EnableDisplayDealyFlag=0;
    EnableDisplay();
   }
   if(!PowerSwitchFlag)
    { 
     DisableDisplay();
     break;
    }
   if(IoGotoISP==0){
    Goto_ISP();
   }
   ClearVariable();
  }
  OSDClear();
  DisableDisplay();
  ResetMcu();
 }

}


void Goto_ISP(void)
 {

  //-------------------for winbord "W78E65"  ISP
  IE=0x00;
  TCON=0x00;
  T2CON=0x00;
  CHPENR=0x87;
  CHPENR=0x59;
  CHPCON=0x03;
  CHPENR=0x00;
  IE=0x82;
  TMOD=0x01;
  TL0=0xF0;
  TH0=0xFF;
  TR0=1;
  PCON = 0x01;
  while(1);
}

 

I2C.c

#include "public.h"
#include <intrins.h>

 

#define#define SetI2CSCL()          (SCL = 1)     
#define ClrI2CSCL()          (SCL = 0)
#define SetI2CSDA()          (SDA = 1)       
#define ClrI2CSDA()          (SDA = 0)
#define CheckI2CSDA()        (SDA == 1)
#define CheckI2CSCL()        (SCL == 1)
#define SetSDAInput()  
#define SetSDAOutput() 
#define SetSCLInput()  
#define SetSCLOutput() 
enum
    {
    I2C_OK,
    I2C_BUSY,
    I2C_ERR
    }; NOP()// _nop_();_nop_();_nop_();_nop_();/***每一条的指令时间为1US****/
        
/*******************************************************************
                     起动总线函数              
函数原型: void  Start_I2C(); 
功能:     启动I2C总线,即发送I2C起始条件.
 
********************************************************************/
static void Start_I2C()
{
 SetSDAOutput();                         //some MCU io prot must be to config for input or ouput
 NOP();
        SetSCLOutput();                        //some MCU io prot must be to config for input or ouput
         NOP(); 
        SetI2CSDA();    // Set SDA
         NOP(); 
 SetI2CSCL();    // Set SCL
  NOP(); 
        SetSCLInput();                          //some MCU io prot must be to config for input or ouput
 while(!(CheckI2CSCL()));         // Wait until bus is released by slave
        SetSCLOutput();                         //some MCU io prot must be to config for input or ouput
 SetI2CSDA();    // Set SDA
  NOP(); 
 ClrI2CSDA();    // Clear SDA
  NOP(); 
 ClrI2CSCL();    // Clear SCL
  NOP(); 
}              


/*******************************************************************
                      结束总线函数              
函数原型:   void  Stop_I2c(); 
功能:       结束I2C总线,即发送I2C结束条件.
 
********************************************************************/
static void Stop_I2C()
{
 ClrI2CSDA();    // Clear SDA
  NOP(); 
 SetI2CSCL();    // Set SCL
  NOP(); 
 SetI2CSDA();    // Set SDA
  NOP(); 
}

/*******************************************************************
                      获取应答状态函数              
函数原型:   unsigned char GetACK_I2c(); 
功能:       获取IIC总线器件IIC应答状态
输出:      SDA口的状态
 
********************************************************************/
static unsigned char GetACK_I2C()
{
 unsigned char ack;

 SetI2CSDA();    // Set SDA line to 1 and check it as input.
  NOP(); 
 SetI2CSCL();
  NOP(); 
 SetSDAInput();                          //some MCU io prot must be to config for input or ouput
  NOP(); 
 ack = CheckI2CSDA();          // Check SDA line
        SetSDAOutput();                         //some MCU io prot must be to config for input or ouput
         NOP(); 
 ClrI2CSCL();
  NOP(); 

 return (ack);    // Return SDA status
}

/*******************************************************************
                      发送应答状态函数              
函数原型:   unsigned char SetACK_I2C(); 
功能:       发送IIC应答状态
 
********************************************************************/
static void SetACK_I2C(unsigned char ACK )
{
 
        if(ACK==0)ClrI2CSDA();          // Write ACK to slave
        else SetI2CSDA();
   NOP(); 
 SetI2CSCL();    // Set SCL
  NOP(); 
 ClrI2CSCL(); 
  NOP(); 
}

/*******************************************************************
                 字节数据传送函数              
函数原型: unsigned char WriteByte_I2C(unsigned char WriteData);
功能: 将数据WriteData发送出去,可以是地址,也可以是数据,返回应答状态.  
输入:WriteData=要发送单字节的数据
输出:返回状态标志
********************************************************************/
static unsigned char WriteByte_I2C(unsigned char WriteData)
{
 unsigned char BitSelect;
 
 BitSelect = 0x80;
 while (BitSelect)
 {
  if (BitSelect & WriteData) // Check data bit
   SetI2CSDA();  // Set SDA
  else
   ClrI2CSDA();  // Clear SDA
   NOP(); 

  SetI2CSCL();   // Set SCL
  NOP(); 
  ClrI2CSCL();   // Clear SCL
  NOP(); 

  BitSelect >>= 1;  // Shift bit position to right
 }
 
 // Check I2C ACK
 if (GetACK_I2C())   // If ACK not seen
 {
  Stop_I2C();   // Issue I2C STOP
  return I2C_BUSY;
 }
 else
  return I2C_OK;
}


/*******************************************************************
                 字节数据接收函数              
函数原型: unsigned char ReadByte_I2C(void)
功能: 从IIC口上接收一个字节的数据 
输出:返回接收到的一个字节数据
********************************************************************/
static unsigned char ReadByte_I2C(void)
{
 unsigned char readData, BitSelect;
 
 readData = 0;    // Init data
 BitSelect = 0x80;
 
 SetI2CSDA();    // Set SDA
  NOP(); 

 SetSDAInput();

 while (BitSelect)
 { 
  // On Novatek micro, clearing SCL while SDA is low
  // results in clearing SDA, so set it again.
  SetI2CSDA();  NOP();    

  SetI2CSCL();   // Set SCL

  if (CheckI2CSDA())
   readData |= BitSelect; // Set data bit
  else
   readData &= ~BitSelect; // Clear data bit

  ClrI2CSCL();   // Clear SCL 
  NOP(); 

  BitSelect >>= 1;  // Shift bit position to right
 }

 SetSDAOutput();

 return readData; 
}

 


/*******************************************************************
                    向有子地址器件发送多字节数据函数              
函数原型: unsigned char  WriteStr_I2C(unsigned char SlaveAdrees,unsigned char SlaveSubAdrees,unsigned char *WriteDataStr,unsigned char WriteDataNumber); 
功能:     向IIC器件连续发送多字节数据
输入:     SlaveAdrees--器件地址
          SlaveSubAdrees--器件子地址
          WriteDataStr--待发送数据串指针
          WriteDataNumber--待发送数据串字节数
输出:     I2C_OK--表示数据发送成功
          I2C_ERR--表示数据发送失败
********************************************************************/
unsigned char WriteStr_I2C(unsigned char SlaveAdrees,unsigned char SlaveSubAdrees,unsigned char *WriteDataStr,unsigned char WriteDataNumber)
{
   unsigned char  ContinueNumber=10;
   unsigned char  NuberBak=WriteDataNumber;

   while(--ContinueNumber){
    Start_I2C();                             //start iic                            
    if( WriteByte_I2C(SlaveAdrees)!=I2C_OK )          //Write SlaveAdrees to slave
        goto ErrorExit;
    if( WriteByte_I2C(SlaveSubAdrees)!=I2C_OK )       //Write slaveSubAdress to slave
        goto ErrorExit;


    while(WriteDataNumber)
    {
        if( WriteByte_I2C(WriteDataStr[--WriteDataNumber])!=I2C_OK )
            goto ErrorExit;
    }      
  
    Stop_I2C();               
    return(I2C_OK);
 ErrorExit:
   delay20ms();
   WriteDataNumber=NuberBak;
   continue;

   }
    Stop_I2C();               
    return(I2C_ERR);
}

 

/*******************************************************************
                    向有子地址器件读取多字节数据函数              
函数原型: unsigned char  ReadStr_I2C(unsigned char SlaveAdrees,unsigned char SlaveSubAdrees,unsigned char *ReadDataStr,unsigned char ReadDataNumber); 
功能:     向IIC器件连续发送多字节数据
输入:     SlaveAdrees--器件地址
          SlaveSubAdrees--器件子地址
          ReadDataStr--接收到的数据串指针
          ReadDataNumber--接收到数据串字节数
输出:     I2C_OK--表示数据接收成功
          I2C_ERR--表示数据接收失败
********************************************************************/
unsigned char ReadStr_I2C(unsigned char SlaveAdrees,unsigned char SlaveSubAdrees,unsigned char *ReadDataStr,unsigned char ReadDataNumber)
{

   unsigned char index=0;
   unsigned char  ContinueNumber=10;

   while(--ContinueNumber){
    Start_I2C();                             //start iic                            
    if( WriteByte_I2C(SlaveAdrees)!=I2C_OK )          //Write SlaveAdrees to slave
        goto ErrorExit;
    if( WriteByte_I2C(SlaveSubAdrees)!=I2C_OK )       //Write slaveSubAdress to slave
        goto ErrorExit;


    Start_I2C();                             //start iic                            
    if( WriteByte_I2C(SlaveAdrees+1)!=I2C_OK )          //Write SlaveAdrees and Read-flag to slave
        goto ErrorExit;

    ReadDataNumber--;
    while(ReadDataNumber>index)
    {     
      ReadDataStr[index++]=ReadByte_I2C();
      SetACK_I2C(0);
    }      
   
    ReadDataStr[index]=ReadByte_I2C();
    SetACK_I2C(1);    
    Stop_I2C();               
    return(I2C_OK);
 ErrorExit:
     delay20ms();
    continue;
   }
       Stop_I2C();               
    return(I2C_ERR);

}

 

/*******************************************************************
                    向有子地址器件发送单 字节数据函数              
函数原型: unsigned char Write_I2C(unsigned char SlaveAdrees,unsigned char SlaveSubAdrees,unsigned char WriteData);
功能:     向IIC器件连续发送多字节数据
输入:     SlaveAdrees--器件地址
          SlaveSubAdrees--器件子地址
          WriteDataStr--待发送数据串指针
          WriteDataNumber--待发送数据串字节数
输出:     I2C_OK--表示数据发送成功
          I2C_ERR--表示数据发送失败
********************************************************************/
unsigned char Write_I2C(unsigned char SlaveAdrees,unsigned char SlaveSubAdrees,unsigned char WriteData)
{
   unsigned char  ContinueNumber=10;

   while(--ContinueNumber){
    Start_I2C();                             //start iic                            
    if( WriteByte_I2C(SlaveAdrees)!=I2C_OK )          //Write SlaveAdrees to slave
        goto ErrorExit;
    if( WriteByte_I2C(SlaveSubAdrees)!=I2C_OK )       //Write slaveSubAdress to slave
        goto ErrorExit;


     if( WriteByte_I2C(WriteData)!=I2C_OK )
         goto ErrorExit;    
  
    Stop_I2C();               
    return(I2C_OK);
 ErrorExit:
           delay20ms();
    continue;
   }
       Stop_I2C();               
    return(I2C_ERR);
}

 


/*******************************************************************
                    向有子地址器件读取多字节数据函数              
函数原型: unsigned char  ReadStr_I2C(unsigned char SlaveAdrees,unsigned char SlaveSubAdrees,unsigned char *ReadDataStr,unsigned char ReadDataNumber); 
功能:     向IIC器件连续发送多字节数据
输入:     SlaveAdrees--器件地址
          SlaveSubAdrees--器件子地址
          ReadDataStr--接收到的数据串指针
          ReadDataNumber--接收到数据串字节数
输出:     I2C_OK--表示数据接收成功
          I2C_ERR--表示数据接收失败
********************************************************************/
unsigned char Read_I2C(unsigned char SlaveAdrees,unsigned char SlaveSubAdrees)
{

    unsigned char ReadData=0;

   unsigned char  ContinueNumber=10;

   while(--ContinueNumber){
    Start_I2C();                             //start iic                            
    if( WriteByte_I2C(SlaveAdrees)!=I2C_OK )          //Write SlaveAdrees to slave
        goto ErrorExit;
    if( WriteByte_I2C(SlaveSubAdrees)!=I2C_OK )       //Write slaveSubAdress to slave
        goto ErrorExit;


    Start_I2C();                             //start iic                            
    if( WriteByte_I2C(SlaveAdrees+1)!=I2C_OK )          //Write SlaveAdrees and Read-flag to slave
        goto ErrorExit;


      ReadData=ReadByte_I2C();
     SetACK_I2C(1);
      Stop_I2C();               
      return(ReadData);     
 ErrorExit:
     delay20ms();
    continue;
   }
       Stop_I2C();               
    return(I2C_ERR);
}

2019-11-06 10:26:15 weixin_41987706 阅读数 9
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3419 人正在学习 去看看 朱有鹏

夜光序言:

 

 想要的,和能得到的,总是相距甚远。有些人注定是拿来错过的。

 

 

 

 

 

正文:

我们针对控件修改一下代码

实现功能

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            int i = 0;
            for(i = 1; i < 100; i++)
            {
                comboBox1.Items.Add(i.ToString() + "秒"); 
            }
        }
    }
}

我们做单片机,都知道毫秒的时间内1000就封顶了

单片机的整型是16位

 

我们一开始搞单片机的时候,那时候就是有好多寄存器

C#中的类可以理解成单片机的SFR


 

大部分原理和C语言相差不大,至于C#就是多了很多的控件,我们需要学会使用

 

 

 

 

 

 

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        int count; //用于定时器计数
        //夜光:因为是字符串
        //所以我们需要一个转换函数
        int time; //存储设定的定时值



        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            int i = 0;
            for(i = 1; i < 100; i++)
            {
                comboBox1.Items.Add(i.ToString() + " 秒"); 
            }
            //夜光:我们想要在label里面显示
            //label3.Text = "";


        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            count++;//记录当前秒
            label3.Text = (time - count).ToString() + "秒"; //显示剩余时间
            progressBar1.Value = count; //设置进度条进度
            if (count == time)
            {
                timer1.Stop();//时间到,停止计时
                System.Media.SystemSounds.Asterisk.Play(); //夜光:声音
                MessageBox.Show("时间到了~", "提示"); //弹出提示框
            }

        }

        private void button1_Click(object sender, EventArgs e)
        {
            string str = comboBox1.Text;//夜光:将下拉框内容添加到一个变量里面
            //为了便于非科班的朋友理解, 科班的朋友应该对面向对象有一个很深入的理解了
            //string data = str.Substring(0, 2);
            //time = Convert.ToInt16(data);

            time = Convert.ToInt16(str.Substring(0, 2));//得到设定定时值(整形)从0开始查,查两位,我们编程一般都是从0开始
            progressBar1.Maximum = time; //进度条最大数值    
            timer1.Start();
        }
    }
}

我们加一个断点看下,证明还是要加一个空格

9 空格 秒

否则从9开始查,直接到秒,额

方法的重载,函数名一样,但是返回的值不同


方法重载:

方法重载是指在一个类中定义多个同名的方法,但要求每个方法具有不同的参数的类型或参数的个数。

方法重载通常用于创建完成一组任务相似但参数的类型或参数的个数不同的方法。


重写:

在C#中,子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖。

重写与重载的区别在于:

重写多态性起作用,对调用被重载过的方法可以大大减少代码的输入量,同一个方法名只要往里面传递不同的参数就可以拥有不同的功能或返回值。

用好重写和重载可以设计一个结构清晰而简洁的类,可以说重写和重载在编写代码过程中的作用非同一般.

 

 

就像我们做单片机开发一样

很多细节问题也要注意一下

但是你会发现不好使

尴尬~

还是要在程序里面设置

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2013-10-10 13:43:36 qhyka 阅读数 1166
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3419 人正在学习 去看看 朱有鹏

 转载自:http://blog.csdn.net/leizi_chn/article/details/7244377


C语言在8051单片机上的扩展(interrupt、using关键字的用法)
直接访问寄存器和端口
定义
sfr P0 0x80
sfr P1 0x81
sfr ADCON; 0xDE
sbit EA 0x9F

操作
ADCON = 0x08;
P1 = 0xFF;
io_status = P0 ;
EA = 1;
在使用了interrupt 1 关键字之后,会自动生成中断向量
在 ISR中不能 与其他 "后台循环代码"(the background loop code) 共享局部变量,因为连接器会复用 在RAM中这些变量的位置 ,所以它们会有不同的意义,这取决于当前使用的不同的函数复用变量对RAM有限的51来讲很重要。所以,这些函数希望按照一定的顺序执行 而不被中断。
void timer0_int(void) interrupt 1 using 2
{
unsigned char temp1;
unsigned char temp2;
executable C statements;
}
"interrupt"声明表向量生成在(8*n+3),这里,n就是interrupt参数后的那个数字这里,在08H的代码区域 生成LJMP timer0_int这样一条指令。
"using" tells the compiler to switch register banks on entry to an interrupt routine. This "context" switch is the fastest way of
providing a fresh registerbank for an interrupt routine's local data and is to be preferred to stacking registers for very time-criticalroutines. Note that interrupts of the same priority can share a register bank, since there is no risk that they will interrupt each other.
"using" 告诉编译器 在进入中断处理器 去切换寄存器的bank。这个"contet"切换是为中断处理程序的局部变量提供一个新鲜的寄存器bank最快的方式。对时序要求严格的程序,是首选的stack寄存器(保存寄存器到stack)方式。
注意:同样优先级别的中断可以共享寄存器bank,因为他们每次将中断没有危险。
If a USING 1 is added to the timer1 interrupt function prototype, the pushing of registers is replaced by a simple MOV to PSW to switch registerbanks. Unfortunately, while the interrupt entry is speeded up, the direct register addressing used on entry to sys_interp fails. This is because C51 has not yet been told that the registerbank has been changed. If no working registers are used and no other function is called, the optimizer eliminiates teh code to switch register banks.
如果在timer1的中断函数原型中使用using 1, 寄存器的pushing将被MOV to PSW切换寄存器bank 所替换。
不幸的是,当一个中断入口被加速时。用在入口的 直接寄存器寻址将失败。这是因为 C51没有告诉 寄存器bank已经改变。如果不工作的寄存器将被使用,如果没有其他函数被调用,优化器.....
Logically, with an interrupt routine, parameters cannot be passed to it or returned. When the interrupt occurs, compiler-inserted code is run which pushes the accumulator, B,DPTR and the PSW (program status word) onto the stack. Finally, on exiting the interrupt routine, the items previously stored on the stack are restored and the closing "}" causes a RETI to be used rather than a normal RET.
逻辑上,一个中断服务程序,不能传递参数进去,也不可返回值。当中断发生时,编译器插入的代码被运行,它将累加器,B,DPTR和PSW(程序状态字)入栈。最后,在退出中断程序时,预先存储在栈中 被恢复。最后的"}"结束符号将插入RETI到中断程序的最后,为了用 Keil C语言创建一个中断服务程序(ISR),利用 interrupt 关键词和正确的中断号声明一个static void函数。Keil C编译器自动生成中断向量,以及中断程序的进口、出口代码。Interrupt 函数属性标志着该函数为ISR。可用using属性指定ISR使用哪一个寄存器区,这是可选的。有效的寄存器区范围为1到3。
中断源的矢量位置
中断源 Keil中断编号 矢量地址
最高优先级 6 0x0033
外部中断0 0 0x0003
定时器0溢出 1 0x000B
外部中断1 2 0x0013
定时器1溢出 3 0x001B
串口 4 0x0023
定时器2溢出 5 0x002B
DMA 7 0x003B
硬件断点 8 0x0043
JTAG 9 0x004B
软件断点 10 0x0053
监视定时器 12 0x0063
1.函数在调用前定义与在调用后定义产生的代码是有很大差别的(特别是在优化级别大于3级时)。(本人也不太清楚为什么,大概因为在调用前定义则调用函数已经知道被调用函数对寄存器的使用情况,则可对函数本身进行优化;而在调用后进行定义则函数不知被调用函数对寄存器的使用情况,它默认被调用函数对寄存器(ACC、 B、 DPH、 DPL、 PSW、 R0、 R1、 R2、 R3、R 4、 R5、, R6、 R7)都已经改变,因此不在这些寄存器中存入有效的数据)
2.函数调用函数时除在堆栈中存入返回地址之外,不在堆栈中保存其它任何寄存器。(ACC、 B、 DPH、 DPL、 PSW、 R0、 R1、 R2、 R3、R 4、 R5、, R6、 R7)的内容。(除非被调用函数使用了using特性)
3.中断函数是一个例外,它会计算自身及它所调用的函数对寄存器(ACC、 B、 DPH、 DPL、 PSW、 R0、 R1、 R2、 R3、R 4、 R5、, R6、 R7)的改变,并保存相应它认为被改变了的寄存器。
4.使用C写程序时,尽量少使用using n (n=0,1,2,3)特性。(这个特性在本人使用的过程中存在一些问题,不知算不算是一个小bug)
默认keil c51中的函数使用的是0寄存器组,当中断函数使用using n时,n = 1,2,3或许是对的,但n=0时,程序就已经存在了bug(只有中断函数及其所调用的函数并没有改变R0 ---- R7的值时,这个bug不会表现出来)
一个结论是,在中断函数中如果使用了using n,则中断不再保存R0----R7的值。由此可以推论出,一个高优先级的中断函数及一个低优先级的中断函数同时使用了using n,(n = 0,1,2,3)当n相同时,这个存在的bug 是多么的隐蔽。(这恰是使人想象不到的)
使用不同寄存器组的函数(特殊情况外)不能相互调用"using"关键字告诉编译器切换register bank
如果中断程序不重要,using关键字能忽略。如果一个函数被从中断程序调用,而此中断强制使用using,当编译一个被调用的函数时,编译器必须告诉它
1)在函数前 必须用伪指令
#pragma NOAREGS
在进入函数
#pragma RESTORE
或者
#pragmas AREGS
这样就不会使用 "绝对地址定位"
2)#pragma REGISTERBANK(n)
用这个指定告诉当前使用的bank
用NOAREGS指令 移除 MOV R7,AR7
中断服务例程
void timer0_int(void) interrupt 1 using 1
{
unsigned char temp1 ;
unsigned char temp2 ;
}
被调用的函数
#pragma SAVE // Rember current registerbank
#pragma REGISTERBANK(1) // Tel C51 base address of current registerbank.
void func(char x)
{
// Called from interrupt routine
// with "using1"
}
#pragma RESTORE // Put back to original registerbank

如果中断服务例程使用了using,被中断服务例程 调用的函数一定要REGISTERBANK(n),一个被ISR调用的函数也可能被后台程序调用为了函数"reentrant(可重入8051 系列MCU的基本结构包括:32 个I/O 口(4 组8 bit 端口);两个16 位定时计数器;全双工串行通信;6 个中断源(2 个外部中断、2 个定时/计数器中断、1 个串口输入/输出中断),两级中断优先级;128 字节内置RAM;独立的 64K 字节可寻址数据和代码区。中断发生后,MCU 转到5个中断入口处之一,然后执行相应的中断服务处理程序。中断程序的入口地址被编译器放在中断向量中,中断向量位于程序代码段的最低地址处,注意这里的串口输入/输出中断共用一个中断向量。8051的中断向量表如下:
中断源 中断向量
---------------------------
上电复位 0000H
外部中断0 0003H
定时器0 溢出 000BH
外部中断1 0013H
定时器1 溢出 001BH
串行口中断 0023H
定时器2 溢出 002BH
interrupt 和 using 都是C5 的关键字。C51中断过程通过使用interrupt关键字和中断号(0 到 31)来实现。中断号指明编译器中断程序的入口地址中断序号对应着8051中断使能寄存器IE 中的使能位,对应关系如下:
IE寄存器 C51中的 8051的
的使能位 中断号 中断源
--------------------------------
IE.0 0 外部中断0
IE.1 1 定时器0 溢出
IE.2 2 外部中断1
IE.3 3 定时器1 溢出
IE.4 4 串口中断
IE.5 5 定时器2 溢出
有了这一声明,编译器不需理会寄存器组参数的使用和对累加器A、状态寄存器、寄存器B、数据指针和默认的寄存器的保护。只要在中断程序中用到,编译器会把它们压栈,在中断程序结束时将他们出栈。C51 支持所有 5 个 8051 标准中断从 0 到 4 和在 8051 系列(增强型)中多达 27 个中断源。
using 关键字用来指定中断服务程序使用的寄存器组。用法是:using 后跟一个0 到3 的数,对应着 4 组工作寄存器。一旦指定工作寄存器组,默认的工作寄存器组就不会被压栈,这将节省 32 个处理周期,因为入栈和出栈都需要 2 个处理周期。这一做法的缺点是所有调用中断的过程都必须使用指定的同一个寄存器组,否则参数传递会发生错误。因此对于using,在使用中需灵活取舍。


STM32的IAP固件升级

阅读数 231

c语言面试题1

阅读数 3071

没有更多推荐了,返回首页