XHB7906021 发表于 2024-12-21 13:54:38

<p>周期性多任务,讲的不错!<img alt="shengli" class="emoji" src="https://www.stcaimcu.com/static/image/smiley/default/shengli.gif" title="shengli" /> 有点理解了。多发点视让大家学习学习。祝愿国产单片机越做越好!</p>

lclbf 发表于 2024-12-21 14:03:15

macrofei 发表于 2024-12-16 04:26
第七集 定时器中断
了解了定时器的执行过程
配置定时器:设置定时器的工作模式、初值、是否启用中断等。


好思路。

askalai 发表于 2024-12-21 21:29:31

第九集和第十集是还没有发资料吗?

我行我速 发表于 2024-12-21 22:40:54

<p>打卡第四集,继续努力学习。。。</p>

蓝鹰 发表于 2024-12-22 07:18:55

神农鼎 发表于 2024-11-19 11:23


学到了

小垃圾 发表于 2024-12-22 12:02:09

<pre><code># 第一集新建工程相关
- 51的新建工程需要注意Memory model,一般选择XSmall即可,tiny模式串口无法正常工作,原理至今还不是很清楚。
- 因为新建的工程默认为tiny模式,修改到XSmall即可。
!(data/attachment/forum/202412/22/114726qlswlzw2idwfkpsv.png &quot;image.png&quot;)

