Ubuntu上STM32开发之printf函数重映射

在之前的博客中,我在尝试使用标准输出函数printf的时候,发现没有显示。

以为自己什么时候出问题了,甚至以为自己没有成功编译下载程序。

但是,我在Windows系统下面,编译了包含printf函数的hex文件,然后,在Ubuntu环境下下载,查看串口。发现没有问题。

我才发现,是因为自己没有重定义printf的底层函数的问题。

首先发代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include "stm32f10x_conf.h"

void uart_init(u32 bound){
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);

//USART1_TX GPIOA.9

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);

//USART1_RX GPIOA.10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//Usart1 NVIC
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

USART_InitStructure.USART_BaudRate = bound;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_Cmd(USART1, ENABLE);
}

int _write (int fd, char *pBuffer, int size)
{
int i=0;
for (i = 0; i < size; i++)
{
while (!(USART1->SR & USART_SR_TXE))
{
}
USART_SendData(USART1, pBuffer[i]);
}
return size;
}

int main(void)
{
uart_init(9600);
int i=0;

while(1)
{
for(i=0;i<1000000;i++);
printf("hello!\r\n");

}
}

这里,我写了一个_write函数,因为在gcc_arm_none_eabi编译器中,printf函数是通过_write函数来实现的,我们需要将printf的输出流重映射到串口上,就需要修改这些底层的实现。

还有,在Windows环境下Keil软件中的arm编译器则采取不同的底层实现方式,所以,在不同编译器的情况下,需要重新修改函数。补充一下,在Keil的C库中,printf和scanf是通过fputc和fgetc来操作的。

还有,听说在用GNU的printf需要在最后添加\n或者使用fflush(stdout)函数。来刷新输出流。否则printf不会输出任何数据,而且会被后来的正确发送的printf数据覆盖。这是由于printf的数据流在扫描到 \n以前会被保存在缓存中,直到 \n出现或是fflush(stdout)强制刷新才会输出数据,如果我们在printf数据的末尾不加入\n或fflush(stdout),这个printf数据就不会被发送出去,而且在新的printf语句也会重写printf的缓存内容,使得新的printf语句不会附带之前的内容一起输出,从而造成上一条错误的printf内容不显示且丢失。

Ubuntu上STM32开发之工程管理

通过makefile来实现工程的管理。
首先工程的结构是:
CORE:cm的核心文件
HARDWARE:硬件方面的.c和.h文件
STM32F10x_FWLib:STM32F10X的固件库
SYSTEM:常用的功能的.c和.h文件
USER:用户写的main.c等
makefile文件
ld文件

CORE和STM32F10x_FWLib都是来自ST官网的固件库里面。
说到ST官网,大概说一下固件库在STM32系列->Tools&Software->Embedded Software->MCU&MPU Embedded Software->Standard Peripheral Libraries

建立自己的工程文件夹,在里面建立CORE,STM32F10x_FWLib,HARDWARE,SYSTEM,USER文件夹。
首先,将固件库的inc和src文件复制到自己工程的STM32F10x_FWLib文件夹中。
再选择文件到CORE文件夹中,按照下面我的工程进行挑选,在选择的过程中,有多个启动文件,选择TrueSTUDIO文件夹下的启动文件,然后,不同启动文件对应不同的内存,我就选startup_stm32f10x_md.s作为我的启动文件,我的芯片是STM32F103RBT6。然后记得要挑选链接文件.ld,

记住不要忘记stm32f10x_conf.h文件。

然后,如果在编译的时候遇到下面的问题

1
2
3
4
5
/tmp/ccSTjuNC.s: Assembler messages:
/tmp/ccSTjuNC.s:890: Error: registers may not be the same -- `strexb r3,r2,[r3]'
/tmp/ccSTjuNC.s:943: Error: registers may not be the same -- `strexh r3,r2,[r3]'
makefile:32: recipe for target 'CORE/core_cm3.o' failed

