yuyy1989 发表于 2023-5-14 16:21:32

第15集,外部中断
1.中断和中断系统
手册里介绍了什么是中断和中断系统


支持的中断源


2.外部中断
外部中断就是在单片机的一个引脚上,由于外部因素导致了一个电平的变化 (比如由高变低),而通过捕获这个变化,单片机内部自主运行的程序就会被暂时打断,转面去执行相应的中断处理程序,执行完后又回到原来中断的地方继续执行原来的程序。
INT0-INT4的中断号


测试例程

#include "config.h"

//USB调试及复位所需定义
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#"; //设置自动复位到ISP区的用户接口命令

void test_int0(void) interrupt 0 //INT0 P3.2可触发
{
    P20 = !P20;
}

void test_int1(void) interrupt 2 //INT1 P3.3可触发
{
    P21 = !P21;
}

void test_int2(void) interrupt 10 //INT2 P3.6可触发
{
    P22 = !P22;
}

void test_int3(void) interrupt 11 //INT3 P3.7可触发
{
    P23 = !P23;
}

void test_int4(void) interrupt 16 //INT4 P3.0可触发
{
    P24 = !P24;
}

/******************** 主函数 **************************/
void main(void)
{
    WTST = 0;//设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
    EAXFR = 1; //扩展寄存器(XFR)访问使能
    CKCON = 0; //提高访问XRAM速度

    RSTFLAG |= 0x04;   //设置硬件复位后需要检测P3.2的状态选择运行区域,否则硬件复位后进入USB下载模式

    P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
    P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
    P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
    P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
    P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
    P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
    P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
    P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口

    //USB调试及复位所需代码-----
    P3M0 &= ~0x03;
    P3M1 |= 0x03;
    IRC48MCR = 0x80;
    while (!(IRC48MCR & 0x01));
    usb_init();
    //-------------------------

    IE0= 0;   //外中断0标志位
    IE1= 0;   //外中断1标志位
    EX0 = 1;    //INT0 使能
    EX1 = 1;    //INT1 使能
    EX2 = 1;    //INT2 使能
    EX3 = 1;    //INT3 使能
    EX4 = 1;    //INT4 使能

    IT0 = 1;      //INT0 下降沿中断      
    IT1 = 1;      //INT1 下降沿中断      

    EUSB = 1;   //IE2相关的中断位操作使能后,需要重新设置EUSB
    EA = 1;   //打开总中断
    LED0 = 1;
   
    printf_hid("STC32G 测试程序\r\n");
    while(1)
    {
    }
}
在外部触发某个需要及时处理的事件时又不能频繁轮询IO状态时需要用到外部中断,例如一些使用电池供电的设备处理按键或者获取传感器数据,由于低功耗的要求不能像以前课程里那样每10ms查询一次IO状态



yuyy1989 发表于 2023-5-16 16:49:43

第16集,IO中断



外部中断只有指定的几个IO口可以触发,IO中断所有的IO都能触发,屠龙刀三.2可以使用下降沿中断

Keil的中断号有限制,可以借助工具扩展Keil的中断号



IO中断配置需要用到的寄存器


各端口使用的中断号


演示代码,没有数码管只能使用虚拟数码管

uint16_t timercount = 0;
uint8_t seg0num = 0;
uint8_t seg1num = 0;
void test_usbseg()
{
    uint8_t segdatas={0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40};
    segdatas = ledsegcodes;
    segdatas = ledsegcodes;
    SEG7_ShowCode(segdatas);
}

void test_timer0_cb(void)
{
    if(timercount < 50)
    {
      timercount++;
    }
    else
    {
      timercount = 0;
      test_usbseg();
    }
}

void test_P3int(void) interrupt 40 //P3端口IO中断
{
    uint8_t intf;
    intf = P3INTF;
    if(intf)
    {
      P3INTF = 0;//清除中断标识
      if(intf&0x20) //P35产生中断
      {
            seg0num++;
            if(seg0num > 9)
                seg0num = 0;
      }
    }
}

/******************** 主函数 **************************/
void main(void)
{
    WTST = 0;//设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
    EAXFR = 1; //扩展寄存器(XFR)访问使能
    CKCON = 0; //提高访问XRAM速度
    RSTFLAG |= 0x04;   //设置硬件复位后需要检测P3.2的状态选择运行区域,否则硬件复位后进入USB下载模式
    P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
    P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
    P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
    P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
    P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
    P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
    P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
    P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口
    //USB调试及复位所需代码-----
    P3M0 &= ~0x03;
    P3M1 |= 0x03;
    IRC48MCR = 0x80;
    while (!(IRC48MCR & 0x01));
    usb_init();
    //-------------------------
    P3IM0 = 0x00;
    P3IM1 = 0x00; //P35下降沿中断,屠龙刀三.2可以使用下降沿中断
    P3INTE = 0x20; //P35中断使能
    P5IM0 = 0x00;
    P5IM1 = 0x00;//P54下降沿中断,屠龙刀三.2可以使用下降沿中断
    P5INTE = 0x10;//P54中断使能
    PIN_IPH = 0x00;
    PIN_IP = 0x20; //P5端口设置优先级1
   
    yuyy_timer0init(10000,test_timer0_cb); //10ms定时
    EUSB = 1;   //IE2相关的中断位操作使能后,需要重新设置EUSB
    EA = 1;   //打开总中断
    while(1)
    {
    }
}

yuyy1989 发表于 2023-5-17 10:43:02

第17集,模数转换器ADC
模数转换器即 A/D 转换器,或简称 ADC(Analog-to-digital converter),通常是指一个将模拟信号转变为数字信号的电子元件。
各通道对应IO


计算公式




