热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

STM32IO口模拟串口通讯

转自:http:ziye334.blog.163.comblogstatic224306191201452833850647前阵子,调项目时需要用到低波

转自:http://ziye334.blog.163.com/blog/static/224306191201452833850647

 

前阵子,调项目时需要用到低波特率串口通讯(300的波特率),才发下发现在正常情况下(PCLK1时钟频率为72M,PCLK2时钟频率为36M):STM32的USART0的最低波特率只能设置到1200,;而USART1最低波特率只能设置到600。怎么设置STM32的600或以下的波特率呢?有两种方法:一种是改变外设时钟频率,而另一种方法就是使用IO口模拟串口通讯。今天就来讲讲,用IO口模拟串口通信!

1、串口传输协议

首先,必须要知道串口通讯时数据是怎样传输的?这里以异步传输字符为例子,如下图所示:
 一般字符传输都采用:1位起始位,8位数据位,1位停止位,没有校验位 的形式传输,其他形式的这里不讲。串口异步传输在空闲状态时都必须是高电平。第一位传输的是起始位,起始位会将原来空闲时的高电平拉成低电平,起始位用来来标识数据开始传输,提示接收方准备开始接收数据;当接收方第一次检测到一个下降沿时,就表示接收到了起始位。起始位后就是8位的数据位,接收方在接收每一位数据的时候会采集几十次,如果结果都是低电平,则接收到的数据位0,如果结果都是高电平,则棘手到的数据位是1。1位停止位会将电平拉成高电平,以为接收下一个数据做准备。


2、IO模拟串口发送程序

IO口模拟串口发送数据,必须严格按照上面的异步传输协议。我们用伪代码实现这一过程:

void VirtualCOM_ByteSend(u8 val)

