(*(volatile unsigned long *) 关于MCU头文件的解析

本文探讨了在不同计算机体系结构中,如何通过内存映射或端口映射访问设备。介绍了使用C语言进行内存映射的具体实现方法,解释了volatile关键字的作用,并通过实例说明了如何直接访问硬件寄存器。
(*(volatile unsigned long *)
对于不同的计算机体系结构,设备可能是端口映射,也可能是内存映射的。如果系统结构支持独立的IO地址空间,并且是端口映射,就必须使用汇编语言完成实际对设备的控制,因为C语言并没有提供真正的端口的概念。如果是内存映射,那就方便的多了。

以 #define IOPIN (*((volatile unsigned long *) 0xE0028000)) 为例:作为一个宏定义语句,define是定义一个变量或常量的伪指令。首先( volatile unsigned long * )的意思是将后面的那个地址强制转换成 volatile unsigned long * ,unsigned long * 是无符号长整形,volatile 是一个类型限定符,如const一样,当使用volatile限定时,表示这个变量是依赖系统实现的,以为着这个变量会被其他程序或者计算机硬件修改,由于地址依赖于硬件,volatile就表示他的值会依赖于硬件。

volatile 类型是这样的,其数据确实可能在未知的情况下发生变化。比如,硬件设备的终端更改了它,现在硬件设备往往也有自己的私有内存地址,比如显存,他们一般是通过映象的方式,反映到一段特定的内存地址当中,这样,在某些条件下,程序就可以直接访问这些私有内存了。另外,比如共享的内存地址,多个程序都对它操作的时候。你的程序并不知道,这个内存何时被改变了。如果不加这个voliatile修饰,程序是利用catch当中的数据,那个可能是过时的了,加了 voliatile,就在需要用的时候,程序重新去那个地址去提取,保证是最新的。归纳起来如下:

1. volatile变量可变允许除了程序之外的比如硬件来修改他的内容
2. 访问该数据任何时候都会直接访问该地址处内容,即通过cache提高访问速度的优化被取消

对于((volatile unsigned long *) 0xE0028000)为随硬件需要定义的一种地址,前面加上“*”指针,为直接指向该地址,整个定义约定符号IOPIN代替,调用的时候直接对指向的地址寄存器写内容既可。这实际上就是内存映射机制的方便性了

其中volatile关键字是嵌入式系统开发的一个重要特点。上述表达式拆开来分析,首先(volatile unsigned long *) 0xE0028000的意思是把0xE0028000强制转换成volatile unsigned long类型的指针,暂记为p,那么就是#define A *p,即AP指针指向位置的内容了。A 这里就为P指针指向地址内存里存放的值。。。。。。。这里就是通过内存寻址访问到寄存器A,可以读/写操作。

对于(volatile unsigned char *)0x20我们再分析一下,它是由两部分组成:
1)(unsigned char *)0x20,0x20只是个值,前面加(unsigned char *)表示0x20是个地址,而且这个地址类型是unsigned char ,意思是说读写这个地址时,要写进unsigned char 的值,读出也是unsigned char 。
2)volatile,关键字volatile 确保本条指令不会因C 编译器的优化而被省略,且要求每次直接读值。例如用while((unsigned char *)0x20)时,有时系统可能不真正去读0x20的值,而是用第一次读出的值,如果这样,那这个循环可能是个死循环。用了volatile 则要求每次都去读0x20的实际值。

那么(volatile unsigned char *)0x20是一个固定的指针,是不可变的,不是变量。而char *u则是个指针变量。
再在前面加"*":*(volatile unsigned char *)0x20则变成了变量(普通的unsigned char变量,不是指针变量),如果#define i (*(volatile unsigned char *)0x20),那么与unsigned char i是一样了,只不过前面的i的地址是固定的。

那么你的问题就可解答了,(*(volatile unsigned char *)0x20)可看作是一个普通变量,这个变量有固定的地址,指向0x20。而0x20只是个常量,不是指针更不是变量。