这是因为GCC更新之后,对于源代码的错误的容忍性降低了,很多旧版本可以忽略的代码错误,在新版本就会被拒绝。
这些问题的最常见原因是在文件core_cm3.c的内联汇编过程中(通常未链接)对strexh和strexb的错误使用,该文件是ARM CMSIS库的一部分,包含在ST外设库中。
您有两种方法可以解决此问题。两种方法均有效,您可以根据自己的情况选择一种:

1.更新所有库(CMSIS,外围设备库等)。这是一种更简洁的方法,并且从长远来看是最好的,但是如果您使用的是旧版本的库,则短期内可能需要大量工作才能适应调用库的代码。(万恶的ST更新库了吗?我不知道,因为我写的时候还没有更新这个问题)

2.编辑core_cm3.c并注释掉函数__STREXH和__STREXB的定义。这些存在问题的函数很少使用,您的应用程序很可能不需要它们。如果确实使用它们,则应更新库(上面的选项1)。

上面来源:http://support.raisonance.com/node/431888

但是,其实我们可以通过修改core_cm3的汇编部分代码来避免这个错误:

在core_cm3.h文件中,我们需要修改__STREXB和 __STREXH

1
2
3
4
5
//原本是这样的
__ASM volatile ("strexh %0, %2, [%1]" : "=r" (result) : "r" (addr), "r"(value) );
//现在是这样的
__ASM volatile ("strexh %0, %2, [%1]" : "=&r" (result) : "r" (addr), "r"(value) );
//就是将=r 修改为了 =&r

这样问题就解决了
下面是我的工程模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
.
├── CORE
│   ├── core_cm3.c
│   ├── core_cm3.h
│   ├── startup_stm32f10x_md.s
│   ├── stm32f10x.h
│   ├── system_stm32f10x.c
│   └── system_stm32f10x.h
├── HARDWARE
│   ├── led.c
│   └── led.h
├── makefile
├── STM32F10x_FWLib
│   ├── inc
│   │   ├── misc.h
│   │   ├── stm32f10x_adc.h
│   │   ├── stm32f10x_bkp.h
│   │   ├── stm32f10x_can.h
│   │   ├── stm32f10x_cec.h
│   │   ├── stm32f10x_crc.h
│   │   ├── stm32f10x_dac.h
│   │   ├── stm32f10x_dbgmcu.h
│   │   ├── stm32f10x_dma.h
│   │   ├── stm32f10x_exti.h
│   │   ├── stm32f10x_flash.h
│   │   ├── stm32f10x_fsmc.h
│   │   ├── stm32f10x_gpio.h
│   │   ├── stm32f10x_i2c.h
│   │   ├── stm32f10x_iwdg.h
│   │   ├── stm32f10x_pwr.h
│   │   ├── stm32f10x_rcc.h
│   │   ├── stm32f10x_rtc.h
│   │   ├── stm32f10x_sdio.h
│   │   ├── stm32f10x_spi.h
│   │   ├── stm32f10x_tim.h
│   │   ├── stm32f10x_usart.h
│   │   └── stm32f10x_wwdg.h
│   └── src
│   ├── misc.c
│   ├── stm32f10x_adc.c
│   ├── stm32f10x_bkp.c
│   ├── stm32f10x_can.c
│   ├── stm32f10x_cec.c
│   ├── stm32f10x_crc.c
│   ├── stm32f10x_dac.c
│   ├── stm32f10x_dbgmcu.c
│   ├── stm32f10x_dma.c
│   ├── stm32f10x_exti.c
│   ├── stm32f10x_flash.c
│   ├── stm32f10x_fsmc.c
│   ├── stm32f10x_gpio.c
│   ├── stm32f10x_i2c.c
│   ├── stm32f10x_iwdg.c
│   ├── stm32f10x_pwr.c
│   ├── stm32f10x_rcc.c
│   ├── stm32f10x_rtc.c
│   ├── stm32f10x_sdio.c
│   ├── stm32f10x_spi.c
│   ├── stm32f10x_tim.c
│   ├── stm32f10x_usart.c
│   └── stm32f10x_wwdg.c
├── stm32_flash.ld
├── SYSTEM
│   ├── delay.c
│   ├── delay.h
│   ├── sys.c
│   └── sys.h
└── USER
├── main.c
└── stm32f10x_conf.h