{

u8 i;

IO_LOW(); //起始位,拉低电平

Delay(sometime);

for(i &#61; 0; i <8; i&#43;&#43;) //8位数据位

{

if(val & 0x01)

IO_HIGH();

else

IO_LOW();

Dealy(sometime);

val >>&#61; 1;

}

IO_HIGH(); //停止位&#xff0c;拉高电平

Delay(sometime);

}


代码很简单&#xff0c;思路也很清晰&#xff0c;完全是按照异步传输的过程写的。这里最重要的是Delay(sometime)&#xff0c;sometime的延时时间就决定了传输的速度&#xff0c;sometime去取某些值才可以设置程序标准的串口波特率(1200、2400、9600、38400、115200等等)。

下面&#xff0c;我采用STM32开发板实现IO模拟串口发送。

&#xff08;1&#xff09;选择IO引脚设置为虚拟串口TX引脚

我选择PA4引脚来模拟串口的TX引脚&#xff0c;所以需要配置下PA4这个引脚为推挽输出&#xff1a;

#define COM_TX_PORT GPIOA

#define COM_TX_PIN GPIO_Pin_4

void VirtualCOM_TX_GPIOConfig(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

/* PA4最为数据输出口&#xff0c;模拟TX */

GPIO_InitStructure.GPIO_Pin &#61; COM_TX_PIN;
GPIO_InitStructure.GPIO_Mode &#61; GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed &#61; GPIO_Speed_50MHz;
GPIO_Init(COM_TX_PORT, &GPIO_InitStructure);
GPIO_SetBits(COM_TX_PORT, COM_TX_PIN);

}

这里需要说明的是&#xff0c;在配置完引脚后&#xff0c;需要将PA4引脚拉高&#xff0c;这样做是为了防止在发送数据起始位时&#xff0c;由于原来引脚是低电平而导致没有产生一个下降沿信号。

&#xff08;2&#xff09;IO模拟串口发送一个字节

遵循串口异步传输协议&#xff0c;编写了STM32上面的相应代码&#xff1a;

#define COM_TX_PORT GPIOA

#define COM_TX_PIN GPIO_Pin_4

#define COM_DATA_HIGH() GPIO_SetBits(COM_TX_PORT, COM_TX_PIN) //高电平
#define COM_DATA_LOW() GPIO_ResetBits(COM_TX_PORT, COM_TX_PIN) //低电平

u32 delayTime;

void VirtualCOM_ByteSend(u8 val)
{
u8 i &#61; 0;
COM_DATA_LOW(); //起始位
Delay_us(delayTime);
for(i &#61; 0; i < 8; i&#43;&#43;) //8位数据位
{
if(val & 0x01)
COM_DATA_HIGH();
else
COM_DATA_LOW();
Delay_us(delayTime);
val >>&#61; 1;
}
COM_DATA_HIGH(); //停止位
Delay_us(delayTime);
}

&#xff08;3&#xff09;IO模拟串口发送字符串

既然发送一个字节的函数已将实现了&#xff0c;那么发送字符串函数就简单了&#xff1a;

void VirtualCOM_StringSend(u8 *str)
{
while(*str !&#61; 0)
{
VirtualCOM_ByteSend(*str);
str&#43;&#43;;
}
}

 
3、IO模拟接收程序
接收的代码比发送的代码复杂些。先讲讲怎么IO口接收数据的思路。为了接收数据&#xff0c;IO引脚必须可以检测到传输数据的起始位&#xff0c;检测起始位其实相当于与要检测一个下降沿信号&#xff0c;那么引脚只要配置成外部中断模式就可以检测到这个起始信号。然后根据传输速率配置一个相应时间定时的定时器。当检测到起始信号后&#xff0c;打开该定时器&#xff0c;每隔一定时间就会进入定时器中断&#xff0c;检测当前的IO引脚高低电平&#xff0c;从而决定接收到的数据是‘1’还是‘0’。当第九次进入定时器中断服务程序时&#xff0c;说明已经收到了一个字节的数据&#xff0c;此时关闭定时器。
下面在讲讲怎么在STM32开发板上实现这一过程。
&#xff08;1&#xff09;选择IO引脚模拟串口接收引脚RX
我选择PA5来模拟串口的接收引脚RX&#xff0c;所以需要配置PA5为输入模式&#xff0c;同时打开它的外部中断。

#define COM_RX_PORT GPIOA

#define COM_RX_PIN GPIO_Pin_5

void VirtualCOM_RX_GPIOConfig(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

EXTI_InitTypeDef EXTI_InitStruct;

NVIC_InitTypeDef NVIC_InitStructure;

 

/* PA5最为数据输入&#xff0c;模拟RX */
GPIO_InitStructure.GPIO_Pin &#61; COM_RX_PIN;
GPIO_InitStructure.GPIO_Mode &#61; GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed &#61; GPIO_Speed_50MHz;
GPIO_Init(COM_RX_PORT, &GPIO_InitStructure);

 

EXTI_InitStruct.EXTI_Line&#61;EXTI_Line5;
EXTI_InitStruct.EXTI_Mode&#61;EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger&#61;EXTI_Trigger_Falling;//下降沿都中断
EXTI_InitStruct.EXTI_LineCmd&#61;ENABLE;
EXTI_Init(&EXTI_InitStruct);

 

NVIC_InitStructure.NVIC_IRQChannel&#61;EXTI9_5_IRQn; //外部中断&#xff0c;边沿触发
NVIC_InitStructure.NVIC_IRQChannelSubPriority &#61; 2;
NVIC_InitStructure.NVIC_IRQChannelCmd&#61;ENABLE;
NVIC_Init(&NVIC_InitStructure);

}

&#xff08;2&#xff09;配置一个定时器用来定时接收数据
我配置TIM2定时器为一定的定时周期&#xff0c;在它的中断服务程序中读取串口发送过来数据。定时器配置代码如下&#xff1a;

void TIM2_Configuration(u16 period)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

NVIC_InitTypeDef NVIC_InitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//使能TIM2的时钟
TIM_DeInit(TIM2); //复位TIM2定时器
TIM_InternalClockConfig(TIM2); //采用内部时钟给TIM2提供时钟源

TIM_TimeBaseStructure.TIM_Prescaler &#61; 72 - 1; //预分频系数为72&#xff0c;这样计数器时钟为72MHz/72 &#61; 1MHz
TIM_TimeBaseStructure.TIM_ClockDivision &#61; 0; //设置时钟分频
TIM_TimeBaseStructure.TIM_CounterMode &#61; TIM_CounterMode_Up;//设置计数器模式为向上计数模式
TIM_TimeBaseStructure.TIM_Period &#61; period - 1; //设置计数溢出大小&#xff0c;每计period个数就产生一个更新事件
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure); //将配置应用到TIM2中

TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除溢出中断标志
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //开启TIM2的中断
TIM_Cmd(TIM2,DISABLE); //关闭定时器TIM2

 

NVIC_InitStructure.NVIC_IRQChannel &#61; TIM2_IRQn; //通道设置为TIM2中断
NVIC_InitStructure.NVIC_IRQChannelSubPriority &#61; 1;//响应式中断优先级0
NVIC_InitStructure.NVIC_IRQChannelCmd &#61; ENABLE; //打开中断
NVIC_Init(&NVIC_InitStructure);
}

&#xff08;3&#xff09;IO口模拟串口接收功能的实现
IO口接收串口数据的功能是通过PA5引脚的外部中断服务程序与定时器的中断服务程序相互配合实现的。首先需要为数据定一些状态机&#xff0c;方便标识接收到数据的状态&#xff1a;

enum{
COM_START_BIT, //停止位
COM_D0_BIT, //bit0
COM_D1_BIT, //bit1
COM_D2_BIT, //bit2
COM_D3_BIT, //bit3
COM_D4_BIT, //bit4
COM_D5_BIT, //bit5
COM_D6_BIT, //bit6
COM_D7_BIT, //bit7
COM_STOP_BIT, //bit8
};

定义好了状态机&#xff0c;还需要一个变量&#xff0c;来保存这些状态机的变化&#xff0c;并定义它的初始状态为COM_STOP_BIT&#xff1a;

u8 recvStat &#61; COM_STOP_BIT; //定义状态机

下面是PA5的外部中断服务程序&#xff0c;它的主要任务是检测起始位&#xff0c;当他第一次检测到下降沿&#xff0c;则说明数据即将到来&#xff0c;这时只要打开定时器就可以了&#xff1a;

#define COM_RX_STAT GPIO_ReadInputDataBit(COM_RX_PORT, COM_RX_PIN)

void EXTI9_5_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line5)!&#61;RESET)
{
if(!COM_RX_STAT) //检测引脚高低电平&#xff0c;如果是低电平&#xff0c;则说明检测到下升沿
{
if(recvStat &#61;&#61; COM_STOP_BIT) //状态为停止位
{
recvStat &#61; COM_START_BIT; //接收到开始位
Delay(1000); //延时一定时间
TIM_Cmd(TIM2, ENABLE); //打开定时器&#xff0c;接收数据
}
}
EXTI_ClearITPendingBit(EXTI_Line5); //清除EXTI_Line1中断挂起标志位
}
}