#ifndef _MAIN_C_ #define _MAIN_C_ /*********************************************************************************************************************/ #include "include/ca51f1s6_config.h" #include "include/ca51f1s6sfr.h" #include "include/ca51f1s6xsfr.h" #include "include/gpiodef_f1s6.h" #include "include/system_clock.h" #include "include/uart.h" #include "include/delay.h" /*********************************************************************************************************************/ #include "stdio.h" // 全局控制变量 unsigned int mode1 = 0; unsigned int KeyCnt = 0; int KEY2_Press = 0; bit yangchao = 0; // 0:亮度调节模式, 1:色温调节模式 unsigned char brightness = 100; // 亮度值(0-100%) unsigned int color_temp = 4500; // 色温值(2000-6500K) // PWM占空比缓存 (100%亮度下的值) unsigned char red_duty100 = 0; unsigned char green_duty100 = 0; unsigned char blue_duty100 = 0; /********************************************************************************************************************* 此模式下,定时器0作为16位定时器/计数器。 **********************************************************************************************************************/ #define EPIE(n) (n<<7) #define EPPL(n) (n<<5) #define EPPSEL(n) (n<<0) bit int3_flag,int4_flag; /*********************************************************************************************************************/ #define INT_TIME 1000 //定时时间,单位为us #define TH_VAL (unsigned char)((0x10000 - (INT_TIME*(FOSC/1000))/12000)>>8) #define TL_VAL (unsigned char)(0x10000 - (INT_TIME*(FOSC/1000))/12000) //PWMCON #define PWMIE(N) (N<<7) #define PWMTOG(N) (N<<6) #define CKS_SYS (0<<0) #define CKS_IL (1<<0) #define CKS_IH (2<<0) #define CKS_TH (3<<0) //PWMPS #define PWM_P00_INDEX 0 #define PWM_P01_INDEX 1 #define PWM_P02_INDEX 2 #define PWM_P03_INDEX 3 #define PWM_P04_INDEX 4 #define PWM_P05_INDEX 5 #define PWM_P06_INDEX 6 #define PWM_P07_INDEX 7 #define PWM_P10_INDEX 8 #define PWM_P11_INDEX 9 #define PWM_P12_INDEX 10 #define PWM_P13_INDEX 11 #define PWM_P14_INDEX 12 #define PWM_P15_INDEX 13 #define PWM_P16_INDEX 14 #define PWM_P17_INDEX 15 #define PWM_P20_INDEX 16 #define PWM_P30_INDEX 17 //PWMCFG #define TOG(N) (N<<7) #define PWM_CH0 0 #define PWM_CH1 1 #define PWM_CH2 2 #define PWM_CH3 3 #define PWM_CH4 4 #define PWM_CH5 5 #define PWM_CH6 6 #define PWM_CH7 7 #define PWM1DIV_V (16000000/3000) //当PWM时钟为其他时钟频率时,需相应修改参数 #define PWM2DIV_V (16000000/3000) //当PWM时钟为其他时钟频率时,需相应修改参数 #define PWM3DIV_V (16000000/3000) //当PWM时钟为其他时钟频率时,需相应修改参数 // duty: 0 - 255 void PWM1_CH_Duty(unsigned char duty) { INDEX = PWM_CH1; PWMDUTH = (unsigned char)(PWM1DIV_V / 255 * duty >> 8); PWMDUTL = (unsigned char)(PWM1DIV_V / 255 * duty); PWMEN |= (1 << PWM_CH1); } void PWM2_CH_Duty(unsigned char duty) { INDEX = PWM_CH2; PWMDUTH = (unsigned char)(PWM2DIV_V / 255 * duty >> 8); PWMDUTL = (unsigned char)(PWM2DIV_V / 255 * duty); PWMEN |= (1 << PWM_CH2); } void PWM3_CH_Duty(unsigned char duty) { INDEX = PWM_CH3; PWMDUTH = (unsigned char)(PWM3DIV_V / 255 * duty >> 8); PWMDUTL = (unsigned char)(PWM3DIV_V / 255 * duty); PWMEN |= (1 << PWM_CH3); } void PWM1_Init(void) { INDEX = PWM_CH1; PWMCON = PWMIE(0) | PWMTOG(0)| CKS_IH; PWMCKD = 0; //设置PWMDIV、PWMDUT PWMDIVH = (unsigned char)(PWM1DIV_V>>8); PWMDIVL = (unsigned char)(PWM1DIV_V); PWMDUTH = 0; PWMDUTL = 0; P21F = P21_PWM1_SETTING; PWMEN |= (1<<PWM_CH1); } void PWM2_Init(void) { INDEX = PWM_CH2; PWMCON = PWMIE(0) | PWMTOG(0)| CKS_IH; PWMCKD = 0; //设置PWMDIV、PWMDUT PWMDIVH = (unsigned char)(PWM2DIV_V>>8); PWMDIVL = (unsigned char)(PWM2DIV_V); PWMDUTH = 0; PWMDUTL = 0; P22F = P22_PWM2_SETTING; PWMEN |= (1<<PWM_CH2); } void PWM3_Init(void) { INDEX = PWM_CH3; PWMCON = PWMIE(0) | PWMTOG(0)| CKS_IH; PWMCKD = 0; //设置PWMDIV、PWMDUT PWMDIVH = (unsigned char)(PWM3DIV_V>>8); PWMDIVL = (unsigned char)(PWM3DIV_V); PWMDUTH = 0; PWMDUTL = 0; P23F = P23_PWM3_SETTING; PWMEN |= (1<<PWM_CH3); } void Key_scan(void) { if(int4_flag) { KeyCnt++; if ((KeyCnt >= 30) && KeyCnt <= 1000) // 1S短按 { KEY2_Press = 1; int4_flag = 0; } } } void TIMER0_ISR (void) interrupt 1 //每10ms产生中断 { TH0 = TH_VAL; TL0 = TL_VAL; Key_scan(); } void Timer0_Init(void) { TMOD = (TMOD&0xFC)|0x01; //模式选择: 定时器0,模式1。 TH0 = TH_VAL; //高8位装初值 TL0 = TL_VAL; //低8位装初值 TR0 = 1; //定时器0使能 ET0 = 1; //定时器0中断使能 // PT0 = 1; //设置定时器0中断优先级为高优先级 } // 根据色温计算100%亮度下的RGB值 void Calculate_RGB_Values(unsigned int temp) { volatile unsigned long position = 0; // 确保色温在有效范围内 if (temp < 2000) temp = 2000; if (temp > 6500) temp = 6500; // 计算标准化位置 (0-255) position = (unsigned long)(temp - 2000) * 255 / 4500; //printf("pos = %ld\r\n",position); // 计算100%亮度下的占空比(共阳LED) red_duty100 = (unsigned char)position; // 红:色温↑占空比↑ blue_duty100 = 255 - (unsigned char)position; // 蓝:色温↑占空比↓ green_duty100 = 192 - (position >> 1); // 绿:中间值 // printf("R = %d\tG = %d\tB = %d\r\n",red_duty,green_duty,blue_duty); } // 应用当前亮度和色温设置 void Apply_RGB_Settings() { // 计算亮度因子 (0.0-1.0) unsigned char brightness_factor = (100 - brightness); // 共阳LED:亮度与占空比成反比 // 计算实际占空比 (带亮度调节) unsigned char red_actual = (red_duty100 * brightness_factor) / 100; unsigned char green_actual = (green_duty100 * brightness_factor) / 100; unsigned char blue_actual = (blue_duty100 * brightness_factor) / 100; // 设置PWM输出 PWM3_CH_Duty(red_actual); // 红色通道 PWM2_CH_Duty(green_actual); // 绿色通道 PWM1_CH_Duty(blue_actual); // 蓝色通道 } /*********************************************************************************************************************/ void System_Init(void) { LVDCON = 0xE2; //设置LVD复位电压为2.7V #ifdef SYSCLK_16MHZ //系统时钟为16MHz,设置CKDIV为0 CKDIV = 0; #endif IHCFG = ReadIHCFG(); TKCCFG = ReadTKCCFG(); #ifdef UART1_EN Uart1_Initial(UART1_BAUTRATE); #endif #ifdef UART2_EN Uart2_Initial(UART2_BAUTRATE); #endif } /*外部中断3控制例程****************************************************************************************************/ void INT3_Init(void) { P03F = INPUT | PU_EN; //INT2中断触发源为固定输入引脚P03 // EP1CON = EPIE(1) | EPPL(1); //使能外部中断,并选择下降沿触发, EP1CON = EPIE(1) | EPPL(2); //使能外部中断,并选择下降触发, INT3EN = 1; //外部中断3中断使能 PX3 = 1; //设置INT3中断为最高优先级 int3_flag = 0; } /*外部中断4控制例程****************************************************************************************************/ void INT4_Init(void) { P25F = INPUT; //INT4可配置信号引脚(P1.0~P0.7/P2.0~P2.7/P0.4~P0.7/P3.0/P3.1)作为中断输入引脚 EP2CON = EPIE(1) | EPPL(1) | EPPSEL(13); //使能外部中断,并选择下降沿触发, 设置P12为INT4中断引脚 INT4EN = 1; //外部中断4中断使能 int4_flag = 0; } void main(void) { System_Init(); EA = 1; //开全局中断 P04F = INPUT | PU_EN; INT3_Init(); INT4_Init(); Timer0_Init(); CKCON |= IHCKE; PWM1_Init(); PWM2_Init(); PWM3_Init(); Calculate_RGB_Values(color_temp); // 初始色温计算 Apply_RGB_Settings(); // 应用初始设置 while(1) { if(KEY2_Press) { KEY2_Press = 0; if(yangchao == 0) { yangchao = 1; mode1 = 1; // printf("mode1 = %d\r\n",mode1); }else{ yangchao = 0; mode1 = 2; // printf("mode1 = %d\r\n",mode1); } // mode = !mode; } if(mode1 == 1) { if(int3_flag) { int3_flag = 0; if(P03 == 0) { if(P04 == 0&& brightness < 100)//顺时针 { brightness += 2; // 顺时针增加亮度 printf("brightness = %d\r\n",brightness); }else if(P04 == 1 && brightness > 0)//逆时针 { brightness -= 2; // 逆时针减少亮度 printf("brightness = %d\r\n",brightness); } } else { if(P04 == 1&& brightness < 100)//顺时针 { brightness += 2; // 顺时针增加亮度 printf("brightness = %d\r\n",brightness); }else if(P04 == 0&& brightness > 0)//逆时针 { brightness -= 2; // 逆时针减少亮度 printf("brightness = %d\r\n",brightness); } } } }else if(mode1 == 2) { if(int3_flag) { int3_flag = 0; if(P03 == 0) { if(P04 == 0&& color_temp < 6500)//顺时针 { color_temp += 100; // 顺时针增加色温 // printf("color_temp = %d\r\n",color_temp); }else if(P04 == 1&& color_temp > 2000)//逆时针 { color_temp -= 100; // 逆时针减少色温 // printf("color_temp = %d\r\n",color_temp); } // 重新计算100%亮度下的RGB值 Calculate_RGB_Values(color_temp); } else { if(P04 == 1&& color_temp < 6500)//顺时针 { color_temp += 100; // 顺时针增加色温 // printf("color_temp = %d\r\n",color_temp); }else if(P04 == 0&& color_temp > 2000)//逆时针 { color_temp -= 100; // 逆时针减少色温 // printf("color_temp = %d\r\n",color_temp); } // 重新计算100%亮度下的RGB值 Calculate_RGB_Values(color_temp); } } } // 应用新设置 Apply_RGB_Settings(); //// // 短延时防止过快调节 //// Delay_ms(50); } } #endif 这是一个51单片机中空编码器控制RGB调色温和调亮度两种模式的代码,现在色温是0-100,请帮我把占空比范围修改成0-1000,不能影响程序功能
最新发布
08-29
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值