博文

目前显示的是标签为“C++”的博文

C编程使用内联汇编控制PC蜂鸣器发声

有了“内联汇编控制PC蜂鸣器”的基础了解后,我们使用内联汇编来改造“C编程控制PC蜂鸣器”一文的例子,最终的代码如下: #include <stdio.h> #include <unistd.h> #include <sys/io.h> /* The clock frequency of the i8253/i8254 PIT */ #define PIT_TICK_RATE 1193182ul void beep(unsigned int value) { unsigned int count = 0; unsigned char val; unsigned short port; if (value > 20 && value < 32767) count = PIT_TICK_RATE / value; iopl(3); if (count) { val = 0xB6; port = 0x43; asm volatile("outb %0,%1" : : "a" (val), "dN" (port)); val = count & 0xff; port = 0x42; asm volatile("outb %0,%1" : : "a" (val), "dN" (port)); val = (count >> 8) & 0xff; asm volatile("outb %0,%1" : : "a" (val), "dN" (port)); port = 0x61; ...

C编程演奏中文版“生日快乐歌”

有了《C编程控制PC蜂鸣器》一文的基础后,我们在其基础上修改函数入口参数,由原来的1个参数(频率)改为现在的2个(频率、延时<即该频率响多长时间>),然后就按照节奏实现中文版的“生日快乐歌”,其源码如下: #include <unistd.h> #include <sys/io.h> /* The clock frequency of the i8253/i8254 PIT */ #define PIT_TICK_RATE 1193182ul void beep(unsigned int freq, unsigned int delay) { unsigned int count = PIT_TICK_RATE / freq; iopl(3); outb_p(0xB6, 0x43); outb_p(count & 0xFF, 0x42); outb((count >> 8) & 0xFF, 0x42); outb_p(inb_p(0x61) | 3, 0x61); usleep(1000*delay); outb_p(inb_p(0x61) & 0xfc, 0x61); iopl(0); } void play(unsigned int* freq, unsigned int* time) { int i; for(i=0;freq[i]!=0;i++) { beep(freq[i], time[i]); } } int main(int argc,char * argv[]) { /*Happy Birthday chinese version*/ unsigned frequency[]= { 392,392,440,392,523,494, 392,392,440,392,587,523, 392...

C编程控制PC蜂鸣器方法2

在《C编程控制PC蜂鸣器》一文中,我们了解并使用了通过IO端口控制的方式操作硬件,而有些时候这对于一些朋友来说太模糊了,很容易让人迷糊,这次采用最基本的write系统调用来写入input_event数据实现相同功能。 这里涉及到的input_event可参考《C编程实现键盘LED闪烁方法2》一文,还有接下来要先加载pcspkr驱动,这个问题可查阅《Shell命令控制蜂鸣器发声》一文了解。 首先,执行sudo modprobe pcspkr命令加载驱动,然后查看/proc/bus/input/devices文件,我这边会有如下内容: I: Bus=0010 Vendor=001f Product=0001 Version=0100 N: Name=”PC Speaker” P: Phys=isa0061/input0 S: Sysfs=/devices/platform/pcspkr/input/input17 U: Uniq= H: Handlers=kbd event15 B: PROP=0 B: EV=40001 B: SND=6 这就是加载Linux内核drivers/input/misc/pcspkr.c文件对应的pcspkr驱动后所生成的。 而我们对应的input_event设置中,其type设置为EV_SND,而code可设置为SND_TONE和SND_BELL,其中前者只接受value为20至32767之间的值,其他值均为无声,后者接受0时无声,其他值时均会转为1000。 接下来上代码吧,你可以从代码中看到我们的处理: #include <stdio.h> #include <linux/input.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #define PCSPKR_EVENT_PATH "/dev/input/event15" int main(int argc,char * argv[]) { unsigned int val; int fd = 0; struct input_event ev; e...

C编程实现键盘LED灯闪烁方法2

在《C编程实现键盘LED灯闪烁》一文中使用了定时器和ioctl的方式实现键盘LED灯周期性闪烁,而键盘本身作为一个输入设备,那么在Linux下也有对应的input_event,故而本次使用它来实现一次,本次很简陋,采用死循环的方式,省去了定时器的麻烦。 关于input_event,需要引用到Linux内核源码的include/linux/input.h头文件,而在应用程序中,我们采用很简单的write系统调用来设置,而写入的数据格式如下: struct input_event { struct timeval time; __u16 type; __u16 code; __s32 value; }; 对于我们来说,本次需要设置的type是EV_LED,而code则分别是LED_NUML、LED_CAPSL、LED_SCROLLL,value为1时亮,为0时灭。 好了,对这初步了解后,那我们write的对象是谁呢?我们通过/proc/bus/input/devices可以找到我们的键盘对应的event,我这边找到的信息如下: I: Bus=0003 Vendor=1241 Product=1603 Version=0110 N: Name=" USB Keyboard" P: Phys=usb-0000:00:1d.7-3.1/input0 S: Sysfs=/devices/pci0000:00/0000:00:1d.7/usb2/2-3/2-3.1/2-3.1:1.0/input/input4 U: Uniq= H: Handlers=sysrq kbd event3 B: PROP=0 B: EV=120013 B: KEY=1000000000007 ff800000000007ff febeffdff3cfffff fffffffffffffffe B: MSC=10 B: LED=7 从上面看到对应是event3,那么等会操作/dev/input/event3文件即可。 至此,基本的内容说明了一下,下面还是上代码吧: #include <stdio.h> #include <linux/input.h> #include <...

C编程控制PC蜂鸣器

