Jim's Depository

this code is not yet written

There is an internal temperature sensor hiding in the RP2040 processor. It is just ever so slightly tricky to read. I notice that a lot of the help on the web skips an important step where you enable the temperature sensor, and glosses over the ADC reference voltage.

It isn't a high precision sensor, about ½ degree Celsius is as good as you are going to read, but it will let you know if your enclosure is cooking your project.

Enable the temperature sensor

In addition to adc_init() you will need to adc_set_temp_sensor_enabled(true). I kind of think there is a period of time before you would not trust the reading. It seems like it creeps up for a second or two. So if you are using this, just turn it on and leave it on.

Understand the ADC reference voltage

The ADC system is all relative to a reference voltage. If you haven't connected anything to the ADC_VREF pin of a Pi Pico, then it is nominally 3.3v, and that constant is present in everyone's sample code. It is worth noting that it is probably lower than that, so if you want to calibrate, check the pin with a multimeter.

If you are interested in accuracy with other analog inputs, you will probably put a 'real' voltage reference on ADC_VREF, then you need to change the constants in the code to match your chosen hardware.

My Example

In any event, my example looks like this.

** NOTE THEE WELL:                                                                                                                    
** You must do these before using this function...                                                                                    
**  adc_init();                                                                                                                       
**  adc_set_temp_sensor_enabled(true);                                                                                                
** You will get ridiculous temperature values if you don't enable                                                                     
** the temperature sensor. It also might take a little bit to converge                                                                
** on something close to right, so don't be tricky and snap it on and                                                                 
** off to save power.                                                                                                                 


void print_info(void) {
    uint16_t bits = adc_read();
    float voltage = bits * REFERENCE_VOLTAGE / 4095;
    float temperature = 27.0 - ( voltage - 0.706) / 0.001721;

    printf("Internal temperature: %4.1fC\n", temperature);

I put this little Makefile in the top of my Pico project directory. It handles making the build directory and invoking cmake with the right flags to build my actual make files.

Mostly I just do make install to build and flash onto my device. (Presuming the device is in UF2 mode, that's a different post.)

# Path to my SDK installation
PICO_SDK_PATH ?= ~/pico-sdk

# Path to the mount point for my pico
PICO_MOUNT ?= /rpi-rp2

# Path to my UF2 image in the build directory
IMAGE = build/sbi-weather.uf2

all : $(IMAGE)

install : all
	mount $(PICO_MOUNT)
	cp $(IMAGE) $(PICO_MOUNT)/foo

clean :
	rm -rf build pico_sdk_import.cmake

build :
	mkdir build

$(IMAGE): build/Makefile
	( cd build ; make all )

build/Makefile : CMakeLists.txt pico_sdk_import.cmake | build
	( cd build ; cmake -DPICO_SDK_PATH=$(PICO_SDK_PATH) .. )

pico_sdk_import.cmake : $(PICO_SDK_PATH)/external/pico_sdk_import.cmake
	cp $< $@

.PHONY : all install clean $(IMAGE)


I'm using a Raspberry Pi running Raspbian as my host. In the install target the sync command is helpful to push the dirty blocks out right now.

The mount command also works fine for my user account because I have this in my /etc/fstab

LABEL=RPI-RP2 /rpi-rp2 msdos defaults,noauto,user 0 1


Find the reset_usb_boot() function in pico/bootrom.h instead.

But for historical reasons, and maybe you want to render it unbootable…

I want to be able to erase and reprogram my Pi Pico without having to touch it and only having a USB cable to it.

I built my program to use USB for the console. If the console receives a "reset" command then it erases the secondary boot loader from flash and forces a restart. This will make it boot into the UF2 loader.

The function looks like this…

#include "wipe.h"
#include "hardware/watchdog.h"
#include "hardware/flash.h"
#include "hardware/sync.h"
#include "pico/multicore.h"

// Remember: Put hardware_flash and pico_multicore in the CMakeLists.txt target_link_libraries

void wipe(void) {
    const uint8_t blank[256] = {0};

    // Stop core1. We better be core0.                                                                                                

    uint32_t saved = save_and_disable_interrupts();
    // Wipe the secondary boot loader                                                                                                 
    flash_range_program( 0, blank, 256);

    // this will reset us in 1 millisecond.                                                                                           
    watchdog_enable( 1, false);

    // await our doom                                                                                                                 
    while( true);

No more physical access required while developing. Until I do something stupid to hang the CPU.

Subtle Note:

I am using flash_range_program instead of flash_range_erase because I want to do only 256 bytes. Otherwise I clobber the code I'm executing and everything ends in tears. In the world of this flash chip, "erase" means "set to all ones" and "program" means "set the zeros to zero". So I can turn the 256 bytes into all zeros without erasing.


If you decide you want to erase the whole flash, there is going to be trouble. You will end up erasing the code you are executing and hang. I would suggest you move the watchdog_enable call above your flash_range_erase (which you will use instead of program so you don't need a buffer) and give it a timeout long enough to accomplish the erase.

Ostensibly you can mark your function as being copied to RAM, but I didn't have success with that. If I used the CMakeLists.txt to make the whole program be in RAM I could erase flash willy nilly without problems.

I'm going to try the Raspberry Pi Pico for some of my small projects. I'm primarily attracted by the deterministic timing available with the Programmable I/O state machines.

I'll mostly be building devices which present as USB peripherals to a host. As such I probably won't bother with the UART for debugging messages during development. I'd also like to avoid the Serial Wire Debug (SWD) so I don't have to wire that up either.

Developer Ergonomics

That presents me with a little development conundrum. I don't like doing the unplug/press/plug/release dance, and the human quadrature dance with a reset button added isn't much better.

My current plan is to build a "destroy yourself" function into my USB interface. That will erase the secondary boot loader block in flash then do a reset from software to force a USB Mass Storage mode boot.

Mounting on Debian

I'm using a Raspberry Pi 4 as a development machine, we'll see. It takes it about 3 seconds to build my 15kB test program. That's really pretty sad. It is mostly cmake generated abominations faffing about. Hopefully it doesn't get much longer as I add real code.

I want a target in my Makefile to kill a running device, flash new, and restart it. That means mounting the mass storage device when it becomes available. For now I've got a line in /etc/fstab to make that easy...

LABEL=RPI-RP2 /rpi-rp2 msdos defaults,noauto,user 0 1

With that my untrusted user account can mount /rpi-rp2 and do what it needs.