嵌入式GPIO 实行(流水灯程序,八段数码管显示程序) ...

打印 上一主题 下一主题

主题 1897|帖子 1897|积分 5691

浮空输入、上拉输入、下拉输入、模拟输入、
   开漏输出、推挽输出、推挽复用、开漏复用     每组GPIO端口的寄存器包罗:      两个32位配置寄存器(GPIOx_CRL ,GPIOx_CRH) ,      两个32位数据寄存器 (GPIOx_IDR和GPIOx_ODR),      一个32位置位/ 复位寄存器(GPIOx_BSRR),      一个16位复位寄存器(GPIOx_BRR),      一个32位锁定寄存器(GPIOx_LCKR)。     每个I/O端口位可以自由编程,然而  I/O端口寄存器必须按32位字被访问  (不答应半字或字节访问) 。  

CRL 0-7
CRH 8-15
IDR 0-15 输入 只读
ODR 0-15 输出 控制上拉/下拉电阻的使能

复位&=(清0 看0的位,别的位置填F)
置为|= (置1 看1的位 别的位置填0)



   如:设置PORTC的11位为上拉输入,12位为推挽输出:      GPIOC->CRH &= 0xFFF00FFF;      GPIOC->CRH |= 0x00038000;      GPIOC->ODR = 1<<11;    引脚模式配置方式PC11上拉输入CNF11=10 + ODR=1PC12推挽输出CNF12=00 + MODE12=1  

  • 为什么用 ODR 设置上拉?
  • STM32 的 上拉/下拉电阻 由 ODR 寄存器 控制:
  • 输入模式下,ODR=0 → 下拉使能

    • 输入模式下,ODR=1 → 上拉使能


  

  

  
  流水灯
  【主要是CRL/CRH 配置 一般为0011 推挽输出50HZ 及ODR】
     电路中有    L0,L1,L2,L3,L4,L5,L6,L7    共八个发光二极管,当引脚    LED_SEL(PB4)   输入为    1   ,对        于    A   、   B   、   C   、   D   、   E   、   F   、   G   、   H(PE8-15)   引脚,只要输入为    1   ,则点亮相连接的发光二极管。        A~H    引脚连接    STM32F108VB    芯片的    PE8~PE15   ,程序初始化时,对其进行初始设置。引        脚    LED_SEL    为    1    时,发光二极管才工作,否则右边的数码管工作。   注意:   LED_SEL    连接于    PB4   

  GPIOB->CRL &= 0xFFF0FFFF;  // 清零 PB4 的配置位(CNF4、MODE4)
GPIOB->CRL |= 0x00030000;  // 设置 PB4 为推挽输出(50MHz) 
GPIOB->ODR |= 0x00000010;  // 初始置 PB4=1(默认点亮 LED) 1<<4 = 0x00000010
  

  • MODE4=11(50MHz 输出速率)。
  • CNF4=00(推挽输出)

  GPIOE->ODR &= ~(0xff << 8);  // 清除PE8-PE15(熄灭所有LED)