7 directories, 62 files

实际工程中的makefile文件是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
TARGET = test_project

PREFIX = arm-none-eabi-
CC = $(PREFIX)gcc
AS = $(PREFIX)as
LD = $(PREFIX)ld
OBJCOPY = $(PREFIX)objcopy

#读取当前工作目录
TOP=$(shell pwd)

#include头文件
INC_FLAGS= -I $(TOP)/CORE \
-I $(TOP)/HARDWARE \
-I $(TOP)/STM32F10x_FWLib/inc \
-I $(TOP)/SYSTEM \
-I $(TOP)/USER

#GCC选项
# -W -Wall :开启警报
# -g :产生调试信息,用于产生.elf调试文件
# mcpu=cortex-m3 :用于设定芯片内核参数
# -mthumb :表明使用的指令集
# -D STM32F10X_MD -D USE_STDPERIPH_DRIVER :宏定义
# -O0 :优化等级有O0,O1,O2等
# -std=gnu11 :设定语言标准GNU11,当然可以换为C98等
CFLAGS = -W -Wall -g -mcpu=cortex-m3 -mthumb -D STM32F10X_MD -D USE_STDPERIPH_DRIVER $(INC_FLAGS) -O0 -std=gnu11
ASFLAGS = -W -Wall -g -mcpu=cortex-m3 -mthumb

#搜索并返回当前路径下的所有.c文件的集合
#将C_SRC包含的文件的.c后缀代替为.o文件
C_SRC=$(shell find ./ -name '*.c')
C_OBJ=$(C_SRC:%.c=%.o)

ASM_SRC=$(shell find ./ -name '*.s')
ASM_OBJ=$(ASM_SRC:%.s=%.o)

#.PHONY关键字,代表后面的执行对象,不是文件
.PHONY: all clean update

#all的依赖对象是$(C_OBJ)和$(ASM_OBJ),一个变量是以.c或.s为后缀文件替换为.o为后缀>的文件的集合,如果.o文件修改则更新all。
all:$(C_OBJ) $(ASM_OBJ)
$(CC) $(C_OBJ) $(ASM_OBJ) -T stm32_flash.ld -o $(TARGET).elf -mthumb -mcpu=cortex-m3 -Wl,--start-group -lc -lm -Wl,--end-group -specs=nano.specs -specs=nosys.specs -static -Wl,-cref,-u,Reset_Handler -Wl,-Map=Project.map -Wl,--gc-sections -Wl,--defsym=malloc_getpagesize_P=0x80
#通过.elf文件,生成.bin和.hex文件
$(OBJCOPY) $(TARGET).elf $(TARGET).bin -Obinary
$(OBJCOPY) $(TARGET).elf $(TARGET).hex -Oihex

#.o文件依赖于.o文件
#其中 $@:目标文件 $^:所有依赖文件 $<:第一个依赖文件
$(C_OBJ):%.o:%.c
$(CC) -c $(CFLAGS) -o $@ $<

#$(ASM_OBJ):%.o:%.c
# $(AS) -c $(ASFLAGS) -o $@ $<
$(ASM_OBJ): $(ASM_SRC)
$(AS) -c $(ASFLAGS) -o $@ $<

clean:
rm -f $(shell find ./ -name '*.o')
rm -f $(shell find ./ -name '*.d')
rm -f $(shell find ./ -name '*.map')
rm -f $(shell find ./ -name '*.elf')
rm -f $(shell find ./ -name '*.bin')
rm -f $(shell find ./ -name '*.hex')