演示代码
//USB调试及复位所需定义
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#"; //设置自动复位到ISP区的用户接口命令
void delay_ms(uint16_t ms)
{
    uint16_t i;
    do{
      i = MAIN_Fosc / 6000;
      while(--i);   //6T per loop
    }while(--ms);
}
/******************** 主函数 **************************/
void main(void)
{
    WTST = 0;//设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
    EAXFR = 1; //扩展寄存器(XFR)访问使能
    CKCON = 0; //提高访问XRAM速度

    RSTFLAG |= 0x04;   //设置硬件复位后需要检测P3.2的状态选择运行区域,否则硬件复位后进入USB下载模式

    P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
    P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
    P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
    P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
    P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
    P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
    P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
    P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口

    //USB调试及复位所需代码-----
    P3M0 &= ~0x03;
    P3M1 |= 0x03;
    IRC48MCR = 0x80;
    while (!(IRC48MCR & 0x01));
    usb_init();
    //-------------------------
    EUSB = 1;   //IE2相关的中断位操作使能后,需要重新设置EUSB
    EA = 1;   //打开总中断

    P1M0 = 0x00;
    P1M1 = 0x01;   //P1.0高阻输入,作为ADC的引脚必须设置为高阻输入
    ADCTIM = 0x3F; //设置ADC内部时序
    ADCCFG = 0x2F; //结果右对齐,设置ADC时钟为系统时钟/2/16
    ADC_POWER = 1; //使能ADC模块
    ADC_CONTR &= 0xF0; //选择通道0;

    while(1)
    {
      uint16_t adcresult;
      float adcv;
      ADC_START = 1;//启动ADC转换
      _nop_();
      _nop_();
      _nop_();
      while(ADC_FLAG == 0);   //wait for ADC finish
      ADC_FLAG = 0;   //清除ADC结束标志
      adcresult = (ADC_RES<<8)&0x0F00;
      adcresult |= ADC_RESL;
      adcv = 5.0*adcresult/4096; //计算为电压
      printf_hid("ADC结果: 0x%03X ADC电压: %.2fV\r\n",adcresult,adcv);
      delay_ms(1000);
    }
}没有2.5v的电压源,通过杜邦线将Vref连接到VCC,P1.0接一个从USB转串口引出来的3.3v,与屠龙刀开发板共地,运行截图


比万用表测得电压高0.08V左右

yuyy1989 发表于 2023-5-17 20:41:40

第18集,ADC采集电源电压和ADC按键上
利用ADC测量VCC供电电压
演示代码
#define VREFH_ADDR CHIPID7
#define VREFL_ADDR CHIPID8

bit busy;
int BGV;

//USB调试及复位所需定义
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#"; //设置自动复位到ISP区的用户接口命令
void delay_ms(uint16_t ms)
{
    uint16_t i;
    do{
      i = MAIN_Fosc / 6000;
      while(--i);   //6T per loop
    }while(--ms);
}

void ADCInit()
{
    ADCTIM = 0x3F;   //设置 ADC 内部时序
    ADCCFG = 0x2F;   //结果右对齐,设置 ADC 时钟为系统时钟/2/16
    ADC_CONTR = 0x8F; //使能 ADC 模块,并选择第 15 通道
}
int ADCRead()
{
    int res;
    ADC_START = 1; //启动 AD 转换
    _nop_();
    _nop_();
    while (!ADC_FLAG); //查询 ADC 完成标志
    ADC_FLAG = 0; //清完成标志
    res = (ADC_RES << 8) | ADC_RESL; //读取 ADC 结果
    return res;
}
/******************** 主函数 **************************/
void main(void)
{
    int res;
    int vcc;
    int i;

    WTST = 0;//设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
    EAXFR = 1; //扩展寄存器(XFR)访问使能
    CKCON = 0; //提高访问XRAM速度

    RSTFLAG |= 0x04;   //设置硬件复位后需要检测P3.2的状态选择运行区域,否则硬件复位后进入USB下载模式

    P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
    P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
    P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
    P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
    P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
    P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
    P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
    P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口

    //USB调试及复位所需代码-----
    P3M0 &= ~0x03;
    P3M1 |= 0x03;
    IRC48MCR = 0x80;
    while (!(IRC48MCR & 0x01));
    usb_init();
    //-------------------------
    EUSB = 1;   //IE2相关的中断位操作使能后,需要重新设置EUSB
    EA = 1;   //打开总中断

    BGV = (VREFH_ADDR << 8) + VREFL_ADDR; //从 CHIPID 中读取内部参考电压值
    ADCInit(); //ADC 初始化
    ADCRead();
    ADCRead();//前两个数据建议丢弃

    while(1)
    {
      res = 0;
      for (i=0; i<8; i++)
      {
            res += ADCRead(); //读取 8 次数据
      }
      res >>= 3; //取平均值
      vcc = (int)(4096L * BGV / res); //(12 位 ADC 算法)计算 VREF 管脚电压,即电池电压 注意,此电压的单位为毫伏(mV)
      printf_hid("VCC电压:%dmv\r\n",vcc);
      delay_ms(1000);
    }
}Vref连接到VCC,USB供电,测量结果




yuyy1989 发表于 2023-5-18 17:40:21

第18集,ADC采集电源电压和ADC按键中
ADC按键,按下按键时,通过电阻分压得到不同的电压值,ADC采集在各个范围内的值来判定是哪个按键按下
优点是只需要一个IO就能实现多个按键的识别
缺点是识别精度差,不能识别同时按下多个按键,程序要一直保持扫描来识别按键按下
示例代码,和之前的普通按键和矩阵按键集成到一起了,因为没有ADC按键所以ADC按键部分未经验证
yuyy_key.h
#ifndef __YUYY_KEY_H_
#define __YUYY_KEY_H_
#include "config.h"

#define KEYSCANINTERVAL 10//按键扫描间隔
#define DEBOUNCETIME 30 //去抖时间
#define LONGPRESSTIME 2000 //长按识别时间
//普通按键
#define GPIOKEYNUMS 4 //按键总数
#define KEYPORT P3 //按键io端口号
#define KEYIOSTART 2 //按键io起始值 屠龙刀的按键从P3.2开始
#define KEYDOWNLEV 0 //按键按下的io电平
//矩阵按键
#define MATRIXKEYCOLS 4 //按键列数
#define MATRIXKEYROWS 2 //按键行数
#define MATRIXKEYNUMS MATRIXKEYCOLS*MATRIXKEYROWS //矩阵按键总数
/**
*按键分布示例
*   P00 P01 P02 P03
* P060   1   2   3
* P074   5   6   7   
*/
#define MATRIXKEYROW0PIN P06 //第0行IO
#define MATRIXKEYROW1PIN P07 //第1行IO
#define MATRIXKEYCOL0PIN P00 //第0列IO
#define MATRIXKEYCOL1PIN P01 //第1列IO
#define MATRIXKEYCOL2PIN P02 //第2列IO
#define MATRIXKEYCOL3PIN P03 //第3列IO

//ADC按键
#define ADCKEYNUMS 16//ADC按键总数
#define ADCKEYPIN P10 //ADC按键io
#define ADCKEYCHANNEL 0//
#define ADCKEYOFFSET 64//允许的误差值