在《I/O映射之I/O端口》一文中,我们实现了在Linux内核驱动里实现IO端口操作8254定时计数器芯片里的寄存器来实现PC主机蜂鸣器的控制,那么在C语言里是不是一样也能用io控制函数来操作呢?当然是可以的,只不过作为应用程序,其需要使用iopl或ioperm函数先获得IO操作的权限,其函数原型如下: 1.iopl - change I/O privilege level #include <sys/io.h> int iopl(int level); 2.ioperm - set port input/output permissions #include <unistd.h> /* for libc5 */ #include <sys/io.h> /* for glibc */ int ioperm(unsigned long from, unsigned long num, int turn_on); 其中,iopl修改程序运行时操作io的权限级别,默认是0,最大是3;而ioperm是设置从from开始的num个io端口turn_on状态。(详细看man) 有了《I/O映射之I/O端口》一文和上面的内容学习,接下来用C来实现就很容易了,其中关于inb等函数头可使用man inb命令查阅,下面上代码吧: #include <stdio.h> #include <unistd.h> #include <sys/io.h> /* The clock frequency of the i8253/i8254 PIT */ #define PIT_TICK_RATE 1193182ul void beep(unsigned int val) { unsigned int count = 0; if (val > 20 && val < 32767) count = PIT_TICK_RATE / val; iopl(3); if (count) { outb_p(0xB6, 0x43); ...

C编程实现键盘LED灯闪烁

在《Shell脚本实现键盘LED灯闪烁》一文中,我们已感受到了控制的乐趣,一步步向硬件逼近,这次我们在Linux下使用C语言进行系统调用来实现该功能。这里面会涉及到应用层定时器和ioctl系统调用来控制键盘LED灯状态。 关于应用层定时器需要涉及到信号机制,其包含有alarm闹钟和timer定时器两种,其与信号机制分别说明如下(这两种均是自动循环的,即不需要处理函数里再设置一遍定时器超时设置): 1.alarm 运行man alarm命令后有关于该函数的使用说明,其函数原型如下: #include <unistd.h> unsigned int alarm(unsigned int seconds); 当设定的时间到了后会发出SIGALRM信号,需要对应的信号处理函数配套处理。 2.timer 运行man setitimer命令后有相关说明,本次会用到的函数原型如下: #include <sys/timer.h> int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value); 其中which是设置的定时器类型,分别对应如下: ITIMER_REAL:实时定时器,发SIGALRM信号; ITIMER_VIRTUAL:应用进程执行时间定时器,发SIGVTALRM信号; ITIMER_PROF:应用进程执行和内核交互时间定时器,发SIGPROF信号。 从上面可以看出,同一时间只能有3个定时器存在,分别对应3种类型。那么上面的信号要如何处理呢?执行man signal命令有相关的函数原型如下: #include <signal.h> typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); 这signal函数除了与定时器配套外,还可以捕获其他信号,执行man 7 signal命令可以查看到。 好了,关于定时器部分就说到这。 而关于键盘LED灯控制,我们需要用到TTY设备的ioctl系统调用函数,该函数是标准的字符设备接口(可以不实现),其接口原型(执...

Linux下C编程使用动态链接库

为了方便程序功能的后期升级扩展,在程序设计时经常会用到动态库,这样子程序只有到运行阶段才会去加载动态库并且使用库中的函数,那么我们往往只需要更新 DLL ( Windows 系统)或 SO ( Linux 系统)文件即可达到,同时打包成库也有利于保密和核心技术的积累,话不多说,接下来看看 Linux 下的动态链接库相关内容: 1. 头文件及编译选项 在 Linux 下对 so 动态链接库进行操作时,需 #include <dlfcn.h> ,将 dlfcn.h 头文件包含进来,并且在使用 gcc 编译时要使用 -ldl 选项。 2. 常用函数 a.void *dlopen(const char *filename, int flag);  其中,函数返回动态库的操作 Handle (句柄), filename 为库名,而 flag 常用的值有如下两个(必须使用其中一个,其他标志可使用 man dlopen 查看): RTLD_LAZY :在打开 SO 时不对共享库的函数进行加载操作,等到 dlsym 调用时才加载指定函数; RTLD_NOW :在打开 SO 时将共享库的所有函数加载至内存。 b.void *dlsym(void *handle, const char *symbol);  从 dlopen 返回的 handle 里找名为 symbol 的函数指针并将其返回,注意这里返回的是 void * ,须做类型转换。 c.int dlclose(void *handle);  关闭 dlopen 打开的 Handle 。 d.char *dlerror(void);  当上述动态链接库操作函数被调用后,再调用本函数时,返回 NULL 表示没有错误发生,如果有错误发现,其将返回一个错误字符串,即出错信息。 3. 如何查找库   这里忽略 ELF 文件里的 DT_RPATH 和 DT_RUNPATH 标志时的情况,下面是标准的查找库顺序: 首先查找 LD_LIBRARY_PATH 环境变量对应的目录,找不到时再查找 /etc/ld.so.cache 文件(由 ldconfig 命令维护刷新),再找不到时则查找 /lib 和 /usr/lib 目录。 4. 如何制作 SO   在 gcc 生成 ...

Linux编程获取指定网口MAC地址

在Linux下,我们可以使用C语言编程获取指定网口的MAC地址,下面是相应的源码(可将eth0改为相应的网络结点): #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <linux/if.h> #include <linux/if_ether.h> #include <sys/ioctl.h> #include <netinet/in.h> #include <arpa/inet.h> void main() {       unsigned char *ptr;       struct ifreq ifr;       int sock;       strcpy(ifr.ifr_name, "eth0");       strcpy(ifr.ifr_hwaddr.sa_data, "");       sock = socket(AF_INET, SOCK_STREAM, 0);       ioctl(sock, SIOCGIFHWADDR, &ifr);       ptr = (unsigned char *) ifr.ifr_hwaddr.sa_data;       printf("x:x:x:x:x:x\n",                   (ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377),  ...