update:
openocd -f /usr/share/openocd/scripts/interface/stlink-v2.cfg -f /usr/share/openocd/scripts/target/stm32f1x_stlink.cfg -c init -c halt -c "flash write_image erase $(TOP)/LED_project.hex" -c reset -c shutdown

Ubuntu上STM32开发之单片机下载程序

我选用的是STM32F103芯片设计的最小系统板,由于自己在画板子的时候忘记了STlink接口,所以,就很尴尬,只能通过USART串口下载了。
然后,我选用stm32flash软件,进行烧录,然后,通过minicom来检测串口。

1.软件准备

stm32flash:用于串口下载源程序。
安装:

1
sudo apt install stm32flash

常用指令:

1
2
3
4
5
6
7
8
9
sudo stm32flash /dev/ttyUSB0 //获取设备信息

sudo stm32flash -w filename -v -g 0 /dev/ttyUSB0 //验证写入,然后写入源程序

sudo stm32flash -r filename /dev/ttyUSB0 //读闪存到文件,没用过。

sudo stm32flash -g 0 /dev/ttyUSB0 //开始执行,说实话,这一句我从来没用过。

输入时,BOOT0置高,BOOT1置低

备注,首先将BOOT0置高,BOOT1置低,复位后,输入指令获取设备信息,接着写入源程序。
接着BOOT0置低,BOOT1置低,复位后,就自动执行程序了。

minicom串口工具。
安装方式:

1
sudo apt install minicom

配置方式:

1
sudo minicom -s //注意在进行串口的操作时候,需要有sudo权限,或者修改串口的权限,修改的方式看下面

选择Serial port setup,然后回车,进行配置。
输入a,选择串口设备,也就是输入串口号/dev/ttyUSB0,如何查询串口号,也看下面。修改之后回车。
然后,输入e,修改波特率及配置为 115200 8N1,修改完成后回车。
然后,输入f,配置硬件控制流,选择No。同上,软件控制流也为No。

配置完成后,回车返回上一个界面,选择save setup as dfl将配置保存在默认位置。
然后exit,关闭minicom。

再次输入sudo minicom,就使得配置生效。

问题1:查询串口号:

1
2
3
dmesg //可以查看安装的驱动信息,里面有串口设备的串口号但是里面的驱动实在是太多了。

ls -l /dev/ttyUSB* //可以查询到串口号,

这样,就成功将单片机通过串口连接到电脑上了,这样就能够接受到串口的数据了。

但是,通过串口传输数据,发送到电脑上我没有做到,后来发现,是源程序的编译链接成hex程序的过程中存在问题,使用printf函数,好像有一些额外条件。之后另外补充。

Ubuntu上STM32开发之编译链接,生成目标文件

1.软件准备

gcc-arm-none-eabi是Ubuntu上针对ARM Cortex-M和Cortex-R处理器的GNU Arm嵌入式工具链。适用于C,C++和汇编的开源套件。

下载方式apt:

1
sudo apt install gcc-arm-none-eabi

makefile用于工程的管理的脚本语言,但是结构很特别。
下载方式同上。

工程模板:
我选用的是STM32F103芯片,STM32不同系列的芯片的存储区域与大小不同,会影响到下载的地址不同,不过可以套用这一套模板进行修改,大概吧。
工程模板是包含了硬件库和一些常用的头文件,以及链接时候的参数文件,以及makefile文件。
关于工程模板的详细分析看后面的博客。

2.进行对程序进行编译连接,生成目标文件(bin/hex文件)

1
make //在有makefile目录中

完成后会在工程文件下出现对应的文件。

补充:
上面仅仅是依靠现有的工程来进行编译,链接,最后形成目标文件的过程,其中,makefile中有编译链接的具体的过程,为了了解其中的过程,需要详细分析工具链的操作方式。
gcc-arm-none-eabi工具链的使用方法:
arm-none-eabi-gcc:
c语言编译器,可以将.c文件转化为.o文件。