enum YUYY_KEYSTATE
{
    KEY_UP = 0,      //按键未按下
    KEY_DOWN,      //按键按下
    KEY_SINGLECLICK, //单击按键 按下按键后在LONGPRESSTIME之前松开
    KEY_LONGPRESS,   //长按按键 按下按键的时间超过LONGPRESSTIME
    KEY_LONGPRESSUP//长按后松开
};

enum YUYY_KEYTYPE
{
    KEYTYPE_GPIO = 0,      //普通按键
    KEYTYPE_MATRIX,      //矩阵按键
    KEYTYPE_ADC,         //ADC按键
};
//定义回调
typedef void (*yuyy_key_cb)(enum YUYY_KEYTYPE keytype,uint8_t keynum,enum YUYY_KEYSTATE state);
/**
* 初始化按键,设置回调函数
* 外部文件通过回调函数监控按键状态
*/
void yuyy_keyinit(yuyy_key_cb cb);

/**
* 10ms一次检查按键状态
*/
void yuyy_keyloop(void);

#endifyuyy_key.c
#include "yuyy_key.h"
#include "yuyy_delay.h"
#if(GPIOKEYNUMS)
uint16_t gpiokeydowncount = {0};
#else
uint16_t gpiokeydowncount = {0};
#endif
#if(MATRIXKEYNUMS)
uint16_t matrixkeydowncount = {0};
#else
uint16_t matrixkeydowncount = {0};
#endif
#if(ADCKEYNUMS)
uint16_t adckeydowncount = {0};
#else
uint16_t adckeydowncount = {0};
#endif
uint16_t adcperkey;
yuyy_key_cb keycb;

void sendkeystate(enum YUYY_KEYTYPE keytype,uint8_t keynum,enum YUYY_KEYSTATE state)
{
    if(keycb)
    {
      keycb(keytype,keynum,state);
    }
}

void yuyy_refreshkey(enum YUYY_KEYTYPE keytype,uint8_t key,uint8_t down)
{
    uint16_t *keydowncount;
    if(keytype == KEYTYPE_GPIO)
    {
      keydowncount = gpiokeydowncount;
    }
    else if(keytype == KEYTYPE_MATRIX)
    {
      keydowncount = matrixkeydowncount;
    }
    else if(keytype == KEYTYPE_ADC)
    {
      keydowncount = adckeydowncount;
    }
    else
    {
      return;
    }
    if(down)
    {
      if(keydowncount < 0xFFFF)
      {
            keydowncount++;
      }
      if(keydowncount*KEYSCANINTERVAL == DEBOUNCETIME)
      {
            sendkeystate(keytype,key,KEY_DOWN);
      }
      if(keydowncount*KEYSCANINTERVAL == LONGPRESSTIME)
      {
            sendkeystate(keytype,key,KEY_LONGPRESS);
      }
    }
    else
    {
      if(keydowncount*KEYSCANINTERVAL > DEBOUNCETIME)
      {
            if(keydowncount*KEYSCANINTERVAL < LONGPRESSTIME)
            {
                sendkeystate(keytype,key,KEY_SINGLECLICK);
            }
            else
            {
                sendkeystate(keytype,key,KEY_LONGPRESSUP);
            }
      }
      keydowncount = 0;
    }
}

void yuyy_gpiokeyloop(void)
{
    uint8_t key = 0;
    while (key<GPIOKEYNUMS)
    {
      if(((KEYPORT >> (key+KEYIOSTART)) & 0x01) == KEYDOWNLEV)
      {
            yuyy_refreshkey(KEYTYPE_GPIO,key,1);
      }
      else
      {
            yuyy_refreshkey(KEYTYPE_GPIO,key,0);
      }
      key++;
    }
}

void yuyy_matrixkeyloop(void)
{
    uint8_t key = 0;
    uint8_t i,j,col = 0,row = 0;
    MATRIXKEYROW0PIN = 1;
    MATRIXKEYROW1PIN = 1;
    MATRIXKEYCOL0PIN = 0;
    MATRIXKEYCOL1PIN = 0;
    MATRIXKEYCOL2PIN = 0;
    MATRIXKEYCOL3PIN = 0;
    yuyy_delay_us(10);
    if (MATRIXKEYROW0PIN == 0)
    {
      row |= 0x01;
    }
    if (MATRIXKEYROW1PIN == 0)
    {
      row |= 0x02;
    }
    MATRIXKEYROW0PIN = 0;
    MATRIXKEYROW1PIN = 0;
    MATRIXKEYCOL0PIN = 1;
    MATRIXKEYCOL1PIN = 1;
    MATRIXKEYCOL2PIN = 1;
    MATRIXKEYCOL3PIN = 1;
    yuyy_delay_us(10);
    if (MATRIXKEYCOL0PIN == 0)
    {
      col |= 0x01;
    }
    if (MATRIXKEYCOL1PIN == 0)
    {
      col |= 0x02;
    }
    if (MATRIXKEYCOL2PIN == 0)
    {
      col |= 0x04;
    }
    if (MATRIXKEYCOL3PIN == 0)
    {
      col |= 0x08;
    }
    MATRIXKEYROW0PIN = 1;
    MATRIXKEYROW1PIN = 1;
    MATRIXKEYCOL0PIN = 1;
    MATRIXKEYCOL1PIN = 1;
    MATRIXKEYCOL2PIN = 1;
    MATRIXKEYCOL3PIN = 1;
    for(i=0;i<MATRIXKEYROWS;i++)
    {
      for (j = 0; j < MATRIXKEYCOLS; j++)
      {
            if((row&(1<<i)) && (col&(1<<j)))
            {
                yuyy_refreshkey(KEYTYPE_MATRIX,key,1);
            }
            else
            {
                yuyy_refreshkey(KEYTYPE_MATRIX,key,0);
            }
            key++;
      }
      
    }
}

uint16_t yuyy_readadc(uint8_t channel)
{
    uint16_t res;
    ADC_CONTR &= 0xF0;
    ADC_CONTR |= (channel&0x0F);
    ADC_START = 1; //启动 AD 转换
    _nop_();
    _nop_();
    while (!ADC_FLAG); //查询 ADC 完成标志
    ADC_FLAG = 0; //清完成标志
    res = ((ADC_RES << 8)&0x0F00) | ADC_RESL; //读取 ADC 结果
    return res;
}

