Add the ability to set clock frequencies on STM32F4 chips (like Pico) with E.setClock (fix #52)

This commit is contained in:
Gordon Williams 2016-03-11 10:57:52 +00:00
parent 37b447f960
commit a2fb33f01a
12 changed files with 139 additions and 72 deletions

View File

@ -8,6 +8,7 @@
Add .removeListener (fix #30)
Added better micro:bit `show()` that works well with Graphics
Add `require("Flash").getFree()` as multiplatform way to find free flash pages (fix #815)
Add the ability to set clock frequencies on STM32F4 chips (like Pico) with E.setClock (fix #52)
1v85 : Ensure HttpServerResponse.writeHead actually sends the header right away
- enables WebSocket Server support from JS

View File

@ -68,6 +68,7 @@ def htmlify(d):
d = re.sub(r'`([^`]+)`', r'<code>\1</code>', d) # code tags
d = re.sub(r'\[([^\]]*)\]\(([^\)]*)\)', r'<a href="\2">\1</a>', d) # links tags
d = re.sub(r'([^">])(http://[^ ]+)', r'\1<a href="\2">\2</a>', d) # links tags
d = re.sub(r'\n###([^\n]*)', r'<B>\1</B>', d) # Heading
lines = d.split("\n");
lines.append("");
@ -77,7 +78,7 @@ def htmlify(d):
if line[0:2]=="* " and starStart==False:
starStart=idx
if line[0:2]!="* ":
if starStart!=False and starStart+2<idx:
if starStart!=False and starStart+2<=idx:
l = lines[starStart:idx]
for i in range(0,len(l)):
l[i] = "<li>"+l[i][1:]+"</li>"

View File

@ -401,10 +401,11 @@ JsVarFloat jshReadVRef();
* default to `rand()` */
unsigned int jshGetRandomNumber();
/** Change the processor speed - the number is given in Hz.
* This returns the *actual* clock speed set - so if unimplemented
* just return what we actually have. */
unsigned int jshSetSystemClockFreq(unsigned int freq);
/** Change the processor clock info. What's in options is platform
* specific - you should update the docs for jswrap_espruino_setClock
* to match what gets implemented here. The return value is the clock
* speed in Hz though. */
unsigned int jshSetSystemClock(JsVar *options);
/** Hacky definition of wait cycles used for WAIT_UNTIL.
* TODO: make this depend on known system clock speed? */

View File

@ -733,26 +733,51 @@ JsVar *jswrap_espruino_memoryArea(int addr, int len) {
/*JSON{
"type" : "staticmethod",
"ifndef" : "SAVE_ON_FLASH",
"class" : "E",
"name" : "setFreq",
"generate" : "jswrap_espruino_setFreq",
"name" : "setClock",
"generate" : "jswrap_espruino_setClock",
"params" : [
["freq","int","The frequency in hz to set Espruino to run at"]
["options","JsVar","Platform-specific options for setting clock speed"]
],
"return" : ["int","The actual frequency set to"]
"return" : ["int","The actual frequency the clock has been set to"]
}
This sets the clock frequency of Espruino. It is only available on some boards.
This sets the clock frequency of Espruino's processor. It will return `0` if
it is unimplemented or the clock speed cannot be changed.
**Note:**
**Note:** On pretty much all boards, UART, SPI, I2C, PWM, etc will change
frequency and will need setting up again in order to work.
### STM32F4
Options is of the form `{ M: int, N: int, P: int, Q: int }` - see the 'Clocks'
section of the microcontroller's reference manual for what these mean.
* System clock = 8Mhz * N / ( M * P )
* USB clock (should be 48Mhz) = 8Mhz * N / ( M * Q )
Optional arguments are:
* `latency` - flash latency from 0..15
* `PCLK1` - Peripheral clock 1 divisor (default: 2)
* `PCLK2` - Peripheral clock 2 divisor (default: 4)
The Pico's default is `{M:8, N:336, P:4, Q:7, PCLK1:2, PCLK2:4}`, use
`{M:8, N:336, P:8, Q:7, PCLK:1, PCLK2:2}` to halve the system clock speed
while keeping the peripherals running at the same speed (omitting PCLK1/2
will lead to the peripherals changing speed too).
On STM32F4 boards (eg. Espruino Pico), the USB clock needs to be kept at 48Mhz
or USB will fail to work. You'll also experience USB instability if the processor
clock falls much below 48Mhz.
### ESP8266
Just specify an integer value, either 80 or 160 (for 80 or 160Mhz)
* On STM32F1 boards (eg Espruino Original, USB will stop working if you
change the clock speed).
* On pretty much all boards, UART, PWM, etc will change
speed and will need setting up again in order to work.
* STM32 will attempt to match
*/
int jswrap_espruino_setFreq(int freq) {
return jshSetSystemClockFreq(freq);
int jswrap_espruino_setClock(JsVar *options) {
return jshSetSystemClock(options);
}
/*JSON{

View File

@ -31,7 +31,7 @@ JsVar *jswrap_espruino_toArrayBuffer(JsVar *str);
JsVar *jswrap_espruino_toUint8Array(JsVar *args);
JsVar *jswrap_espruino_toString(JsVar *args);
JsVar *jswrap_espruino_memoryArea(int addr, int len);
int jswrap_espruino_setFreq(int freq);
int jswrap_espruino_setClock(JsVar *options);
int jswrap_espruino_reverseByte(int v);
void jswrap_espruino_dumpTimers();

View File

@ -1024,3 +1024,7 @@ unsigned int jshGetRandomNumber() {
/* EFM32 TODO This is not random */
return 1337;
}
unsigned int jshSetSystemClock(JsVar *options) {
return 0;
}

View File

@ -1258,6 +1258,16 @@ void jshFlashErasePage(
res == SPI_FLASH_RESULT_ERR ? "error" : "timeout");
}
unsigned int jshSetSystemClock(JsVar *options) {
int newFreq = jsvGetInteger(options);
if (newFreq != 80 && newFreq != 160) {
jsExceptionHere(JSET_ERROR, "Invalid frequency value, must be 80 or 160.");
return 0;
}
system_update_cpu_freq(newFreq);
return system_get_cpu_freq()*1000000;
}
/**
* Callback for end of runtime. This should never be called and has been

View File

@ -189,6 +189,8 @@ void jswrap_ESP8266_dumpSocketInfo(void) {
["freq", "JsVar", "Desired frequency - either 80 or 160."]
]
}
**Note:** This is deprecated. Use `E.setClock(80/160)`
**Note:**
Set the operating frequency of the ESP8266 processor. The default is 160Mhz.
**Warning**: changing the cpu frequency affects the timing of some I/O operations, notably of software SPI and I2C, so things may be a bit slower at 80Mhz.
@ -197,16 +199,7 @@ Set the operating frequency of the ESP8266 processor. The default is 160Mhz.
void jswrap_ESP8266_setCPUFreq(
JsVar *jsFreq //!< Operating frequency of the processor. Either 80 or 160.
) {
if (!jsvIsInt(jsFreq)) {
jsExceptionHere(JSET_ERROR, "Invalid frequency.");
return;
}
int newFreq = jsvGetInteger(jsFreq);
if (newFreq != 80 && newFreq != 160) {
jsExceptionHere(JSET_ERROR, "Invalid frequency value, must be 80 or 160.");
return;
}
system_update_cpu_freq(newFreq);
jshSetSystemClock(jsFreq);
}
//===== ESP8266.getState
@ -262,7 +255,7 @@ JsVar *jswrap_ESP8266_getState() {
"generate" : "jswrap_ESP8266_getFreeFlash",
"return" : ["JsVar", "Array of objects with `addr` and `length` properties describing the free flash areas available"]
}
**Note:** This is deprecated. Use `require("flash").getFreee()`
**Note:** This is deprecated. Use `require("flash").getFree()`
*/
JsVar *jswrap_ESP8266_getFreeFlash() {
return jshFlashGetFree();
@ -435,3 +428,5 @@ void jswrap_ESP8266_neopixelWrite(Pin pin, JsVar *jsArrayOfData) {
#endif
}

View File

@ -804,3 +804,7 @@ JsVar *jshFlashGetFree() {
void jshFlashErasePage(uint32_t addr) { }
void jshFlashRead(void *buf, uint32_t addr, uint32_t len) { memset(buf, 0, len); }
void jshFlashWrite(void *buf, uint32_t addr, uint32_t len) { }
unsigned int jshSetSystemClock(JsVar *options) {
return 0;
}

View File

@ -271,5 +271,9 @@ void jshFlashRead(void *buf, uint32_t addr, uint32_t len) {
void jshFlashWrite(void *buf, uint32_t addr, uint32_t len) {
}
unsigned int jshSetSystemClock(JsVar *options) {
return 0;
}
// ----------------------------------------------------------------------------
} // extern C

View File

@ -740,3 +740,7 @@ JsVarFloat jshReadVRef() {
unsigned int jshGetRandomNumber() {
return (unsigned int) nrf_utils_get_random_number();
}
unsigned int jshSetSystemClock(JsVar *options) {
return 0;
}

View File

@ -2855,61 +2855,79 @@ void jshFlashWrite(void *buf, uint32_t addr, uint32_t len) {
#endif
}
/** Change the processor speed - the number is given in Hz.
* This returns the *actual* clock speed set - so if unimplemented
* just return what we actually have. */
unsigned int jshSetSystemClockFreq(unsigned int freq) {
// see system_stm32f4xx.c
unsigned int m = 8; // divisor (usually crystal in Mhz) <= 63
unsigned int n = 336; // (freq+4000000) / (m*1000000); // 192-432
unsigned int p = 2; // CPU divisor 2,4,6,8
unsigned int q = 7; // USB divisor (must be 48Mhz) 4-15
int diff = -1;
unsigned int im,in,ip;
// Exhaustively search for best clock combo
for (im=1;im<=63;im++) {
unsigned int mfreq = 8000000 / im;
for (in=192;in<=432;in++) {
unsigned int nfreq = mfreq * in;
unsigned int iq = nfreq / 48000000;
if (iq<4) iq=4;
if (iq>15) iq=15;
unsigned int qfreq = nfreq / iq;
int jshSetSystemClockPClk(JsVar *options, const char *clkName) {
JsVar *v = jsvObjectGetChild(options, clkName, 0);
JsVarInt i = jsvGetIntegerAndUnLock(v);
if (i==1) return RCC_HCLK_Div1;
if (i==2) return RCC_HCLK_Div2;
if (i==4) return RCC_HCLK_Div4;
if (i==8) return RCC_HCLK_Div8;
if (i==16) return RCC_HCLK_Div16;
if (v) {
jsExceptionHere(JSET_ERROR, "Invalid %s value %d", clkName, i);
return -2;
}
return -1;
}
for (ip=2;ip<=8;ip+=2) {
unsigned int pfreq = nfreq / ip;
int d = (int)pfreq - (int)freq;
if (d<0) d=-d;
if (qfreq!=48000000) d += 1000000000; // discourage choosing bad USB clocks!
if (diff<0 || d<=diff) {
m = im;
n = in;
p = ip;
q = iq;
diff = d;
}
}
unsigned int jshSetSystemClock(JsVar *options) {
// see system_stm32f4xx.c for clock configurations
#ifdef STM32F4
unsigned int m = (unsigned int)jsvGetIntegerAndUnLock(jsvObjectGetChild(options, "M", 0));
unsigned int n = (unsigned int)jsvGetIntegerAndUnLock(jsvObjectGetChild(options, "N", 0));
unsigned int p = (unsigned int)jsvGetIntegerAndUnLock(jsvObjectGetChild(options, "P", 0));
unsigned int q = (unsigned int)jsvGetIntegerAndUnLock(jsvObjectGetChild(options, "Q", 0));
if (!IS_RCC_PLLM_VALUE(m)) {
jsExceptionHere(JSET_ERROR, "Invalid PLL M value %d", m);
return 0;
}
if (!IS_RCC_PLLN_VALUE(n)) {
jsExceptionHere(JSET_ERROR, "Invalid PLL N value %d", n);
return 0;
}
if (!IS_RCC_PLLP_VALUE(p)) {
jsExceptionHere(JSET_ERROR, "Invalid PLL P value %d", p);
return 0;
}
if (!IS_RCC_PLLQ_VALUE(q)) {
jsExceptionHere(JSET_ERROR, "Invalid PLL Q value %d", q);
return 0;
}
uint8_t latency = 255;
JsVar *v = jsvObjectGetChild(options, "latency", 0);
if (v) {
latency = (uint8_t)jsvGetIntegerAndUnLock(v);
if (!IS_FLASH_LATENCY(latency)) {
jsExceptionHere(JSET_ERROR, "Invalid flash latency %d", latency);
return 0;
}
}
int pclk1 = jshSetSystemClockPClk(options, "PCLK1");
if (pclk1<-1) return 0;
int pclk2 = jshSetSystemClockPClk(options, "PCLK2");
if (pclk2<-1) return 0;
jsiConsolePrintf("CLK M=%d N=%d P=%d Q=%d\n",m,n,p,q);
jshTransmitFlush();
int i;for (i=0;i<100;i++) jshDelayMicroseconds(1000);
if (freq>84000000) FLASH_SetLatency(FLASH_Latency_6);
else FLASH_SetLatency(FLASH_Latency_2);
// Run off external clock - 8Mhz - while we configure everything
RCC_SYSCLKConfig(RCC_SYSCLKSource_HSE);
// set latency
if (latency!=255) FLASH_SetLatency(latency);
if (pclk1>=0) RCC_PCLK1Config(pclk1);
if (pclk2>=0) RCC_PCLK2Config(pclk2);
// update PLL
RCC_PLLCmd(DISABLE);
RCC_PLLConfig(RCC_PLLSource_HSE, m, n, p, q);
RCC_PLLCmd(ENABLE);
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) {}
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
SystemCoreClockUpdate();
// force recalculate of the timer speeds
SystemCoreClockUpdate();
#ifdef USE_RTC
jshResetRTCTimer();
hasSystemSlept = true;
#endif
return SystemCoreClock;
#else
return 0;
#endif
}