找回密码
 立即注册
楼主: srkxrolz

记录我的8051u学习过程

[复制链接]
  • 打卡等级:偶尔看看I
  • 打卡总天数:10
  • 最近打卡:2025-06-28 10:06:21
已绑定手机

1

主题

32

回帖

111

积分

注册会员

积分
111
发表于 5 天前 | 显示全部楼层
第十五集 计数器用法
16位自动重装载+8位预分频=24位  累加计数器 每个脉冲+1
图片1.png
8051ut0 t1 t2 t3 t4 t11 6个定时器
定时计数器功能选择位 0=定时器   1=计数器
T0 T1   TMOD^2 (T0_CT)  TMOD^6
T2                 AUXR^3
T3 T4   T4T3M^2  T4T3M^6
T11                T11CR^6
其实这很多的位都已经在头文件中定义好了的可以直接位寻址
定时器的四种工作模式:
0                 16位自动重装载
1         16位不可重装载
2         8位自动装置载
3                不可屏蔽中断的16位自动重装载
t0  支持四种
T1  不支持3
T2  只支持0 可以当波特率发生器
T3 4 11 只支持0 t2一样 但是11不能当波特率发生器
t0工作在模式3的时候可以做systick 中断不可屏蔽且优先级最高
// t1做计时器  P35  来10个脉冲溢出一次
void t1_init()
{
    u16 i = 0xff-10;
    P3M0 &= ~0x00; //设置为双向口即可
    P3M1 &= ~0x00;
    P3PU |= 0x20; // 使能P35上拉电阻
    T1_CT = 1;   // 设置定时器1为计数器模式
    T1_GATE = 0; // 禁止定时器1的门控功能
    T1_M0 = 0;   // 设置定时器1为16位自动重载模式
    T1_M1 = 0;   // 设置定时器1为16位自动重载模式
    TH1 = i>>8;  // 设置定时器1初值高8位
    TL1 = i;  // 设置定时器1初值低8位
TR1 = 1;     // 启动定时器1
ET1=1; //打开中断
printf_usb("t1计数器初始化完成!");
}
void t1_isr(void) interrupt 3
{
    int0_flag++;
}
void fun2(void)
{
    u16 c = 0;
    c = TH1;
    c <<= 8;
    c += TL1;
    SEG7_ShowString("%u", int0_flag);
    printf("systick:%u  cont:%u \r\n", systick, c); // 通过USB输出systick计数值
}

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:偶尔看看I
  • 打卡总天数:10
  • 最近打卡:2025-06-28 10:06:21
已绑定手机

1

主题

32

回帖

111

积分

注册会员

积分
111
发表于 5 天前 | 显示全部楼层
第十六集 18B20  
18B20 数字温度计  可以使用IO电源,也可外接电源,一个IO口即可完成通信,采用时间片方式,没有硬件,所以只能参考时序图搞搞
#include "config.h"
#include "18b20.h"

void DS18B20_Reset()
{
    CY = 1;
    while (CY)
    {
        DQ = 0;                     //送出低电平复位信号
        delay_us(480);              //延时至少480us
     
        DQ = 1;                     //释放数据线
        delay_us(60);               //等待60us
        CY = DQ;                    //检测存在脉冲
        delay_us(480);              //等待设备释放数据线
    }
}

u8 DS18B20_ReadByte()
{
    u8 i;
    u8 dat = 0;

    for (i=0; i<8; i++)             //8位计数器
    {
        dat >>= 1;
        DQ = 0;                     //开始时间片
        delay_us(1);                //延时等待
        DQ = 1;                     //准备接收
        delay_us(1);                //接收延时
        if (DQ) dat |= 0x80;        //读取数据
        delay_us(60);               //等待时间片结束
    }

    return dat;
}