void yuyy_adcKeyloop(void)
{
    uint16_t keyadc,key = 0,adccmp = adcperkey;
    keyadc = yuyy_readadc(ADCKEYCHANNEL);
    while (key < ADCKEYNUMS)
    {
      if(keyadc>=adccmp-ADCKEYOFFSET && keyadc<adccmp+ADCKEYOFFSET)
      {
            //按键按下
            yuyy_refreshkey(KEYTYPE_ADC,key,1);
      }
      else
      {
            //按键未按下
            yuyy_refreshkey(KEYTYPE_ADC,key,0);
      }
      key++;
      adccmp += adcperkey;
    }
}

void yuyy_keyloop(void)
{
    #if(GPIOKEYNUMS)
    yuyy_gpiokeyloop();
    #endif
    #if(MATRIXKEYNUMS)
    yuyy_matrixkeyloop();
    #endif
    #if(ADCKEYNUMS)
    yuyy_adcKeyloop();
    #endif
}

void yuyy_adcKeyinit(void)
{
    P1M0 = 0x00;
    P1M1 = 0x01;   //P1.0高阻输入,作为ADC的引脚必须设置为高阻输入
    ADCTIM = 0x3F; //设置ADC内部时序
    ADCCFG = 0x2F; //结果右对齐,设置ADC时钟为系统时钟/2/16
    ADC_POWER = 1;
    adcperkey = 4096/ADCKEYNUMS;
}

void yuyy_keyinit(yuyy_key_cb cb)
{
    #if(ADCKEYNUMS)
    yuyy_adcKeyinit();
    #endif
    keycb = cb;
}main.c
#include "config.h"
#include "string.h"
#include "yuyy.h"

//USB调试及复位所需定义
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#"; //设置自动复位到ISP区的用户接口命令

void test_timer0cb(void)
{
    yuyy_keyloop();
}
void test_keycb(enum YUYY_KEYTYPE keytype,uint8_t keynum,enum YUYY_KEYSTATE state)
{
    printf("按键类型:%d 按键号:%d 按键行为:%d\r\n",keytype,keynum,state);
}
/******************** 主函数 **************************/
void main(void)
{
    int res;
    int vcc;
    int i;

    WTST = 0;//设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
    EAXFR = 1; //扩展寄存器(XFR)访问使能
    CKCON = 0; //提高访问XRAM速度

    RSTFLAG |= 0x04;   //设置硬件复位后需要检测P3.2的状态选择运行区域,否则硬件复位后进入USB下载模式

    P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
    P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
    P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
    P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
    P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
    P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
    P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
    P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口

    //USB调试及复位所需代码-----
    P3M0 &= ~0x03;
    P3M1 |= 0x03;
    IRC48MCR = 0x80;
    while (!(IRC48MCR & 0x01));
    //如果使用USB-CDC需要下面的两行代码
    USBCLK = 0x00;
    USBCON = 0x90;
    //如果使用USB-HID注释掉上面两行代码
    usb_init();
    //-------------------------
    yuyy_timer0init(10000,test_timer0cb); //10ms触发一次回调
    yuyy_keyinit(test_keycb);

    EUSB = 1;   //IE2相关的中断位操作使能后,需要重新设置EUSB
    EA = 1;   //打开总中断

    while(1)
    {
    }
}只验证了普通按键和矩阵按键的工作情况



yuyy1989 发表于 2023-5-19 19:45:35

第18集,ADC采集电源电压和ADC按键下
没有ADC键盘,用屠龙刀上的P32-P354个按键做了个时钟,用P20上的LED模拟蜂鸣器,USB-CDC模拟数码管显示
P32 长按进入时钟设置,短按设置位右移
P33 点击改变数字,长按自动增加数字
P34 长按设置闹钟,短按设置位左移
P25 点击退出设置
运行效果

attach://10429.mp4

代码实现

#include "config.h"
#include "string.h"
#include "yuyy.h"

//USB调试及复位所需定义
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#"; //设置自动复位到ISP区的用户接口命令

const uint8_t ledsegcodes[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x40};
uint8_t clock10ms = 0; //毫秒
uint8_t clocksec = 0; //秒
uint8_t clockmin = 0; //分
uint8_t clockhour = 0; //时

uint8_t alertsec = 30; //闹钟秒
uint8_t alertmin = 0; //闹钟分
uint8_t alerthour = 0; //闹钟时

uint8_t settingmode = 0; // 1配置时钟 2配置闹钟
uint8_t settingpos = 0; //配置的数码管位
uint8_t settingtimechanged = 0;
uint8_t settingautoaddtimecount = 0;
uint8_t settingautoadd = 0;
uint8_t settingclock10ms = 0;

void test_showclock(void)
{
    uint8_t segdatas={0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00};
    segdatas = ledsegcodes;
    segdatas = ledsegcodes;
    segdatas = ledsegcodes;
    segdatas = ledsegcodes;
    segdatas = ledsegcodes;
    segdatas = ledsegcodes;
    SEG7_ShowCode(segdatas);
}
void test_showclocksetting(void)
{
    uint8_t segdatas={0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00};
    if(settingmode == 1)
    {
      segdatas = ledsegcodes;
      segdatas = ledsegcodes;
      segdatas = ledsegcodes;
      segdatas = ledsegcodes;
      segdatas = ledsegcodes;
      segdatas = ledsegcodes;
    }
    else if(settingmode == 2)
    {
      segdatas = ledsegcodes;
      segdatas = ledsegcodes;
      segdatas = ledsegcodes;
      segdatas = ledsegcodes;
      segdatas = ledsegcodes;
      segdatas = ledsegcodes;
    }
    if(settingclock10ms == 0)
    {
      segdatas = 0x00;
      SEG7_ShowCode(segdatas);
    }
    else if(settingclock10ms == 50)
    {
      SEG7_ShowCode(segdatas);
    }
    if(settingtimechanged)
    {
      if(settingclock10ms < 50)
            segdatas = 0x00;
      SEG7_ShowCode(segdatas);
      settingtimechanged = 0;
    }
    if(settingclock10ms < 100)
      settingclock10ms++;
    else
      settingclock10ms = 0;
}

void test_settingposright(void)
{
    if(settingpos == 0)
    {
      if(settingmode == 1 && clockhour > 23)
      {
            clockhour = 23;
      }
      else if(settingmode == 2 && alerthour > 23)
      {
            alerthour = 23;
      }
      settingpos = 1;
    }
    else if(settingpos == 1)
      settingpos = 3;
    else if(settingpos == 3)
      settingpos = 4;
    else if(settingpos == 4)
      settingpos = 6;
    else if(settingpos == 6)
      settingpos = 7;
    else
      settingpos = 0;
    settingtimechanged = 1;
}