LED_SEL = 1;                 // 使能LED控制信号
light = 0x01;                // 初始化light(从PE8开始)
  

  • GPIOE->ODR &= ~(0xff << 8)

    • 0xff << 8:二进制 0000 0000 1111 1111 0000 0000(即PE8-PE15全1)。
    • ~取反后:1111 1111 0000 0000 1111 1111。
    • &=操作:将PE8-PE15的位清零(熄灭LED),其他位不变。
    • while(1) {
          GPIOE->ODR |= (light << 8);  // 点亮当前LED
          delay_ms(300);               // 延时300ms
          light = light << 1;          // 左移1位,切换到下一个LED
          if(light == 0x00) {          // 如果light溢出(所有LED已点亮一轮)
              GPIOE->ODR &= ~(0xff << 8); // 熄灭所有LED
              delay_ms(300);              // 延时300ms
              light = 0x01;              // 重置light,重新开始循环
          }
      }

  • 法二四个灯
    1. typedef unsigned long  u32;
    2. #define RCC_APB2ENR (*(u32 *)0x40021018)  //定义APB2ENR寄存器地址
    3. #define GPIOE_CRL   (*(u32 *)0x40011800)  //定义GPIOE_CRH寄存器
    4. #define GPIOE_ODR   (*(u32 *)0x4001180C)  //定义GPIOE_ODR寄存器
    5. #define LED0  (*(u32 *)(0x42000000 +(0x4001180C-0x40000000)*32 + 0*4))  //位带定义PE0
    6. #define LED1  (*(u32 *)(0x42000000 +(0x4001180C-0x40000000)*32 + 1*4))  //位带定义PE1
    7. #define LED2  (*(u32 *)(0x42000000 +(0x4001180C-0x40000000)*32 + 2*4))  //位带定义PE2
    8. #define LED3  (*(u32 *)(0x42000000 +((u32)(0x4001180C & 0xFFFFF)<<5) + (3<<2)))  //位带定义PE3
    9. int delay(int Time)
    10. {
    11.     //简单延时程序
    12.     unsigned short t, i, j;
    13.     for(t = 0; t < Time; t++)
    14.         for(i = 0; i < 1000; i++)
    15.             for(j = 0; j < 1000; j++)
    16.                 ;
    17.     return 0;
    18. }
    19. int  main(void)
    20. {
    21.     // 先声明所有局部变量
    22.     u32 mode_left[] = {0x00000001, 0x00000002, 0x00000004, 0x00000008};
    23.     u32 mode_right[] = {0x00000008, 0x00000004, 0x00000002, 0x00000001};
    24.     int i;
    25.                 int delay_time = 1;
    26.     // 硬件初始化 GPIOE端口基地址为:0x4001 1800。
    27.     (*(u32 *)0x40021018) |= 1 << 6;  //使能PORTE时钟
    28.     (*(u32 *)0x40011800) &= 0XFFFF0000;//清除PE.0-3原先配置
    29.     (*(u32 *)0x40011800) |= 0X00003333; //PE.0-3配置为推挽输出
    30.     (*(u32 *)0x4001180C) |= 0x0000000F; //PE.0-3输出高,四个LED灯全亮1111
    31.     delay(1);
    32.     (*(u32 *)0x4001180C) &= 0xFFFFFFF0; //复位 PE.0-3输出低,四个LED灯全灭 保留寄存器中不需要修改的位(高28位),仅清零目标位(低4位)。
    33.     delay(1);
    34. //        (*(u32 *)0x4001180C) = 0x00000001; //仅LED1亮,其他灯灭 向GPIOE_ODR写入 0x00000001(Bit0=1,其他位=0)。 0001
    35. //        delay(1);
    36. //        (*(u32 *)0x4001180C) = 0x00000002; //仅LED2亮,其他灯灭 写入 0x00000001(Bit0=1,其他位=0)0010
    37. //        delay(1);
    38. //        (*(u32 *)0x4001180C) = 0x00000004; //仅LED3亮,其他灯灭 0x00000004(Bit2=1,其他位=0)。0100
    39. //        delay(1);
    40. //        (*(u32 *)0x4001180C) = 0x00000008; //仅LED4亮,其他灯灭 0x00000008(Bit3=1,其他位=0)。1000
    41. //        delay(1);       
    42.                
    43.                
    44.     while(1)
    45.     {
    46.                         // 从中间向两边扩散
    47.         (*(u32 *)0x4001180C) = 0x00000002; // LED2亮
    48.         delay(delay_time);
    49.         (*(u32 *)0x4001180C) = 0x00000004; // LED3亮
    50.         delay(delay_time);
    51.         (*(u32 *)0x4001180C) = 0x00000006; // LED2+3亮
    52.         delay(delay_time);
    53.         (*(u32 *)0x4001180C) = 0x0000000F; // 全亮
    54.         delay(delay_time);
    55.                           (*(u32 *)0x4001180C) &= 0xFFFFFFF0;
    56.                                 delay(3);
    57.                          // 从两边向中间收缩
    58.         (*(u32 *)0x4001180C) = 0x00000009; // LED1+4亮
    59.         delay(delay_time);
    60.         (*(u32 *)0x4001180C) = 0x00000006; // LED2+3亮
    61.         delay(delay_time);
    62.         (*(u32 *)0x4001180C) = 0x00000002; // LED2亮
    63.         delay(delay_time);
    64.         (*(u32 *)0x4001180C) = 0x00000004; // LED3亮
    65.         delay(delay_time);
    66.                                 (*(u32 *)0x4001180C) &= 0xFFFFFFF0;
    67.                                 delay(3);
    68.                                 //上
    69.         for(i = 0; i < 4; i++) {
    70.             (*(u32 *)0x4001180C) = mode_left[i];
    71.             delay(1);
    72.         }
    73.                                 (*(u32 *)0x4001180C) &= 0xFFFFFFF0;
    74.                                 delay(3);
    75.         //下
    76.         for(i = 0; i < 4; i++) {
    77.             (*(u32 *)0x4001180C) = mode_right[i];
    78.             delay(1);
    79.         }
    80.                                 (*(u32 *)0x4001180C) &= 0xFFFFFFF0;
    81.     }
    82. }
    复制代码
       
             8      位数码管显示原理              8      位数码管的连接原理图如图      3.1      所示,     LED      数码管引脚定义如图      3.2      所示,其中      a-g      分              别对应一个笔画,     dp      对应小数点,负极连接到一起接到      cc     ,因此叫共阴极数码管。共阴极              8      位数码管显示      0-F      时对应亮的      LED      如图      3.2      所示。
  • u8 segTable[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f}; 
    //0-9 0 DP和G不亮 0011111111    1:00000110
             数码管中的      A~G     、     DP      段分别连接到电路图中的      A~G     、     H      线上,当某段上有一定的电压              差值时,便会点亮该段。              当      E3      输入为      1     ,也就是      LED_SEL      输入为      0      时,根据      SEL0~SEL2      的值确定选中的数码管位, 即位选,再根据 A~H      引脚的高低电平,点亮对应段,即段选。             // 3. 配置 PB.0-PB.2 为推挽输出(位选控制)
        GPIOB->CRL &= 0xFFF0F000; //清除PB.0-3配置
        GPIOB->CRL |= 0x00030333; //PB.0-2 4推挽输出
        GPIOB->ODR |= 0x00000017; //PB.0-2 4输出高
      // 4. 配置 PE.8-PE.15 为推挽输出(段选控制)
        GPIOE->CRH &= 0X00000000; //清除PE.8-15配置
        GPIOE->CRH |= 0X33333333; //PE.8-15推挽输出
        GPIOE->ODR |= 0x0000FF00; //PE.8-15输出高                 /***************************************
    * 流水灯选择,或数码管段选
    * value:显示的数值对应的段选二进制值 //设置数码管E-A的值;流水灯L7-L0的值    
         
         设置数码管的段选信号(a~g + dp)或 控制8个LED的亮灭(L0~L7)。
         通过操作 GPIOE->ODR 寄存器,将 value 的值映射到 PE8~PE15 引脚。
         void LedValue(u8 value)
    {
    //    LED0 = (value&0x01)?1:0;
    //    LED1 = (value&0x02)?1:0;
    //    LED2 = (value&0x04)?1:0;
    //    LED3 = (value&0x08)?1:0;
    //    LED4 = (value&0x10)?1:0;
    //    LED5 = (value&0x20)?1:0;
    //    LED6 = (value&0x40)?1:0;
    //    LED7 = (value&0x80)?1:0;
             GPIOE->ODR &= ~(0xff << 8);     
                  GPIOE->ODR |= value << 8;      // 将value左移8位,写入PE8~PE15       value 是8位数(0x00~0xFF),左移8位对齐到PE8~PE15。
    }     /***************************************
    * 数码管显示不带小数点的数值
    * 参数 w:显示的位置,即位选,左-右:0-7
    *      value:要显示的数值
    ****************************************/
         void SetLed(u8 w, u8 value) {
        // 1. 设置位选(选择哪一位数码管)
        SEL0 = w % 2;     // 最低位
        SEL1 = w / 2 % 2; // 中心位
        SEL2 = w / 4;     // 最高位
         //方法二
         GPIOB->ODR &= ~(0x07 << 0);  // 清除GPIOB的低3位(SEL0~SEL2)
    GPIOB->ODR |= (w & 0x07);    // 将w的低3位写入GPIOB(位选信号)

         
         w & 0x07:取 w 的低3位(确保值在0~7范围内)。
         |= 操作将 w 的值写入 GPIOB->ODR 的低3位(SEL0~SEL2)。
         示例

    • w=3(011)→ GPIOB->ODR 的低3位变为 011,选中第4位数码管。
             // 2. 设置段选(显示什么数字)
        LedValue(segTable[value]);

    }
         位选控制(SEL0/1/2)
         

    • 假设 SEL0/1/2 是连接到3-8译码器(如74HC138)的引脚,用于选择8位数码管中的某一位。
    • 盘算方式:将位置 w 转换为3位二进制(SEL2 SEL1 SEL0):

      • w=0 → 000 → 第1位数码管
      • w=3 → 011 → 第4位数码管
      • w=7 → 111 → 第8位数码管

         段选控制(LedValue)
         

    • 从预定义的段码表 segTable 中获取数字 value 对应的段码,通报给 LedValue 函数。
    • 示例

      • value=0 → segTable[0]=0x3F → 数码管显示 0。
      • value=1 → segTable[1]=0x06 → 数码管显示 1。

         SetLed(2, 5);  // 在第3位数码管(w=2)显示数字5
         
         位选
         

    • w=2 → SEL0=0, SEL1=1, SEL2=0(二进制 010)→ 选中第3位数码管。
         段选
         

    • segTable[5] = 0x6D → LedValue(0x6D) → PE8~PE15输出 0110 1101,点亮对应段。
         

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

数据人与超自然意识

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表