void DS18B20_WriteByte(u8 dat)
{
    char i;

    for (i=0; i<8; i++)             //8位计数器
    {
        DQ = 0;                     //开始时间片
        delay_us(1);                //延时等待
        dat >>= 1;                  //送出数据
        DQ = CY;
        delay_us(60);               //等待时间片结束
        DQ = 1;                     //恢复数据线
        delay_us(1);                //恢复延时
    }
}
u16 ReadTemperature()
{
    u16 TempH, TempL, Temperature,MinusFlag;
   
    DS18B20_Reset();                //设备复位
    DS18B20_WriteByte(0xCC);        //跳过ROM命令
    DS18B20_WriteByte(0x44);        //开始转换命令
    while (!DQ);                    //等待转换完成

    DS18B20_Reset();                //设备复位
    DS18B20_WriteByte(0xCC);        //跳过ROM命令
    DS18B20_WriteByte(0xBE);        //读暂存存储器命令
    TempL = DS18B20_ReadByte();     //读温度低字节
    TempH = DS18B20_ReadByte();     //读温度高字节
   
    if(TempH & 0xf8)    //判断是否位负数
    {
        MinusFlag = 1;  //设置负数标志
        Temperature = (TempH<<8) | TempL;
        Temperature = ~Temperature + 1;
        Temperature *= 0.625;       //0.0625 * 10,保留1位小数点
    }
    else
    {
        MinusFlag = 0;  //清除负数标志
        Temperature = (((TempH<<8) | TempL) * 0.625); //0.0625 * 10,保留1位小数点
    }

    return Temperature;
}


回复 支持 反对

使用道具 举报 送花

  • 打卡等级:偶尔看看I
  • 打卡总天数:10
  • 最近打卡:2025-06-28 10:06:21
已绑定手机

1

主题

32

回帖

111

积分

注册会员

积分
111
发表于 4 天前 | 显示全部楼层
第十七集  串口

8051u提供了4个串口  uasrt1usart2 是同步/异步串口  uart3uart4是异步串口

起始位  1->0  一个位的时间
数据位  8的数据位
停止位  0->1 一个位的时间
Stc-isp可以迅速生成串口配置与切换端口定义
#include "usart.h"

u8 rec[50], len = 0;
void usart_date_send(u8 *str)
{
    for (; *str != 0; str++)
    {

        S2BUF = *str;
       while (!S2TI);
    }
}
u8 usart_date_handle(void)
{
    if (len > 5 && rec[len - 1] == '\n' && rec[len - 2] == '\r')
    {
        if (rec[len - 6] == 'o' && rec[len - 5] == 'p' && rec[len - 4] == 'e' && rec[len - 3] == 'n')
        {
            P21 = 0;
            len = 0;
            usart_date_send("P21_open \r\n");
        }
        else if (rec[len - 7] == 'c' && rec[len - 6] == 'l' && rec[len - 5] == 'o' && rec[len - 4] == 's' && rec[len - 3] == 'e')
        {
            P21 = 1;
            len = 0;
             usart_date_send("P21_close \r\n");
        }
    }

    return 0;
}
void Uart2_Isr(void) interrupt 8
{
    if (S2CON & 0x02) // 检测串口2发送中断
    {
        S2CON &= ~0x02; // 清除串口2发送中断请求位
    }
    if (S2CON & 0x01) // 检测串口2接收中断
    {
        rec[len++] = S2BUF;
        S2CON &= ~0x01; // 清除串口2接收中断请求位
    }
}
void PortSwitch(void)
{
    P_SW2 |= 0x01; // UART2/USART2: RxD2(P4.2), TxD2(P4.3)
}

void Uart2_Init(void) // 9600bps@40.000MHz
{
    PortSwitch();
    S2CON = 0x50; // 8位数据,可变波特率
    AUXR |= 0x04; // 定时器时钟1T模式
    T2L = 0xEE;   // 设置定时初始值
    T2H = 0xFB;   // 设置定时初始值
    AUXR |= 0x10; // 定时器2开始计时
    IE2 |= 0x01;  // 使能串口2中断
}

Usart_data_handle() 这个函数丢到task里面去即可
别忘了调用一次usart_init()

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:偶尔看看I
  • 打卡总天数:10
  • 最近打卡:2025-06-28 10:06:21
已绑定手机

1

主题

32

回帖

111

积分

注册会员