void test_settingposleft(void)
{
    if(settingpos == 0)
    {
      if(settingmode == 1 && clockhour > 23)
      {
            clockhour = 23;
      }
      else if(settingmode == 2 && alerthour > 23)
      {
            alerthour = 23;
      }
      settingpos = 7;
    }
    else if(settingpos == 1)
      settingpos = 0;
    else if(settingpos == 3)
      settingpos = 1;
    else if(settingpos == 4)
      settingpos = 3;
    else if(settingpos == 6)
      settingpos = 4;
    else
      settingpos = 6;
    settingtimechanged = 1;
}

void test_settingadd(void)
{
    uint8_t setnum = 0;
    uint8_t *times,*timem,*timeh;
    if(settingmode == 1)
    {
      times = &clocksec;
      timem = &clockmin;
      timeh = &clockhour;
    }
    else if(settingmode == 2)
    {
      times = &alertsec;
      timem = &alertmin;
      timeh = &alerthour;
    }
    if(settingpos == 0)
    {
      setnum = *timeh/10;
      setnum++;
      if(setnum > 2)
      {
            setnum = 0;
      }
      *timeh = setnum*10 + *timeh%10;
    }
    else if(settingpos == 1)
    {
      setnum = *timeh%10;
      *timeh -= setnum;
      setnum++;
      if(*timeh > 19)
      {
            if(setnum > 3)
                setnum = 0;
      }
      else if(setnum > 9)
            setnum = 0;
      *timeh = setnum + *timeh;
    }
    else if(settingpos == 3)
    {
      setnum = *timem/10;
      setnum++;
      if(setnum > 5)
            setnum = 0;
      *timem = setnum*10 + *timem%10;
    }
    else if(settingpos == 4)
    {
      setnum = *timem%10;
      *timem -= setnum;
      setnum++;
      if(setnum > 9)
            setnum = 0;
      *timem = setnum + *timem;
    }
    else if(settingpos == 6)
    {
      setnum = *times/10;
      setnum++;
      if(setnum > 5)
            setnum = 0;
      *times = setnum*10 + *times%10;
    }
    else if(settingpos == 7)
    {
      setnum = *times%10;
      *times -= setnum;
      setnum++;
      if(setnum > 9)
            setnum = 0;
      *times = setnum + *times;
    }
    settingtimechanged = 1;
    settingclock10ms = 50;
}

void test_clockadd10ms(void)
{
    uint8_t timechanged = 0;
    if(clock10ms < 100)
      clock10ms++;
    else
    {
      clock10ms = 0;
      clocksec++;
      timechanged = 1;
    }
    if(clocksec == 60)
    {
      clocksec = 0;
      clockmin++;
    }
    if(clockmin == 60)
    {
      clockmin = 0;
      clockhour++;
    }
    if(clockhour == 24)
      clockhour = 0;
    if(timechanged)
    {
      if(settingmode == 0)
            test_showclock();
      if(clocksec == alertsec && clockmin == alertmin && clockhour == alerthour)
      {
            yuyy_beep(300);
      }
    }
}

void test_timer0cb(void)
{
    if(settingmode == 0)
      test_clockadd10ms();
    else
    {
      if(settingautoadd)
      {
            if(settingautoaddtimecount < 25)
            {
                settingautoaddtimecount++;
            }
            else
            {
                settingautoaddtimecount = 0;
                test_settingadd();
            }
      }
      test_showclocksetting();
      if(settingmode == 2) //设置闹钟时时间正常走
      {
            test_clockadd10ms();
      }
    }
    yuyy_keyloop();
    yuyy_beep_loop();
}
void test_keycb(enum YUYY_KEYTYPE keytype,uint8_t keynum,enum YUYY_KEYSTATE state)
{
    if(keytype == KEYTYPE_GPIO)
    {
      switch (keynum)
      {
      case 0://P32
            if(state == KEY_LONGPRESS)
            {
                //长按进入配置,时钟暂停
                settingmode = 1;
                settingpos = 0;
                settingclock10ms = 0;
            }
            else if(state == KEY_SINGLECLICK)
            {
                if(settingmode)
                {
                  test_settingposright();//右移1位
                }
            }
            break;
      case 1://P33
            if(state == KEY_SINGLECLICK)
            {
                //点击增加数字
                test_settingadd();
            }
            else if(state == KEY_LONGPRESS)
            {
                settingautoaddtimecount = 0;
                settingautoadd = 1;
            }
            else if(state == KEY_LONGPRESSUP)
            {
                settingautoaddtimecount = 0;
                settingautoadd = 0;
            }
            break;
      case 2://P34
            if(state == KEY_LONGPRESS)
            {
                //长按进入闹钟配置
                settingmode = 2;
                settingpos = 0;
                settingclock10ms = 0;
            }
            else if(state == KEY_SINGLECLICK)
            {
                if(settingmode)
                {
                  test_settingposleft();//左移1位
                }
            }
            break;
      case 3://P35
            if(state == KEY_SINGLECLICK)
            {
                settingmode = 0;//退出设置
                if(settingmode == 1)
                  clock10ms = 0;
            }
            break;
      default:
            break;
      }
    }
}
/******************** 主函数 **************************/
void main(void)
{
    WTST = 0;//设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
    EAXFR = 1; //扩展寄存器(XFR)访问使能
    CKCON = 0; //提高访问XRAM速度

    RSTFLAG |= 0x04;   //设置硬件复位后需要检测P3.2的状态选择运行区域,否则硬件复位后进入USB下载模式

    P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
    P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
    P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
    P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
    P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
    P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
    P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
    P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口

    //USB调试及复位所需代码-----
    P3M0 &= ~0x03;
    P3M1 |= 0x03;
    IRC48MCR = 0x80;
    while (!(IRC48MCR & 0x01));
    //如果使用USB-CDC需要下面的两行代码
    USBCLK = 0x00;
    USBCON = 0x90;
    //如果使用USB-HID注释掉上面两行代码
    usb_init();
    //-------------------------
    yuyy_timer0init(10000,test_timer0cb); //10ms触发一次回调
    yuyy_keyinit(test_keycb);

    EUSB = 1;   //IE2相关的中断位操作使能后,需要重新设置EUSB
    EA = 1;   //打开总中断

    while(1)
    {
    }
}