上面代码中&#xff0c;检测到下降沿并设置了状态之后&#xff0c;延时了一定的时候&#xff0c;才打开定时器&#xff0c;这样做的原因是让定时器每次在信号的中间检测&#xff0c;而不要在信号边沿检测。正如下面图所示&#xff1a;
 下面就是定时器的中断服务程序&#xff0c;它主要是接收串口发送过来的数据&#xff0c;在它之前我们需要线定义一个变量用来保存接收到的数据&#xff1a;

u8 recvData;

然后&#xff0c;定时器中断中&#xff0c;每收到1位数据就改变下状态机并同时写入这个recvData对应的数据位中&#xff0c;当收到8为数据后&#xff0c;然后关闭定时器定时&#xff0c;以等待新的数据到来&#xff1a;

void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2, TIM_IT_Update) !&#61; RESET) //检测是否发生溢出更新事件
{
TIM_ClearITPendingBit(TIM2 , TIM_FLAG_Update);//清除中断标志
recvStat&#43;&#43;; //改变状态机
if(recvStat &#61;&#61; COM_STOP_BIT) //收到停止位
{
TIM_Cmd(TIM2, DISABLE); //关闭定时器
return; //并返回
}
if(COM_RX_STAT) //&#39;1&#39;
{
recvData |&#61; (1 << (recvStat - 1));
}
else //&#39;0&#39;
{
recvData &&#61; ~(1 <<(recvStat - 1));
}
}
}

4、一些精确延时与不精确延时的实现
上面代码中&#xff0c;需要定义一个不精确定时与两个精确定时&#xff0c;分别用在检测到下降沿后延时一段时间在打开定时器和控制传输速率中&#xff1a;

void Delay(u32 t)
{
while(t--);
}

void Delay_us(u32 nus)
{
SysTick->LOAD&#61;nus*9; //时间加载
SysTick->CTRL|&#61;0x01; //开始倒数
while(!(SysTick->CTRL&(1<<16)));//等待时间到达
SysTick->CTRL&#61;0X00000000; //关闭计数器
SysTick->VAL&#61;0X00000000; //清空计数器
}