1
arm-none-eabi-gcc -c hello.c

arm-none-eabi-g++:
c++语言编译器,可以将.cpp文件转化为.o执行文件。

arm-none-eabi-ld:
将所有.o文件进行链接,生成可执行文件,但是听说对c,cpp文件混合生成的.o文件的支持性不好。所以推荐使用arm-none-eabi-gcc代替。

1
arm-none-eabi-gcc -o hello hello.o

arm-none-eabi-gdb:
调试器,还没有用过。

知道了这些,就可以编写makefile文件了。

游戏设计

推箱子+剪刀石头布+魔塔

PS:
1.玩家自制游戏
2.格移动
推箱子:

推箱子+激光+魔塔

地图+多个操控任务+不同地形+可以多人

我的回忆

–2019.12.12 周四 01:49

在很久很久以前,

有一个小男孩,

他翻开了他的第一张语文试卷。

上面问道:太阳公公为什么哭了?

他好奇地写下来了:因为太阳公公很伤心。

写完之后,他很开心,

但是,这不是这个世界的正确答案。

他的世界和这个世界的摩擦在预料中那样发生了。



在很久很久之后,

小男孩对自己的世界有些彷徨。

因为,这个世界不是他的世界。

他的世界被掩埋,只有灰烬。

他的世界一直如此。

他只能寻找自己存在的过程,以及被磨灭的痕迹。

随机数生成

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
srand(1);
int i;
for (i = 0; i < 10; i++)
printf("%d, ", rand()%11);
}

结果一直不变,为:

1
8, 9, 9, 1, 7, 5, 5, 10, 1, 0,

那么比较:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
srand(time(NULL));
int i;
for (i = 0; i < 10; i++)
printf("%d, ", rand()%11);
}

结果改变,每次都不一样:

1
2
3
4

6, 3, 4, 5, 5, 9, 8, 10, 10, 4,
6, 4, 2, 4, 3, 2, 5, 1, 2, 9,

分析:

rand()函数是按指定的顺序来产生整数,因此每次执行上面的语句都打印相同的两个值,所以说C语言的随机并不是真正意义上的随机,有时候也叫伪随机数,使用 rand() 生成随机数之前需要用随机发生器的初始化函数 srand(unsigned seed)(也位于 stdlib.h 中) 进行伪随机数序列初始化,seed 又叫随机种子,通俗讲就是,如果每次提供的 seed 是一样的话,最后每一轮生成的几个随机值也都是一样的,因此叫伪随机数,所以需要每次提供不同的 seed 达到完全的随机,我们通常用时间函数 time(NULL) 作为 seed ,因为时间值每秒都不同。

注意 a%100的范围是0-99.

桌游之谁是牛头王

unity 桌游入门:
https://www.taikr.com/my/course/65

1 简介:

谁是牛头王(6 Nimmt!,又有牛头王、数字牌、Slide 5、Category 5和Take 6!等别称)是1994年由Wolfgang Kramer设计,德国Amigo Spiele公司发行的一款纸牌游戏。此游戏可以同时容许2-10名玩家进行。

设计者 Wolfgang Kramer
插图家 Franz Vohwinkel
发行商 Amigo Spiele
玩家数目 2-10
适用年龄 10+
准备时间 约5分钟
游戏时间 45分钟

2 设计:

2.1 卡牌(一共104张):

1牛头号码牌:76张
2牛头号码牌:(5,15,25,35,45,65,75,85,95)9张
数字是5的倍数但不是十的倍数
3牛头号码牌:(10,20,30,40,50,60,70,80,90,100)10张
数字是10的倍数
5牛头号码牌:(11,22,33,44,55,66,77,88,99)8张
数字十位和个位相同
7牛头号码牌:(55)1张
数字是5的倍数也是十位和个位相同

判断牛头的程序流程:
定义 牛头为0
如果数字的十位和个位相同:
牛头加5
如果数字为5的倍数:
牛头加2
如果数字为10的倍数:
牛头加1
如果牛头为0:
牛头为1