yuyy1989 发表于 2023-5-20 16:59:24

第19集,NTC温度采集
NTC(Negative Temperature Coe ficient)是指随温度上升电阻呈指数关系减小、具有负温度系数的热敏电阻现象和材料。
利用这一特性可以通过ADC来测量环境温度,将测量出的adc值通过查表的方式逆推为温度值。



yuyy1989 发表于 2023-5-21 15:06:12

第20集,串口通信
通信指设备之间通过一定的协议进行的信息交换。
每次发送一位数据的称为串行通信,多位一起传输的称为并行通信。串口通信是串行通信的其中的一种
串口通信(Serial Communication),是指外设和计算机间,通过数据信号线、地线、控制线等,按位进行传输数据的一种通讯方式。这种通信方式使用的数据线少,在远距离通信中可以节约通信成本,但其传输速度比并行传输低。
接线方式


课后练习


由于没有数码管和测温模块,所以用usb-hid模拟数码管,用存储的几个数据模拟温度值,用P35上的LED亮灭模拟蜂鸣器开关
运行效果
attach://10493.mp4

代码
//USB调试及复位所需定义
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#"; //设置自动复位到ISP区的用户接口命令

const uint8_t ledsegcodes[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x40};
#define FOSC 24000000UL
#define BRT (65536 - (FOSC / 115200+2) / 4)
//加 2 操作是为了让 Keil 编译器
//自动实现四舍五入运算
bit busy;
uint8_t rxbuffer;
uint8_t rxcount = 0;
int temps[] = {2650,-1035,40,-10,560,-100};//模拟温度值 26.50 -10.35
uint8_t tempindex = 0;
uint8_t rxfinishflag = 0;

void Uart2Init()
{
    P_SW2 = 0x80; //串口2 rxP10 txP11
    S2CFG = 0x01; //无帧错检测功能,波特率不加倍,固定为 Fosc/12
    S2CON = 0x50; //可变波特率8位数据方式,允许串口接收数据

    T2L = BRT;
    T2H = BRT >> 8;
    T2x12 = 1;
    T2R = 1;
    busy = 0;
}

void Uart2Send(uint8_t dat)
{
    while (busy);
    busy = 1;
    S2BUF = dat;
}
void Uart2SendStr(char *p)
{
    while (*p)
    {
      Uart2Send(*p++);
    }
}

void procrxdata()
{
    int temp;
    char txbuffer = {0};
    uint8_t segdatas={0x00};
    uint8_t segindex = 7;
    switch (rxbuffer)
    {
    case 'A'://点亮对应的LED
      if(rxcount == 2 && rxbuffer >= '0' && rxbuffer < '8')
      {
            P2 = ~(1<<(rxbuffer - '0'));
      }
      break;
    case 'B'://数码管显示数字
      if(rxcount > 1 || rxcount < 9)
      {
            while (rxcount > 1)
            {
                rxcount--;
                if(rxbuffer >= '0' && rxbuffer <= '9')
                {
                  segdatas = ledsegcodes-'0'];
                }
                segindex--;
            }
            SEG7_ShowCode(segdatas);
      }
      break;
    case 'C'://打开关闭蜂鸣器
      if(rxcount == 2 && (rxbuffer == '0' || rxbuffer == '1'))
      {
            P35 = '1' - rxbuffer;
      }
      break;
    case 'D'://发送温度
      if(rxcount == 2)
      {
            temp = temps;
            rxcount = 0;
            if(temp < 0)
            {
                txbuffer = '-';
                temp = 0 - temp;
            }
            if(temp > 999)
            {
                txbuffer = '0' + (temp / 1000);
                temp = temp%1000;
            }
            txbuffer = '0' + (temp / 100);
            temp = temp%100;
            txbuffer = '.';
            txbuffer = '0' + (temp / 10);
            temp = temp%10;
            txbuffer = '0' + temp;
            Uart2SendStr(txbuffer);
            tempindex++;
            if(tempindex > 5)
            {
                tempindex = 0;
            }
      }
      break;
    case 'Z'://发送"Hello STC"
      if(rxcount == 1)
      {
            Uart2SendStr("Hello STC");
      }
      break;
    default:
      break;
    }
    rxcount = 0;
    yuyy_delay_ms(1);
}

void Uart2Isr() interrupt 8
{
    if (S2TI)
    {
      S2TI = 0;
      busy = 0;
    }
    if (S2RI)
    {
      S2RI = 0;
      rxbuffer = S2BUF;
      if(rxcount > 1 && rxbuffer=='\r' && rxbuffer=='\n')
      {
            rxcount-=1;
            rxfinishflag = 1;
            S2REN = 0;//关闭接收
      }
      else
            rxcount++;
      if(rxcount > 15)
            rxcount = 0;
    }
}

/******************** 主函数 **************************/
void main(void)
{
    WTST = 0;//设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
    EAXFR = 1; //扩展寄存器(XFR)访问使能
    CKCON = 0; //提高访问XRAM速度

    RSTFLAG |= 0x04;   //设置硬件复位后需要检测P3.2的状态选择运行区域,否则硬件复位后进入USB下载模式

    P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
    P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
    P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
    P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
    P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
    P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
    P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
    P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口

    //USB调试及复位所需代码-----
    P3M0 &= ~0x03;
    P3M1 |= 0x03;
    IRC48MCR = 0x80;
    while (!(IRC48MCR & 0x01));
    //如果使用USB-CDC需要下面的两行代码
    // USBCLK = 0x00;
    // USBCON = 0x90;
    //如果使用USB-HID注释掉上面两行代码
    usb_init();
    //-------------------------
   
    Uart2Init();
    IE2 = 0x01;
    EUSB = 1;   //IE2相关的中断位操作使能后,需要重新设置EUSB
    EA = 1;   //打开总中断

    while (P32 != 0);
    Uart2SendStr("STC32G test");
    S2REN = 1;//开启接收
    while(1)
    {
      if(rxfinishflag)
      {
            procrxdata();
            rxfinishflag = 0;
            S2REN = 1;//开启接收
      }
    }
}

yuyy1989 发表于 2023-5-22 08:58:58

