In my Arduino projects sometimes I happen to need
to use 3.3V or 3.15V instead of 5V on digital I/O pins (we
will assume that the peripheral GND and Arduino's GND
are common).

When reading data, no hardware is needed. When a peripheral
outputs a logical "0", the 0V are detected as logical "0",
and anything above 2.5V is detected as logical "1". This
means that 3V, 3.15V, 3.3V, 3.7V... up to 5V are good inputs.

When writing data, outputting a logical "0" works; but
if I write a logical "1"... we'll see 5V going out!

While many 3.3V peripherals are guaranteed to be
"5V-tolerant", one cannot assume that putting more than 5%
voltage will be always safe; also, it is not a good practice
to use too much lower voltages than needed.

A couple of resistors can be used to have voltage drop to
an acceptable level. Given an input voltage c and two
a,b resistors mounted as seen below, we can get an output
level x depending on resistor values: x = c * b / (a+b)

For example, using a=1.8 kOhm and b=3.1 kOhm and c=5Vcc
as input, we will have output x = "somewhat" 3.16V (it
actually depends on resistors values, generally +-5%).

I got a bunch of spare 9.74k-9.82k resistors and a few 10.1k
(scavenged from an old 28.8k modem). Since two resistors in
series sum up their values, I just used b=9.82k+9.74k and
a=10.1k getting a gorgeous result:
5/(10.1+9.74+9.82)*(9.82+9.74) = 3.297V (yay!)