积分
111
发表于 4 天前 | 显示全部楼层
第十八集 串口的高级应用  校验位
校验: 串口的奇偶校验是数据8+一个校验位总共9位中的1的个数
奇校验  数据中如果有双数个1 那么校验位=1  如果数据单但数个1  校验位=0
偶校验  数据中如果有双数个1 那么校验位=0  如果数据单但数个1  校验位=1
因为校验位会占用一位,通常使用校验的话会使用串口为波特率9位的模式
串口在发送或者接收时候 TB8  RB8就是第九位校验位
吧收到的数据交给acc就可以得到P的校验值 然后放到校验位就可以了  奇校验需要取反,
#include "usart.h"
u8 rec[50], len = 0;
void usart_date_send(u8 *str)
{
    u8 dat;
    for (; *str != 0; str++)
    {
        dat = *str;
        ACC = dat;     //数据交给acc得到p  这里采用奇校验
        if (P)            
        {
            S2TB8 = 0;  //判断p=1 就给校验位=0
        }
        else
        {
            S2TB8 = 1;  //判断p=0 就给校验位=1
        }
        S2BUF = dat;     //吧数据发出去
        while (!S2TI)
            ;
    }
}
u8 usart_date_handle(void)
{
    if (len > 5 && rec[len - 1] == '\n' && rec[len - 2] == '\r')
    {
        if (rec[len - 6] == 'o' && rec[len - 5] == 'p' && rec[len - 4] == 'e' && rec[len - 3] == 'n')
        {
            P21 = 0;
            len = 0;
            usart_date_send("P21_open \r\n");
        }
        else if (rec[len - 7] == 'c' && rec[len - 6] == 'l' && rec[len - 5] == 'o' && rec[len - 4] == 's' && rec[len - 3] == 'e')
        {
            P21 = 1;
            len = 0;
            usart_date_send("P21_close \r\n");
        }
    }
    return 0;
}
void Uart2_Isr(void) interrupt 8
{
    u8 dat;
    if (S2CON & 0x02) // 检测串口2发送中断
    {
        S2CON &= ~0x02; // 清除串口2发送中断请求位
    }
    if (S2CON & 0x01) // 检测串口2接收中断
    {
        dat = S2BUF;
        ACC = dat;       // 给到累加器得到P值
        if (S2RB8 == !P) // 奇校验部分
            rec[len++] = dat;
        S2CON &= ~0x01; // 清除串口2接收中断请求位
    }
}
void PortSwitch(void)
{
    P_SW2 |= 0x01; // UART2/USART2: RxD2(P4.2), TxD2(P4.3)
}
void Uart2_Init(void) // 9600bps@40.000MHz
{
    PortSwitch();
    //S2CON = 0x50; // 8位数据,可变波特率
    S2CON = 0xD0;       //9位数据,可变波特率 有校验位了
    AUXR |= 0x04; // 定时器时钟1T模式
    T2L = 0xEE;   // 设置定时初始值
    T2H = 0xFB;   // 设置定时初始值
    AUXR |= 0x10; // 定时器2开始计时
    IE2 |= 0x01;  // 使能串口2中断
}
串口超时寄存器
UR2TOCR  串口2接收超时寄存器
图片1.png
图片2.png
图片3.png


#include "usart.h"
u8 rec[50], len = 0, rx_timeout = 0;
void usart_date_send(u8 *str)
{
    u8 dat;
    for (; *str != 0; str++)
    {
        dat = *str;
        ACC = dat; // 数据交给acc得到p  这里采用奇校验
        if (P)
        {
            S2TB8 = 0; // 判断p=1 就给校验位=0
        }
        else
        {
            S2TB8 = 1; // 判断p=0 就给校验位=1
        }
        S2BUF = dat; // 吧数据发出去
        while (!S2TI)
            ;
    }
}
u8 usart_date_handle(void)
{
    if (len > 5 && rec[len - 1] == '\n' && rec[len - 2] == '\r')
    {
        if (rec[len - 6] == 'o' && rec[len - 5] == 'p' && rec[len - 4] == 'e' && rec[len - 3] == 'n')
        {
            P21 = 0;
            len = 0;
            usart_date_send("P21_open \r\n");
        }
        else if (rec[len - 7] == 'c' && rec[len - 6] == 'l' && rec[len - 5] == 'o' && rec[len - 4] == 's' && rec[len - 3] == 'e')
        {
            P21 = 1;
            len = 0;
            usart_date_send("P21_close \r\n");
        }
    }
if (rx_timeout==1)
{
    usart_date_send("usart2 received timeout! \r\n");
    rx_timeout=0;
}
    return 0;
}
void Uart2_Isr(void) interrupt 8
{
    u8 dat;
    if (S2CON & 0x02) // 检测串口2发送中断
    {
        S2CON &= ~0x02; // 清除串口2发送中断请求位
    }
    if (S2CON & 0x01) // 检测串口2接收中断
    {
        dat = S2BUF;
        ACC = dat;       // 给到累加器得到P值
        if (S2RB8 == !P) // 奇校验部分
            rec[len++] = dat;
        S2CON &= ~0x01; // 清除串口2接收中断请求位
        UR2TOCR = 0xe0; // 开启接受超市中断
    }
    if (UR2TOSR & 0x01) // 检测是不是接收超时中断触发的
    {
        UR2TOSR = 0x00; // 清除超时中断标志位
        UR2TOCR = 0x00; // 关闭超时中断  也就是用的时候开 不用的时候关掉
        rx_timeout = 1; // 整一个全局变量作为标志,在handle中可以用作判断是不是很久没有接收到数据了
    }
}
void PortSwitch(void)
{
    P_SW2 |= 0x01; // UART2/USART2: RxD2(P4.2), TxD2(P4.3)
}
void Uart2_Init(void) // 9600bps@40.000MHz
{
    PortSwitch();
    UR2TOCR = 0x00; // 关闭超时接受功能
    UR2TOTL = 0x04; // 超时的计数时长
    UR2TOTH = 0x3b;
    UR2TOTE = 0x01;
    // S2CON = 0x50; // 8位数据,可变波特率
    S2CON = 0xD0; // 9位数据,可变波特率 有校验位了
    AUXR |= 0x04; // 定时器时钟1T模式
    T2L = 0xEE;   // 设置定时初始值
    T2H = 0xFB;   // 设置定时初始值
    AUXR |= 0x10; // 定时器2开始计时
    IE2 |= 0x01;  // 使能串口2中断
}

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:偶尔看看I
  • 打卡总天数:10
  • 最近打卡:2025-06-28 10:06:21