第21集,串口应用
MCU串口出来的信号都是TTL电平
TTL电平信号规定,+5V等价于逻辑“1”,0V等价于逻辑“0”(采用二进制来表示数据时)。这样的数据通信及电平规定方式,被称做TTL(晶体管-晶体管逻辑电平)信号系统。这是计算机处理器控制的设备内部各部分之间通信的标准技术。
232或485的通讯距离更长抗干扰能力更强
C库函数 int sprintf(char *str, const char *format, ...) 发送格式化输出到 str 所指向的字符串。
课后练习


使用串口1和串口2互相通讯,由于没有那么多按键,使用USB-HID虚拟键盘上的0-9ABC按键,按键按下后串口1向串口2发送数据,串口2如果有数据返回,串口1接收后通过USB-HID打印出来

运行效果

attach://10546.mp4

代码实现

//USB调试及复位所需定义
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#"; //设置自动复位到ISP区的用户接口命令
const uint8_t ledsegcodes[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x40};
#define FOSC 24000000UL
#define BRT (65536 - (FOSC / 115200+2) / 4)
//加 2 操作是为了让 Keil 编译器
//自动实现四舍五入运算
bit u1busy;
uint8_t u1rxbuffer;
uint8_t u1rxcount = 0;
uint8_t u1rxfinishflag = 0;
bit u2busy;
uint8_t u2rxbuffer;
uint8_t u2rxcount = 0;
uint8_t u2rxfinishflag = 0;
int temps[] = {2650,-1035,40,-10,560,-100};//模拟温度值 26.50 -10.35
uint8_t tempindex = 0;
void Uart1Init()
{
    P_SW1 = 0x80; //串口1 rxP16 txP17
    SCON = 0x50;
    T2L = BRT;
    T2H = BRT >> 8;
    S1BRT = 1;
    T2x12 = 1;
    T2R = 1;
    u1busy = 0;
}
void Uart1Send(uint8_t dat)
{
    while (u1busy);
    u1busy = 1;
    SBUF = dat;
}
void Uart1SendStr(char *p)
{
    while (*p)
    {
      Uart1Send(*p++);
    }
}

void Uart2Init()
{
    P_SW2 = 0x80; //串口2 rxP10 txP11
    S2CFG = 0x01; //无帧错检测功能,波特率不加倍,固定为 Fosc/12
    S2CON = 0x50; //可变波特率8位数据方式,允许串口接收数据

    T2L = BRT;
    T2H = BRT >> 8;
    T2x12 = 1;
    T2R = 1;
    u2busy = 0;
}

void Uart2Send(uint8_t dat)
{
    while (u2busy);
    u2busy = 1;
    S2BUF = dat;
}
void Uart2SendStr(char *p)
{
    while (*p)
    {
      Uart2Send(*p++);
    }
}

void procrxdata()
{
    int temp;
    char txbuffer = {0};
    uint8_t segdatas={0x00};
    uint8_t segindex = 7;
    switch (u2rxbuffer)
    {
    case 'A'://点亮对应的LED
      if(u2rxcount == 2 && u2rxbuffer >= '0' && u2rxbuffer < '8')
      {
            P2 = ~(1<<(u2rxbuffer - '0'));
      }
      break;
    case 'B'://数码管显示数字
      if(u2rxcount > 1 || u2rxcount < 9)
      {
            while (u2rxcount > 1)
            {
                u2rxcount--;
                if(u2rxbuffer >= '0' && u2rxbuffer <= '9')
                {
                  segdatas = ledsegcodes-'0'];
                }
                segindex--;
            }
            SEG7_ShowCode(segdatas);
      }
      break;
    case 'C'://打开关闭蜂鸣器
      if(u2rxcount == 2 && (u2rxbuffer == '0' || u2rxbuffer == '1'))
      {
            P35 = '1' - u2rxbuffer;
      }
      break;
    case 'D'://发送温度
      if(u2rxcount == 2)
      {
            temp = temps;
            u2rxcount = 0;
            if(temp < 0)
            {
                txbuffer = '-';
                temp = 0 - temp;
            }
            if(temp > 999)
            {
                txbuffer = '0' + (temp / 1000);
                temp = temp%1000;
            }
            txbuffer = '0' + (temp / 100);
            temp = temp%100;
            txbuffer = '.';
            txbuffer = '0' + (temp / 10);
            temp = temp%10;
            txbuffer = '0' + temp;
            txbuffer = '\r';
            txbuffer = '\n';
            Uart2SendStr(txbuffer);
            tempindex++;
            if(tempindex > 5)
            {
                tempindex = 0;
            }
      }
      break;
    case 'Z'://发送"Hello STC"
      if(u2rxcount == 1)
      {
            Uart2SendStr("Hello STC\r\n");
      }
      break;
    default:
      break;
    }
    u2rxcount = 0;
    yuyy_delay_ms(1);
}

void Uart1Isr() interrupt 4
{
    if (TI)
    {
      TI = 0;
      u1busy = 0;
    }
    if (RI)
    {
      RI = 0;
      u1rxbuffer = SBUF;
      if(u1rxbuffer=='\r' && u1rxbuffer=='\n')
      {
            u1rxfinishflag = 1;
            REN = 0;//关闭接收
      }
      else
            u1rxcount++;
      if(u1rxcount > 15)
            u1rxcount = 0;
    }
}

void Uart2Isr() interrupt 8
{
    if (S2TI)
    {
      S2TI = 0;
      u2busy = 0;
    }
    if (S2RI)
    {
      S2RI = 0;
      u2rxbuffer = S2BUF;
      if(u2rxcount > 1 && u2rxbuffer=='\r' && u2rxbuffer=='\n')
      {
            u2rxcount-=1;
            u2rxfinishflag = 1;
            S2REN = 0;//关闭接收
      }
      else
            u2rxcount++;
      if(u2rxcount > 15)
            u2rxcount = 0;
    }
}