2.2 游戏规则:

多人对战,收集到最少牛头的人获胜。

每名玩家拿到十张牌,从牌库中选四张牌打开放在游戏桌上。
程序流程:
方法一:将牌随机,玩家按顺序发牌

方法二:玩家从牌中随机取排

每一回合,玩家在不知道其他玩家出牌的情况下,选择自己的出牌。

玩家选择完毕后,开牌。

每个人每回合开牌后将从最小号码开始将牌放入4行卡牌中。

以每一行最后一张卡牌为基准,玩家将选择的卡牌放入最接近选中的号码而且每一行卡牌按照升序排列。

如果玩家选择的卡牌为某一行的第六张卡牌,玩家需要将这一行前五张拿走。

如果玩家所出的卡牌比任何一行的最后一张都要少,玩家选择其中的一行,将所有的牌拿掉,将所出的卡牌作为该行新的起点。

当手上的牌用完后,玩家将之前拿回来的纸牌上的牛头数量加起来,最小的就是胜出者。

Mysql用户配置教程

问题1:忘记root密码之后,如何修改密码

1.在/etc/mysql/my.conf中添加

1
2
[mysqld]
skip-grant-tables

2.重启mysql

1
sudo service mysql restart

3.重新登录

1
sudo mysql

4.修改密码

1
2
select user, plugin from mysql.user
update mysql.user set authentication_string=PASSWORD('你的密码'), plugin='mysql_native_plugin' where user='root';

5.退出

php使用指南

PHP允许Web开发人员可以创建动态内容与数据库交互。PHP基本上用于开发基于Web的软件应用程序。
PHP并不是在浏览器上运行,PHP是在服务器的环境中运行,你在浏览器中访问服务器上的php路径,得到的是PHP在服务器上运行之后输出的结果。

想要在浏览器上访问本地的PHP文件,则:
首先需要在本地搭建一个php的运行环境,WAMP、phpstudy、xampp都可以,安装一个。然后把php文件扔进www文件夹中,浏览器输入访问路径:http://localhost/你的文件,就可运行PHP文件显示结果了。

但是,通常我们直接在服务器上配置。
1.安装apache,开启服务,见相关博客
2.安装php

1
sudo apt install php7.2

3.编写网页检测程序/var/www/html/index.php

1
<?php phpinfo( ); ?>

HELLO WORLD

1
2
3
4
5
6
7
8
9
10
11
<html>

<head>
<title>Hello World</title>
</head>

<body>
<?php echo "Hello, World!";?>
</body>

</html>

如果检查以上示例的HTML输出,您会注意到从服务器发送到Web浏览器的文件中没有PHP代码。网页中存在的所有PHP均已处理并从页面中删除;从Web服务器返回给客户端的唯一内容是纯HTML输出。
所有PHP代码都必须包含在PHP解析器可识别的三个特殊标记ATE之一中。

1
2
3
4
5
<?php PHP code goes here ?>

<? PHP code goes here ?>

<script language = "php"> PHP code goes here </script>

为了开发和运行PHP网页,需要在计算机系统上安装三个重要组件。

Web服务器
相关资料查看博客关于Apache。

数据库
相关资料查看博客关于MySQL数据库。

PHP解析器
-为了处理PHP脚本指令,必须安装解析器以生成可以发送到Web浏览器的HTML输出。本教程将指导您如何在计算机上安装PHP解析器。
在继续之前,重要的是要确保您的计算机上具有正确的环境设置,以便使用PHP开发Web.

注释PHP代码

1
2
3
4
5
#this is a comment
//this is the second coment
/*
this is a comment with multiline
*/

php对空格不敏感

php区分大小写

可以在命令提示符号中运行PHP脚本

变量:
所有变量前面有$
不必在分配之前分配变量
变量没有内部类型,事先不知道是数字或者字符串