已绑定手机

1

主题

32

回帖

111

积分

注册会员

积分
111
发表于 4 天前 | 显示全部楼层
第十九集  ADC
模数转换器
模拟信号        电压(可以随时间变化)
数字信号        具体的电压数值(离散量,某一时间点的电压数值)
8051u1612位的adc, 16adc是专用测试内部1.19v参考信号的
41
单片机的Vref+可以作为参考电压,也就是用这个电压进行12位分解,分成4096份进行逐次逼近测量.
因此测量的输入电压不得大于Vref,如果要测高电压,需要接电阻进行分压到参考电压以下进行测量,最后将测量结果乘以分压系数.
一般在配置adc时候.先配置各项参数之后再打开adc,用完记得顺手关灯以降低功耗.
Adc打开电源后等1ms,等电源稳定在测量.
用于adc测量的口 需要设置位高阻态
#include "adc.h"
void AdcSetRate(void) // 100KSPS@40.000MHz
{
    ADCCFG &= ~0x0f;
    ADCCFG |= 0x03; // SPEED(3)
    ADCTIM = 0xff;  // CSSETUP(1), CSHOLD(3), SMPDUTY(31)
}
void adc_init()
{
    AdcSetRate();  // 设定adc的转换速度
    P1M0 &= ~0x01; // P10设置位高阻输入
    P1M1 |= 0x01;
    ADCCFG |= 0x20; // 转换的结果右对齐
    ADC_POWER = 1;  // 打开adc电源
    delay_ms(1);
}
u16 adc_st(u8 no) // 0-15
{
    u16 adc_value = 0;
    ADC_CONTR &= 0xf0; // adc通道选择清零 adc_contr的低四位
    ADC_CONTR |= no;   // 00就是p10
    ADC_START = 1; // adc转换启动
    _nop_();
    _nop_();
    while (!ADC_FLAG)
        ; // 等待转换完成标志位置1
    ADC_FLAG = 0;
    adc_value = ADC_RES;   // 获得高位
    adc_value <<= 8;       // 高位移位
    adc_value += ADC_RESL; // 获得低位
    return adc_value;
}
void fun1(void)
{
    u8 cod[8];
    u32 adc_value=0;
    float v=0.0;
    P20 = ~P20;
    cod[0] = 0x3f; // p0-p3   这八位从高到低代表 p7-p0
    cod[1] = P0;   // P0^1 高电平
    cod[2] = P1;   // P1^1 高电平
    cod[3] = P2;   // P2^1 高电平
    cod[4] = P3;   // P3^1 高电平
    cod[5] = P4;
    cod[6] = P5;
    LED40_SendData(cod, 5);
    adc_value=adc_st(0x00);
    v=adc_value*5.0/4096;
    SEG7_ShowFloat(v);
    printf("adc_p10 val=%d V=%f \r\n",adc_value,v );
    //SEG7_ShowString("%d", adc_value);
}
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:偶尔看看I
  • 打卡总天数:10
  • 最近打卡:2025-06-28 10:06:21
已绑定手机

1

主题

32

回帖

111

积分

注册会员

积分
111
发表于 3 天前 | 显示全部楼层
第二十集 ADC采集NTC换算温度
主要内容
1. ADC的用途分析
测温:光敏电阻
测声音:咪咪头
测气体:MQ-7等气体传感器
测温度:NTC热敏电阻
测电流:电阻丝
  