void Delay_ms(u16 nms)
{
SysTick->LOAD&#61;(u32)nms*9000; //给重装载寄存器赋值&#xff0c;9000时&#xff0c;将产生1ms的时基
SysTick->CTRL|&#61;0x01; //开始倒数
while(!(SysTick->CTRL&(1<<16))); //等待时间到达
SysTick->CTRL&#61;0X00000000; //关闭计数器
SysTick->VAL&#61;0X00000000; //清空计数器
}

5、编写一个配置波特率的函数

这里能配置的只有300、600、1200三种波特率&#xff0c;其他的波特率我不想弄&#xff0c;也没有必要弄。下面编写一个初始化IO模拟的串口&#xff0c;包括引脚配置、波特率设置、定时时间设置等&#xff1a;

#define _300BuadRate 3150
#define _600BuadRate 1700
#define _1200BuadRate 800

void VirtualCOM_Config(u16 baudRate)

{
u32 period;
VirtualCOM_TX_GPIOConfig();

VirtualCOM_RX_GPIOConfig();
if(baudRate &#61;&#61; _300BuadRate) //波特率300
period &#61; _300BuadRate &#43; 250;
else if (baudRate &#61;&#61; _600BuadRate) //波特率600
period &#61; _600BuadRate &#43; 50;
else if (baudRate &#61;&#61; _1200BuadRate) //波特率1200
period &#61; _1200BuadRate &#43; 50;
TIM2_Configuration(period); //设置对应模特率的定时器的定时时间
delayTime &#61; baudRate; //设置IO串口发送的速率
}

要问上面的那些数字是怎么得来的&#xff0c;实话说&#xff1a;我是试出来&#xff0c;但是是有根据地试出来的。我以波特率为1200为例&#xff1a;IO串口发送函数VirtualCOM_ByteSend()中&#xff0c;我们用Delay_us(delayTime)来控制传输的速率&#xff0c;如果波特率设为1200&#xff0c;则1/1200&#61;830us相当于没830us传输1bit数据&#xff0c;所以在delayTime理论上应该设为830才能保证以波特率1200的速率发送数据&#xff0c;但是由于发送是由代码实现&#xff0c;有一定的延时&#xff0c;而不像真正串口通过移位寄存器发送那样快速&#xff0c;所以需要将delayTime的值在830附近调整&#xff0c;最后我试出来delayTime&#61;800时&#xff0c;正好实现了波特率为1200的速率发送。
同样的&#xff0c;在IO串口接收时&#xff0c;需要设定定时周期&#xff0c;这个周期也是试出来的&#xff0c;但是也是有依据的。还是以1200波特率接收为例&#xff1a;理论上应该设置定时时间为1/1200&#61;830us&#xff0c;则需要的定时值为72000000/(1/830us)&#61;72*830&#xff0c;这里设置定时器的预分频为72&#xff0c;则周期值应该为830&#xff0c;所以上面代码中period的理论上应该等于830&#xff0c;但是接收是由代码写成的&#xff0c;有一定的延时&#xff0c;而不像真正串口一样全部有硬件完成那样快速&#xff0c;所以需要将period的值在830附近调整&#xff0c;最后试出来period&#61;850时&#xff0c;可以正常接收串口发送过来的数据。
6、其他函数的编写
首先需要编写的BSP_Init()函数&#xff0c;来初始化板子的其他一些外设的的初始化&#xff1a;

void BSP_Init(void)

{

static volatile ErrorStatus HSEStartUpStatus &#61; SUCCESS;

RCC_DeInit(); //默认配置SYSCLK, HCLK, PCLK2, PCLK1, 复位后就是该配置

RCC_HSEConfig(RCC_HSE_ON); //使能外部高速晶振
HSEStartUpStatus &#61; RCC_WaitForHSEStartUp();//等待外部高速稳定

if(HSEStartUpStatus &#61;&#61; SUCCESS)
{
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);//使能flash预读取缓冲区
FLASH_SetLatency(FLASH_Latency_2); //令Flash处于等待状态&#xff0c;2是针对高频时钟的
RCC_HCLKConfig(RCC_SYSCLK_Div1); //HCLK &#61; SYSCLK 设置高速总线时钟&#61;系统时钟
RCC_PCLK2Config(RCC_HCLK_Div1); //PCLK2 &#61; HCLK 设置低速总线2时钟&#61;高速总线时钟
RCC_PCLK1Config(RCC_HCLK_Div2); //PCLK1 &#61; HCLK/2 设置低速总线1的时钟&#61;高速时钟的二分频
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //PLLCLK &#61; 8MHz * 9 &#61; 72 MHz 利用锁相环讲外部8Mhz晶振9倍频到72Mhz
RCC_PLLCmd(ENABLE); //使能PLL锁相环
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) &#61;&#61; RESET){} //等待锁相环输出稳定
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);//将锁相环输出设置为系统时钟
while(RCC_GetSYSCLKSource() !&#61; 0x08){} //等待校验成功
} //使能GPIO口所使用的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOE|RCC_APB2Periph_GPIOF|RCC_APB2Periph_GPIOG, ENABLE);
最后是main函数&#xff0c;main函数很简单&#xff0c;只要调用配置IO串口的配置函数就可以了&#xff1a;

extern u8 recvData;

int main(void)

{

BSP_Init();

VirtualCOM_Config(_600BuadRate); //配置IO模拟串口的波特率为600

VirtualCOM_StringSend("HelloWorld!\r\n"); //发送“HelloWorld!”字符串

while(1)

{

VirtualCOM_ByteSend(recvData);

Delay(5000000);

}

}

7、现象

首先。我们需要用TTL转USB的串口线&#xff0c;连接到电脑&#xff0c;打开串口调试工具&#xff0c;设置波特率为600&#xff0c;1位停止位&#xff0c;然后就可以收到IO模拟串口发过来的"HelloWorld"&#xff0c;然后&#xff0c;我们发送一个字符&#39;a&#39;过去&#xff0c;然后就会每间隔一段时间打印出该字符。如下图所示&#xff1a;



推荐阅读
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • Java SE从入门到放弃(三)的逻辑运算符详解
    本文详细介绍了Java SE中的逻辑运算符,包括逻辑运算符的操作和运算结果,以及与运算符的不同之处。通过代码演示,展示了逻辑运算符的使用方法和注意事项。文章以Java SE从入门到放弃(三)为背景,对逻辑运算符进行了深入的解析。 ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • 设计模式——模板方法模式的应用和优缺点
    本文介绍了设计模式中的模板方法模式,包括其定义、应用、优点、缺点和使用场景。模板方法模式是一种基于继承的代码复用技术,通过将复杂流程的实现步骤封装在基本方法中,并在抽象父类中定义模板方法的执行次序,子类可以覆盖某些步骤,实现相同的算法框架的不同功能。该模式在软件开发中具有广泛的应用价值。 ... [详细]
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 李逍遥寻找仙药的迷阵之旅
    本文讲述了少年李逍遥为了救治婶婶的病情,前往仙灵岛寻找仙药的故事。他需要穿越一个由M×N个方格组成的迷阵,有些方格内有怪物,有些方格是安全的。李逍遥需要避开有怪物的方格,并经过最少的方格,找到仙药。在寻找的过程中,他还会遇到神秘人物。本文提供了一个迷阵样例及李逍遥找到仙药的路线。 ... [详细]
  • ***byte(字节)根据长度转成kb(千字节)和mb(兆字节)**parambytes*return*publicstaticStringbytes2kb(longbytes){ ... [详细]
  • 本文详细介绍了使用C#实现Word模版打印的方案。包括添加COM引用、新建Word操作类、开启Word进程、加载模版文件等步骤。通过该方案可以实现C#对Word文档的打印功能。 ... [详细]
  • 本文介绍了在go语言中利用(*interface{})(nil)传递参数类型的原理及应用。通过分析Martini框架中的injector类型的声明,解释了values映射表的作用以及parent Injector的含义。同时,讨论了该技术在实际开发中的应用场景。 ... [详细]
  • 使用C++编写程序实现增加或删除桌面的右键列表项
    本文介绍了使用C++编写程序实现增加或删除桌面的右键列表项的方法。首先通过操作注册表来实现增加或删除右键列表项的目的,然后使用管理注册表的函数来编写程序。文章详细介绍了使用的五种函数:RegCreateKey、RegSetValueEx、RegOpenKeyEx、RegDeleteKey和RegCloseKey,并给出了增加一项的函数写法。通过本文的方法,可以方便地自定义桌面的右键列表项。 ... [详细]
author-avatar
捕鱼达人2602929461
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有