- 然后添加c文件到项目中就可以快乐的写代码了。
- AI8051 IO单独操作的方法,
- PnM1&amp;=~0x20;给P15设置为推挽输出。P1M1的第五位置为0
- PnM0|=0x20;//P1M0的第五位置为1
```c
#include&quot;AI8051U.h&quot;
#include&quot;intrins.h&quot;//一定要加这两个头文件
typedef unsigned char u8;
typedef unsigned short int u16;
void delay_us(u16 us)
{
        do
        {
                NOP36();
        }
        while(us--);//us--需要先对us进行减一次,然后赋值给us,然后判断一次,一共需要4个指令周期,NOP36是36个指令周期,40*25=1000ns=1us
    //MOV R6,R7 //1个指令周期
    //DEC R7,#0X01//1个指令周期
    //CJNE R6,#0X00,C:0X0005//如果发生跳转,就是3个指令周期,正常情况为2个指令周期。
}
void delay_ms(u16 ms)        //unsigned int
{
        do
        {
                delay_us(1000);//减少对函数的调用,增加性能
        }while(--ms);
}
void main()
{
        P0M0=0XFF;
        P0M1=0X00;//设置P0的IO为推挽输出
                while(1)
                {
                        P00=1;//设置P00口为高电平
                        delay_ms(500);
                        P00=0;
                        delay_ms(500);//设置P00口为低电平
                }
}
</code></pre>
<h1>第二集 不停电烧录</h1>
<ul>
<li>使用手册中的电路可以进行不停电烧录,挺方便的。<br />
<img src="data/attachment/forum/" alt="image-1.png" title="image-1.png" /></li>
<li>打开AIapp-ISP-v6.xx.exe软件,选择你需要烧录的hex文件,如果没有的话,去keil的魔法棒(options for target)中的output那一栏,勾选Create HEX file,点击OK,关闭这个(options for target)之后编译一下即可。hex文件一般在工程文件所在的目录下,找Objects。一般来说,默认的hex创建路径就在这个目录下。</li>
<li>然后直接下载编程即可。</li>
</ul>
<h1>第三集 点亮rgb灯</h1>
<ul>
<li>先上代码,</li>
<li>基本上就是根据ws2812b的手册,确定发送数据的高低电平的长度就行。</li>
</ul>
<pre><code class="language-c">#include &quot;AI8051U.h&quot;
#include &quot;intrins.h&quot;

typedef unsigned char u8;
typedef unsigned short int u16;

// 延时1us的时间为1000ns/25ns=40;
//1/Fosc=x ns
//1000ns/(1/Fosc)ns= x 个时钟周期
//实际计算时:1000e-9*Fosc=x 个时钟周期
//while(i--)四个时钟周期,(x-4)=NOP(val);延时1us所需要的时间
//这个代码使用的是IO口高低电平进数据发送。
u8 LED1; // 红色(R=255, G=0, B=0)
u8 LED2; // 绿色(R=0, G=255, B=0)
u8 color_table = {
    {0xFF, 0x00, 0x00}, // 红色
    {0x00, 0xFF, 0x00}, // 绿色
    {0x00, 0x00, 0xFF}, // 蓝色
    {0xFF, 0xFF, 0x00}, // 黄色
    {0xFF, 0x00, 0xFF}// 紫色
};
#define LED_PIN P15
// 该延时函数仅在40Mhz下是准确的,其他频率需要自行计算NOPx()的值
void delay_us(u16 us)
{
    do
    {
      NOP(36);
    } while (us--); // us--需要先对us进行减一次,然后赋值给us,然后判断一次,一共需要4个指令周期,NOP36是36个指令周期,40*25=1000ns=1us
    // MOV R6,R7 //1个指令周期
    // DEC R7,#0X01//1个指令周期
    // CJNE R6,#0X00,C:0X0005//如果发生跳转,就是3个指令周期,正常情况为2个指令周期。
    // 方案2
    // NOP40();
}
void delay_ms(u16 ms) // unsigned int
{
    do
    {
      delay_us(1000); // 减少对函数的调用,增加性能
    } while (--ms);
}
void send_byte(unsigned char byte)
{
    unsigned char i;
    for (i = 0; i &lt; 8; i++)
    {
      if (byte &amp; 0x80)
      {                // 如果最高位为 1
            LED_PIN = 1; // 发1 低电平为0.3us
            delay_us(1); //
            LED_PIN = 0;
            NOP13();
      }
      else
      {                // 如果最高位为 0
            LED_PIN = 1; // 发0 高电平为0.3us
            NOP13();   //
            LED_PIN = 0; //
            delay_us(1);
      }
      byte &lt;&lt;= 1; // 左移 1 位,准备发送下一位
    }
}

void switch_to_next_color(u8 *LED, u8 index)
{
    unsigned char i;
    LED = color_table; // G
    LED = color_table; // R
    LED = color_table; // B

    for (i = 0; i &lt; 3; i++)
    {
      send_byte(LED); // 发送每个颜色字节(G, R, B)
    }
}

void ws2812b_reset()
{
    LED_PIN = 0;
    delay_us(300); // 拉低时间大于 50 微秒即可
}
// 主程序
void main()
{

    u8 index1 = 0; // LED1 的颜色索引
    u8 index2 = 2; // LED2 的颜色索引

        P_SW2 = 0X80;
    CKCON = 0x00;
    WTST = 0x00;
    P4M0 = 0X00;
    P4M1 = 0X00;
    P1M0 |= 0x20;//设置P15为数据输出口
    P1M1 &amp;= ~0x20;
    while (1)
    {
      ws2812b_reset();
      switch_to_next_color(LED1, index1);
      switch_to_next_color(LED2, index2);
      index1 = (index1 + 1) % 5; // 颜色表循环
      index2 = (index2 + 1) % 5; // 颜色表循环
      // 延时 5 毫秒,避免过快发送
      delay_ms(500);
    }
}
- 效果图

!(data/attachment/forum/202412/22/115343m21a668fk6cm8k7j.jpg &quot;IMG_20241222_112051.jpg&quot;)
</code></pre>
<h1>第四集 串口使用</h1>
<pre><code class="language-c">#include &quot;AI8051U.h&quot;
#include &quot;intrins.h&quot;
#include &quot;stdio.h&quot;
#define FOSC 40000000UL
#define BRT (65536 - (FOSC / 115200 + 2) / 4)
char buffer;
bit busy;
char wptr;
char rptr;
typedef unsigned char u8;
typedef unsigned int u16;
void delay_ms(u16 ms) // unsigned int
{
    u16 i = 0;
    do
    {
      for(i=0;i&lt;900;++i)//11059200 243即可,40M i=900即可
      NOP40(); // 减少对函数的调用,增加性能
    } while (--ms);
}
void Uart2Isr() interrupt 8
{
    if (S2TI)
    {
      S2TI = 0;
      busy = 0;
    }
    if (S2RI)
    {
      S2RI = 0;
      buffer = S2BUF;
      wptr &amp;= 0x0f;
    }
}
void Uart2Init()
{
    P_SW2 = 0x80;
    S2CFG = 0x01;
    S2CON = 0x50;
    T2L = BRT;
    T2H = BRT &gt;&gt; 8;
    T2x12 = 1;
    T2R = 1;
    wptr = 0x00;
    rptr = 0x00;
    busy = 0;
      P1M0&amp;=~0X0C;
                P1M1&amp;=~0X0C;
    IE2 = 0x01;
    EA = 1;
}
void Uart2Send(char dat)
{
    while (busy)
      ;
    busy = 1;
    S2BUF = dat;
}
void Uart2SendStr(char *p)
{
    while (*p)
    {
      Uart2Send(*p++);
    }
}
char putchar(char c)
{
        Uart2Send(c);
        return c;
}
// 主程序
void main()
{
        u16 index=0;
    P_SW2 = 0X80;
    CKCON = 0x00;
    WTST = 0x00;
    Uart2Init();
    Uart2SendStr(&quot;Uart Test !\r\n&quot;);
                printf(&quot;%d\n&quot;,index);
    while (1)
    {

                        for(index=0;index&lt;10;++index)
                        {
                                printf(&quot;Uart Test index=%d!\r\n&quot;,index);
                      delay_ms(1000);
                        }

    }
}
- 主要的重点在于使用printf函数进行串口输出打印。加一个stdio.h的头文件,然后重定义一下putchar函数。
char putchar(char c)
{
        Uart2Send(c);
        return c;
}

!(data/attachment/forum/202412/22/114917yldlkwxot9kdizk3.png &quot;image-2.png&quot;)
</code></pre>
<h1>第五集 按键使用</h1>
<ol>
<li>按键io初始化</li>
</ol>
<pre><code class="language-c">void GPIO_init()
{
        P_SW2 = 0X80;
    CKCON = 0X00;
    WTST = 0X00;
    P2M0 = 0X00;
    P2M1 = 0X00;
    P1M0 &amp;= ~0x10;//设置P14为双向口
    P1M1 &amp;= ~0x10;
    P5M0 &amp;= ~0x0c;//设置P52,P53为双向口
    P5M1 &amp;= ~0x0c;
    P4M0 = 0X00;//设置P4为双向口
    P4M1 = 0X00;
    P1IM0 &amp;=~0X10;//设置P14为下降沿中断
    P1IM1 &amp;=~0X10;
    P1INTE|= 0x10;
    P5IM0 &amp;=~0x0c;//设置P52 P53为下降沿中断
    P5IM1 &amp;=~0x0c;//
    P5INTE|= 0x0c;//使能P52 P53中断
    P1INTE|= 0x10;
}
</code></pre>
<ol start="2">
<li>中断执行</li>
</ol>
<pre><code class="language-c">void common_isr() interrupt 13
{
unsigned char intf;
unsigned char intf_5;
intf = P1INTF;
intf_5 = P5INTF;

    if(intf&amp;0x10)//使用char型临时变量判断中断是否到来。
    {
      flag_ = 1;
    }
    if(intf_5&amp;0x08)
    {
      flag_ = 3;

    }
    if(intf_5&amp;0x04)
    {
      flag_ = 2;
    }
    P1INTF&amp;=~0X10;//清除中断标志位
    P5INTF&amp;=~0X0c;
}
</code></pre>
<p>把下面的代码。新建一个文件isr.asm,添加到项目中。由于中断向量大于31,借用13号中断地址入口。</p>
<pre><code class="language-asm">CSEG AT 0133H
JMP P1INT_ISR
P1INT_ISR:
JMP 006BH
CSEG AT 0153H
JMP P5INT_ISR
P5INT_ISR:
JMP 006BH
END
</code></pre>
<h1>第六集 多文件编译</h1>
<ul>
<li>全局变量一定定义在头文件下面,函数声明和定义的前面</li>
<li>函数在单独的C文件中需要有定义,在头文件中声明。需要调用这个函数的文件夹,需要extern 该函数的声明。</li>
<li>具体demo见附件</li>
</ul>
<h1>第七集 pwm使用</h1>
<ul>
<li>这个代码实现的功能为在PWMA的P00 和P01输出一个互补带死区的PWM波。用于驱动电机。</li>
</ul>
<pre><code class="language-c">#include &quot;AI8051U.H&quot;
#include &quot;intrins.h&quot;

int main(void)

{
        P_SW2 = 0X80;
        CKCON = 0x00;
        WTST = 0x00;
        P0M1 = 0x00;
        P0M0 = 0xFF;
        P1M1&amp;=~0x80;
        P1M0|=0x80;

        PWMA_ENO = 0xFF;
        PWMA_PS = 0x01;
        PWMA_PSCRH = 0x00;//预分频寄存器
        PWMA_PSCRL = 0x00;
        PWMA_DTR = 0x10;
        PWMA_CCMR1 = 0x68;
        PWMA_CCMR2 = 0x68;
        PWMA_CCMR3 = 0x68;
        PWMA_CCMR4 = 0x68;
        PWMA_ARRH = 0x08;
        PWMA_ARRL = 0x00;
        PWMA_CCR1H = 0x04;
PWMA_CCR1L = 0x00;
PWMA_CCR2H = 0x02;
PWMA_CCR2L = 0x00;
PWMA_CCR3H = 0x01;
PWMA_CCR3L = 0x00;
PWMA_CCR4H = 0x01;
PWMA_CCR4L = 0x00;

PWMA_CCER1 = 0x55;
PWMA_CCER2 = 0x55;
//配置通道输出使能和极性
//配置通道输出使能和极性
PWMA_BKR = 0x80;
PWMA_IER = 0x02;
PWMA_CR1 = 0x01;
EA = 1;
P17=1;
while (1);
}
void PWMA_ISR() interrupt 26
{
if(PWMA_SR1 &amp; 0X02)
{
P03 = ~P03;
PWMA_SR1 &amp;=~0X02;
}
}
</code></pre>
<p><img src="data/attachment/forum/" alt="wx_camera_17345370721871.jpg" title="wx_camera_1734537072187.jpg" /></p>
<h1>补充说明</h1>
<h2>时钟周期、机器周期、指令周期</h2>
<p>机器周期、时钟周期和指令周期是8051单片机中的重要概念。</p>
<ul>
<li><strong>时钟周期</strong>:单片机时钟信号的一个完整周期,即时钟源的周期,通常由晶振频率决定。</li>
<li><strong>机器周期</strong>:单片机执行基本操作所需的时间,通常由时钟周期数来决定。例如8051的一个机器周期为12个时钟周期。AI8051是单时钟/机器周期(1T)的单片机</li>
<li><strong>指令周期</strong>:执行一条指令的时间,通常由多个机器周期组成。<br />
由于AI8051为单时钟机器周期的单片机,所以其执行一条指令的时间为一个时钟周期。<br />
在40MHz的时钟下,时钟周期为1/40,000,000 = 25ns,因此一个机器周期为25ns。</li>
<li><strong>延时1us</strong>:1us = 1000ns,因此需要延时1000ns / 25ns =40;</li>
<li><strong>延时1ms</strong>:1ms = 1,000,000ns,延时1000000ns / 25ns = 40000。<br />
所以ai8051的较为精确的延时函数计算公式为</li>
</ul>
<p>1/Fosc=x ns<br />
1000ns/(1/Fosc)ns= x 个时钟周期<br />
实际计算时:1000e-9*Fosc=x 个时钟周期<br />
while(i--)四个时钟周期,(x-4)=NOP(val);延时1us所需要的时间</p>
<p>可以通过以下代码实现延时:<br />
while(--us)</p>
<pre><code class="language-C">MOV R6,R7 //一个指令周期
DEC R7,#0X01//一个指令周期
CJNE R6,#0X00,C:0X0005//如果发生跳转,就是3个指令周期,正常情况为2个指令周期。
</code></pre>
<p><img src="data/attachment/forum/" alt="alt text" /></p>
<pre><code class="language-c">void delay_us(u16 us)
{
        do
        {
                NOP36();
        }
        while(us--);//us--需要先对us进行减一次,然后赋值给us,然后判断一次,一共需要4个指令周期,NOP36是36个指令周期,40*25=1000ns=1us
    //MOV R6,R7 //1个指令周期
    //DEC R7,#0X01//1个指令周期
    //CJNE R6,#0X00,C:0X0005//如果发生跳转,就是3个指令周期,正常情况为2个指令周期。
}
void delay_ms(u16 ms)        //unsigned int
{
        do
        {
                delay_us(1000);//减少对函数的调用,增加性能
        }while(--ms);
}
//如果实现ns级别的延时,直接NOP(40),40*25ns=1us
</code></pre>
<p>这种方法给出了粗略的延时,可以根据需要微调延时精度。</p>
<h1>上面文件的源代码,这里</h1>
<p><a href="forum.php?mod=attachment&amp;aid=75482" title="attachment"><img src="data/attachment/forum/" alt="upload" /> 附件:test_51.zip</a></p>
<h1>我使用板子的原理图</h1>
<p><a href="forum.php?mod=attachment&amp;aid=75487" title="attachment"><img src="data/attachment/forum/" alt="upload" /> 附件:SCH_Schematic1_2024-12-22.pdf</a></p>

小垃圾 发表于 2024-12-22 12:03:37

小垃圾 发表于 2024-12-22 12:02
# 第一集新建工程相关
- 51的新建工程需要注意Memory model,一般选择XSmall即可,tiny模式串口无法正常工作 ...

图片暂时看不了不知道为啥

白水大虾2016 发表于 2024-12-22 12:40:48

<p>学习中</p>

jackfangxq 发表于 2024-12-22 13:06:06

522810886 发表于 2024-12-20 10:34
学习打卡区在那里?

论坛主页下边

wamcncn 发表于 2024-12-22 14:19:28

定时器编译警告,哪里出错了
找到问题了,原来是魔法棒里设置的4字节中断不打√
页: 31 32 33 34 35 36 37 38 39 40 [41] 42 43 44 45 46 47 48 49 50
查看完整版本: 《8051U深度入门到32位51大型实战视频》,【免费 + 包邮 送】实验箱@Ai8051U,100万套