2. Adc采集NTC换算温度
NTC热敏电阻
常用温度换算公式:
Rt=RT0*EXP(Bn*(1/T-1/T0))
RT,RT0是温度位T,T0时候的电阻值,Bn位材料常数
视频中只介绍了查表法 涉及到二分查找和等比例换算的过程
u16 temp_cal(u16 adc)
{
    u16 j, k, min, max;
    adc = 4096 - adc; // 获取ntc上的电压数值
    if (adc < temp_table[0])
        return 0xffff;
    if (adc > temp_table[160])
        return 0xffff;
    min = 0;
    max = 160;
    for (j = 0; j < 5; j++)
    {
        k = (min + max) / 2;
        if (adc <= temp_table[k])
        {
            max = k;
        }
        else
        {
            min = k;
        }
    }
    if (adc == temp_table[min])
        return (min * 10) - 400;
    if (adc == temp_table[max])
        return (max * 10) - 400;
    while (min <= max)
    {
        min++;
        if (temp_table[min] == adc)
        {
            j = (min * 10) - 400;
            break;
        }
        else if (adc < temp_table[min])
        {
            min--;
            
            j = temp_table[min + 1] -temp_table[min];  //两档差值45  min=1867  max=1912   61   65
            printf("check: %d  min=%d\r\n",j,min);
            j = (adc - temp_table[min]) * 10 / j;  //adc差值 1880-1867=13   也就是小数部分在45分之13位置
            j = min * 10-400 + j;
            break;
        }
    }
printf("adc:%d  %d  %d %d \r\n",adc,min,max,j);
    return j;
}
3. 使用adc15测量内部1.19V信号源,反推电源电压
不知道准不准 测出来3.57 但是万用表量出来是4.3,
    adc_value = adc_st(15);
    vcc_base = 4096 / adc_value * 1.19;

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:偶尔看看I
  • 打卡总天数:10
  • 最近打卡:2025-06-28 10:06:21
已绑定手机

1

主题

32

回帖

111

积分

注册会员

积分
111
发表于 3 天前 | 显示全部楼层
第二十一集 Flash模拟EEPROM

Flash        EEPROM
按块读写        按字节读写
修改数据需要整块读出后擦掉然后在吧修改后的数据整块写入,略慢        可以擦写而单个字节,比较快
擦写寿命10w-100w        10-100w
容量大MB-GB        容量小KB-MB
写慢读快        读写都差不多
功耗略高        功耗低
常用于存储大容量数据:固件,操作系统,多媒体文件等        用于小容量存储:参数,配置文件等
       
       
A8051u有64K flash   512字节为一个块
EEPROM读写时只能把1写成0,如果需要写1 就需要整块擦除,擦除后就为1.
读写是按字节操作的也就是8个位,擦除操作是按块/扇区操作的512字节为一个块,
修改数据的操作就是读整个块------->>>擦除块---->>>>修改内存的数据----->>>吧内存数据写回去.
操作时间由系统自动控制,但是需要告诉系统你的频率是多少
IAP_TPS=系统时钟/1M    // 小数四舍五入 尽量向上
IAP_DATE                                   //读写数据保存在这个8位寄存器中
IAP_ADDR 里面由三个寄存器IAP_ADDRE,IAP_ADDRH,IAP_ADDRL
                                //由三个寄存器组成的24位地址
IAP_CMD    //操作命令,四种命令 0=不操作 1=读字节 2写字节 3=擦除地址所在的扇区

IAP_TRIG   //触发要读写擦操作,设置完cmd  addr data后往这个寄存器写5AH,A5H执行
IAP_CONTR  // 使能IAP=1和CMD成功标志位,失败=1 触发软复位和复位到哪的选择
IAP_TPS                //设置工作频率

stc-isp下载选项里面可以设置eeprom的大小,需要注意eeprom+程序代码需要<64K
单片机对这个EEprom的访问方式有两种,IAP方式可以读写擦,MOV方式只能读
Eeprom的储存空间划分是从后往前规划的,所以空间结束地址是FF:FFFFh
Iap是从前往后找
mov是从后往前找

代码移植 略 AIcebe已提供函数库
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:193
  • 最近打卡:2025-06-28 08:36:03
已绑定手机

95

主题

990

回帖

2537

积分

超级版主

积分
2537
QQ
发表于 3 天前 | 显示全部楼层
热线19952583534
www.STCAI.com
回复 支持 反对

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-6-28 22:31 , Processed in 0.323931 second(s), 86 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表