有8种数据类型
1.整数
2.双精度
3.布尔值
4.NULL
5.字符串
6.数组
7.对象
8.资源 特殊变量,保存外部资源

布尔值true的确定:
1.数字精确的等于0为false
2.字符串为空或者字符串‘0’为false
3.NULL类型
4.数组不包含其他值,
5.不要将双精度作为布尔值

字符串:
‘’字符串按照字面来处理
“”用变量来替代变量
\转移字符
\n
\r
\t
$
"
\

$变量替换

变量范围
1.局部变量
2.功能参数
3.全局变量
4.静态变量

变量命名
1.字母或者下划线开头
2.数字字母或者下划线组成,

常量:
定义一个常数

1
define("CON",50);

常量和变量之间的区别:
1.不用在常量之前写$
2.不能够直接通过简单的赋值来定义,要通过define
3.在任意位置定义和访问常量,无需考虑作用域
4.一旦设置了无法修改

PHP为脚本提供了大量预订义常量

决策:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
if(condition)
...
else
...

if(condition)
...
elseif(condition)
...
else
...

switch(expression)
{
case label1:
...
break;
.
.
.

default:
...
}

循环:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
for(initialization;condition;increment)
{

}

while(condition)
{

}

do
{

}
while(condition);

//遍历数组
foreach(array as value)
{

}

break或者continue

网络概念:
HTML页面中任何表单元素都会自动被PHP脚本所用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
if( $_POST["name"] || $_POST["age"] ) {
if (preg_match("/[^A-Za-z'-]/",$_POST['name'] )) {
die ("invalid name and name should be alpha");
}

echo "Welcome ". $_POST['name']. "<br />";
echo "You are ". $_POST['age']. " years old.";

exit();
}
?>
<html>
<body>

<form action = "<?php $_PHP_SELF ?>" method = "POST">
Name: <input type = "text" name = "name" />
Age: <input type = "text" name = "age" />
<input type = "submit" />
</form>

</body>
</html>

有两种方式从浏览器客户端发送数据到网页服务器。
1.get方法
1.1get方法产生一个长字符串,
1.2仅发送最多1024个字符
1.3敏感信息不要通过get发送
1.4不能将二进制数据例如图像和文档发送到服务器
1.5发送的数据可以通过query_string环境变量访问
1.6提供了$_get关联数组,访问所有已经发送 的信息

2.post方法
2.1post方法对数据大小没有限制
2.2可以发送ASCII以及二进制数据
2.3POST数据通过HTTP标头传递
2.4提供$_POST关联数组,访问信息

$_REQUEST变量
包含$_GET,$_POST,$_COOKIE的内容

会话:
使数据能够跨整个网站的各个页面访问的方法。

会话会在服务器的临时目录中创建一个文件,存储了已经注册的会话变量和值。在访问期间,该数据可以用于在网站上的所有界面。

临时文件的位置由php.ini文件中的session.save_path确定。在使用会话变量前需要确保设置了路径。

开启会话

1
session_start();

关闭会话:

1
2
unset($_SESSION['VARIABLE']);//取消单个会话变量
session_destroy();//取消所有会话变量

开启自动会话:
在php.ini文件中将session.auto_start置一
当用户访问你的网站之后不必开启会话函数来开启会话。

不用cookie的会话:
当用户不允许将cookie保存在机器中,就有另一种方法价格你会话ID发送到浏览器中。

你可以,在会话开始时使用固定的SID,如果客户端不发送一个忽而是的的会话cookie,session_name=session_id,或者它增加一个空的字符串,因此,你可以将它嵌入URL中。

htmlspecialchars()可以防止XSS相关的攻击打印SID使用。

使用PHP发送电子邮件:
在php.ini文件中正确配置PHP。详细说明系统如何发送文件。打开/ETC/目录中的php.ini文件。

Linux用户需要PHP知道sendmail应用程序的位置,指定sendmail_path指令。

1.安装sendmail程序:

PHP从MySQL读取数据