/******************** 主函数 **************************/
void main(void)
{
    char u1txdats[] = {'A','0','\r','\n'};
    WTST = 0;//设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
    EAXFR = 1; //扩展寄存器(XFR)访问使能
    CKCON = 0; //提高访问XRAM速度

    RSTFLAG |= 0x04;   //设置硬件复位后需要检测P3.2的状态选择运行区域,否则硬件复位后进入USB下载模式

    P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
    P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
    P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
    P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
    P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
    P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
    P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
    P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口

    //USB调试及复位所需代码-----
    P3M0 &= ~0x03;
    P3M1 |= 0x03;
    IRC48MCR = 0x80;
    while (!(IRC48MCR & 0x01));
    //如果使用USB-CDC需要下面的两行代码
    // USBCLK = 0x00;
    // USBCON = 0x90;
    //如果使用USB-HID注释掉上面两行代码
    usb_init();
    //-------------------------
   
    Uart1Init();
    Uart2Init();
    ES = 1;
    ES2 = 1;
    EUSB = 1;   //IE2相关的中断位操作使能后,需要重新设置EUSB
    EA = 1;   //打开总中断
    REN = 1;
    S2REN = 1;//开启接收
    while(1)
    {
      if (DeviceState != DEVSTATE_CONFIGURED)      //判断USB设备识别是否完成
            continue;
      if (bUsbOutReady)
      {
            if ((UsbOutBuffer == 'K') &&
                (UsbOutBuffer == 'E') &&
                (UsbOutBuffer == 'Y') &&
                (UsbOutBuffer == 'P'))
            {
                switch (UsbOutBuffer)
                {
                  case 0x30:
                  case 0x31:
                  case 0x32:
                  case 0x33:
                  case 0x34:
                  case 0x35:
                  case 0x36:
                  case 0x37: //按键0-7
                        u1txdats = '0' + (UsbOutBuffer-0x30);
                        Uart1SendStr(u1txdats);
                        break;
                  case 0x38: //按键8
                        Uart1SendStr("B0000\r\n");
                        break;
                  case 0x39: //按键9
                        Uart1SendStr("Z\r\n");
                        break;
                  case 0x41: //按键A
                        Uart1SendStr("C0\r\n");
                        break;
                  case 0x42: //按键B
                        Uart1SendStr("C1\r\n");
                        break;
                  case 0x43: //按键C
                        Uart1SendStr("D0\r\n");
                        break;
                  default:
                        break;
                }
                usb_OUT_done();
            }
      }
      if(u1rxfinishflag)
      {
            //接收到的数据通过usb-hid打印
            printf(u1rxbuffer);
            u1rxfinishflag = 0;
            u1rxcount = 0;
            memset(u1rxbuffer,0);
            REN = 1;//开启接收
      }
      if(u2rxfinishflag)
      {
            procrxdata();
            u2rxfinishflag = 0;
            S2REN = 1;//开启接收
      }
    }
}


yuyy1989 发表于 2023-5-22 19:03:26

第22集,CDC串口通信
无需USB转TTL,一根数据线就能通讯,P30和P31连接USB的D-和D+加上单片机外围即可实现下载与通讯
CDC串口的优点


CDC不停电下载回顾,本帖6楼https://www.stcaimcu.com/forum.php?mod=redirect&goto=findpost&ptid=2110&pid=13843
CDC串口通信练习


运行效果
attach://10588.mp4

代码实现
//USB调试及复位所需定义
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#"; //设置自动复位到ISP区的用户接口命令

const uint8_t ledsegcodes[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x40};
int temps[] = {2650,-1035,40,-10,560,-100};//模拟温度值 26.50 -10.35
uint8_t tempindex = 0;

/******************** 主函数 **************************/
void main(void)
{
    int temp;
    char txbuffer = {0};
    uint8_t segdatas={0x00};
    uint8_t segindex = 7;
    WTST = 0;//设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
    EAXFR = 1; //扩展寄存器(XFR)访问使能
    CKCON = 0; //提高访问XRAM速度

    RSTFLAG |= 0x04;   //设置硬件复位后需要检测P3.2的状态选择运行区域,否则硬件复位后进入USB下载模式

    P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
    P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
    P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
    P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
    P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
    P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
    P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
    P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口

    //USB调试及复位所需代码-----
    P3M0 &= ~0x03;
    P3M1 |= 0x03;
    IRC48MCR = 0x80;
    while (!(IRC48MCR & 0x01));
    //如果使用USB-CDC需要下面的两行代码
    USBCLK = 0x00;
    USBCON = 0x90;
    //如果使用USB-HID注释掉上面两行代码
    usb_init();
    //-------------------------
   
    EUSB = 1;   //IE2相关的中断位操作使能后,需要重新设置EUSB
    EA = 1;   //打开总中断
    while(1)
    {
      if (DeviceState != DEVSTATE_CONFIGURED)      //判断USB设备识别是否完成
            continue;
      if (bUsbOutReady)
      {
            // printf("rec len %d \r\n",OutNumber);
            // USB_SendData(UsbOutBuffer,OutNumber);
            switch (UsbOutBuffer)
            {
                case 'A'://点亮对应的LED
                  if(OutNumber == 2 && UsbOutBuffer >= '0' && UsbOutBuffer < '8')
                  {
                        P2 = ~(1<<(UsbOutBuffer - '0'));
                  }
                  break;
                case 'B'://数码管显示数字
                  if(OutNumber > 1 || OutNumber < 9)
                  {
                        segindex = 7;
                        while (OutNumber > 1)
                        {
                            OutNumber--;
                            if(UsbOutBuffer >= '0' && UsbOutBuffer <= '9')
                            {
                              segdatas = ledsegcodes-'0'];
                            }
                            segindex--;
                        }
                        SEG7_ShowCode(segdatas);
                  }
                  break;
                case 'C'://打开关闭蜂鸣器
                  if(OutNumber == 2 && (UsbOutBuffer == '0' || UsbOutBuffer == '1'))
                  {
                        P35 = '1' - UsbOutBuffer;
                  }
                  break;
                case 'D'://发送温度
                  if(OutNumber == 2)
                  {
                        temp = temps;
                        OutNumber = 0;
                        if(temp < 0)
                        {
                            txbuffer = '-';
                            temp = 0 - temp;
                        }
                        if(temp > 999)
                        {
                            txbuffer = '0' + (temp / 1000);
                            temp = temp%1000;
                        }
                        txbuffer = '0' + (temp / 100);
                        temp = temp%100;
                        txbuffer = '.';
                        txbuffer = '0' + (temp / 10);
                        temp = temp%10;
                        txbuffer = '0' + temp;
                        USB_SendData(txbuffer,OutNumber);
                        tempindex++;
                        if(tempindex > 5)
                        {
                            tempindex = 0;
                        }
                  }
                  break;
                case 'Z'://发送"Hello STC"
                  if(OutNumber == 1)
                  {
                        USB_SendData("Hello STC",9);
                  }
                  break;
                default:
                  break;
            }
            usb_OUT_done();
      }
    }
}

页: 1 2 [3] 4
查看完整版本: 冲哥32位8051视频学习日记-已看到27集-实验箱到了