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();
}
}
}