理解(*(volatile unsigned char *)0x5F[转]

本文解析了#defineSREG (*(volatile unsigned char*)0x5F)的含义,介绍了如何通过C语言访问特定内存地址,并解释了volatile关键字的作用。同时,文章还提供了具体的示例来演示如何对内存地址进行读写操作。

理解#define SREG    (*(volatile unsigned char *)0x5F)
这样的定义,总是感觉很奇怪,不知道为什么,今天终于有了一点点心得,请大虾们多多批砖~~~

   嵌入式系统编程,要求程序员能够利用C语言访问固定的内存地址。既然是个地址,那么按照C语言的语法规则,这个表示地址的量应该是指针类型。所以,知道要访问的内存地址后,比如0x5F,
   第一步是要把它强制转换为指针类型
(unsigned char *)0x5F,AVR的SREG是八位寄存器,所以0x5F强制转换为指向
unsigned char类型。
   volatile(可变的)这个关键字说明这变量可能会被意想不到地改变,这样编译器就不会去假设这个变量的值了。这种“意想不到地改变”,不是由程序去改变,而是由硬件去改变——意想不到。
   第二步,对指针变量解引用,就能操作指针所指向的地址的内容了
   *(volatile unsigned char *)0x5F
   第三步,小心地把#define宏中的参数用括号括起来,这是一个很好的习惯,所以#define SREG    (*(volatile unsigned char *)0x5F)

    类似的,如果使用一个32位处理器,要对一个32位的内存地址进行访问,可以这样定义#define RAM_ADDR     (*(volatile unsigned long  *)0x0000555F)
    然后就可以用C语言对这个内存地址进行读写操作了
    读:tmp = RAM_ADDR;
    写:RAM_ADDR = 0x55;
zhiwei 发表于 2005-4-30 18:59 AVR 单片机

定义未volatile是因为它的值可能会改变,大家都知道为什么改变了;
如果在一个循环操作中需要不停地判断一个内存数据,例如要等待SREG的I标志位置位,因为SREG也是映射在SRAM空间,为了加快速度,编译器可能会编译出这样的代码:把SREG读取到Register中,然后不停地判断Register相应位。而不会再读取SREG,这样当然是不行了,因为程序或其它事件(中断等)会改变SREG,结果很可能是一个死循环出不来了。如果定义成volatile型变量,编译的代码是这样的:每次要操作一个变量的时候都从内存中读取一次。
#define SREG (*(volatile unsigned char *)0x5F) 之后,可以进行如下基本操作,
unsigned char temp,*ptr;
temp=SREG;把SREG值保存到temp中
SREG=temp;把temp的值赋给SREG
ptr = & SREG; 不知对否,大家试一下。

 
void I2CInit (void) { RCC->AHB1ENR |=1<<1; RCC->APB1ENR |=1<<21; GPIOB->MODER = (GPIOB->MODER &~(0xF<<16))|(0xA<<16); GPIOB->OTYPER |=(0x3<<8); GPIOB->OSPEEDR = (GPIOB->OSPEEDR &~(0xF<<16))|(0xA<<16); GPIOB->AFR[1]= (GPIOB->AFR[1] &~(0xFF<<0))|(0x4<<0)|(0x4<<4); I2C1->CR1 &=~1; I2C1->CR2 = 16; I2C1->CCR = 80; I2C1->TRISE = 17; I2C1->CR1 |=1; } void I2C_Write(uint8_t address,uint8_t reg,uint8_t data) { while (I2C1->SR2 &(1<<1)){}//等待总线空闲BUSY寄存器 I2C1->CR1 |=1<<8;//设置START位,启动I2C通信 while (!(I2C1->SR1 &1)){}//等待Start Bit标志置位,表示条件已成功发出,可以输入下一个字节 I2C1->DR =(address<<1);//发送设备地址(写模式) while (!(I2C1->SR1 &(1<<1))){}//等待ADDR标志置位,表示地址已发送,并且从设备返回了应答 (void)I2C1->SR2;//清除ADDR标志,确保后续可以继续通信 while (!(I2C1->SR1 &(1<<7))){} I2C1->DR = reg; while (!(I2C1->SR1 &(1<<7))){}//等待TEX置位,来发送寄存器地址 I2C1->DR = data;//将真正要写入DR寄存器的数据DATA放入其中 while (!(I2C1->SR1 &(1<<2))){}//等待传输完成, I2C1->CR1 |=1<<9;//设置STOP位,来停止I2C通信 } void OledInit (void)// OLED初始化命令序列(I2C地址0x3C,控制字节0x00表示写命令) { for (volatile int i=0;i<=100000;i++); I2C_Write(0x3C, 0x00, 0xAE); // 关闭显示 I2C_Write(0x3C, 0x00, 0xD5); // 设置时钟分频因子 I2C_Write(0x3C, 0x00, 0x80); I2C_Write(0x3C, 0x00, 0xA8); // 设置多路复用比 I2C_Write(0x3C, 0x00, 0x3F); // 64点阵高度(0.96寸屏) I2C_Write(0x3C, 0x00, 0xD3); // 设置显示偏移 I2C_Write(0x3C, 0x00, 0x00); I2C_Write(0x3C, 0x00, 0x40); // 设置显示起始行 I2C_Write(0x3C, 0x00, 0x8D); // 启用内部DC-DC升压 I2C_Write(0x3C, 0x00, 0x14); I2C_Write(0x3C, 0x00, 0x20); // 设置内存寻址模式 I2C_Write(0x3C, 0x00, 0x00); // 水平寻址模式 I2C_Write(0x3C, 0x00, 0xA1); // 段重定向设置(左右镜像) I2C_Write(0x3C, 0x00, 0xC8); // COM输出扫描方向(上下翻) I2C_Write(0x3C, 0x00, 0xDA); // 设置COM引脚配置 I2C_Write(0x3C, 0x00, 0x12); I2C_Write(0x3C, 0x00, 0x81); // 对比度控制 I2C_Write(0x3C, 0x00, 0xCF); I2C_Write(0x3C, 0x00, 0xD9); // 设置预充电周期 I2C_Write(0x3C, 0x00, 0xF1); I2C_Write(0x3C, 0x00, 0xDB); // 设置VCOMH电压 I2C_Write(0x3C, 0x00, 0x40); I2C_Write(0x3C, 0x00, 0xA4); // 禁用全亮模式 I2C_Write(0x3C, 0x00, 0xA6); // 正常显示(非反色) I2C_Write(0x3C, 0x00, 0xAF); // 开启显示 } void OLED_Clear(void) //清屏函数 { for (uint8_t page = 0; page < 8; page++) // SSD1306有8页(每页8行,共64行) { I2C_Write(0x3C, 0x00, 0xB0 + page); // 设置当前操作的页地址 I2C_Write(0x3C, 0x00, 0x00); // 设置列地址低4位 I2C_Write(0x3C, 0x00, 0x10); // 设置列地址高4位(起始列=0) for (uint8_t i = 0; i < 128; i++) // 每页有128列 { I2C_Write(0x3C, 0x40, 0x00); // 写入0x00,表示该点不亮 } } } const unsigned char font_6x8[][6] = // 定义6x8字体点阵数据(仅包含空格到 '~' 的部分ASCII) { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 空格 ' ' {0x00, 0x00, 0x5F, 0x00, 0x00, 0x00}, // '!' {0x00, 0x07, 0x00, 0x07, 0x00, 0x00}, // '"' {0x14, 0x7F, 0x14, 0x7F, 0x14, 0x00}, // '#' {0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x00}, // '$' {0x23, 0x13, 0x08, 0x64, 0x62, 0x00}, // '%' {0x36, 0x49, 0x55, 0x22, 0x50, 0x00}, // '&' {0x00, 0x05, 0x03, 0x00, 0x00, 0x00}, // ''' {0x00, 0x1C, 0x22, 0x41, 0x00, 0x00}, // '(' {0x00, 0x41, 0x22, 0x1C, 0x00, 0x00}, // ')' {0x14, 0x08, 0x3E, 0x08, 0x14, 0x00}, // '*' {0x08, 0x08, 0x3E, 0x08, 0x08, 0x00}, // '+' {0x00, 0x50, 0x30, 0x00, 0x00, 0x00}, // ',' {0x08, 0x08, 0x08, 0x08, 0x08, 0x00}, // '-' {0x00, 0x60, 0x60, 0x00, 0x00, 0x00}, // '.' {0x20, 0x10, 0x08, 0x04, 0x02, 0x00}, // '/' {0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00}, // '0' {0x00, 0x42, 0x7F, 0x40, 0x00, 0x00}, // '1' {0x42, 0x61, 0x51, 0x49, 0x46, 0x00}, // '2' {0x21, 0x41, 0x45, 0x4B, 0x31, 0x00}, // '3' {0x18, 0x14, 0x12, 0x7F, 0x10, 0x00}, // '4' {0x27, 0x45, 0x45, 0x45, 0x39, 0x00}, // '5' {0x3C, 0x4A, 0x49, 0x49, 0x30, 0x00}, // '6' {0x01, 0x71, 0x09, 0x05, 0x03, 0x00}, // '7' {0x36, 0x49, 0x49, 0x49, 0x36, 0x00}, // '8' {0x06, 0x49, 0x49, 0x29, 0x1E, 0x00}, // '9' {0x00, 0x36, 0x36, 0x00, 0x00, 0x00}, // ':' {0x00, 0x56, 0x36, 0x00, 0x00, 0x00}, // ';' {0x08, 0x14, 0x22, 0x41, 0x00, 0x00}, // '<' {0x14, 0x14, 0x14, 0x14, 0x14, 0x00}, // '=' {0x00, 0x41, 0x22, 0x14, 0x08, 0x00}, // '>' {0x02, 0x01, 0x51, 0x09, 0x06, 0x00}, // '?' {0x32, 0x49, 0x79, 0x41, 0x3E, 0x00}, // '@' {0x7E, 0x11, 0x11, 0x11, 0x7E, 0x00}, // 'A' {0x7F, 0x49, 0x49, 0x49, 0x36, 0x00}, // 'B' {0x3E, 0x41, 0x41, 0x41, 0x22, 0x00}, // 'C' {0x7F, 0x41, 0x41, 0x22, 0x1C, 0x00}, // 'D' {0x7F, 0x49, 0x49, 0x49, 0x41, 0x00}, // 'E' {0x7F, 0x09, 0x09, 0x09, 0x01, 0x00}, // 'F' {0x3E, 0x41, 0x49, 0x49, 0x7A, 0x00}, // 'G' {0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00}, // 'H' {0x00, 0x41, 0x7F, 0x41, 0x00, 0x00}, // 'I' {0x20, 0x40, 0x41, 0x3F, 0x01, 0x00}, // 'J' {0x7F, 0x08, 0x14, 0x22, 0x41, 0x00}, // 'K' {0x7F, 0x40, 0x40, 0x40, 0x40, 0x00}, // 'L' {0x7F, 0x02, 0x0C, 0x02, 0x7F, 0x00}, // 'M' {0x7F, 0x04, 0x08, 0x10, 0x7F, 0x00}, // 'N' {0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00}, // 'O' {0x7F, 0x09, 0x09, 0x09, 0x06, 0x00}, // 'P' {0x3E, 0x41, 0x51, 0x21, 0x5E, 0x00}, // 'Q' {0x7F, 0x09, 0x19, 0x29, 0x46, 0x00}, // 'R' {0x46, 0x49, 0x49, 0x49, 0x31, 0x00}, // 'S' {0x01, 0x01, 0x7F, 0x01, 0x01, 0x00}, // 'T' {0x3F, 0x40, 0x40, 0x40, 0x3F, 0x00}, // 'U' {0x1F, 0x20, 0x40, 0x20, 0x1F, 0x00}, // 'V' {0x3F, 0x40, 0x38, 0x40, 0x3F, 0x00}, // 'W' {0x63, 0x14, 0x08, 0x14, 0x63, 0x00}, // 'X' {0x07, 0x08, 0x70, 0x08, 0x07, 0x00}, // 'Y' {0x61, 0x51, 0x49, 0x45, 0x43, 0x00}, // 'Z' }; void OLED_ShowChar(uint8_t x, uint8_t y, char ch) { if (ch >= 'A' && ch <= 'Z') // 显示单个字符(6列宽) { const unsigned char *p = font_6x8[ch - ' ']; for (uint8_t i = 0; i < 6; i++) { I2C_Write(0x3C, 0x40, p[i]); } } else if (ch == ' ') // 显式处理空格:写6个0x00 { for (uint8_t i = 0; i < 6; i++) { I2C_Write(0x3C, 0x40, 0x00); } } else // 其他非法字符显示为 '?' { const unsigned char *p = font_6x8['?' - ' ']; for (uint8_t i = 0; i < 6; i++) { I2C_Write(0x3C, 0x40, p[i]); } } } void OLED_ShowString(uint8_t x, uint8_t y, char *str) { uint8_t current_x = x; while (*str) { OLED_ShowChar(current_x, y, *str++); current_x += 6; // 每个字符宽6列 } } 头文件我在前面已经加上了目前代码还有什么问题吗?我想要通过oled显示hello world,可是为什么现在屏是黑的?
最新发布
12-15
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值